jhon dev
jhon dev

Reputation: 13

Read a binary (exe,zip etc..) file into a char*, c++

I'm trying to read a file and put bytes in a byte buffer, but when i try to read exe or zip file not all bytes are loaded in the buffer. my function:

char* read_file_bytes(const string &name) {
   FILE *img = fopen(name.c_str(), "rb");
   fseek(img, 0, SEEK_END);
   unsigned long filesize = ftell(img);
   char *buffer = (char*)malloc(sizeof(char)*filesize);
   rewind(img);
   fread(buffer, sizeof(char), filesize, img);
   return buffer;
}

the piece of code that check the buffer:

char* bytes = read_file_bytes(path);
for(int i = 0; i < strlen(bytes); i++)
    cout << hex << (unsigned int)(bytes[i]);

Upvotes: 1

Views: 712

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595295

strlen() is designed for text characters, not for binary bytes. It stops counting when it encounters a nul char (0x00), which binary data is likely to contain.

Your read_file_bytes() function knows how many bytes it reads in. You need to return that number to the caller, eg:

typedef unsigned char byte;

byte* read_file_bytes(const std::string &name, unsigned long &filesize)
{
    filesize = 0;

    FILE *img = fopen(name.c_str(), "rb");
    if (!img)
        return NULL;

    if (fseek(img, 0, SEEK_END) != 0)
    {
        fclose(img);
        return NULL;
    }

    long size = ftell(img);
    if (size == -1L)
    {
        fclose(img);
        return NULL;
    }

    byte *buffer = static_cast<byte*>(std::malloc(size));
    if (!buffer)
    {
        fclose(img);
        return NULL;
    }

    rewind(img);
    if (fread(buffer, 1, size, img) < size)
    {
        free(buffer);
        close(img);
        return NULL;
    }

    fclose(img);

    filesize = size;
    return buffer;
}
unsigned long filesize;
byte* bytes = read_file_bytes(path, filesize);

for(unsigned long i = 0; i < filesize; ++i)
    std::cout << std::hex << static_cast<unsigned int>(bytes[i]);

free(bytes);

Note that this approach is very C-ish and error prone. A more C++ approach would look like this instead:

using byte = unsigned char;
// or, use std::byte in C++17 and later...

std::vector<byte> read_file_bytes(const std::string &name)
{
    std::ifstream img;
    img.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    img.open(name.c_str(), std::ifstream::binary | std::ifstream::ate);

    std::ifstream::pos_type size = img.tellg();
    ifs.seekg(0, std::ios::beg);
    // or, use std::filesystem::file_size() instead...

    std::vector<byte> buffer(size);
    img.read(buffer.data(), size);

    return buffer;
}
std::vector<byte> bytes = read_file_bytes(path);
for(byte b : bytes)
    std::cout << std::hex << static_cast<unsigned int>(b);

Upvotes: 1

Related Questions