C++ Send a file containing \0 via sockets without accidental closure - c++

I serialize the file via the code beneath, and send it over winsocks, this works fine with textfiles, but when I tried to send a jpg, the string contains \0 as some of the character elements, so the sockets only send part of the string, thinking \0 is the end, i was considering replacing \0 with something else, but say i replace it with 'xx', then replace it back on the other end, what if the file had natural occurrences of 'xx' that get lost? Sure I could make a large, unlikely sequence, but that bloats the file.
Any help appreciated.
char* read_file(string path, int& len)
{
std::ifstream infile(path);
infile.seekg(0, infile.end);
size_t length = infile.tellg();
infile.seekg(0, infile.beg);
len = length;
char* buffer = new char[len]();
infile.read(buffer, length);
return buffer;
}
string load_to_buffer(string file)
{
char* img;
int ln;
img = read_file(file, ln);
string s = "";
for (int i = 1; i <= ln; i++){
char c = *(img + i);
s += c;
}
return s;
}

Probably somewhere in your code (that isn't seen in the code you have posted) you use strlen() or std::string::length() to send the data, and/or you use std::string::c_str() to get the buffer. This results in truncated data because these functions stop at \0.
std::string is not good to handle binary data. Use std::vector<char> instead, and remove the new[] stuff.

Related

Load shellcode from file to char* comes strange characters in end of text

I have a char array[] and is like following:
// MessageBox
char xcode[] = "\x31\xc9\x64\x8b\x41\x30\x8b\x40\xc\x8b\x70\x14\xad\x96\xad\x8b\x58\x10\x8b\x53\x3c\x1\xda\x8b\x52\x78\x1\xda\x8b\x72\x20\x1\xde\x31\xc9\x41\xad\x1\xd8\x81\x38\x47\x65\x74\x50\x75\xf4\x81\x78\x4\x72\x6f\x63\x41\x75\xeb\x81\x78\x8\x64\x64\x72\x65\x75\xe2\x8b\x72\x24\x1\xde\x66\x8b\xc\x4e\x49\x8b\x72\x1c\x1\xde\x8b\x14\x8e\x1\xda\x31\xc9\x53\x52\x51\x68\x61\x72\x79\x41\x68\x4c\x69\x62\x72\x68\x4c\x6f\x61\x64\x54\x53\xff\xd2\x83\xc4\xc\x59\x50\x51\x66\xb9\x6c\x6c\x51\x68\x33\x32\x2e\x64\x68\x75\x73\x65\x72\x54\xff\xd0\x83\xc4\x10\x8b\x54\x24\x4\xb9\x6f\x78\x41\x0\x51\x68\x61\x67\x65\x42\x68\x4d\x65\x73\x73\x54\x50\xff\xd2\x83\xc4\x10\x68\x61\x62\x63\x64\x83\x6c\x24\x3\x64\x89\xe6\x31\xc9\x51\x56\x56\x51\xff\xd0";
Then i had inserted all this content of variable above into a file (file with UTF-8 format and content without the "") and tried load this way:
ifstream infile;
infile.open("shellcode.bin", std::ios::in | std::ios::binary);
infile.seekg(0, std::ios::end);
size_t file_size_in_byte = infile.tellg();
char* xcode = (char*)malloc(sizeof(char) * file_size_in_byte);
infile.seekg(0, std::ios::beg);
infile.read(xcode, file_size_in_byte);
printf("%s\n", xcode); // << prints content of xcode after load from file
if (infile.eof()) {
size_t bytes_really_read = infile.gcount();
}
else if (infile.fail()) {
}
infile.close();
I'm seeing some strange characters in end of text see:
What is need to fix it?
The issue is that the printf format specifier "%s" requires that the string is null-terminated. In your case, the null-terminator just happens to be after those characters you're seeing, but nothing guarantees where the null is unless you put one there.
Since you're using C++, one way to print the characters is to use the write() function available for streams:
#include <iostream>
//...
std::cout.write(xcode, file_size_in_bytes);
The overall point is this -- if you have a character array that is not null-terminated and contains data, you must either:
Put the null in the right place before using the array in functions that look for the null-terminator or
Use functions that state how many characters to process from the character array.
The answer above uses item 2.

Problem writting vectors of strings into binary file

I'm serializing data into binary file using ofstream/ifstream. Data is divided in 2 vectors of strings, one for data names and other for data values, std::vector<std::string> dataNames, std::vector<std::string> dataValues.
I'm writting the data using this function:
void Data::SaveData(std::string path)
{
std::ofstream outfile(path, std::ofstream::binary);
outfile.write(reinterpret_cast<const char *>(&dataNames[0]), dataNames.size() * sizeof(std::string));
outfile.write(reinterpret_cast<const char *>(&dataValues[0]), dataValues.size() * sizeof(std::string));
outfile.close();
}
And reading it using:
bool Data::LoadData(std::string path)
{
bool ret = false;
std::ifstream file(path, std::ifstream::in | std::ifstream::binary);
if (file.is_open())
{
// get length of file:
file.seekg(0, file.end);
int length = file.tellg();
file.seekg(0, file.beg);
char * buffer = new char[length];
file.read(buffer, length);
if (file)
{
char* cursor = buffer;
uint32_t bytes = length / 2;
dataNames.resize(bytes / sizeof(std::string));
memcpy(dataNames.data(), cursor, bytes);
cursor += bytes;
dataValues.resize(bytes / sizeof(std::string));
memcpy(dataValues.data(), cursor, bytes);
delete[] buffer;
buffer = nullptr;
}
file.close();
ret = true;
}
return ret;
}
It works. I can write and read it correctly. Except if any of the strings in dataNames or dataValues has 16 chars or more.
Example of data using strings with less than 16 chars:
dataNames[0] = "Type"
dataNames[1] = "GameObjectCount"
dataValues[0] = "Scene"
dataValues[1] = "5"
data 15 chars
Example of data using strings with more than 16 chars:
dataNames[0] = "Type"
dataNames[1] = "GameObjectsCount" //Added a s. Now have 16 chars
dataValues[0] = "Scene"
dataValues[1] = "5"
data 16 chars
Here you can see that word "GameObjectsCount" doesn't appear and extrange characters are shown.
When reading this file the string is not valid. Sometimes it's empty, sometimes says "Error reading characters of string", sometimes is a radom letter...
Any idea?
Reinterpreting a vector in the manner you have above isn't correct.
outfile.write(reinterpret_cast<const char *>(&dataNames[0]), dataNames.size() * sizeof(std::string));
You don't know how the vector stores data (on the heap, etc..), and you can't assume that you can blindly cast the pointer and write whatever you see out to a file as a method to serialize the data. Furthermore, a std::string isn't necessarily an in-place character array of the size of the input. It's more likely a pointer to an object on the heap.
So, if you want to serialize the data in a vector or another stdlib type, you'll need to write a function to do that manually by iterating over the items and writing them in a properly delimited way.

Reading null characters into char array for hex conversion

I am writing a program which reads input from a text file and converts it to hexadecimal for later processing.
ifstream fin("input.txt"); //open file with ifstream
if(fin.is_open()){ //check if file is open
for(int i = 0; i<length; i++){ //int length for how many characters I need
fin.get(buffer[i]); //write into char array buffer[256]
}
}
else{
cout<<"Can't open file";
exit(0);
}
fin.close();
So everything is fine, it opens up nicely and gets exactly as many characters as I need from there, later I use a function to turn that into a hexadecimal string:
std::string string_to_hex(const std::string input)
{
static const char* const lut = "0123456789ABCDEF";
size_t len = input.length();
string output;
output.reserve(2 * len);
for (size_t i = 0; i < len; ++i)
{
const unsigned char c = input[i];
output.push_back(lut[c >> 4]);
output.push_back(lut[c & 15]);
}
return output;
}
which also works fine, it gives me the correct hexadecimal value for the characters that I get.
Now here is the problem: my file contains null characters (hexadecimal "00" characters aka '\0') which I need to read into my program and do a proper conversion so every null character in my array after conversion should look like "00". Every time I
try to convert null characters in my array they change into spaces (hexadecimal "20"), which ruins my data for processing later. Changing "20" into "00" is also not an option since the file contains real spaces that also need to be properly processed.
This is my first time working with null characters and I am absolutely confused on how to properly process them. All I know is that null characters are used at the end of a string or an array so when I print them out the program would know when to stop.
I do not know how I should do it properly. I tried opening the file differently using fopen, I tried copying my input.txt into an array differently. Maybe it copies it properly but the hex conversion doesn't work. Maybe I shouldn't send it as a string for conversion. I don't know. I also noticed that when I tried to copy paste a sample from the file to another file, all the null characters have been replaced by spaces, maybe it has something to do with this?

How to write NULL character '\0' to a text file

I have to write some data to a text file, and at the end of each output I have to append a NULL terminating character '\0'. Currently this is what I have come up so far. It works well for some inputs, however for some it sometimes write the whole text file with garbage value. I there a better way to do this?? In my program I have to write some data, store its location on file and use that for some operations. the next write operation starts at address = address + 500;
long int address = get_address();
void write_to_file()
{
fstream pFILE ("my file.txt");
char * buffer = new char [500];
cin.getline(buffer,500);
pFILE.seekp(address);
pFILE << buffer;
pFILE.seekp(address + strlen(buffer));
pFILE << '\0';
address += 500;
}
To write a '\0' to file:
fstream output_file("output_file.txt", ios::binary);
output_file.put('\0');
The ios::binary prevents the compiler or OS from translating the '\0'.

C++ Char pointer to char array

None of the posted answers I've read work, so I'm asking again.
I'm trying to copy the string data pointed to by a char pointer into a char array.
I have a function that reads from a ifstream into a char array
char* FileReader::getNextBytes(int numberOfBytes) {
char *buf = new char[numberOfBytes];
file.read(buf, numberOfBytes);
return buf;
}
I then have a struct :
struct Packet {
char data[MAX_DATA_SIZE]; // can hold file name or data
} packet;
I want to copy what is returned from getNextBytes(MAX_DATA_SIZE) into packet.data;
EDIT: Let me show you what I'm getting with all the answers gotten below (memcpy, strcpy, passing as parameter). I'm thinking the error comes from somewhere else. I'm reading a file as binary (it's a png). I'll loop while the fstream is good() and read from the fstream into the buf (which might be the data array). I want to see the length of what I've read :
cout << strlen(packet.data) << endl;
This returns different sizes every time:
8
529
60
46
358
66
156
After that, apparently there are no bytes left to read although the file is 13K + bytes long.
This can be done using standard library function memcpy, which is declared in / :
strcpy(packet.data, buf);
This requires file.read returns proper char series that ends with '\0'. You might also want to ensure numberOfBytes is big enough to accommodate the whole string. Otherwise you could possibly get segmentation fault.
//if buf not properly null terminated added a null char at the end
buf[numberofbytes] = "\0"
//copy the string from buf to struc
strcpy(packet.data, buf);
//or
strncpy(packet.data, buf);
Edit:
Whether or not this is being handled as a string is a very important distinction. In your question, you referred to it as a "string", which is what got us all confused.
Without any library assistance:
char result = reader.getNextBytes(MAX_DATA_SIZE);
for (int i = 0; i < MAX_DATA_SIZE; ++MAX_DATA_SIZE) {
packet.data[i] = result[i];
}
delete [] result;
Using #include <cstring>:
memcpy(packet.data, result, MAX_DATA_SIZE);
Or for extra credit, rewrite getNextBytes so it has an output parameter:
char* FileReader::getNextBytes(int numberOfBytes, char* buf) {
file.read(buf, numberOfBytes);
return buf;
}
Then it's just:
reader.getNextBytes(MAX_DATA_SIZE, packet.data);
Edit 2:
To get the length of a file:
file.seekg (0, ios::end);
int length = file.tellg();
file.seekg (0, ios::beg);
And with that in hand...
char* buffer = new char[length];
file.read(buffer, length);
Now you have the entire file in buffer.
strlen is not a valid way to determine the amount of binary data. strlen just reads until it finds '\0', nothing more. If you want to read a chunk of binary data, just use a std::vector, resize it to the amount of bytes you read from the file, and return it as value. Problem solved.