Need to add multiple null values from a binary file to string

I want to encrypt a exe(executable file). I am reading the file as binary but when i Store the data in string it skips the null values and the end result after decrypting is not same. someone please help so i can store null values in string so that i can encrypt and decrypt to get the same file.

1
2
  ifstream newfile("C:\\Users\\Admin\\Downloads\\AES128.exe", std::ios::in | std::ios::binary);
while (getline(newfile >> noskipws,tp))
I am reading the file as binary but when i Store the data in string it skips the null values and the end result after decrypting is not same

You have to be careful with how you handle the data. If you treat the data as anything other than bytes at any point, you'll color the data everything will screw up.

In this case, you want to use newfile.read() and newfile.write() rather than the >> and << operators.

You can find lots of examples of read(), but here's one:

https://cplusplus.com/reference/istream/istream/read/


Personally, I found that AES encryption was extremely easy using C#. C# has a dedicated library to helping you do AES encryption.
As a test to write and read std::string containing null chars to/from a file, consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>
#include <fstream>

using namespace std::string_literals;

int main() {
	const std::string str { "the\0quick\0\0brown fox"s };
	const std::string str1 { "jumps\0over\0the lazy\0dog"s };

	std::cout << str << '\n' << str1 << '\n';

	{
		std::ofstream out("nulltest.dat", std::ios::binary);

		out << str << '\n' << str1 << '\n';
	}

	{
		std::ifstream in("nulltest.dat", std::ios::binary);
		std::string read;

		std::getline(in, read);

		if (read == str)
			std::cout << "str Read same\n";
		else
			std::cout << "str NOT read same\n";

		std::getline(in, read);

		if (read == str1)
			std::cout << "str1 Read same\n";
		else
			std::cout << "str1 NOT read same\n";
	}
}



the quick  brown fox
jumps over the lazy dog
str Read same
str1 Read same


Note that as str and str1 contains a null, they need to be specified as type string - otherwise str and str1 end at the first null as the string is treated as a c-null-terminated string!

Also note that getline() will terminate on a '\n' and will extract and discard this. If you're trying to read binary data that might include a '\n' (or any other char specified to getline() as a delimiter) then you need to deal with this in the reading code. As stated above, the common method of reading from a binary file is to use .read() - but this requires that you specify the number of chars to read. If the size of the file is such that it can all be read into memory, then you can read the whole file into a std::string as binary.

Using .read(), consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <string>
#include <fstream>

using namespace std::string_literals;

int main() {
	const std::string str { "the\0quick\0\0brown fox"s };
	const std::string str1 { "jumps\0over\0the lazy\0dog"s };

	std::cout << str << '\n' << str1 << '\n';

	{
		std::ofstream out("nulltest.dat", std::ios::binary);

		out << str << '\n' << str1 << '\n';
	}

	{
		std::ifstream in("nulltest.dat", std::ios::binary);

		in.seekg(0, std::ios_base::end);

		const auto sz { in.tellg() };

		in.clear();
		in.seekg(0);

		std::string read(sz, 0);

		in.read(read.data(), sz);
		std::cout << read << '\n';
	}
}



the quick  brown fox
jumps over the lazy dog
the quick  brown fox
jumps over the lazy dog

Last edited on
I think it is not a very good idea to read a binary format, such as an EXE file (PE format), into a string.

https://en.wikipedia.org/wiki/Portable_Executable#Technical_details

Even encryption libraries/functions usually operate on byte sequences, not characters strings.

For binary data, it is better to use an std::vector of type uint8_t (byte). You can try something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define CHUNK_SIZE 1024U

std::vector<uint8_t> read_file(const char *const file_name)
{
	std::vector<uint8_t> contents;
	uint8_t temp[CHUNK_SIZE];
	std::ifstream in_file(file_name, std::ios::in | std::ios::binary);
	while (in_file.good())
	{
		in_file.read(reinterpret_cast<char*>(temp), CHUNK_SIZE);
		if (!in_file.bad())
		{
			contents.insert(contents.end(), temp, temp + file.gcount());
		}
	}
	return contents;
}
Last edited on
1
2
> std::ifstream in("nulltest.dat", std::ios::binary);
> in.seekg(0, std::ios_base::end);


This may not be a good idea.
A binary stream need not meaningfully support fseek calls with a whence value of SEEK_END.
https://cplusplus.com/forum/windows/284140/#msg1230397


A portable way to do this is to first write the size of the string, and then write the characters in the string. Note that if the file is to be created on one machine and then read back from a different machine, byte-ordering would have to be taken care of.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <string>
#include <vector>
#include <fstream>

std::ostream& write_string( std::ostream& stm, const std::string& str )
{
    // write the size of the string (number of characters in the string)
    const std::size_t nbytes = str.size() ;
    stm.write( reinterpret_cast<const char*>( std::addressof(nbytes) ), sizeof(nbytes) ) ;

    // write the characters in the string
    return nbytes ? stm.write( str.data(), nbytes ) : stm ;
}

std::istream& read_string( std::istream& stm, std::string& str )
{
    // read the size of the string (number of characters in the string)
    std::size_t nbytes = 0 ;
    if( stm.read( reinterpret_cast<char*>( std::addressof(nbytes) ), sizeof(nbytes) ) )
    {
        // resize the string and read the characters into the string
        str.resize(nbytes) ;
        if(nbytes) stm.read( const_cast<char*>( str.data() ), nbytes ) ;
    }

    return stm ;
}

std::ostream& write_strings( std::ostream& stm, const std::vector<std::string>& strings )
{
    for( const std::string& str : strings ) write_string( stm, str ) ;
    return stm ;
}

std::vector<std::string> read_strings( std::istream& stm )
{
    std::vector<std::string> strings ;

    std::string str ;
    while( read_string( stm, str ) ) strings.push_back( std::move(str) ) ;

    return strings ;
}

int main()
{
    const std::vector<std::string> strings =
    {
        "I\0 want\0 to\0 encrypt\0 a\0 exe(executable\0 file).",
        "",
        "I\0 am\0 reading\0 the\0 file\0 as\0 binary\0 but\0 when\0 i\0 Store\0 the\0 data\0 in\0 string",
        "",
        "\0\0\0\0",
        "it\0 skips\0 the\0 null\0 values\0 and\0 the\0 end\0 result\0 after\0 decrypting\0 is\0 not\0 same."
    };

    const std::string file_name = "test_file.dat" ;

    {
        std::ofstream test_file( file_name, std::ios::binary ) ;
        write_strings( test_file, strings ) ;
    }

    {
        std::ifstream test_file( file_name, std::ios::binary ) ;
        std::cout << ( strings == read_strings(test_file) ? "ok\n" : "failed\n" ) ;
    }
}

http://coliru.stacked-crooked.com/a/723f3ae0b9af4fb2
Reading a binary .exe file into a std::string like this works OK for me on Windows/VS (without error testing):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <string>
#include <fstream>

using namespace std::string_literals;

int main() {
	std::ifstream in("test65.exe", std::ios::binary);

	in.seekg(0, std::ios_base::end);

	const auto sz { in.tellg() };

	std::cout << "size: " << sz << '\n';

	in.clear();
	in.seekg(0);

	std::string read(sz, 0);

	in.read(read.data(), sz);

	std::ofstream out("test65.dat", std::ios::binary);

	out.write(read.data(), read.size());
}



size: 30720

c:\MyProgs>comp test65.exe test65.dat
Comparing test65.exe and test65.dat...
Files compare OK

Compare more files (Y/N) ? n

Last edited on
Topic archived. No new replies allowed.