Simple XoR Encryption
Introduction to XOR Encryption
XOR encryption is a very simple algorithm to hide data. The idea is that you take some data you want to protect and use a secret key to transform it into something that only someone with the same key can understand.
A good analogy is a locked box: to see what’s inside, you need the right key. Without the key, the contents stay hidden.
For example, if our data is the letter A
, and our secret key is the letter K
, then applying the XOR operation transforms A
into \n
. Here, \n
is the encrypted data, and A
is the original plain text.
XOR is also called a symmetric encryption method, because the same process is used both to encrypt and to decrypt. If you apply XOR with the same key again, you recover the original data. In other words:
A XOR K = \n
\n XOR K = A
XOR at the Bit Level
XOR encryption works at the bit level. A byte is simply a group of 8 bits, so when we XOR two bytes, the operation is applied bit by bit.
- If the data bit is
0
and the key bit is0
, the result is0
. - If the data bit is
1
and the key bit is0
, the result is1
. - If the data bit is
0
and the key bit is1
, the result is1
. - If the data bit is
1
and the key bit is1
, the result is0
.
Another way to say it: if the bits are the same, the result is 0
. If the bits are different, the result is 1
.
For example, let’s XOR the letter 'A' with the letter 'K':
Plain byte: 01000001 ('A')
Key byte: 01001011 ('K')
XOR result: 00001010 ('\n')
This shows that XOR is applied bit by bit, and each byte is just 8 XOR operations happening at once.
- Plaintext:
A
(binary01000001
) - Key:
K
(binary01001011
) - Encrypt:
01000001 ^ 01001011 = 00001010
Now if we take the result 00001010
and apply XOR with the same key again:
00001010 ^ 01001011 = 01000001 (A)
We get back the original. That’s why XOR is called symmetric: the same operation with the same key both encrypts and decrypts.
Simple XoR Encryption
XoR Encryption in C
#include <stdio.h>
// encrypt "test"
unsigned char code[] = "test";
int main() {
char key = 's';
int i = 0;
for (i; i < sizeof(code); i++){
printf("\\x%02x", code[i] ^ key);
}
}
1. THE ARRAY - unsigned char code[] = "test";
- This makes a byte array (unsigned char = 1 byte).
- "test" in C is stored as:
0x74 0x65 0x73 0x74 0x00 't' 'e' 's' 't' '\0' (string terminator)
So "sizeof(code) = 5", not "4" (because of the extra "\0").
2. The Encryption Key - char key = 's';
- This is your encryption key — a single character.
- 's' is ASCII 0x73.
3. The loop
for (i; i < sizeof(code); i++){
printf("\\x%02x", code[i] ^ key);
}
- "i" starts at 0 and runs until "sizeof(code) - 1".
- Each time, it takes a byte from code[i] and XORs it with the key.
- ^ = XOR operator in C.
- printf("\\x%02x", …) prints the result in hex, formatted like "\\x??".
4. The output
Let’s actually work it out:
- 't' = 0x74, 's' = 0x73 → 0x74 ^ 0x73 = 0x07
- 'e' = 0x65, 's' = 0x73 → 0x65 ^ 0x73 = 0x16
- 's' = 0x73, 's' = 0x73 → 0x73 ^ 0x73 = 0x00
- 't' = 0x74, 's' = 0x73 → 0x74 ^ 0x73 = 0x07
- '\0' = 0x00, 's' = 0x73 → 0x00 ^ 0x73 = 0x73
\x07\x16\x00\x07\x73
Simple XoR Decryption
XoR Decryption in C
#include <stdio.h>
// decrypt an encrypted "test"
unsigned char code[] = "\x07\x16\x00\x07\x73";
int main() {
char key = 's';
int i = 0;
for (i; i < sizeof(code) - 1; i++) {
code[i] = code[i] ^ key;
}
printf("Decrypted Code is: %s", code);
}
1. The data is already encrypted
unsigned char code[] = "\x07\x16\x00\x07\x73";
- Instead of "test", the array now holds the XOR-ed bytes.
- These bytes were produced earlier by XOR’ing "test" with the key 's' (ASCII 0x73).
2. The loop “undoes” the XOR
for (i; i < sizeof(code) - 1; i++) {
code[i] = code[i] ^ key;
}
- Each byte is XOR’ed with the same key ('s').
- Because XOR is symmetric, this recovers the original text.
- The - 1 in sizeof(code) - 1 skips the trailing "\0" (string terminator), so it stays intact.
3. Print as a string
printf("Decrypted Code is: %s", code);
- After the loop, code holds the original "test" plus the "\0" terminator.
- Printing with "%s" now works as expected.
Executing the Encrypted Payload
Same Encryption code and the a function to execute
#include <stdio.h>
#include <windows.h>
// encrypt "test"
unsigned char code[] = "test";
int main() {
char key = 's';
int i = 0;
for (i; i < sizeof(code); i++){
code[i] = code[i]^key;
}
void *exec = VirtualAlloc(0, sizeof code, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, code, sizeof code);
((void(*)())exec)();
return 0;
}
1. VOID *exec
void *exec
- void * in C means a generic pointer — it can point to anything, but you don’t yet know the type of the thing it points to.
- Here, it’s being used to store the address of some memory you’ll allocate.
2. Allocating Memory Region
void *exec = VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- VirtualAlloc(...) (Windows API) reserves a chunk of memory and returns a pointer to it.
- So after this line, exec holds the address of the newly allocated memory region.
- reserves/commits memory that is both writable and executable (RWX). The idea is to make a region you can fill with bytes and then run them
3. Next, we copy our encrypted code into that memory buffer
memcpy(exec, code, sizeof(code));
Copy the contents of your code array into that executable buffer.
4. VOID
((void (*)(void))exec)();
- exec is a void* → You got it from VirtualAlloc(...). It points to a block of memory you allocated.
- Cast exec to a function pointer → (void (*)(void))exec tells the compiler: “treat this address as a pointer to a function that takes no arguments and returns nothing.”
- Call it → The trailing () invokes that “function,” i.e., it jumps to the first byte of the allocated memory and starts executing the machine code there.
So, in one line: cast the allocated address to a callable function pointer, then call it.