0

UPDATE

I solved it with the answer that's marked as valid, but with one slight difference. I open the file using fopen(file, "r+b"), not fopen(file, "r+"). The b opens it in binary mode, and doesn't screw up the file.


I was doing a simple program which I called "fuzzer".
This is my code:

int main(int argc, char* argv[]){
    // Here go some checks, such as argc being correct, etc.
    // ...

    // Read source file
    FILE *fSource;
    fSource = fopen(argv[1], "r+");
    if(fSource == NULL){
        cout << "Can't open file!";
        return 2;
    }

    // Loop source file
    char b;
    int i = 0;
    while((b = fgetc(fSource)) != EOF){
        b ^= 0x13;
        fseek(fSource, i++, SEEK_SET);
        fwrite(&b, 1, sizeof(b), fSource);
    }

    fclose(fSource);
    cout << "Fuzzed.";

    return 0;
}

However, it doesn't work. Before, I used while(!feof), but it didn't work either, and I saw that it's not correct, so I changed it to (b = fgetc()) != EOF (I suppose it's correct, right?).

When I run it, it gets stuck on an endless loop, and it doesn't modify the original file, but rather appends tildes to it (and the file quickly increases its size, until I stop it). If I change the open mode from "a+" to "r+", it simply deletes the contents of the file (but it at least doesn't get stuck in an endless loop).

Note: I understand that this isn't any kind of obfuscation or encryption. I'm not trying to encode files, just practicing with C++ and files.

Community
  • 1
  • 1
rev
  • 1,861
  • 17
  • 27
  • I am unsure about this line `fwrite(&b, 1, sizeof(b), fSource);` In you code b is just a single char, not an array. When you call '&b' you are passing the address of b, where i am pretty sure you should just pass 'b'. – Elias May 10 '14 at 00:44
  • @Elias: wrong, look up the documentation for `fwrite`. It can write any number of characters from an address, including 1. – user253751 May 10 '14 at 00:45
  • 1
    AcidShout: what happens if you `fseek(fSource, i, SEEK_SET);` at the end of the while loop? – user253751 May 10 '14 at 00:46
  • @immibis absolutely nothing. Stays exactly like it was before – rev May 10 '14 at 00:49
  • 1
    It shouldn't really affect the correctness of your program, but if you're using `fgetc()` to read, why not use `fputc()` to write? Strictly, you need a positioning operation (such as `fseek()`) after the write (`fwrite()`) as well as after the read. It is also best to report errors to `cerr` rather than `cout`, and to include the name of the file in the message when you fail to open the file. – Jonathan Leffler May 10 '14 at 01:12
  • 1
    `char b;` needs to be `int b;`. If you're not sure why, then read the documentation of `fgetc`. – M.M May 10 '14 at 02:37

2 Answers2

2

This code worked for me when tested on an Ubuntu 12.04 derivative with GCC 4.9.0:

#include <iostream>
#include <stdio.h>
using namespace std;

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
      cerr << "Usage: " << argv[0] << " file\n";
      return 1;
    }

    FILE *fSource = fopen(argv[1], "r+");
    if (fSource == NULL)
    {
        cerr << "Can't open file: " << argv[1] << "\n";
        return 2;
    }

    int c;
    int i = 0;
    while ((c = fgetc(fSource)) != EOF)
    {
        char b = c ^ 0x13;
        fseek(fSource, i++, SEEK_SET);
        fwrite(&b, 1, sizeof(b), fSource);
        fseek(fSource, i, SEEK_SET);
    }

    fclose(fSource);
    cout << "Fuzzed: " << argv[1] << "\n";

    return 0;
}

It reports file names; it reports errors to standard error (cerr); it uses int c; to read the character, but copies that to char b so that the fwrite() works. When run on (a copy of) its own source code, the first time the output looks like gibberish, and the second time recovers the original.

This loop, using fputc() instead of fwrite(), also works without needing the intermediate variable b:

    while ((c = fgetc(fSource)) != EOF)
    {
        fseek(fSource, i++, SEEK_SET);
        fputc(c ^ 0x13, fSource);
        fseek(fSource, i, SEEK_SET);
    }

The use of an fseek() after the read and after the write is mandated by the C standard. I'm not sure whether that's the main cause of your trouble, but it could in theory be one of the issues.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Works! First, I've corrected my code to use `cerr` and proper output. Then, I tried using `fputc`, but nothing. Finally, I found out that I **must** use `fseek` before and after writing. My code now looks like [this](http://pastebin.com/01ZgMf17) Thank you! – rev May 10 '14 at 01:44
1

You need int b;. A char can never be EOF. The manual describes all this. All in all, something like this:

for (int b, i = 0; (b = fgetc(fSource)) != EOF; ++i)
{
    unsigned char x = b;
    x ^= 0x13;
    fseek(fSource, i, SEEK_SET);
    fwrite(&x, 1, 1, fSource);
    fseek(fSource, i + 1, SEEK_SET);
}

You should also open the file with mode "rb+", and seek between each read and write (thanks @Jonathan Leffler).

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I tried (and indeed, you are right, it's `int`, not `char`), but it still says "Fuzzed." with no results (aka the file remains untouched) – rev May 10 '14 at 00:49
  • 1
    You need a positioning operation after the write as well as after the read, because the C standard says so, and the code is mainly C code except for the error messages which are incorrectly reported to standard output (`cout`) rather than `cerr` or `clog`. – Jonathan Leffler May 10 '14 at 01:10
  • @JonathanLeffler: Interesting, I was looking around but couldn't see anything specific. It seems to me that both `fread` and `fwrite` just advance the offset by the amount of data they transferred. That's what the hook description in `fopencookie` says, too. So the final `fwrite` in the loop should leave the cursor at the next unread position...? – Kerrek SB May 10 '14 at 10:55
  • 1
    @KerrekSB: ISO/IEC 9899:2011 §7.21.5.3 **The `fopen` function**, ¶7 _When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the `fflush` function or to a file positioning function (`fseek`, `fsetpos`, or `rewind`), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of- file._ – Jonathan Leffler May 10 '14 at 14:43