Author:
# we will use this function later in this notebook
import pandas as pd
import matplotlib
def hex_dump(data):
#Hex Dump
line_num = 0
line_width = 46
for byte_array in [data[i:i+16] for i in range(0, len(data), 16)]: #or use islice
s_hex = " ".join([f"{b:02x}" for b in byte_array])
s_ascii = "".join([chr(b) if 32 <= b <= 127 else "." for b in byte_array])
print(f"{line_num * 16:08x} {s_hex:<{line_width}} {s_ascii}")
line_num += 1
ciphertext = (b'\xA9\xA4\x6E\xFE\xF3\xFE\xFE\xFE\xF2\xFE\xFE\xFE\xFF\xFF\xFE\xFE'
b'\x46\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xBE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\x1E\xFE\xFE\xFE'
b'\xE8\xDF\x44\xE8\xFE\x42\xF5\x29\xDD\x46\xFD\xAA\x29\xDD\xA2\x96'
b'\x95\x83\xDE\x8E\x8C\x8F\x97\x8C\x9D\x89\xDE\x93\x9D\x88\x88\x8F'
b'\x82\xDE\x9C\x91\xDE\x8C\x81\x88\xDE\x95\x88\xDE\xB2\xAF\xA3\xDE'
b'\x89\x8F\x92\x91\xC8\xE9\xE9\xF4\xD2\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\x13\xFC\x59\xE2\x57\x93\x03\xB7\x57\x93\x03\xB7\x57\x93\x03\xB7'
b'\x48\xDB\x90\xB7\x40\x93\x03\xB7\x48\xDB\x8E\xB7\x29\x93\x03\xB7'
b'\x48\xDB\x87\xB7\x61\x93\x03\xB7\x7E\x51\x76\xB7\x4A\x93\x03\xB7'
b'\x57\x93\x0C\xB7\x23\x93\x03\xB7\x48\xDB\x85\xB7\x50\x93\x03\xB7'
b'\x48\xDB\x9D\xB7\x50\x93\x03\xB7\x48\xDB\x9C\xB7\x50\x93\x03\xB7'
b'\xAC\x95\x93\x96\x57\x93\x03\xB7\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xAE\xB1\xFE\xFE\xAA\xFD\xF1\xFE\xCF\x61\x16\xA7\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\x1E\xFE\xFC\xDD\xEB\xFD\xF5\xFE\xFE\x1C\xFE\xFE'
b'\xFE\x90\xFE\xFE\xFE\xFE\xFE\xFE\x2C\xC6\xFE\xFE\xFE\xEE\xFE\xFE'
b'\xFE\xFE\xFD\xFE\xFE\xFE\xFE\xEE\xFE\xEE\xFE\xFE\xFE\xFC\xFE\xFE'
b'\xF1\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xF1\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\x5E\xFD\xFE\xFE\xF2\xFE\xFE\x9A\x87\xFD\xFE\xFC\xFE\xBE\xFD'
b'\xFE\xFE\xEE\xFE\xFE\xEE\xFE\xFE\xFE\xFE\xEE\xFE\xFE\xEE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xEE\xFE\xFE\xFE\x8E\xC7\xFD\xFE\xAD\xFE\xFE\xFE'
b'\x52\xC9\xFD\xFE\x86\xFE\xFE\xFE\xFE\x8E\xFD\xFE\x42\xFD\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\x7E\xFD\xFE\x76\xEA\xFE\xFE\x1E\xFD\xFD\xFE\xDA\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\x56\xDC\xFD\xFE\xBE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFD\xFE\x56\xFD\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xC8\x82\x91\x86\x82\xFE\xFE\xFE'
b'\x49\x1D\xFE\xFE\xFE\xEE\xFE\xFE\xFE\x1C\xFE\xFE\xFE\xF2\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xDE\xFE\xFE\x9E'
b'\xC8\x8C\x92\x9D\x82\x9D\xFE\xFE\x3D\xC7\xFE\xFE\xFE\xFE\xFD\xFE'
b'\xFE\xC6\xFE\xFE\xFE\x10\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xBE\xFE\xFE\xBE\xC8\x92\x9D\x82\x9D\xFE\xFE\xFE'
b'\xBA\xCA\xFE\xFE\xFE\xBE\xFD\xFE\xFE\xEC\xFE\xFE\xFE\xD8\xFD\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xBE\xFE\xFE\x3E'
b'\xC8\x8C\x83\x8C\x93\xFE\xFE\xFE\x42\xFD\xFE\xFE\xFE\x8E\xFD\xFE'
b'\xFE\xFC\xFE\xFE\xFE\xCE\xFD\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xBE\xFE\xFE\xBE\xC8\x8C\x91\x8A\x8F\x93\xFE\xFE'
b'\x90\xE6\xFE\xFE\xFE\x7E\xFD\xFE\xFE\xE4\xFE\xFE\xFE\xCC\xFD\xFE')
print()
hex_dump(ciphertext)
print()
00000000 a9 a4 6e fe f3 fe fe fe f2 fe fe fe ff ff fe fe ..n............. 00000010 46 fe fe fe fe fe fe fe be fe fe fe fe fe fe fe F............... 00000020 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ................ 00000030 fe fe fe fe fe fe fe fe fe fe fe fe 1e fe fe fe ................ 00000040 e8 df 44 e8 fe 42 f5 29 dd 46 fd aa 29 dd a2 96 ..D..B.).F..)... 00000050 95 83 de 8e 8c 8f 97 8c 9d 89 de 93 9d 88 88 8f ................ 00000060 82 de 9c 91 de 8c 81 88 de 95 88 de b2 af a3 de ................ 00000070 89 8f 92 91 c8 e9 e9 f4 d2 fe fe fe fe fe fe fe ................ 00000080 13 fc 59 e2 57 93 03 b7 57 93 03 b7 57 93 03 b7 ..Y.W...W...W... 00000090 48 db 90 b7 40 93 03 b7 48 db 8e b7 29 93 03 b7 H...@...H...)... 000000a0 48 db 87 b7 61 93 03 b7 7e 51 76 b7 4a 93 03 b7 H...a...~Qv.J... 000000b0 57 93 0c b7 23 93 03 b7 48 db 85 b7 50 93 03 b7 W...#...H...P... 000000c0 48 db 9d b7 50 93 03 b7 48 db 9c b7 50 93 03 b7 H...P...H...P... 000000d0 ac 95 93 96 57 93 03 b7 fe fe fe fe fe fe fe fe ....W........... 000000e0 ae b1 fe fe aa fd f1 fe cf 61 16 a7 fe fe fe fe .........a...... 000000f0 fe fe fe fe 1e fe fc dd eb fd f5 fe fe 1c fe fe ................ 00000100 fe 90 fe fe fe fe fe fe 2c c6 fe fe fe ee fe fe ........,....... 00000110 fe fe fd fe fe fe fe ee fe ee fe fe fe fc fe fe ................ 00000120 f1 fe fe fe fe fe fe fe f1 fe fe fe fe fe fe fe ................ 00000130 fe 5e fd fe fe f2 fe fe 9a 87 fd fe fc fe be fd .^.............. 00000140 fe fe ee fe fe ee fe fe fe fe ee fe fe ee fe fe ................ 00000150 fe fe fe fe ee fe fe fe 8e c7 fd fe ad fe fe fe ................ 00000160 52 c9 fd fe 86 fe fe fe fe 8e fd fe 42 fd fe fe R...........B... 00000170 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ................ 00000180 fe 7e fd fe 76 ea fe fe 1e fd fd fe da fe fe fe .~..v........... 00000190 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ................ 000001a0 fe fe fe fe fe fe fe fe 56 dc fd fe be fe fe fe ........V....... 000001b0 fe fe fe fe fe fe fe fe fe fe fd fe 56 fd fe fe ............V... 000001c0 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ................ 000001d0 fe fe fe fe fe fe fe fe c8 82 91 86 82 fe fe fe ................ 000001e0 49 1d fe fe fe ee fe fe fe 1c fe fe fe f2 fe fe I............... 000001f0 fe fe fe fe fe fe fe fe fe fe fe fe de fe fe 9e ................ 00000200 c8 8c 92 9d 82 9d fe fe 3d c7 fe fe fe fe fd fe ........=....... 00000210 fe c6 fe fe fe 10 fe fe fe fe fe fe fe fe fe fe ................ 00000220 fe fe fe fe be fe fe be c8 92 9d 82 9d fe fe fe ................ 00000230 ba ca fe fe fe be fd fe fe ec fe fe fe d8 fd fe ................ 00000240 fe fe fe fe fe fe fe fe fe fe fe fe be fe fe 3e ...............> 00000250 c8 8c 83 8c 93 fe fe fe 42 fd fe fe fe 8e fd fe ........B....... 00000260 fe fc fe fe fe ce fd fe fe fe fe fe fe fe fe fe ................ 00000270 fe fe fe fe be fe fe be c8 8c 91 8a 8f 93 fe fe ................ 00000280 90 e6 fe fe fe 7e fd fe fe e4 fe fe fe cc fd fe .....~..........
Quick eyeballing (or frequency analysis) of the bytes above suggests that the file was possibly XOR encoded using 0xfe as XOR key. Let's XOR decode the encoded bytes above:
xor_decoded = list()
for c in ciphertext:
xor_decoded.append(c ^ 0xFE)
print()
hex_dump(xor_decoded)
print()
00000000 57 5a 90 00 0d 00 00 00 0c 00 00 00 01 01 00 00 WZ.............. 00000010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@....... 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 e0 00 00 00 ................ 00000040 16 21 ba 16 00 bc 0b d7 23 b8 03 54 d7 23 5c 68 .!......#..T.#\h 00000050 6b 7d 20 70 72 71 69 72 63 77 20 6d 63 76 76 71 k} prqircw mcvvq 00000060 7c 20 62 6f 20 72 7f 76 20 6b 76 20 4c 51 5d 20 | bo rv kv LQ] 00000070 77 71 6c 6f 36 17 17 0a 2c 00 00 00 00 00 00 00 wqlo6...,....... 00000080 ed 02 a7 1c a9 6d fd 49 a9 6d fd 49 a9 6d fd 49 .....m.I.m.I.m.I 00000090 b6 25 6e 49 be 6d fd 49 b6 25 70 49 d7 6d fd 49 .%nI.m.I.%pI.m.I 000000a0 b6 25 79 49 9f 6d fd 49 80 af 88 49 b4 6d fd 49 .%yI.m.I...I.m.I 000000b0 a9 6d f2 49 dd 6d fd 49 b6 25 7b 49 ae 6d fd 49 .m.I.m.I.%{I.m.I 000000c0 b6 25 63 49 ae 6d fd 49 b6 25 62 49 ae 6d fd 49 .%cI.m.I.%bI.m.I 000000d0 52 6b 6d 68 a9 6d fd 49 00 00 00 00 00 00 00 00 Rkmh.m.I........ 000000e0 50 4f 00 00 54 03 0f 00 31 9f e8 59 00 00 00 00 PO..T...1..Y.... 000000f0 00 00 00 00 e0 00 02 23 15 03 0b 00 00 e2 00 00 .......#........ 00000100 00 6e 00 00 00 00 00 00 d2 38 00 00 00 10 00 00 .n.......8...... 00000110 00 00 03 00 00 00 00 10 00 10 00 00 00 02 00 00 ................ 00000120 0f 00 00 00 00 00 00 00 0f 00 00 00 00 00 00 00 ................ 00000130 00 a0 03 00 00 0c 00 00 64 79 03 00 02 00 40 03 ........dy....@. 00000140 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................ 00000150 00 00 00 00 10 00 00 00 70 39 03 00 53 00 00 00 ........p9..S... 00000160 ac 37 03 00 78 00 00 00 00 70 03 00 bc 03 00 00 .7..x....p...... 00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000180 00 80 03 00 88 14 00 00 e0 03 03 00 24 00 00 00 ............$... 00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001a0 00 00 00 00 00 00 00 00 a8 22 03 00 40 00 00 00 ........."..@... 000001b0 00 00 00 00 00 00 00 00 00 00 03 00 a8 03 00 00 ................ 000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001d0 00 00 00 00 00 00 00 00 36 7c 6f 78 7c 00 00 00 ........6|ox|... 000001e0 b7 e3 00 00 00 10 00 00 00 e2 00 00 00 0c 00 00 ................ 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ............ ..` 00000200 36 72 6c 63 7c 63 00 00 c3 39 00 00 00 00 03 00 6rlc|c...9...... 00000210 00 38 00 00 00 ee 00 00 00 00 00 00 00 00 00 00 .8.............. 00000220 00 00 00 00 40 00 00 40 36 6c 63 7c 63 00 00 00 ....@..@6lc|c... 00000230 44 34 00 00 00 40 03 00 00 12 00 00 00 26 03 00 D4...@.......&.. 00000240 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 c0 ............@... 00000250 36 72 7d 72 6d 00 00 00 bc 03 00 00 00 70 03 00 6r}rm........p.. 00000260 00 02 00 00 00 30 03 00 00 00 00 00 00 00 00 00 .....0.......... 00000270 00 00 00 00 40 00 00 40 36 72 6f 74 71 6d 00 00 ....@..@6rotqm.. 00000280 6e 18 00 00 00 80 03 00 00 1a 00 00 00 32 03 00 n............2..
If you have ever dealt with PE files in hexadecimal/binary format you would be able to recognize that the bytes above resemble a PE header structure (albeit an obfuscated one). Let's walk the structure to see if we can glean any useful information that can help us with decoding the file (the structure of the encoded PE header and certain visible bytes above suggest that we are not dealing with a Pascal, Delphi or an uncommon file type hence we can make certain assumptions about the presumed/potential location of certain bytes in the PE header, more on that below). I have already highlighted some of the interesting fields that can assist with creating a character mapping table that maps the highlighted bytes to their potential plaintext values. If we can map a decent number of bytes to their plaintext values, we might be able to learn something about the encoding methodology used to encode the file.
Legend & Highlighted Offsets:
There are other fields in the PE header that we might be able to map with lower confidence if needed but for now let's use the character mappings we have discovered above to create a Pandas DataFrame named df_all_known_mappings
(where the Ciphertext column contains bytes from the encoded file shared by Florian, XOR_Decoded column contains the XOR 0xFE decoded bytes used above and Plaintext column contains the plaintext bytes we have discovered above):
d = {'Ciphertext': [0xFE, 0xFF, 0xEB, 0xE9, 0xDE, 0xDD, 0xD6, 0xC8, 0xBE, 0xBC, 0xB1, 0xAE, 0xAF, 0xAC, 0xAA, 0xA9, 0x9E,
0x9C, 0x9D, 0x96, 0x97, 0x8E, 0x8F, 0x8C, 0x8A, 0x88, 0x89, 0x86, 0x6E, 0x93, 0xF4, 0xF2, 0xF3, 0xF1,
0xD2, 0xB2, 0xB1, 0xA4, 0xA2, 0xA3, 0x95, 0x92, 0x91, 0x82, 0x83, 0x81, 0x3E, 0x29, 0x1E],
'XOR_Decoded': [0x00, 0x01, 0x15, 0x17, 0x20, 0x23, 0x28, 0x36, 0x40, 0x42, 0x4F, 0x50, 0x51, 0x52, 0x54, 0x57, 0x60,
0x62, 0x63, 0x68, 0x69, 0x70, 0x71, 0x72, 0x74, 0x76, 0x77, 0x78, 0x90, 0x6D, 0x0A, 0x0C, 0x0D, 0x0F,
0x2C, 0x4C, 0x4F, 0x5A, 0x5C, 0x5D, 0x6B, 0x6C, 0x6F, 0x7C, 0x7D, 0x7F, 0xC0, 0xD7, 0xE0],
'Plaintext': [0x00, 0xFF, 0x0B, 0x0D, 0x20, 0x21, 0x28, 0x2E, 0x40, 0x42, 0x45, 0x50, 0x4F, 0x52, 0x4C, 0x4D, 0x60,
0x62, 0x61, 0x68, 0x67, 0x70, 0x6F, 0x72, 0x6C, 0x6E, 0x6D, 0x78, 0x90, 0x63, 0x0A, 0x4, 0x3, 0x5,
0x24, 0x44, 0x45, 0x5A, 0x54, 0x53, 0x69, 0x64, 0x65, 0x74, 0x73, 0x75, 0xC0, 0xCD, 0xE0]}
df_all_known_mappings = pd.DataFrame(data=d).sort_values(by=['XOR_Decoded']).reset_index(drop=True)
df_all_known_mappings.style.format({'Ciphertext':"{:#x}", 'XOR_Decoded':"{:#x}", 'Plaintext':"{:#x}"})
Ciphertext | XOR_Decoded | Plaintext | |
---|---|---|---|
0 | 0xfe | 0x0 | 0x0 |
1 | 0xff | 0x1 | 0xff |
2 | 0xf4 | 0xa | 0xa |
3 | 0xf2 | 0xc | 0x4 |
4 | 0xf3 | 0xd | 0x3 |
5 | 0xf1 | 0xf | 0x5 |
6 | 0xeb | 0x15 | 0xb |
7 | 0xe9 | 0x17 | 0xd |
8 | 0xde | 0x20 | 0x20 |
9 | 0xdd | 0x23 | 0x21 |
10 | 0xd6 | 0x28 | 0x28 |
11 | 0xd2 | 0x2c | 0x24 |
12 | 0xc8 | 0x36 | 0x2e |
13 | 0xbe | 0x40 | 0x40 |
14 | 0xbc | 0x42 | 0x42 |
15 | 0xb2 | 0x4c | 0x44 |
16 | 0xb1 | 0x4f | 0x45 |
17 | 0xb1 | 0x4f | 0x45 |
18 | 0xae | 0x50 | 0x50 |
19 | 0xaf | 0x51 | 0x4f |
20 | 0xac | 0x52 | 0x52 |
21 | 0xaa | 0x54 | 0x4c |
22 | 0xa9 | 0x57 | 0x4d |
23 | 0xa4 | 0x5a | 0x5a |
24 | 0xa2 | 0x5c | 0x54 |
25 | 0xa3 | 0x5d | 0x53 |
26 | 0x9e | 0x60 | 0x60 |
27 | 0x9c | 0x62 | 0x62 |
28 | 0x9d | 0x63 | 0x61 |
29 | 0x96 | 0x68 | 0x68 |
30 | 0x97 | 0x69 | 0x67 |
31 | 0x95 | 0x6b | 0x69 |
32 | 0x92 | 0x6c | 0x64 |
33 | 0x93 | 0x6d | 0x63 |
34 | 0x91 | 0x6f | 0x65 |
35 | 0x8e | 0x70 | 0x70 |
36 | 0x8f | 0x71 | 0x6f |
37 | 0x8c | 0x72 | 0x72 |
38 | 0x8a | 0x74 | 0x6c |
39 | 0x88 | 0x76 | 0x6e |
40 | 0x89 | 0x77 | 0x6d |
41 | 0x86 | 0x78 | 0x78 |
42 | 0x82 | 0x7c | 0x74 |
43 | 0x83 | 0x7d | 0x73 |
44 | 0x81 | 0x7f | 0x75 |
45 | 0x6e | 0x90 | 0x90 |
46 | 0x3e | 0xc0 | 0xc0 |
47 | 0x29 | 0xd7 | 0xcd |
48 | 0x1e | 0xe0 | 0xe0 |
Closer inspection of all XOR_Decoded-Plaintext pairs above revealed that the distance between the numbers in each pair ranges between 0 and 10. To get a better idea of the distance/difference/sub values, we can subtract each XOR_Decoded value from its corresponding Plaintext value and store the resulting value under a new column named Sub:
df_all_known_mappings['Sub'] = (df_all_known_mappings['Plaintext'] - df_all_known_mappings['XOR_Decoded'] & 0xFF) # using 0xFF as mask, we can also use np.ubyte (or a list of ctypes c_ubyte's) to store bytes/chars as unsigned char
df_all_known_mappings[['XOR_Decoded', 'Plaintext', 'Sub']].style.format({'XOR_Decoded':"{:#x}", 'Plaintext':"{:#x}", 'Sub':"{:#x}"})
XOR_Decoded | Plaintext | Sub | |
---|---|---|---|
0 | 0x0 | 0x0 | 0x0 |
1 | 0x1 | 0xff | 0xfe |
2 | 0xa | 0xa | 0x0 |
3 | 0xc | 0x4 | 0xf8 |
4 | 0xd | 0x3 | 0xf6 |
5 | 0xf | 0x5 | 0xf6 |
6 | 0x15 | 0xb | 0xf6 |
7 | 0x17 | 0xd | 0xf6 |
8 | 0x20 | 0x20 | 0x0 |
9 | 0x23 | 0x21 | 0xfe |
10 | 0x28 | 0x28 | 0x0 |
11 | 0x2c | 0x24 | 0xf8 |
12 | 0x36 | 0x2e | 0xf8 |
13 | 0x40 | 0x40 | 0x0 |
14 | 0x42 | 0x42 | 0x0 |
15 | 0x4c | 0x44 | 0xf8 |
16 | 0x4f | 0x45 | 0xf6 |
17 | 0x4f | 0x45 | 0xf6 |
18 | 0x50 | 0x50 | 0x0 |
19 | 0x51 | 0x4f | 0xfe |
20 | 0x52 | 0x52 | 0x0 |
21 | 0x54 | 0x4c | 0xf8 |
22 | 0x57 | 0x4d | 0xf6 |
23 | 0x5a | 0x5a | 0x0 |
24 | 0x5c | 0x54 | 0xf8 |
25 | 0x5d | 0x53 | 0xf6 |
26 | 0x60 | 0x60 | 0x0 |
27 | 0x62 | 0x62 | 0x0 |
28 | 0x63 | 0x61 | 0xfe |
29 | 0x68 | 0x68 | 0x0 |
30 | 0x69 | 0x67 | 0xfe |
31 | 0x6b | 0x69 | 0xfe |
32 | 0x6c | 0x64 | 0xf8 |
33 | 0x6d | 0x63 | 0xf6 |
34 | 0x6f | 0x65 | 0xf6 |
35 | 0x70 | 0x70 | 0x0 |
36 | 0x71 | 0x6f | 0xfe |
37 | 0x72 | 0x72 | 0x0 |
38 | 0x74 | 0x6c | 0xf8 |
39 | 0x76 | 0x6e | 0xf8 |
40 | 0x77 | 0x6d | 0xf6 |
41 | 0x78 | 0x78 | 0x0 |
42 | 0x7c | 0x74 | 0xf8 |
43 | 0x7d | 0x73 | 0xf6 |
44 | 0x7f | 0x75 | 0xf6 |
45 | 0x90 | 0x90 | 0x0 |
46 | 0xc0 | 0xc0 | 0x0 |
47 | 0xd7 | 0xcd | 0xf6 |
48 | 0xe0 | 0xe0 | 0x0 |
A closer inspection of the values in the Sub column above revealed a repeating pattern consisting of 0xfe, 0xf8, 0xf6, and 0. Another notable observation is the relationship between the least-significant octet (LSO) of the values in the XOR_Decoded column and their corresponding values in the Sub column. To make the pattern in the Sub column more noticeable/visible, let's add a colormap to the dataframe (if you the colormap is not visible e.g. if you are viewing this on Github, try downloading a copy of this notebook or use the Binder link in the header of this notebook to launch an interactive version of this notebook):
df_all_known_mappings[['XOR_Decoded', 'Plaintext', 'Sub']].style.format({'Ciphertext':"{:#x}", 'XOR_Decoded':"{:#x}", 'Plaintext':"{:#x}", 'Sub':"{:#x}"}).background_gradient(cmap='plasma')
XOR_Decoded | Plaintext | Sub | |
---|---|---|---|
0 | 0x0 | 0x0 | 0x0 |
1 | 0x1 | 0xff | 0xfe |
2 | 0xa | 0xa | 0x0 |
3 | 0xc | 0x4 | 0xf8 |
4 | 0xd | 0x3 | 0xf6 |
5 | 0xf | 0x5 | 0xf6 |
6 | 0x15 | 0xb | 0xf6 |
7 | 0x17 | 0xd | 0xf6 |
8 | 0x20 | 0x20 | 0x0 |
9 | 0x23 | 0x21 | 0xfe |
10 | 0x28 | 0x28 | 0x0 |
11 | 0x2c | 0x24 | 0xf8 |
12 | 0x36 | 0x2e | 0xf8 |
13 | 0x40 | 0x40 | 0x0 |
14 | 0x42 | 0x42 | 0x0 |
15 | 0x4c | 0x44 | 0xf8 |
16 | 0x4f | 0x45 | 0xf6 |
17 | 0x4f | 0x45 | 0xf6 |
18 | 0x50 | 0x50 | 0x0 |
19 | 0x51 | 0x4f | 0xfe |
20 | 0x52 | 0x52 | 0x0 |
21 | 0x54 | 0x4c | 0xf8 |
22 | 0x57 | 0x4d | 0xf6 |
23 | 0x5a | 0x5a | 0x0 |
24 | 0x5c | 0x54 | 0xf8 |
25 | 0x5d | 0x53 | 0xf6 |
26 | 0x60 | 0x60 | 0x0 |
27 | 0x62 | 0x62 | 0x0 |
28 | 0x63 | 0x61 | 0xfe |
29 | 0x68 | 0x68 | 0x0 |
30 | 0x69 | 0x67 | 0xfe |
31 | 0x6b | 0x69 | 0xfe |
32 | 0x6c | 0x64 | 0xf8 |
33 | 0x6d | 0x63 | 0xf6 |
34 | 0x6f | 0x65 | 0xf6 |
35 | 0x70 | 0x70 | 0x0 |
36 | 0x71 | 0x6f | 0xfe |
37 | 0x72 | 0x72 | 0x0 |
38 | 0x74 | 0x6c | 0xf8 |
39 | 0x76 | 0x6e | 0xf8 |
40 | 0x77 | 0x6d | 0xf6 |
41 | 0x78 | 0x78 | 0x0 |
42 | 0x7c | 0x74 | 0xf8 |
43 | 0x7d | 0x73 | 0xf6 |
44 | 0x7f | 0x75 | 0xf6 |
45 | 0x90 | 0x90 | 0x0 |
46 | 0xc0 | 0xc0 | 0x0 |
47 | 0xd7 | 0xcd | 0xf6 |
48 | 0xe0 | 0xe0 | 0x0 |
As depicted above, there is a direct relationship between the least-significant octet (LSO) of the values in the XOR_Decoded column and their corresponding values in the Sub column. For example, if the LSO of the XOR_Decoded value is 4 (e.g. in 0x04, 0x14, 0x24, etc.) then by adding 0xF8 (or its signed char equivalent -0x8) to the XOR_Decoded value we can obtain its plaintext value!
Formula:
| LS Octet | Sub Value | |:--------: |:---------: | | 0 | 0 | | 1 | 0xfe | | 2 | 0 | | 3 | 0xfe | | 4 | 0xf8 | | 5 | 0xf6 | | 6 | 0xf8 | | 7 | 0xf6 | | -------- | --------- | | 8 | 0 | | 9 | 0xfe | | 0xa | 0 | | 0xb | 0xfe | | 0xc | 0xf8 | | 0xd | 0xf6 | | 0xe | Unknown | | 0xf | 0xf6 |
Since our known character mapping table does not contain an XOR_Decoded value that ends with the octet 0xe (i.e. LSO set to 0xe), the Sub value for 0xe is intentionally set to Unknown in the table above; however, using symmetry to our advantage, we can see that the Sub value of 0xe is most likely identical to that of 6 (i.e. 0xf8 from the table above).
At this point we can use the LSO-Sum mapping in the table above to generate a new dataframe that maps *all* possible XOR_Decoded bytes (i.e. 0x00-0xFF) to their corresponding Sum values and better yet, to their corresponding plaintext values!
lso_sub_mapping = {
0: 0,
1: 0xfe,
2: 0,
3: 0xfe,
4: 0xf8,
5: 0xf6,
6: 0xf8,
7: 0xf6,
8: 0,
9: 0xfe,
0xa: 0,
0xb: 0xfe,
0xc: 0xf8,
0xd: 0xf6,
0xe: 0xf8,
0xf: 0xf6
}
sub_bytes = list()
plaintext_bytes = list()
for xor_decoded_byte in range(0, 256):
sub_byte = lso_sub_mapping[xor_decoded_byte & 0xf]
sub_bytes.append(sub_byte)
plaintext_byte = (xor_decoded_byte + sub_byte ) & 0xFF # AND with 0xFF to discard carry out
plaintext_bytes.append(plaintext_byte)
d = {'XOR_Decoded': range(0, 256), 'Plaintext':plaintext_bytes, 'Sub': sub_bytes}
df_final = pd.DataFrame.from_dict(data=d,dtype='int').sort_values(by=['XOR_Decoded']).reset_index(drop=True)
df_final.style.format({'XOR_Decoded':"{:#x}", 'Plaintext':"{:#x}", 'Sub':"{:#x}"}).background_gradient(cmap='plasma')
XOR_Decoded | Plaintext | Sub | |
---|---|---|---|
0 | 0x0 | 0x0 | 0x0 |
1 | 0x1 | 0xff | 0xfe |
2 | 0x2 | 0x2 | 0x0 |
3 | 0x3 | 0x1 | 0xfe |
4 | 0x4 | 0xfc | 0xf8 |
5 | 0x5 | 0xfb | 0xf6 |
6 | 0x6 | 0xfe | 0xf8 |
7 | 0x7 | 0xfd | 0xf6 |
8 | 0x8 | 0x8 | 0x0 |
9 | 0x9 | 0x7 | 0xfe |
10 | 0xa | 0xa | 0x0 |
11 | 0xb | 0x9 | 0xfe |
12 | 0xc | 0x4 | 0xf8 |
13 | 0xd | 0x3 | 0xf6 |
14 | 0xe | 0x6 | 0xf8 |
15 | 0xf | 0x5 | 0xf6 |
16 | 0x10 | 0x10 | 0x0 |
17 | 0x11 | 0xf | 0xfe |
18 | 0x12 | 0x12 | 0x0 |
19 | 0x13 | 0x11 | 0xfe |
20 | 0x14 | 0xc | 0xf8 |
21 | 0x15 | 0xb | 0xf6 |
22 | 0x16 | 0xe | 0xf8 |
23 | 0x17 | 0xd | 0xf6 |
24 | 0x18 | 0x18 | 0x0 |
25 | 0x19 | 0x17 | 0xfe |
26 | 0x1a | 0x1a | 0x0 |
27 | 0x1b | 0x19 | 0xfe |
28 | 0x1c | 0x14 | 0xf8 |
29 | 0x1d | 0x13 | 0xf6 |
30 | 0x1e | 0x16 | 0xf8 |
31 | 0x1f | 0x15 | 0xf6 |
32 | 0x20 | 0x20 | 0x0 |
33 | 0x21 | 0x1f | 0xfe |
34 | 0x22 | 0x22 | 0x0 |
35 | 0x23 | 0x21 | 0xfe |
36 | 0x24 | 0x1c | 0xf8 |
37 | 0x25 | 0x1b | 0xf6 |
38 | 0x26 | 0x1e | 0xf8 |
39 | 0x27 | 0x1d | 0xf6 |
40 | 0x28 | 0x28 | 0x0 |
41 | 0x29 | 0x27 | 0xfe |
42 | 0x2a | 0x2a | 0x0 |
43 | 0x2b | 0x29 | 0xfe |
44 | 0x2c | 0x24 | 0xf8 |
45 | 0x2d | 0x23 | 0xf6 |
46 | 0x2e | 0x26 | 0xf8 |
47 | 0x2f | 0x25 | 0xf6 |
48 | 0x30 | 0x30 | 0x0 |
49 | 0x31 | 0x2f | 0xfe |
50 | 0x32 | 0x32 | 0x0 |
51 | 0x33 | 0x31 | 0xfe |
52 | 0x34 | 0x2c | 0xf8 |
53 | 0x35 | 0x2b | 0xf6 |
54 | 0x36 | 0x2e | 0xf8 |
55 | 0x37 | 0x2d | 0xf6 |
56 | 0x38 | 0x38 | 0x0 |
57 | 0x39 | 0x37 | 0xfe |
58 | 0x3a | 0x3a | 0x0 |
59 | 0x3b | 0x39 | 0xfe |
60 | 0x3c | 0x34 | 0xf8 |
61 | 0x3d | 0x33 | 0xf6 |
62 | 0x3e | 0x36 | 0xf8 |
63 | 0x3f | 0x35 | 0xf6 |
64 | 0x40 | 0x40 | 0x0 |
65 | 0x41 | 0x3f | 0xfe |
66 | 0x42 | 0x42 | 0x0 |
67 | 0x43 | 0x41 | 0xfe |
68 | 0x44 | 0x3c | 0xf8 |
69 | 0x45 | 0x3b | 0xf6 |
70 | 0x46 | 0x3e | 0xf8 |
71 | 0x47 | 0x3d | 0xf6 |
72 | 0x48 | 0x48 | 0x0 |
73 | 0x49 | 0x47 | 0xfe |
74 | 0x4a | 0x4a | 0x0 |
75 | 0x4b | 0x49 | 0xfe |
76 | 0x4c | 0x44 | 0xf8 |
77 | 0x4d | 0x43 | 0xf6 |
78 | 0x4e | 0x46 | 0xf8 |
79 | 0x4f | 0x45 | 0xf6 |
80 | 0x50 | 0x50 | 0x0 |
81 | 0x51 | 0x4f | 0xfe |
82 | 0x52 | 0x52 | 0x0 |
83 | 0x53 | 0x51 | 0xfe |
84 | 0x54 | 0x4c | 0xf8 |
85 | 0x55 | 0x4b | 0xf6 |
86 | 0x56 | 0x4e | 0xf8 |
87 | 0x57 | 0x4d | 0xf6 |
88 | 0x58 | 0x58 | 0x0 |
89 | 0x59 | 0x57 | 0xfe |
90 | 0x5a | 0x5a | 0x0 |
91 | 0x5b | 0x59 | 0xfe |
92 | 0x5c | 0x54 | 0xf8 |
93 | 0x5d | 0x53 | 0xf6 |
94 | 0x5e | 0x56 | 0xf8 |
95 | 0x5f | 0x55 | 0xf6 |
96 | 0x60 | 0x60 | 0x0 |
97 | 0x61 | 0x5f | 0xfe |
98 | 0x62 | 0x62 | 0x0 |
99 | 0x63 | 0x61 | 0xfe |
100 | 0x64 | 0x5c | 0xf8 |
101 | 0x65 | 0x5b | 0xf6 |
102 | 0x66 | 0x5e | 0xf8 |
103 | 0x67 | 0x5d | 0xf6 |
104 | 0x68 | 0x68 | 0x0 |
105 | 0x69 | 0x67 | 0xfe |
106 | 0x6a | 0x6a | 0x0 |
107 | 0x6b | 0x69 | 0xfe |
108 | 0x6c | 0x64 | 0xf8 |
109 | 0x6d | 0x63 | 0xf6 |
110 | 0x6e | 0x66 | 0xf8 |
111 | 0x6f | 0x65 | 0xf6 |
112 | 0x70 | 0x70 | 0x0 |
113 | 0x71 | 0x6f | 0xfe |
114 | 0x72 | 0x72 | 0x0 |
115 | 0x73 | 0x71 | 0xfe |
116 | 0x74 | 0x6c | 0xf8 |
117 | 0x75 | 0x6b | 0xf6 |
118 | 0x76 | 0x6e | 0xf8 |
119 | 0x77 | 0x6d | 0xf6 |
120 | 0x78 | 0x78 | 0x0 |
121 | 0x79 | 0x77 | 0xfe |
122 | 0x7a | 0x7a | 0x0 |
123 | 0x7b | 0x79 | 0xfe |
124 | 0x7c | 0x74 | 0xf8 |
125 | 0x7d | 0x73 | 0xf6 |
126 | 0x7e | 0x76 | 0xf8 |
127 | 0x7f | 0x75 | 0xf6 |
128 | 0x80 | 0x80 | 0x0 |
129 | 0x81 | 0x7f | 0xfe |
130 | 0x82 | 0x82 | 0x0 |
131 | 0x83 | 0x81 | 0xfe |
132 | 0x84 | 0x7c | 0xf8 |
133 | 0x85 | 0x7b | 0xf6 |
134 | 0x86 | 0x7e | 0xf8 |
135 | 0x87 | 0x7d | 0xf6 |
136 | 0x88 | 0x88 | 0x0 |
137 | 0x89 | 0x87 | 0xfe |
138 | 0x8a | 0x8a | 0x0 |
139 | 0x8b | 0x89 | 0xfe |
140 | 0x8c | 0x84 | 0xf8 |
141 | 0x8d | 0x83 | 0xf6 |
142 | 0x8e | 0x86 | 0xf8 |
143 | 0x8f | 0x85 | 0xf6 |
144 | 0x90 | 0x90 | 0x0 |
145 | 0x91 | 0x8f | 0xfe |
146 | 0x92 | 0x92 | 0x0 |
147 | 0x93 | 0x91 | 0xfe |
148 | 0x94 | 0x8c | 0xf8 |
149 | 0x95 | 0x8b | 0xf6 |
150 | 0x96 | 0x8e | 0xf8 |
151 | 0x97 | 0x8d | 0xf6 |
152 | 0x98 | 0x98 | 0x0 |
153 | 0x99 | 0x97 | 0xfe |
154 | 0x9a | 0x9a | 0x0 |
155 | 0x9b | 0x99 | 0xfe |
156 | 0x9c | 0x94 | 0xf8 |
157 | 0x9d | 0x93 | 0xf6 |
158 | 0x9e | 0x96 | 0xf8 |
159 | 0x9f | 0x95 | 0xf6 |
160 | 0xa0 | 0xa0 | 0x0 |
161 | 0xa1 | 0x9f | 0xfe |
162 | 0xa2 | 0xa2 | 0x0 |
163 | 0xa3 | 0xa1 | 0xfe |
164 | 0xa4 | 0x9c | 0xf8 |
165 | 0xa5 | 0x9b | 0xf6 |
166 | 0xa6 | 0x9e | 0xf8 |
167 | 0xa7 | 0x9d | 0xf6 |
168 | 0xa8 | 0xa8 | 0x0 |
169 | 0xa9 | 0xa7 | 0xfe |
170 | 0xaa | 0xaa | 0x0 |
171 | 0xab | 0xa9 | 0xfe |
172 | 0xac | 0xa4 | 0xf8 |
173 | 0xad | 0xa3 | 0xf6 |
174 | 0xae | 0xa6 | 0xf8 |
175 | 0xaf | 0xa5 | 0xf6 |
176 | 0xb0 | 0xb0 | 0x0 |
177 | 0xb1 | 0xaf | 0xfe |
178 | 0xb2 | 0xb2 | 0x0 |
179 | 0xb3 | 0xb1 | 0xfe |
180 | 0xb4 | 0xac | 0xf8 |
181 | 0xb5 | 0xab | 0xf6 |
182 | 0xb6 | 0xae | 0xf8 |
183 | 0xb7 | 0xad | 0xf6 |
184 | 0xb8 | 0xb8 | 0x0 |
185 | 0xb9 | 0xb7 | 0xfe |
186 | 0xba | 0xba | 0x0 |
187 | 0xbb | 0xb9 | 0xfe |
188 | 0xbc | 0xb4 | 0xf8 |
189 | 0xbd | 0xb3 | 0xf6 |
190 | 0xbe | 0xb6 | 0xf8 |
191 | 0xbf | 0xb5 | 0xf6 |
192 | 0xc0 | 0xc0 | 0x0 |
193 | 0xc1 | 0xbf | 0xfe |
194 | 0xc2 | 0xc2 | 0x0 |
195 | 0xc3 | 0xc1 | 0xfe |
196 | 0xc4 | 0xbc | 0xf8 |
197 | 0xc5 | 0xbb | 0xf6 |
198 | 0xc6 | 0xbe | 0xf8 |
199 | 0xc7 | 0xbd | 0xf6 |
200 | 0xc8 | 0xc8 | 0x0 |
201 | 0xc9 | 0xc7 | 0xfe |
202 | 0xca | 0xca | 0x0 |
203 | 0xcb | 0xc9 | 0xfe |
204 | 0xcc | 0xc4 | 0xf8 |
205 | 0xcd | 0xc3 | 0xf6 |
206 | 0xce | 0xc6 | 0xf8 |
207 | 0xcf | 0xc5 | 0xf6 |
208 | 0xd0 | 0xd0 | 0x0 |
209 | 0xd1 | 0xcf | 0xfe |
210 | 0xd2 | 0xd2 | 0x0 |
211 | 0xd3 | 0xd1 | 0xfe |
212 | 0xd4 | 0xcc | 0xf8 |
213 | 0xd5 | 0xcb | 0xf6 |
214 | 0xd6 | 0xce | 0xf8 |
215 | 0xd7 | 0xcd | 0xf6 |
216 | 0xd8 | 0xd8 | 0x0 |
217 | 0xd9 | 0xd7 | 0xfe |
218 | 0xda | 0xda | 0x0 |
219 | 0xdb | 0xd9 | 0xfe |
220 | 0xdc | 0xd4 | 0xf8 |
221 | 0xdd | 0xd3 | 0xf6 |
222 | 0xde | 0xd6 | 0xf8 |
223 | 0xdf | 0xd5 | 0xf6 |
224 | 0xe0 | 0xe0 | 0x0 |
225 | 0xe1 | 0xdf | 0xfe |
226 | 0xe2 | 0xe2 | 0x0 |
227 | 0xe3 | 0xe1 | 0xfe |
228 | 0xe4 | 0xdc | 0xf8 |
229 | 0xe5 | 0xdb | 0xf6 |
230 | 0xe6 | 0xde | 0xf8 |
231 | 0xe7 | 0xdd | 0xf6 |
232 | 0xe8 | 0xe8 | 0x0 |
233 | 0xe9 | 0xe7 | 0xfe |
234 | 0xea | 0xea | 0x0 |
235 | 0xeb | 0xe9 | 0xfe |
236 | 0xec | 0xe4 | 0xf8 |
237 | 0xed | 0xe3 | 0xf6 |
238 | 0xee | 0xe6 | 0xf8 |
239 | 0xef | 0xe5 | 0xf6 |
240 | 0xf0 | 0xf0 | 0x0 |
241 | 0xf1 | 0xef | 0xfe |
242 | 0xf2 | 0xf2 | 0x0 |
243 | 0xf3 | 0xf1 | 0xfe |
244 | 0xf4 | 0xec | 0xf8 |
245 | 0xf5 | 0xeb | 0xf6 |
246 | 0xf6 | 0xee | 0xf8 |
247 | 0xf7 | 0xed | 0xf6 |
248 | 0xf8 | 0xf8 | 0x0 |
249 | 0xf9 | 0xf7 | 0xfe |
250 | 0xfa | 0xfa | 0x0 |
251 | 0xfb | 0xf9 | 0xfe |
252 | 0xfc | 0xf4 | 0xf8 |
253 | 0xfd | 0xf3 | 0xf6 |
254 | 0xfe | 0xf6 | 0xf8 |
255 | 0xff | 0xf5 | 0xf6 |
As depicted above, the repeating Sub pattern is now easier to spot. Now let's bring back the Ciphertext column to view the full mapping between all Ciphertext, XOR_Decoded and Plaintext values!
df_final.insert(loc=0, column='Ciphertext', value=df_final['XOR_Decoded'] ^ 0xFE)
df_final = df_final.sort_values(by=['Ciphertext']).reset_index(drop=True)
df_final.style.format({'Ciphertext':"{:#x}", 'XOR_Decoded':"{:#x}", 'Plaintext':"{:#x}", 'Sub':"{:#x}"}).background_gradient(cmap='plasma')
Ciphertext | XOR_Decoded | Plaintext | Sub | |
---|---|---|---|---|
0 | 0x0 | 0xfe | 0xf6 | 0xf8 |
1 | 0x1 | 0xff | 0xf5 | 0xf6 |
2 | 0x2 | 0xfc | 0xf4 | 0xf8 |
3 | 0x3 | 0xfd | 0xf3 | 0xf6 |
4 | 0x4 | 0xfa | 0xfa | 0x0 |
5 | 0x5 | 0xfb | 0xf9 | 0xfe |
6 | 0x6 | 0xf8 | 0xf8 | 0x0 |
7 | 0x7 | 0xf9 | 0xf7 | 0xfe |
8 | 0x8 | 0xf6 | 0xee | 0xf8 |
9 | 0x9 | 0xf7 | 0xed | 0xf6 |
10 | 0xa | 0xf4 | 0xec | 0xf8 |
11 | 0xb | 0xf5 | 0xeb | 0xf6 |
12 | 0xc | 0xf2 | 0xf2 | 0x0 |
13 | 0xd | 0xf3 | 0xf1 | 0xfe |
14 | 0xe | 0xf0 | 0xf0 | 0x0 |
15 | 0xf | 0xf1 | 0xef | 0xfe |
16 | 0x10 | 0xee | 0xe6 | 0xf8 |
17 | 0x11 | 0xef | 0xe5 | 0xf6 |
18 | 0x12 | 0xec | 0xe4 | 0xf8 |
19 | 0x13 | 0xed | 0xe3 | 0xf6 |
20 | 0x14 | 0xea | 0xea | 0x0 |
21 | 0x15 | 0xeb | 0xe9 | 0xfe |
22 | 0x16 | 0xe8 | 0xe8 | 0x0 |
23 | 0x17 | 0xe9 | 0xe7 | 0xfe |
24 | 0x18 | 0xe6 | 0xde | 0xf8 |
25 | 0x19 | 0xe7 | 0xdd | 0xf6 |
26 | 0x1a | 0xe4 | 0xdc | 0xf8 |
27 | 0x1b | 0xe5 | 0xdb | 0xf6 |
28 | 0x1c | 0xe2 | 0xe2 | 0x0 |
29 | 0x1d | 0xe3 | 0xe1 | 0xfe |
30 | 0x1e | 0xe0 | 0xe0 | 0x0 |
31 | 0x1f | 0xe1 | 0xdf | 0xfe |
32 | 0x20 | 0xde | 0xd6 | 0xf8 |
33 | 0x21 | 0xdf | 0xd5 | 0xf6 |
34 | 0x22 | 0xdc | 0xd4 | 0xf8 |
35 | 0x23 | 0xdd | 0xd3 | 0xf6 |
36 | 0x24 | 0xda | 0xda | 0x0 |
37 | 0x25 | 0xdb | 0xd9 | 0xfe |
38 | 0x26 | 0xd8 | 0xd8 | 0x0 |
39 | 0x27 | 0xd9 | 0xd7 | 0xfe |
40 | 0x28 | 0xd6 | 0xce | 0xf8 |
41 | 0x29 | 0xd7 | 0xcd | 0xf6 |
42 | 0x2a | 0xd4 | 0xcc | 0xf8 |
43 | 0x2b | 0xd5 | 0xcb | 0xf6 |
44 | 0x2c | 0xd2 | 0xd2 | 0x0 |
45 | 0x2d | 0xd3 | 0xd1 | 0xfe |
46 | 0x2e | 0xd0 | 0xd0 | 0x0 |
47 | 0x2f | 0xd1 | 0xcf | 0xfe |
48 | 0x30 | 0xce | 0xc6 | 0xf8 |
49 | 0x31 | 0xcf | 0xc5 | 0xf6 |
50 | 0x32 | 0xcc | 0xc4 | 0xf8 |
51 | 0x33 | 0xcd | 0xc3 | 0xf6 |
52 | 0x34 | 0xca | 0xca | 0x0 |
53 | 0x35 | 0xcb | 0xc9 | 0xfe |
54 | 0x36 | 0xc8 | 0xc8 | 0x0 |
55 | 0x37 | 0xc9 | 0xc7 | 0xfe |
56 | 0x38 | 0xc6 | 0xbe | 0xf8 |
57 | 0x39 | 0xc7 | 0xbd | 0xf6 |
58 | 0x3a | 0xc4 | 0xbc | 0xf8 |
59 | 0x3b | 0xc5 | 0xbb | 0xf6 |
60 | 0x3c | 0xc2 | 0xc2 | 0x0 |
61 | 0x3d | 0xc3 | 0xc1 | 0xfe |
62 | 0x3e | 0xc0 | 0xc0 | 0x0 |
63 | 0x3f | 0xc1 | 0xbf | 0xfe |
64 | 0x40 | 0xbe | 0xb6 | 0xf8 |
65 | 0x41 | 0xbf | 0xb5 | 0xf6 |
66 | 0x42 | 0xbc | 0xb4 | 0xf8 |
67 | 0x43 | 0xbd | 0xb3 | 0xf6 |
68 | 0x44 | 0xba | 0xba | 0x0 |
69 | 0x45 | 0xbb | 0xb9 | 0xfe |
70 | 0x46 | 0xb8 | 0xb8 | 0x0 |
71 | 0x47 | 0xb9 | 0xb7 | 0xfe |
72 | 0x48 | 0xb6 | 0xae | 0xf8 |
73 | 0x49 | 0xb7 | 0xad | 0xf6 |
74 | 0x4a | 0xb4 | 0xac | 0xf8 |
75 | 0x4b | 0xb5 | 0xab | 0xf6 |
76 | 0x4c | 0xb2 | 0xb2 | 0x0 |
77 | 0x4d | 0xb3 | 0xb1 | 0xfe |
78 | 0x4e | 0xb0 | 0xb0 | 0x0 |
79 | 0x4f | 0xb1 | 0xaf | 0xfe |
80 | 0x50 | 0xae | 0xa6 | 0xf8 |
81 | 0x51 | 0xaf | 0xa5 | 0xf6 |
82 | 0x52 | 0xac | 0xa4 | 0xf8 |
83 | 0x53 | 0xad | 0xa3 | 0xf6 |
84 | 0x54 | 0xaa | 0xaa | 0x0 |
85 | 0x55 | 0xab | 0xa9 | 0xfe |
86 | 0x56 | 0xa8 | 0xa8 | 0x0 |
87 | 0x57 | 0xa9 | 0xa7 | 0xfe |
88 | 0x58 | 0xa6 | 0x9e | 0xf8 |
89 | 0x59 | 0xa7 | 0x9d | 0xf6 |
90 | 0x5a | 0xa4 | 0x9c | 0xf8 |
91 | 0x5b | 0xa5 | 0x9b | 0xf6 |
92 | 0x5c | 0xa2 | 0xa2 | 0x0 |
93 | 0x5d | 0xa3 | 0xa1 | 0xfe |
94 | 0x5e | 0xa0 | 0xa0 | 0x0 |
95 | 0x5f | 0xa1 | 0x9f | 0xfe |
96 | 0x60 | 0x9e | 0x96 | 0xf8 |
97 | 0x61 | 0x9f | 0x95 | 0xf6 |
98 | 0x62 | 0x9c | 0x94 | 0xf8 |
99 | 0x63 | 0x9d | 0x93 | 0xf6 |
100 | 0x64 | 0x9a | 0x9a | 0x0 |
101 | 0x65 | 0x9b | 0x99 | 0xfe |
102 | 0x66 | 0x98 | 0x98 | 0x0 |
103 | 0x67 | 0x99 | 0x97 | 0xfe |
104 | 0x68 | 0x96 | 0x8e | 0xf8 |
105 | 0x69 | 0x97 | 0x8d | 0xf6 |
106 | 0x6a | 0x94 | 0x8c | 0xf8 |
107 | 0x6b | 0x95 | 0x8b | 0xf6 |
108 | 0x6c | 0x92 | 0x92 | 0x0 |
109 | 0x6d | 0x93 | 0x91 | 0xfe |
110 | 0x6e | 0x90 | 0x90 | 0x0 |
111 | 0x6f | 0x91 | 0x8f | 0xfe |
112 | 0x70 | 0x8e | 0x86 | 0xf8 |
113 | 0x71 | 0x8f | 0x85 | 0xf6 |
114 | 0x72 | 0x8c | 0x84 | 0xf8 |
115 | 0x73 | 0x8d | 0x83 | 0xf6 |
116 | 0x74 | 0x8a | 0x8a | 0x0 |
117 | 0x75 | 0x8b | 0x89 | 0xfe |
118 | 0x76 | 0x88 | 0x88 | 0x0 |
119 | 0x77 | 0x89 | 0x87 | 0xfe |
120 | 0x78 | 0x86 | 0x7e | 0xf8 |
121 | 0x79 | 0x87 | 0x7d | 0xf6 |
122 | 0x7a | 0x84 | 0x7c | 0xf8 |
123 | 0x7b | 0x85 | 0x7b | 0xf6 |
124 | 0x7c | 0x82 | 0x82 | 0x0 |
125 | 0x7d | 0x83 | 0x81 | 0xfe |
126 | 0x7e | 0x80 | 0x80 | 0x0 |
127 | 0x7f | 0x81 | 0x7f | 0xfe |
128 | 0x80 | 0x7e | 0x76 | 0xf8 |
129 | 0x81 | 0x7f | 0x75 | 0xf6 |
130 | 0x82 | 0x7c | 0x74 | 0xf8 |
131 | 0x83 | 0x7d | 0x73 | 0xf6 |
132 | 0x84 | 0x7a | 0x7a | 0x0 |
133 | 0x85 | 0x7b | 0x79 | 0xfe |
134 | 0x86 | 0x78 | 0x78 | 0x0 |
135 | 0x87 | 0x79 | 0x77 | 0xfe |
136 | 0x88 | 0x76 | 0x6e | 0xf8 |
137 | 0x89 | 0x77 | 0x6d | 0xf6 |
138 | 0x8a | 0x74 | 0x6c | 0xf8 |
139 | 0x8b | 0x75 | 0x6b | 0xf6 |
140 | 0x8c | 0x72 | 0x72 | 0x0 |
141 | 0x8d | 0x73 | 0x71 | 0xfe |
142 | 0x8e | 0x70 | 0x70 | 0x0 |
143 | 0x8f | 0x71 | 0x6f | 0xfe |
144 | 0x90 | 0x6e | 0x66 | 0xf8 |
145 | 0x91 | 0x6f | 0x65 | 0xf6 |
146 | 0x92 | 0x6c | 0x64 | 0xf8 |
147 | 0x93 | 0x6d | 0x63 | 0xf6 |
148 | 0x94 | 0x6a | 0x6a | 0x0 |
149 | 0x95 | 0x6b | 0x69 | 0xfe |
150 | 0x96 | 0x68 | 0x68 | 0x0 |
151 | 0x97 | 0x69 | 0x67 | 0xfe |
152 | 0x98 | 0x66 | 0x5e | 0xf8 |
153 | 0x99 | 0x67 | 0x5d | 0xf6 |
154 | 0x9a | 0x64 | 0x5c | 0xf8 |
155 | 0x9b | 0x65 | 0x5b | 0xf6 |
156 | 0x9c | 0x62 | 0x62 | 0x0 |
157 | 0x9d | 0x63 | 0x61 | 0xfe |
158 | 0x9e | 0x60 | 0x60 | 0x0 |
159 | 0x9f | 0x61 | 0x5f | 0xfe |
160 | 0xa0 | 0x5e | 0x56 | 0xf8 |
161 | 0xa1 | 0x5f | 0x55 | 0xf6 |
162 | 0xa2 | 0x5c | 0x54 | 0xf8 |
163 | 0xa3 | 0x5d | 0x53 | 0xf6 |
164 | 0xa4 | 0x5a | 0x5a | 0x0 |
165 | 0xa5 | 0x5b | 0x59 | 0xfe |
166 | 0xa6 | 0x58 | 0x58 | 0x0 |
167 | 0xa7 | 0x59 | 0x57 | 0xfe |
168 | 0xa8 | 0x56 | 0x4e | 0xf8 |
169 | 0xa9 | 0x57 | 0x4d | 0xf6 |
170 | 0xaa | 0x54 | 0x4c | 0xf8 |
171 | 0xab | 0x55 | 0x4b | 0xf6 |
172 | 0xac | 0x52 | 0x52 | 0x0 |
173 | 0xad | 0x53 | 0x51 | 0xfe |
174 | 0xae | 0x50 | 0x50 | 0x0 |
175 | 0xaf | 0x51 | 0x4f | 0xfe |
176 | 0xb0 | 0x4e | 0x46 | 0xf8 |
177 | 0xb1 | 0x4f | 0x45 | 0xf6 |
178 | 0xb2 | 0x4c | 0x44 | 0xf8 |
179 | 0xb3 | 0x4d | 0x43 | 0xf6 |
180 | 0xb4 | 0x4a | 0x4a | 0x0 |
181 | 0xb5 | 0x4b | 0x49 | 0xfe |
182 | 0xb6 | 0x48 | 0x48 | 0x0 |
183 | 0xb7 | 0x49 | 0x47 | 0xfe |
184 | 0xb8 | 0x46 | 0x3e | 0xf8 |
185 | 0xb9 | 0x47 | 0x3d | 0xf6 |
186 | 0xba | 0x44 | 0x3c | 0xf8 |
187 | 0xbb | 0x45 | 0x3b | 0xf6 |
188 | 0xbc | 0x42 | 0x42 | 0x0 |
189 | 0xbd | 0x43 | 0x41 | 0xfe |
190 | 0xbe | 0x40 | 0x40 | 0x0 |
191 | 0xbf | 0x41 | 0x3f | 0xfe |
192 | 0xc0 | 0x3e | 0x36 | 0xf8 |
193 | 0xc1 | 0x3f | 0x35 | 0xf6 |
194 | 0xc2 | 0x3c | 0x34 | 0xf8 |
195 | 0xc3 | 0x3d | 0x33 | 0xf6 |
196 | 0xc4 | 0x3a | 0x3a | 0x0 |
197 | 0xc5 | 0x3b | 0x39 | 0xfe |
198 | 0xc6 | 0x38 | 0x38 | 0x0 |
199 | 0xc7 | 0x39 | 0x37 | 0xfe |
200 | 0xc8 | 0x36 | 0x2e | 0xf8 |
201 | 0xc9 | 0x37 | 0x2d | 0xf6 |
202 | 0xca | 0x34 | 0x2c | 0xf8 |
203 | 0xcb | 0x35 | 0x2b | 0xf6 |
204 | 0xcc | 0x32 | 0x32 | 0x0 |
205 | 0xcd | 0x33 | 0x31 | 0xfe |
206 | 0xce | 0x30 | 0x30 | 0x0 |
207 | 0xcf | 0x31 | 0x2f | 0xfe |
208 | 0xd0 | 0x2e | 0x26 | 0xf8 |
209 | 0xd1 | 0x2f | 0x25 | 0xf6 |
210 | 0xd2 | 0x2c | 0x24 | 0xf8 |
211 | 0xd3 | 0x2d | 0x23 | 0xf6 |
212 | 0xd4 | 0x2a | 0x2a | 0x0 |
213 | 0xd5 | 0x2b | 0x29 | 0xfe |
214 | 0xd6 | 0x28 | 0x28 | 0x0 |
215 | 0xd7 | 0x29 | 0x27 | 0xfe |
216 | 0xd8 | 0x26 | 0x1e | 0xf8 |
217 | 0xd9 | 0x27 | 0x1d | 0xf6 |
218 | 0xda | 0x24 | 0x1c | 0xf8 |
219 | 0xdb | 0x25 | 0x1b | 0xf6 |
220 | 0xdc | 0x22 | 0x22 | 0x0 |
221 | 0xdd | 0x23 | 0x21 | 0xfe |
222 | 0xde | 0x20 | 0x20 | 0x0 |
223 | 0xdf | 0x21 | 0x1f | 0xfe |
224 | 0xe0 | 0x1e | 0x16 | 0xf8 |
225 | 0xe1 | 0x1f | 0x15 | 0xf6 |
226 | 0xe2 | 0x1c | 0x14 | 0xf8 |
227 | 0xe3 | 0x1d | 0x13 | 0xf6 |
228 | 0xe4 | 0x1a | 0x1a | 0x0 |
229 | 0xe5 | 0x1b | 0x19 | 0xfe |
230 | 0xe6 | 0x18 | 0x18 | 0x0 |
231 | 0xe7 | 0x19 | 0x17 | 0xfe |
232 | 0xe8 | 0x16 | 0xe | 0xf8 |
233 | 0xe9 | 0x17 | 0xd | 0xf6 |
234 | 0xea | 0x14 | 0xc | 0xf8 |
235 | 0xeb | 0x15 | 0xb | 0xf6 |
236 | 0xec | 0x12 | 0x12 | 0x0 |
237 | 0xed | 0x13 | 0x11 | 0xfe |
238 | 0xee | 0x10 | 0x10 | 0x0 |
239 | 0xef | 0x11 | 0xf | 0xfe |
240 | 0xf0 | 0xe | 0x6 | 0xf8 |
241 | 0xf1 | 0xf | 0x5 | 0xf6 |
242 | 0xf2 | 0xc | 0x4 | 0xf8 |
243 | 0xf3 | 0xd | 0x3 | 0xf6 |
244 | 0xf4 | 0xa | 0xa | 0x0 |
245 | 0xf5 | 0xb | 0x9 | 0xfe |
246 | 0xf6 | 0x8 | 0x8 | 0x0 |
247 | 0xf7 | 0x9 | 0x7 | 0xfe |
248 | 0xf8 | 0x6 | 0xfe | 0xf8 |
249 | 0xf9 | 0x7 | 0xfd | 0xf6 |
250 | 0xfa | 0x4 | 0xfc | 0xf8 |
251 | 0xfb | 0x5 | 0xfb | 0xf6 |
252 | 0xfc | 0x2 | 0x2 | 0x0 |
253 | 0xfd | 0x3 | 0x1 | 0xfe |
254 | 0xfe | 0x0 | 0x0 | 0x0 |
255 | 0xff | 0x1 | 0xff | 0xfe |
As this point, we have a table that maps each ciphertext/encoded value to its corresponding plaintext value. Let's grab the first 4 bytes of the file shared by Florian and use the mapping table above to decode the bytes:
Ciphertext = 0xa9 0xa4 0x6e 0xfe
plaintext = list()
ciphertext = (b'\xA9\xA4\x6E\xFE\xF3\xFE\xFE\xFE\xF2\xFE\xFE\xFE\xFF\xFF\xFE\xFE'
b'\x46\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xBE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE\x1E\xFE\xFE\xFE'
b'\xE8\xDF\x44\xE8\xFE\x42\xF5\x29\xDD\x46\xFD\xAA\x29\xDD\xA2\x96'
b'\x95\x83\xDE\x8E\x8C\x8F\x97\x8C\x9D\x89\xDE\x93\x9D\x88\x88\x8F'
b'\x82\xDE\x9C\x91\xDE\x8C\x81\x88\xDE\x95\x88\xDE\xB2\xAF\xA3\xDE'
b'\x89\x8F\x92\x91\xC8\xE9\xE9\xF4\xD2\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\x13\xFC\x59\xE2\x57\x93\x03\xB7\x57\x93\x03\xB7\x57\x93\x03\xB7'
b'\x48\xDB\x90\xB7\x40\x93\x03\xB7\x48\xDB\x8E\xB7\x29\x93\x03\xB7'
b'\x48\xDB\x87\xB7\x61\x93\x03\xB7\x7E\x51\x76\xB7\x4A\x93\x03\xB7'
b'\x57\x93\x0C\xB7\x23\x93\x03\xB7\x48\xDB\x85\xB7\x50\x93\x03\xB7'
b'\x48\xDB\x9D\xB7\x50\x93\x03\xB7\x48\xDB\x9C\xB7\x50\x93\x03\xB7'
b'\xAC\x95\x93\x96\x57\x93\x03\xB7\xFE\xFE\xFE\xFE\xFE\xFE\xFE\xFE'
b'\xAE\xB1\xFE\xFE\xAA\xFD\xF1\xFE\xCF\x61\x16\xA7\xFE\xFE\xFE\xFE'
b'\xFE\xFE\xFE\xFE\x1E\xFE\xFC\xDD\xEB\xFD\xF5\xFE\xFE\x1C\xFE\xFE')
for c in ciphertext:
plaintext.append(df_final.iloc[c]['Plaintext'] & 0xFF)
print('\n[+] Shared by Florian:\n')
hex_dump(ciphertext)
print('\n\n[+] Decoded:\n')
hex_dump(plaintext)
print('\n')
[+] Shared by Florian: 00000000 a9 a4 6e fe f3 fe fe fe f2 fe fe fe ff ff fe fe ..n............. 00000010 46 fe fe fe fe fe fe fe be fe fe fe fe fe fe fe F............... 00000020 fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe ................ 00000030 fe fe fe fe fe fe fe fe fe fe fe fe 1e fe fe fe ................ 00000040 e8 df 44 e8 fe 42 f5 29 dd 46 fd aa 29 dd a2 96 ..D..B.).F..)... 00000050 95 83 de 8e 8c 8f 97 8c 9d 89 de 93 9d 88 88 8f ................ 00000060 82 de 9c 91 de 8c 81 88 de 95 88 de b2 af a3 de ................ 00000070 89 8f 92 91 c8 e9 e9 f4 d2 fe fe fe fe fe fe fe ................ 00000080 13 fc 59 e2 57 93 03 b7 57 93 03 b7 57 93 03 b7 ..Y.W...W...W... 00000090 48 db 90 b7 40 93 03 b7 48 db 8e b7 29 93 03 b7 H...@...H...)... 000000a0 48 db 87 b7 61 93 03 b7 7e 51 76 b7 4a 93 03 b7 H...a...~Qv.J... 000000b0 57 93 0c b7 23 93 03 b7 48 db 85 b7 50 93 03 b7 W...#...H...P... 000000c0 48 db 9d b7 50 93 03 b7 48 db 9c b7 50 93 03 b7 H...P...H...P... 000000d0 ac 95 93 96 57 93 03 b7 fe fe fe fe fe fe fe fe ....W........... 000000e0 ae b1 fe fe aa fd f1 fe cf 61 16 a7 fe fe fe fe .........a...... 000000f0 fe fe fe fe 1e fe fc dd eb fd f5 fe fe 1c fe fe ................ [+] Decoded: 00000000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 MZ.............. 00000010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@....... 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 e0 00 00 00 ................ 00000040 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 ........!..L.!Th 00000050 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f is program canno 00000060 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 t be run in DOS 00000070 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 mode....$....... 00000080 e3 02 9d 14 a7 63 f3 47 a7 63 f3 47 a7 63 f3 47 .....c.G.c.G.c.G 00000090 ae 1b 66 47 b6 63 f3 47 ae 1b 70 47 cd 63 f3 47 ..fG.c.G..pG.c.G 000000a0 ae 1b 77 47 95 63 f3 47 80 a5 88 47 ac 63 f3 47 ..wG.c.G...G.c.G 000000b0 a7 63 f2 47 d3 63 f3 47 ae 1b 79 47 a6 63 f3 47 .c.G.c.G..yG.c.G 000000c0 ae 1b 61 47 a6 63 f3 47 ae 1b 62 47 a6 63 f3 47 ..aG.c.G..bG.c.G 000000d0 52 69 63 68 a7 63 f3 47 00 00 00 00 00 00 00 00 Rich.c.G........ 000000e0 50 45 00 00 4c 01 05 00 2f 95 e8 57 00 00 00 00 PE..L.../..W.... 000000f0 00 00 00 00 e0 00 02 21 0b 01 09 00 00 e2 00 00 .......!........
At this point I was still curious to know if there was anything else to the XOR+ADD operation logic so I wrote a tiny brute force script to implement a simple known-plaintext attack (KPA) tool. Instead of using the whole file or even the first 256 bytes header shown above I decided to only use the first 4 bytes of the encoded file as ciphertext to speed up the brute force process.
Ciphertext: 0xa9 0xa4 0x6e 0xfe
Using the values above, I wrote a script that uses all combinations of single-byte XOR keys and ADD keys to decode the ciphertext value above (can be optimized but left unrolled for readability).
1st attempt: XOR Key: 0x01, ADD Key: 0x01
cipher = '\xA9\xA4\x6E\xFE'
plaintext = '\x4d\x5a\x90\x00'
# XOR-ADD Routine
print('\n[*] (c ^ xor_key) + add_key:')
for xor_key in range(1, 256): #no need to XOR with 0
for add_key in range(1, 256): #no need attemp add 0
if ''.join(chr(((ord(c) ^ xor_key) + add_key) & 0xFF) for c in cipher) == plaintext:
print('\t[+] (c ^ {0:#2X}) + {1:#2X}'.format(xor_key, add_key))
# XOR-SUB Routine
print('\n[*] (c ^ xor_key) - sub_key:')
for xor_key in range(1, 256):
for sub_key in range(1, 256):
if ''.join(chr(((ord(c) ^ xor_key) - sub_key) & 0xFF) for c in cipher) == plaintext:
print('\t[+] (c ^ {0:#2X}) - {1:#2X}'.format(xor_key, sub_key))
print()
[*] (c ^ xor_key) + add_key: [+] (c ^ 0X5B) + 0X5B [+] (c ^ 0X7B) + 0X7B [+] (c ^ 0XDB) + 0XDB [+] (c ^ 0XFB) + 0XFB [*] (c ^ xor_key) - sub_key: [+] (c ^ 0X5B) - 0XA5 [+] (c ^ 0X7B) - 0X85 [+] (c ^ 0XDB) - 0X25 [+] (c ^ 0XFB) - 0X5
As shown above, for the XOR-ADD operation 0x5B, 0x7B, 0xDB and 0xFB are all valid and matching XOR and ADD key values (i.e. all 4 matching keys pairs can be used to XOR-ADD decode the encoded file). There is certainly a relationship between the Sub pattern described in the sections above and the matching XOR-ADD keys above (especially considering XOR is technically a modulo-2 addition) but more research and analysis is required to discover the relationship.