crackMe
附件
- crackme.exe
分析
IDA定位到wmain函数
1 | int wmain() |
开始的 while 循环条件中的 checkInput_CE1000 用来判断输入的用户名和密码是否只有字母和数字.
sub_CE1090(v7);
中, v7就是输入的用户名, sub_CE1090用来根据用户名生成一个Sbox.
Sbox: 一个有 256 个 uint8_t
类型元素的数组,常用于加密算法.Sbox的识别方法: 遇到形如for ( i = 0; i < 256; ++i ) sbox[i] = i;
以及 while (i < 256) { /* ...*/ }
就要引起注意. 目前常见加密算法RC4、AES等都有用到Sbox.
根据 sub_CE1090
的代码:
1 | _BYTE *__cdecl sub_CE1090(_BYTE *a1) |
可以看到该函数第一个循环就是将Sbox先初始化为 [0, 1, 2, 3, … …, 255] 的数组, 然后接下来的第三个循环可以用代码表示:
1 | { |
接下来的if ( sub_CE1830((int)v7, v6) )
就是将用户名(v7)和密码(v6)传入参数并检查:
1 | bool __cdecl sub_CE1830(const char *username_a1, const char *pwd_a2) |
上面第一个循环将密码(pwd_a2)转换为数字, isdigit 函数检测字符是否为'0' ~ '9'
, isxdigit函数检测字符是否为'0' ~ '9'
或 'a' ~ 'f'
或 'A' ~ 'F'
, 根据字符类型的不同使用不同的计算方式, 比如字符'a'
最后就会被转换为数字0x0A
.
使用 Python命令行模拟计算一下:
1 | Python 3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)] on win32 |
而 if ( *((_DWORD *)NtCurrentPeb()->ProcessHeap + 3) != 2 )
以及 __rdtsc()
就是反调试以及花指令, 对于反调试可以用插件(如ScyllaHide) 来绕过并且再静态分析时忽略if
块里面的内容.
来到第二个循环, 固定循环8次, 第一行的v10每次取sbox的一个值加到自身, 从第二行开始的 4 行是用来交换byte_CF6050[v10]
和 byte_CF6050[v11]
两个sbox元素.
if ( (NtCurrentPeb()->NtGlobalFlag & 0x70) != 0 )
是根据有PEB来进行反调试, 里面的代码在静态分析时候直接忽略不看.
接下来的 v16[v5] = byte_CF6050[(unsigned __int8)(v7 + v12)] ^ v15[v4 - 1];
是将sbox的一个元素与我们在第一个循环由输入的密码计算得到的数字进行异或操作并存到v16这个数组里.
if ( (unsigned __int8)*(_DWORD *)&NtCurrentPeb()->BeingDebugged )
处理方法和上面的PEB反调试一样处理.
最后是调用了一个inner_encrypt_CE1710
函数, 将v16和username进行加密并将结果存储到v16当前元素中.
1 | const char *__cdecl inner_encrypt_CE1710(int a1, const char *a2, signed int a3) |
第一个if根据STARTUPINFO进行反调试, 里面内容直接不看. else if ( a3 <= v4 )
部分将用户名索引为a3
的ASCII值与v16索引为a3
的值进行进行异或, 并存回v16[a3]
. 通过动态调试可以发现对于用户名welcomebeijing
是不会进入else块的, 直接忽略else块.
第二个循环结束之后, 调用了一个sub_CE1470
,将计算结果通过指针返回到 v13 中, 最后return返回 v13 时候等于 0xAB94
.
1 | unsigned int *__usercall sub_CE1470@<eax>(int a1@<ebx>, _BYTE *a2, unsigned int *a3) |
还是反调试if块和花指令一律不看, 根据计算可以观察到可以若最初v13为0, 且v16的元素分别为[0x64, 0x62, 0x61, 0x70, 0x70, 0x73, 0x65, 0x63]
就可以让v13最终等于0xAB94
.
还是拿Python命令行模拟计算一下:
1 | hex(((((((0 | 4) | 0x14) | 0x84) | 0x380) | 0xA04 ) | 0x2310 ) | 0x8A10) |
对sub_CE1830
总结一下就是把password转换成数字, 然后使用username和password对v16进行加密, 目前用户名和v16都已经知道, 那么就可以把v15求出来, 然后把v15转换成字符串即可.
解密脚本
1 |
|
最终结果:
1 | $ .\crackMe.exe |