Crackme 714 pts (9 solves) :
The crackme is an MFC application :
We can locate the routine of interest by setting a breakpoint on GetWindowTextW. Keep in mind that the input is in Unicode.
Later on, we find that the program generates two distinct blocks of values. These are generated from hard-coded values independently from the user input, so they're always the same. We call the first one : static_block1 and the second static_block2.
Then, there's the call to the encrypt function which takes static_block1 as an argument.
The encrypted block will then be XORed with static_block2.
We also find a reference to the encrypted valid key here, which we can extract easily during runtime :
The loop above performs a double-word by double-word comparison of the encrypted user input with the encrypted valid key that already came with the crackme.
In order to solve the challenge we need to reverse engineer the encrypt function and do the exact reverse. We also don't have to forget the XOR that is done with static_block2. For that matter, we supply to the decrypt function (the one we need to write) encrypted_valid_key XOR static_block2.
The script below has my implementation of the decrypt function, it outputs the key to flag.txt :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
RCTF - 2017 | |
Author : SOUHAIL HAMMOU | |
Crackme 714 pts (9 solves) | |
Description : | |
============ | |
Please submit the flag like RCTF{flag} | |
https://static2017.teamrois.cn/re_b889ffe02c96c38274f76c67f8a1ddf3/crackme_63074830f0b1b6b4fff6ad910bea34fc.zip | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define __ROL__(x, y) _rotl(x, y) | |
inline unsigned int __ROL4__(unsigned int value, int count) { return __ROL__((unsigned int)value, count); } | |
inline unsigned int __ROR4__(unsigned int value, int count) { return __ROL__((unsigned int)value, -count); } | |
typedef unsigned char BYTE; | |
typedef unsigned int DWORD; | |
void _decrypt(DWORD* input) | |
{ | |
BYTE static_block_2[] = { | |
0xCA,0xF4,0x60,0x16,0xDC,0xB7,0x2F,0x71,0x3D,0xEA,0x7D,0xF3,0xC8,0x2E,0xA8,0x17, | |
0x2E,0x3A,0x47,0xCE,0x33,0x85,0xE3,0x10,0x4F,0xB1,0x85,0x5D,0x87,0xB0,0x05,0x84, | |
0xFC,0xCA,0x8F,0x89,0xF4,0x36,0x17,0xBD,0xE1,0x8D,0xF1,0x89,0x5B,0x3F,0x37,0xE4, | |
0x27,0x26,0xEF,0xD1,0x6A,0xDF,0xB1,0xDA,0xF0,0x63,0x04,0x8F,0xD7,0x79,0x50,0x22, | |
0x4D,0xD4,0x66,0xD0,0xE1,0xE0,0xCC,0x12,0x3A,0xFB,0xE5,0x04,0xDA,0x24,0x62,0xCA, | |
0x33,0xC5,0xD4,0x95,0x4D,0xA8,0x81,0x81,0x44,0x20,0xF3,0xBA,0x39,0x77,0x26,0x44, | |
0xF7,0xBD,0x61,0x6B,0x6C,0x84,0x1D,0x25,0x38,0x73,0x5F,0x76,0xD1,0xE0,0x2B,0x57, | |
0x1B,0xC2,0x9B,0x36,0xF6,0x23,0xBA,0x82,0xAA,0xF6,0x72,0x4F,0xD3,0xB1,0x67,0x18 | |
}; | |
DWORD prev_1, prev_2; | |
int ROL_1_res; | |
int ROL_2_res; | |
int ROL_4_res; | |
int ROL_3_res; | |
input = input + 1; | |
for (unsigned int n = 0; n < 4; n++) | |
{ | |
*input += 0x5BF76637; | |
input[2] += 0x4748DA7A; | |
int i = 0; | |
int j = 4; | |
do | |
{ | |
//Deduce the ROL values | |
ROL_2_res = __ROL4__(*input * (2 * *input + 1), 8); | |
ROL_4_res = ROL_2_res ^ *(input - 1); | |
ROL_1_res = __ROL4__(input[2] * (2 * input[2] + 1), 8); | |
ROL_3_res = input[1] ^ ROL_1_res; | |
///// The values of the 4 double-words in the next loop : | |
///// next loop's *(ptr-1) is current *ptr | |
///// next loop's ptr[1] is ptr[2] | |
///// next loop's ptr[2] is calculated below via interm | |
///// next loop's *ptr is calculated below via interm2 | |
DWORD interm = __ROR4__(ROL_4_res, 32 - (ROL_1_res & 0x1F)); | |
prev_2 = interm + ((DWORD*)static_block_2)[i / 4]; | |
DWORD interm2 = __ROR4__(ROL_3_res, 32 - (ROL_2_res & 0x1F)); | |
prev_1 = interm2 + ((DWORD*)static_block_2)[j >> 2]; | |
//now that we have all the necessary values, write them to the array for the next loop | |
*(input - 1) = *input; | |
*input = prev_1; | |
input[1] = input[2]; | |
input[2] = prev_2; | |
i += 8; | |
j += 8; | |
} while (j <= 124); | |
*(input - 1) += 0x7FAF076D; | |
input[1] -= 0x642805B4; | |
input += 4; | |
} | |
} | |
int main() | |
{ | |
FILE* fp = _wfopen(L"flag.txt", L"w, ccs=UTF-16LE"); | |
/* | |
Result of : | |
AF C7 E8 A9 8F 75 75 5E 51 3D 9D 5E AD 88 8E 1D <= | |
7F 78 F2 70 E3 E1 12 9F AF 11 8E D9 2F DF 54 DB <= static block 1 | |
C1 11 1B D5 12 B2 9E 82 1B 12 0B 86 44 60 26 B8 <= | |
4D 9C 20 73 AF A3 C2 AB B8 17 DC EB 22 C3 4D E6 <= | |
XOR | |
7d 34 13 f9 89 07 0e ff a1 3b f8 fa 30 14 9a b7 <= | |
00 48 29 78 f6 32 3c 31 db 1a c1 c6 98 2e 7f ba <= XORed encrypted key | |
4c e9 27 81 ce fe 23 5d 06 42 7c 50 d3 07 e4 56 <= | |
99 ea a6 d2 36 9d a5 52 af 75 82 cc da 14 59 d9 <= | |
= key's value after the _encrypt routine (sub_013A1040); i.e : it's ready for decryption now | |
*/ | |
BYTE key[] = { | |
0x7d,0x34,0x13,0xf9,0x89,0x07,0x0e,0xff,0xa1,0x3b,0xf8,0xfa,0x30,0x14,0x9a,0xb7, | |
0x00,0x48,0x29,0x78,0xf6,0x32,0x3c,0x31,0xdb,0x1a,0xc1,0xc6,0x98,0x2e,0x7f,0xba, | |
0x4c,0xe9,0x27,0x81,0xce,0xfe,0x23,0x5d,0x06,0x42,0x7c,0x50,0xd3,0x07,0xe4,0x56, | |
0x99,0xea,0xa6,0xd2,0x36,0x9d,0xa5,0x52,0xaf,0x75,0x82,0xcc,0xda,0x14,0x59,0xd9 | |
}; | |
_decrypt((DWORD*)key); | |
// | |
// Decrypted key : 你好,欢迎进入rctf2017,希望你学到东西。 | |
// | |
fwprintf(fp,(wchar_t*)key); | |
} |
The flag is : RCTF{rtf2017crackmebyw31y1l1n9}
See you again soon
Follow me on Twitter : here