Rule: Decrypt File (EXE)
Run target to get basic information:
Let’s open the target in OllyDBG, search all referenced text strings, we’ll get like this:
Follow 0x0044A775 address and set BP at 0044A792 |. FF15 B8C04400 call dword ptr [<&MSVCR100.scanf>]; \scanf
:
Press F9 to run and input any key you like, then press enter, we’ll back to OllyDBG:
After target gets the input key, next it calculates the input key length:
Next, it opens the file whose name is specified in the parameter filename and mode is rb (read, binary file):
This target opens file that named file in the same directory to read in binary mode, then gets character from stream and stores to the buffer at 0x5415B8 address:
Compare values store at the buffer with the file content is opened with WinHex, we have the same values:
Next, we will see the snippet that use the input key to decrypt the values are stored at the 0x5415B8 address. Let’s see how decryption routine performs:
This is the pseudo-code for the snippet:
For (i=0; i< sizeof(file); i++) { FileContent[i] = FileContent[i] ^ InputKey[i] ^ 0xFF; }
The decryption result:
Next, reopen file in write mode:
The next snippet takes each value from the buffer and writes back to file:
Content of file after write back:
Because the rule of this challenge is “Decrypt File (EXE)”, so we know that the file after decrypt will be a normal exe file. As we know, the normal exe file has the valid PE header same as the picture bellow:
We also have the decryption algorithm in the previous analysis: FileContent[i] = FileContent[i] ^ InputKey[i] ^ 0xFF
; so FileContent[i]
must equal 0x4D, 0x5A, 0x90, etc
. To find the InputKey we only need to do: ValidPEHeader[i] ^ FileContent[i] ^ 0xFF = InputKey[i]
. Summarize we have calculation table like this:
Validheader[i] | operator | Filecontent[i] | operator | default value | inputkey[i] |
0x4D | ^ | 0xDE | ^ | 0xFF | 0x6C (l) |
0x5A | ^ | 0xC0 | ^ | 0xFF | 0x65 (e) |
0x90 | ^ | 0x1B | ^ | 0xFF | 0x74 (t) |
0x00 | ^ | 0x8C | ^ | 0xFF | 0x73 (s) |
0x03 | ^ | 0x8C | ^ | 0xFF | 0x70 (p) |
0x00 | ^ | 0x93 | ^ | 0xFF | 0x6C (l) |
0x00 | ^ | 0x9E | ^ | 0xFF | 0x61 (a) |
0x00 | ^ | 0x86 | ^ | 0xFF | 0x79 (y) |
0x04 | ^ | 0x98 | ^ | 0xFF | 0x63 (c) |
0x00 | ^ | 0x97 | ^ | 0xFF | 0x68 (h) |
0x00 | ^ | 0x9A | ^ | 0xFF | 0x65 (e) |
0x00 | ^ | 0x8C | ^ | 0xFF | 0x73 (s) |
0xFF | ^ | 0x73 | ^ | 0xFF | 0x73 (s) |
0xFF | ^ | 0x6C | ^ | 0xFF | 0x6C (l) |
0x00 | ^ | 0x9A | ^ | 0xFF | 0x65 (e) |
0x00 | ^ | 0x8B | ^ | 0xFF | 0x74 (t) |
Code in C:
#include <stdio.h> #include <stdlib.h> int main() { unsigned int valid_PE[16] = {0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00}; unsigned int encrypted_PE[16] = {0xDE, 0xC0, 0x1B, 0x8C, 0x8C, 0x93, 0x9E, 0x86, 0x98, 0x97, 0x9A, 0x8C, 0x73, 0x6C, 0x9A, 0x8B}; unsigned int defVal = 0xFF, i = 0; unsigned char key[16] = {0}; for (i=0; i<16; i++) { key[i] = valid_PE[i] ^ encrypted_PE[i] ^ defVal; } printf("The decrypt key is: %s \n", key); return 0; }
Okay, we have the decrypt key is: letsplaychess. Run program and input key we will have the decrypt file. Let’s rename file to add the exe extension. Use RDG Packer Detector to check file:
Ohhh, it’s packed by UPX. Using UPX to unpack file:
After unpack successful, run file to get the key:
End.