fread into multiple variables - c

I have the following code:
// fp is a FILE opened in binary mode
unsigned char bit_depth;
unsigned char color_type;
unsigned char compression_method;
unsigned char filter_method;
unsigned char interlace_method;
fread(&bit_depth, 1, 1, fp);
fread(&color_type, 1, 1, fp);
fread(&compression_method, 1, 1, fp);
fread(&filter_method, 1, 1, fp);
fread(&interlace_method, 1, 1, fp);
I'd like to to all freads in one go, perhaps using something akin to a format string (but fscanf is only for text files, right?).
Any help is much appreciated, thanks!

Sounds like you just want a struct.
typedef struct info {
unsigned char bit_depth;
unsigned char color_type;
unsigned char compression_method;
unsigned char filter_method;
unsigned char interlace_method;
} info_t;
info_t my_info;
fread(&my_info, sizeof(info_t), 1, fp);
But, be careful about structure padding. If the file was written with just 5 bytes, you'll want to check in your debugger and make sure that this doesn't mess up your file pointer. Frequently, structs are aligned to 8-byte boundaries, so see what sizeof(info_t) evaluates to, and if necessary, put a pragma around the struct to affect its alignment.

Make an array with 5 elements and read it into the array, something like this:
unsigned char infos[5];
fread(infos, 1, 5, fp);
unsigned char bit_depth = infos[0];
/* etc. */
Or you could put it into a struct, but watch out for alignment. To remedy that you could prefix it with a #pragma pack directive, but that's less portable. As the elements are of the same type I recommend the array-approach.

Read the data into a buffer of sufficient size-:
#define READ_BUF_SZ 5
unsigned char readbuf[READ_BUF_SZ];
fread(readbuf, READ_BUF_SZ, 1, fp);
then assign the variables from the buffer
bit_depth = readbuf[0];
etc.

****** 30 chars needed *******
See
http://www.cplusplus.com/reference/cstdio/fread

Related

How to import information from PE_file as binary in C

