Thread: Issues with writing data to binary files

  1. #1
    Registered User
    Join Date
    Dec 2018
    Posts
    13

    Issues with writing data to binary files

    Im trying to write my custom file encrypter, but the outfile fp2 is empty so I'm doing something wrong here:

    Code:
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    #defineSIZE(a) sizeof(a) / sizeof(a[0])
    
    void encrypt(const char *fileName, const char *newFileName, const char *KWORD,
                 int K)
    {
      FILE *fp, *fp2;
      fp = fopen(fileName, "rb");
      fp2 = fopen(newFileName, "a+");
      if (!fp) {
        printf("\nError: Could not open the file %s\n", fileName);
      } else {
        printf("\nFile has been opened successfully\n");
    
        int block[8192], xorblock[8192];
        char banner[128];
    
        size_t allBytes, byteCount =
            fread(banner, sizeof(banner), sizeof(banner), fp);
        printf("BASE FILE BANNER: %s\n", banner);
        fseek(fp, 0, SEEK_SET);
    
        while (byteCount) {
          byteCount = fread(block, sizeof(block), sizeof(block), fp);
          allBytes += byteCount;
          printf("\rByte has been read: %zu\r", byteCount);
    
          for (size_t i = 0; i < SIZE(block); i++) {
            K = K % strlen(KWORD);
            volatile char kwordPos = KWORD[K];
            xorblock = block ^ (int) kwordPos;
            K++;
          }
          fwrite(xorblock, sizeof(xorblock), sizeof(xorblock), fp2);
        }
        printf("\nAll bytes read:%zu\n", allBytes);
    
      }
      fclose(fp);
      fclose(fp2);
    
      FILE *fp2read;
      fp2read = fopen(newFileName, "rb");
      char newBanner[128];
      fread(newBanner, sizeof(newBanner), sizeof(newBanner), fp2read);
      printf("\nNEW FILE BANNER: %s\n", newBanner);
    }
    
    int main()
    {
      const char *myFileName = "Example.txt";
      char newFileName[17] = "_NEW_";
      const char *keyWord = "Keyword";
      int Key = 'K';
    
      strcat(newFileName, myFileName);
      encrypt(myFileName, newFileName, keyWord, Key);
      printf("%s%zu\n", newFileName, strlen(newFileName));
      printf("\nAll done.Quitting now.\n");
      return 0;
    }
    fsanitizer is showing that something is crashing pretty badly:
    File has been opened successfullyBASE FILE BANNER: C plus plus


    All bytes read:0
    AddressSanitizer:DEADLYSIGNAL
    ================================================== ===============
    ==571==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f2229726005 bp 0x7ffe1b18abe0 sp 0x7ffe1b18a320 T0)
    ==571==The signal is caused by a READ memory access.
    ==571==Hint: address points to the zero page.
    AddressSanitizer:DEADLYSIGNAL
    AddressSanitizer: nested bug in the same thread, aborting.
    Last edited by Salem; 11-19-2021 at 09:37 PM. Reason: Removed crayola - AGAIN - Learn how to paste code without the mickey mouse

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,633
    You should open both files in binary mode "rb" and "wb".

    Key (a.k.a., K in encrypt) doesn't make sense. It's just an index mod strlen(KWORD). Why are you passing it in? Why would it start at 'K'? It should be a local starting at 0.

    I assume the banner business is just a peek at the first part of the file for testing.
    But you can't just print out the binary file's data as a string as you attempt to do at the end of encrypt.

    "volatile" is unlikely to be of use to you.

    The block (and xorblock) should be chars (bytes), not ints.
    You don't need a separate xorblock. You could just xor the original block.

    Your inner encrypt loop should be up to byteCount, not SIZE(block), since you may not have read an entire block, such as at the end of the file.

    When reading and writing the blocks, you can't make the size and count members of fread/fwrite both the size of the block! The size is the size of each element (they should be chars, so 1), while the count is the number of elements to try to read/write. So the count for the fwrite should just be byteCount.

    Try the following modification with:
    my_encrypt Example.txt out.bin keyword
    my_encrypt out.bin Example2.txt keyword
    Then see if Example2.txt is the same as Example.txt.
    You could also look at the binary data of out.bin (e.g., with hd out.bin on linux, or write your own little program to print out the hex values of the bytes).
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void encrypt(const char* fileName, const char* newFileName, const char* keyword)
    {
        FILE *fin = fopen(fileName, "rb");
        if (!fin)
        {
            fprintf(stderr, "Error: Could not open the file %s\n", fileName);
            exit(EXIT_FAILURE);
        }
     
        FILE *fout = fopen(newFileName, "wb");
     
        char block[512];
        size_t keylen = strlen(keyword); 
        size_t allBytes = 0, byteCount, k = 0;
     
        while ((byteCount = fread(block, 1, sizeof(block), fin)) > 0)
        {
            allBytes += byteCount;
            for (size_t i = 0; i < byteCount; ++i)
            {
                block[i] ^= keyword[k];
                k = (k + 1) % keylen;
            }
            fwrite(block, 1, byteCount, fout);
        }
        printf("Bytes read: %zu\n", allBytes);
     
        fclose(fin);
        fclose(fout);
    }
     
    int main(int argc, char **argv)
    {
        if (argc != 4)
        {
            fprintf(stderr, "Usage: my_encrypt INPUT_FILE OUTPUT_FILE KEYWORD\n");
            exit(EXIT_FAILURE);
        }
     
        encrypt(argv[1], argv[2], argv[3]);
     
        return 0;
    }
    Last edited by john.c; 11-19-2021 at 08:31 PM.
    A little inaccuracy saves tons of explanation. - H.H. Munro

  3. #3
    Registered User
    Join Date
    Dec 2018
    Posts
    13
    Hey. Thank you for answering and reviewing my code, you made really good points there and i've learn a lot.
    I mostly do specialize in scripting languagees so I terribly suck in algo and i don't know well asm so I hope it wasn't too much pain for your head, in python bytes are integers so I thought it'd same here.

    This 'K' was just simple additional layer of an encryption or more like placeholder for it, I think I will add some random bytes to the files based on this key in the final version.

    Everything works now, I have only some inconvenience with cross-compiling for windows on linux cause it can't find some files:
    $mingw32-gcc.exe encrypter.c -oEncrypterWIN -Wall -Wextra -pedantic -fsanitize=address -g3 -ld -lz --verbose
    (...)
    c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/bin/ld.exe: cannot find -ld
    c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/bin/ld.exe: cannot find -lz
    c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/bin/ld.exe: cannot find -lasan
    collect2.exe: error: ld returned 1 exit status
    So I could also use some additional tips on how to deal with it. Cheers!

  4. #4
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    As always, john.c codes are flawless. But I would insert two more checks on encrypt() - to see if both files are different and to see if the output file was opened. Consider the case where 'text.txt' is said to be the input and output file... Someone can think of testing only the filenames... But consider "./text.txt" and "text.txt" (different strings) or another combination of paths... Well, on Unix systems you can use stat() and check if st_ino are equal (hardlinks and same files have the same inode) -- this doesn't work on Windows! On Windows you can open the files with OpenFile() and use GetFileInformationByHandle() to check nFileIndexHigh and nFileIndexLow struct members:

    Code:
    // Gets the inode of a file (<0 if error or file doen't exist)
    long long getfile_ino( char *path )
    {
    #ifdef WIN32
      OFSTRUCT ofstruc;
      HFILE h;
      BY_HANDLE_FILE_INFORMATION fi;
    
      h = OpenFile( path, ofstruc, OF_READ );
    
      if ( h == NULL )
        return -1LL;
    
      if ( ! GetFileInformationByHandle( h, &fi ) ) 
      { 
        CloseFile( h );
        return -1LL;
      }
    
      CloseFile( h );
    
      return fi.nFileIndexLow + (long long)fi.nFileIndexHigh << 32;
    }
    #else
      struct stat st;
    
      if ( stat( path, &st ) )
        return -1LL;
      
      return st.st_ino;
    #endif
    }
    So, to check if both input and output file are the same:

    Code:
    long long ino1, ino2;
    
    ino1 = getfile_ino( path1 );
    ino2 = getfile_ino( path2 );
    if ( ino1 != -1 && ino1 == ino2 )
    { /* same file! */ }
    As for the check of openess of the output file, you should test for NULL after fopen, for fout...

    In case of success or error, encrypt() can return an int.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Am I writing binary data correctly
    By generaltso78 in forum C Programming
    Replies: 13
    Last Post: 03-06-2013, 03:14 PM
  2. Replies: 3
    Last Post: 09-30-2008, 12:10 AM
  3. Writing binary data to a file
    By zacs7 in forum C Programming
    Replies: 5
    Last Post: 10-24-2007, 04:00 PM
  4. Help with Reading and writing binary data
    By Yasir_Malik in forum C Programming
    Replies: 3
    Last Post: 12-12-2004, 09:24 AM
  5. writing to binary files....
    By jverkoey in forum C++ Programming
    Replies: 3
    Last Post: 02-09-2003, 09:58 PM

Tags for this Thread