After reading several white papers on cryptography and runtime PE crypters, I decided to write my own. It's very simple and only for educational purposes.
Here is the GitHub repo: https://github.com/Jyang772/XOR_Crypter
I have two questions.
Shared
. I have to right click it and select share with Nobody
. Does this have something to do with the File Access and Security Rights? I am using CreateFile()
and Readfile
to read and write the input and output files. http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
Here is my XOR implementation:
fs = byte size Rsize = byte size Should be the same.
Builder:
char cipher[] ="penguin";
for (int i = 0; i < fs; i++)
{
FB[i] ^= cipher[i % strlen(cipher)]; // Simple Xor chiper
}
Stub:
char cipher[] = "penguin";
for (int i = 0; i < Rsize; i++)
{
RData[i] ^= cipher[i % strlen(cipher)];
}
If I were to comment out the encryption function in the Builder and Stub, the crypted file runs fine. Uhh, except with the permissions error.
I'm also trying to include a options menu where the user can select the encryption method used. Perhaps I might have done something wrong there? The Builder.exe adds one byte containing the user's choice to the end of FB
buffer. Stub.exe reads that and determines which encryption method is used to decrypt the data.
XOR Encryption is an encryption method used to encrypt data and is hard to crack by brute-force method, i.e generating random encryption keys to match with the correct one.
To encrypt, we simply XOR a plaintext message M with our secret key K so that M⊕K = E. To decrypt we simply XOR the encrypted message E with the same key, E⊕K = M. Conveniently, this means the same operation (or program, in our case) can be used to both encipher and decipher (or encrypt and decrypt).
The XOR encryption algorithm is an example of symmetric encryption where the same key is used to both encrypt and decrypt a message. Symmetric Encryption: The same cryptographic key is used both to encrypt and decrypt messages.
The problem with XOR encryption is that for long runs of the same characters, it is very easy to see the password. Such long runs are most commonly spaces in text files. Say your password is 8 chars, and the text file has 16 spaces in some line (for example, in the middle of ASCII-graphics table).
First off, with XOR "encryption", your "encrypt" and "decrypt" functions should be the same:
void xor_crypt(const char *key, int key_len, char *data, int data_len)
{
for (int i = 0; i < data_len; i++)
data[i] ^= key[ i % key_len ];
}
You should be able to use this same function in both the "XOR Crypter" program as well as your "Stub" program.
It's not a very C++ style; ordinarily you'd use std::string
or std::vector
. For example:
void xor_crypt(const std::string &key, std::vector<char>& data)
{
for (size_t i = 0; i != data.size(); i++)
data[i] ^= key[ i % key.size() ];
}
Then in the program that calls this, you'd declare:
std::string key = "penguin";
and you'd read your file in like so:
std::vector<char> file_data; // With your current program, make this a global.
fs = GetFileSize(efile, NULL);
file_data.resize(fs); // set vector length equal to file size
// Note: Replace &( file_data[0] ) with file_data.data() if you have C++11 support
ReadFile(efile, (LPVOID)( &( file_data[0] )), fs, &bt, NULL);
if (fs != bt)
// error reading file: report it here.
Then you would simply encrypt with xor_crypt( key, file_data );
. To write the XOR-crypted data to your resource, I believe you'd call your existing function with:
// replace &( file_data[0] ) with file_data.data() if C++11
WriteToResources(output, 1, (BYTE *)&( file_data[0] ), file_data.size() );
I suspect the real issue is with the Windows APIs you're using. Does LoadResource
give you mutable data, or are you required to copy it? I don't know the Windows API, but I wouldn't be surprised if LoadResource
gives you a read-only copy.
If you do need to make your own copy in order to modify the resource, then in your "Stub" program recovering the XOR-crypted resource should look something like this:
std::vector<char> RData;
void Resource(int id)
{
size_t Rsize;
HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(1), RT_RCDATA);
HGLOBAL temp = LoadResource(NULL, hResource);
Rsize = SizeofResource(NULL, hResource);
RData.resize(RSize);
memcpy( (void*)&(RData[0]), temp, RSize ); // replace &RData[0] with RData.data() if C++11
}
and the decryption in your "Stub" should just be xor_crypt( key, RData );
.
I have one last thought. The biggest bug I see in your "Stub" program is this line:
switch (RData[strlen(RData)-1])
Once you've XOR-crypted your data, some of the bytes will become zero. The strlen()
function will not return the index of the last byte in your RData
as a result. And, there's a different, more subtle error: This returns the last byte of the string, not the last byte of the resource. I can't really see how this line was ever correct; rather, I suspect your program was working when encryption was disabled in spite of itself, by falling through to the default
of the switch-case.
If you really intend to distinguish between different types of data based on the last byte of the resource payload, then you really should just use the size returned by the Windows API to find that byte.
If you switch to using vector<char>
as I suggest above, then you can find that with RData.back()
. Otherwise, if you continue using char *
, then that byte would be RData[RSize - 1]
.
Depending on your content data, you write char option in allocated memory pointed by FB or after it (buffer overrun) in "C++ Builder/main.cpp" when calling strcat(FB, choice).
Fix: allocate enough space in FB for data + option char. As you are dealing with binary data you should not use string functions (ex: strcat).
FB = new char[fs + 1];
memcpy(FB +fs, option, 1); // copy the option at end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With