I'm making PE file format analyzer in C language.
When I used Visual Studio, I could make it much easier.
BUT, I have tired to make it without Visual Studio to improve my C programing.
It was made by GCC(My laptop is macbook).
To analyze PE file format, as you know I have to use file pointer and read file as 'rb' type.
I did it and it looks like worked well.... I wanted to print first and second words("MZ") but It printed unkown(to me) numbers.
P.S I write little different code refer to previous one to smaller than before. But It print NULL.
It means that I wrote code really wrong... PLZ tell me which parts are wrong
#include <stdio.h>
#include <stdlib.h>
// struct to save info of PE_File format
typedef struct _IMAGE_DOS_HEADER
{
unsigned short e_magic;
unsigned short e_cblp;
unsigned short e_cp;
unsigned short e_crlc;
unsigned short e_cparhdr;
unsigned short e_minalloc;
unsigned short e_maxalloc;
unsigned short e_ss;
unsigned short e_sp;
unsigned short e_csum;
unsigned short e_ip;
unsigned short e_cs;
unsigned short e_lfarlc;
unsigned short e_ovno;
unsigned short e_res[4];
unsigned short e_oemid;
unsigned short e_oeminfo;
unsigned short e_res2[10];
unsigned long e_lfanew;
} DOS_HEADER;
// I use two different functions.
// First one was made to import PE File format info from file that I used.
// Another was made to print some imformation. I use some comment because I didn't realize those.
DOS_HEADER get_dos_header(FILE *fp, char* fp_buffer);
void print_data_of_structures(DOS_HEADER/*, NT_HEADER, FILE_HEADER, OPTIONAL_HEADER, DATA_DIRECTORY*/);
int main(void)
{
DOS_HEADER dos_header;
char file_path[1000];
printf("Please input your file path : ");
scanf("%[^\n]s", file_path);
FILE* fp = NULL;
fp = fopen(file_path, "rb");
if(fp == NULL)
printf("Unalbe to open file :/\n");
// To calculate file size.
long fp_size = 0;
fseek(fp, 0, SEEK_END);
fp_size = ftell(fp);
rewind(fp);
// Declare to use as buffer
char* fp_buffer = malloc(sizeof(char) * (fp_size + 1));
// Use function to save analyzed information.
DOS_HEADER info_dos_header = get_dos_header(fp, fp_buffer);
// Print information.
print_data_of_structures(info_dos_header);
fclose(fp);
free(fp_buffer);
return 0;
}
DOS_HEADER get_dos_header(FILE* fp, char* fp_buffer)
{
DOS_HEADER raw_info_dos_header = {0, };
// Read hex info from fp 2 byte 1 time and save at fp_buffer.
fseek(fp, 0, SEEK_SET);
fread(fp_buffer, 2, 1, fp);
raw_info_dos_header.e_magic = fp_buffer;
return raw_info_dos_header;
}
void print_data_of_structures(DOS_HEADER info_dos_header/*, NT_HEADER info_nt_header, FILE_HEADER info_file_header, OPTIONAL_HEADER info_optional_header, DATA_DIRECTORY info_data_directory*/)
{
// print info_dos_header
printf("Print DOS_HEADER\n");
// I tried several print format(for example %hu, %x, %d) But I coudn't "MZ" or any numbers has realation with "MZ".
printf("%s", info_dos_header.e_magic);
}
You have 2 big problems with your code: you are mixing types.
In DOS_HEADER all members (except the last one) are of type unsigned short.
That's important.
In get_dos_header you are doing
fread(fp_buffer, 2, 1, fp);
raw_info_dos_header.e_magic = fp_buffer;
which is wrong.
Don't use magic numbers, use sizeof to get the correct sizes
Check the return value of fread, specially if you are analyzing a binary
format, you must be sure that you've read exactly what you're expecting.
It's much easier to deal with fread when the size argument is 1, because
only when the size argument is 1 the return value of fread matches the
number of bytes read.
e_magic is a unsigned short, fp_buffer is a char*. This assignment is
not copying the contents pointed to by fp_buffer, you are storing an address
as if it were a unsigned short value. You need to copy the memory pointed to
by fp_buffer.
Also I don't see the point of you allocating a buffer of the size of the file
when you are reading chunk by chunk. It would be easier if you don't allocate a
buffer at all and use an array of a size larger than what you are going to read.
int get_dos_header(FILE* fp, DOS_HEADER *dos)
{
unsigned char buffer[8];
size_t ret = fread(buffer, 1, sizeof(dos->e_magic), fp);
if(ret != sizeof(dos->e_magic))
{
fprintf(stderr, "Invalid file size\n");
return 0;
}
memcpy(&dos->e_magic, buffer, sizeof(dos->e_magic));
// DO the others fread operation
...
return 1;
}
Then in main you can do this:
DOS_HEADER raw_info_dos_header = {0, };
if(get_dos_header(fp, &raw_info_dos_header) == 0)
{
fprintf(stderr, "failed to get the DOS header\n");
return 1;
}
...
The second problem is in print_data_of_structures:
printf("%s", info_dos_header.e_magic);
e_magic is a unsigned short, %s expects a pointer to char, it expects a
string. e_magic is definitively not a string. This yield undefined behaviour.
The printf should look like this:
printf("e_magic: %hu\n", info_dos_header.e_magic);
or
printf("e_magic: 0x%04hx\n", info_dos_header.e_magic);
to print it in hexadecimal format.
I also advice you to use pointers instead. Your struct is big and passing a
pointer to a function is much cheaper than passing a copy of a large struct to a
functions. So instead of
void print_data_of_structures(DOS_HEADER info_dos_header);
declare it as
void print_data_of_structures(DOS_HEADER *info_dos_header);
And also don't declare function that can fail (like get_dos_header) as
void, otherwise how do you tell the calling function that it failed? It's
better for such functions to return an int (1 for success, 0 for failure) and
expect a pointer to the struct where the information is stored (see how I
changed get_dos_header above.

Read 1 byte from file

I need to read a single byte from a file. Normally when reading, I use a char array as the buffer. However, I need to be able to perform binary operations with byte, which can't be done with a char array. Here's what I've got:
read(file, buffer, 1);
I've tried making buffer both a char and an int, but both give me warnings that it expects a void * argument. But of course, that doesn't allow for binary operations like &.
How can I store buffer such that I can perform binary operations on it?
You could use the function int fgetc (FILE *fp).
The return value will be an int value from 0 to 255 or EOF.
You can read into a char array as you (supposedly) know how to do, and then perform your binary operations on the first element of that array.
Binary operation can be performed on char data type with unsigned. Even if its an array binary operation can be done to the single element of the array.
for your reference.
ssize_t read(int fd, void *buf, size_t count);
buffer has to be address of the character or character array, in your single char is sufficient. If you want to use array use the element array[0] and perform operation on that.
As mentioned in the comments above
unsigned char c;
read(file, &c, 1);
typedef unsigned char uint8_t; /* Not necessary if you are using C99, i.e. <stdint.h>. */
uint8_t buffer[BUF_SIZE] = {0};
.
.
.
if (fread(buffer, sizeof(buffer[0]), 1, file) == 1)
{
buffer[0] ^= 0xFFu; /* Or any other bit-wise operation. */
}
Refer -
1. https://linux.die.net/man/3/fread
2. http://en.cppreference.com/w/c/types/integer

How to create a multiple null-terminated char array?

I have a char array and I want it to have a certain format, for example:
(first 4 bytes) block type
(next 4 bytes) error code
(next 32 bytes)
email address
(next 64 bytes) home address
(next 32 bytes) Full Name
and so forth. About 45 different fields that are padded with 0's to field size. I can use memcpy and advance the pointer each time by the field size but it seems like a tedious task and an ugly code. Maybe a more clever and elegant way to create such a format?
You can do such it using union together with struct:
#define BLOCK_TYPE_SIZE 4
#define ERROR_CODE_SIZE 4
#define EMAIL_ADDRESS_SIZE 32
#define HOME_ADDRESS_SIZE 64
#define FULL_NAME_SIZE 32
struct format_entry
{
char block_type[BLOCK_TYPE_SIZE];
char error_code[ERROR_CODE_SIZE];
char email_address[EMAIL_ADDRESS_SIZE];
char home_address[HOME_ADDRESS_SIZE];
char full_name[FULL_NAME_SIZE];
};
union format_union
{
char full_string[sizeof(struct format_entry)];
struct format_entry entry;
};
And then you can fill it in like these:
union format_union f;
memset (f.full_string, 0, sizeof f.full_string);
strcpy (f.entry.block_type, "TYPE");
strcpy (f.entry.error_code, "CODE");
strcpy (f.entry.email_address, "EM#AI.L");
strcpy (f.entry.home_address, "ADDR");
strcpy (f.entry.full_name, "NA ME");
strncpy(), despite its name, is not a "string" function
char data[136/* maybe add 1 for extra '\0' */] = {0}; // fill array with zeroes
strncpy(data, "block type", 4);
strncpy(data + 4, "error code", 4);
strncpy(data + 8, "email address", 32);
strncpy(data + 40, "home address ...", 64);
strncpy(data + 104, "Full Name even if it is very very long", 32);
To add to Nick's response, in C, but not C++, you can skip the union and directly zero the structure:
struct format_entry
{
char block_type[BLOCK_TYPE_SIZE];
char error_code[ERROR_CODE_SIZE];
char email_address[EMAIL_ADDRESS_SIZE];
char home_address[HOME_ADDRESS_SIZE];
char full_name[FULL_NAME_SIZE];
};
struct format_entry data;
memset( &data, 0, sizeof data ); /* zero-fill structure */
If you need to update just some of the fields later on you might also consider making your own fill routine that assures zero fill.
char *strncpy0( char *target, const char *source, size_t s )
{
memset( target, 0, s );
strncpy( target, source, s );
return target;
}
While the above code is safer for early users of C, to be more efficient you could calculate the number of bytes at the end of target that strncpy() will not touch, then just fill those bytes.
That's truly a tedious task. But it seems there is no better way.
Maybe you could conceal them into functions and never consider the ugly code inside.

Strange value when reading from binary file with fread() in C

I have an issue that I can't solve...
I used fwrite to write an int value (among other values that come from a struct) into a file that I've opened using fopen(file, "wb");
This worked fine, and when I view the contents of the file, I can see the HEX value of the int that I've "written" (and all the other values as well).
Next, I try to read the file. So, I open the file using fopen(file, "rb"); and start reading it using fread. The first int value is read correctly (e.g. 7) and all the other values after it - which belong to the first struct that I've written.
I then try to read the next struct and when I try to read the first int value, I receive a very strange number (e.g. 1140850688) but it should be 39 instead.
So the question is: Why is this happening? And how can I solve it?
My struct looks like this:
struct a {
int a1;
char* a2;
struct b* a3;
unsigned char a4;
int a5;
char* a6;
}
And the fread call looks like this: fread(&(tmpA->a1), sizeof(int), 1, file);
EDIT:
The fwrite part:
fwrite(&(tmpA->a1), sizeof(int), 1, file);
fwrite(tmpA->a2, sizeof(char), strlen(tmpA->a2), file);
fwrite(tmpA->a3, sizeof(struct b), 1, file);
fwrite(&(tmpA->a4), sizeof(unsigned char), 1, file);
fwrite(&(tmpA->a5), sizeof(int), 1, file);
fwrite(tmpA->a6, sizeof(char), strlen(tmpA->a6), file);
The fread is almost the same.
I suspect your problem is on this line
fwrite(tmpA->a3, sizeof(struct b), 1, file);
where you are writing a struct. This struct will have padding bytes, as per this wikipedia article which will offset the rest of your data. You will likely have to come up with a better way of writing that struct to memory. This page provides additional explanation.
You can check to see if this is indeed the problem by checking if a4 has the value you expect, or by comparing the size of the entire struct versus the sum of the sizes of its members.
Also, these lines look dangerous:
fwrite(tmpA->a2, sizeof(char), strlen(tmpA->a2), file);
fwrite(tmpA->a6, sizeof(char), strlen(tmpA->a6), file);
they say that the length of the string being printed may vary in your stored data. strlen will give you the length of your string without counting the terminating \0 so there is no good way to know where a string ends once you've written it. I can't imagine how you plan on reading the string back in.
If you wrote a struct from a file, you should read a struct back; trying to read an int is not going to work. However, it rarely, if ever, makes sense two write out as binary a struct that contains pointers. You should either make them arrays of fixed size, or serialize them separately from your struct.
EDIT : (in response to posted code) Most likely the problem has to do with writing an unknown number of characters in the second call of fwrite. When you serialize a C string, you should write out the number of characters before writing the characters themselves, so that when you read you'd know how much characters to allocate and to read back:
int len = strlen(tmpA->a2);
fwrite(&len, sizeof(int), 1, file);
fwrite(tmpA->a2, sizeof(char), len, file);
...
fread(&(tmpA->a1), sizeof(int), 1, file);
tmpA->a2 = malloc(tmpA->a1+1);
fread(tmpA->a2, sizeof(char), tmpA->a1, file);
tmpA->a2[tmpA->a1] = '\0';

Write pointer to file in C

I have a structure:
typedef struct student {
char *name;
char *surname;
int age;
} Student;
I need to write the structure into a binary file.
Here is my attempt :
Student *s = malloc(sizeof(*s));
I fill my structure with data and then I write the struct into the file with :
fwrite(s, sizeof(*s), 1, fp);
In my file, name and surname don't exist. Instead they have their respective address of their char * pointer.
How can I write a char * into a file instead of the address of the pointer ?
You need to dereference your pointer and write the parts of the struct. (You should not fwrite a struct directly, but rather encode its parts and write those:
Student *s = malloc(sizeof(*s));
s->name = "Jon";
s->surname = "Skeet";
s->age = 34;
// ....
fwrite(s->name, sizeof(char), strlen(s->name) + 1, fp);
fwrite(s->surname, sizeof(char), strlen(s->surname) + 1, fp);
//This one is a bit dangerous, but you get the idea.
fwrite(&(s->age), sizeof(s->age), 1, fp);
You will have to do some extra work to write out the data pointed to by the structure elements - probably just write it out element by element.
Alternatively change your structure to something like this:
typedef struct
{
char name[MAX_NAME_LEN];
char surname[MAX_SURNAME_LEN];
int age;
} Student;
if you want to directly write the structure as it is to a file you would want to define the sizes of name and surname rather than dynamically allocating it.
typedef structure student { char name[100]; char surname[100]; int age; }
else you will need to write each info in the structure one by one.
In C, you have to serialize structures (convert them to a series of bytes) manually. If you want the data you output to be readable by another machine, you have to take endianness account endianness.
Here's a simple example of serializing your structure (writing it to a file), disregarding endianness/differing word size and making the code unportable:
size_t length;
length = strlen(s->name) + 1;
fwrite(&length, sizeof(length), 1, fp);
fwrite(s->name, 1, length, fp);
length = strlen(s->surname) + 1;
fwrite(&length, sizeof(length), 1, fp);
fwrite(s->surname, 1, length, fp);
fwrite(&s->age, sizeof(s->age), 1, fp);
And to unserialize:
size_t length;
fread(&length, sizeof(length), 1, fp);
s->name = malloc(length);
fread(s->name, 1, length, fp);
fread(&length, sizeof(length), 1, fp);
s->surname = malloc(length);
fread(s->surname, 1, length, fp);
fread(&s->age, sizeof(s->age), 1, fp);
In a real application, you should check the validity of your input rather than blindly assuming the input is valid and trustworthy. Also, you need to decide what byte order you'll use to store your ints, size_ts, etc, and make sure you read/write them in the correct byte order by using bit shifting.
By the way, you may want to look at tpl, a simple binary serialization library for C.
You need to dereference s and individually write out each element of the structure to actually write thecontents to the file:
size_t nameCount = strlen(s->name) + 1;
fwrite(s->name, sizeof(char), nameCount, fp);
size_t surnameCount = strlen(s->surname) + 1;
fwrite(s->surname, sizeof(char), surnameCount, fp);
Note that anyway you won't be able to see the fields in a 'normal' form because you write it to a binary file. Try to open the file without the "b" in the mode argument of fopen (r+\ w\ a etc. instead of rb+\ wb\ ab).
You can see that when using fread (from the binary file, after opening it in "rb" mode you should get the structure fields as expected.
You'll have to write out the fields individually, as others have said. One thing which may help reading back the data is to write out the string lengths before each entry, eg.
size_t len;
len = strlen(s->name)+1;
fwrite(&len,sizeof(len),1,stream);
fwrite(s->name,sizeof(*(s->name)),len,stream);
...
Reading back is then a matter of
Student * s = malloc(sizeof(*s));
size_t len;
fread(&len,sizeof(len),1,stream);
s->name = malloc(len);
fread(s->name,sizeof(*(s->name)),len,stream);
...

Resources