[原创]看雪CTF 2016 第29题分析 29-weigou

yber 2016-12-31 15:45 1726
从我实际找注册码的角度,分析下第29题。
A1.拿到该题,首先分析其窗口过程函数DialogFunc,找到按钮”Check”的相应函数

如果String[8] != ’\0’,这提示”Err”,即注册码长度是8.
计算8字符的和,传参数到函数yboncke1sub_401B0A ,发现是一个switch循环,比较绕,无法发现到弹出”Good”的地方。
A2.陷入僵局的情况下,重启调试器X64Dbg,修改任意指令,如4012B7 cmp ax,69cmp ax68,F9后程序不能启动,报异常。怀疑是有防止篡改的功能,从程序头401000开始单步执行,sub_401039执行后异常.
1.        00401000 | 6A 00            | push 0                             
2.        00401002 | E8 43 0F 00 00   | call <crackerme.GetModuleHandleA>  
3.        00401007 | A3 9C 3C 40 00   | mov dword ptr ds:[403C9C],eax      
4.        0040100C | 6A 00            | push 0                             
5.        0040100E | 6A 00            | push 0                             
6.        00401010 | 6A 00            | push 0                             
7.        00401012 | E8 22 00 00 00   | call crackerme.401039              
8.        00401017 | E8 64 0F 00 00   | call <crackerme.InitCommonControls
9.        0040101C | 6A 00            | push 0                             
10.        0040101E | 68 65 12 40 00   | push crackerme.401265              
11.        00401023 | 6A 00            | push 0                             
12.        00401025 | 6A 65            | push 65                           
13.        00401027 | FF 35 9C 3C 40 00| push dword ptr ds:[403C9C]         
14.        0040102D | E8 24 0F 00 00   | call <crackerme.DialogBoxParamA>  

A3.  Sub_401039先调用sub_401080,后比较内存,被篡改则触发异常。   

A4. 观察sub_401080,从win32系统库user32.dll  kernel.32.dll ntdll.dll 加载函数到全局变量。程序通过全局变量函数完成真正的注册码校验功能,看不到函数名,给静态分析造成不便。

A5.  任意选择一个全局变量,如dword_403BA8,查找引用,
0040150E | B8 A8 3B 40 00   | mov eax,crackerme.403BA8  
00401513 | 80 38 00         | cmp byte ptr ds:[eax],0   
00401516 | 74 2F            | je crackerme.401547      
00401518 | 66 8B 58 07     | mov bx,word ptr ds:[eax+7]//都是通过”+7”,”+9”找到真正的函数地址
0040151C | C1 E3 10         | shl ebx,10               
0040151F | 66 03 58 09      | add bx,word ptr ds:[eax+9]
00401523 | 6A 00            | push 0                    
00401525 | 6A 00            | push 0                    
00401527 | 6A 00            | push 0                    
00401529 | 68 7C 19 40 00   | push crackerme.40197C     
0040152E | 6A 00            | push 0                    
00401530 | 6A 00            | push 0                    
00401532 | FF D3            | call ebx            //调用从      403BA8  转化来的函数

A6.调试器正常加载并运行程序的情况下,在内存中转到地址403BA8  ,
“86767534”(7686<<16 + 3475)=76863475,汇编页转到地址76863475,
提示“正确的表达式->kernel.CreateThread”,即dword_403BA8就表示函数CreateThread。
其他全局变量按此方法查找,可以找到GetDlgItemTextAdword_403B3A,WaitForMultipleObjectdword_403BBE,VirtualAllocdword_403C0B等关键全局变量的实际意义。
分析到这里,可以事半功倍了。^_^

A7.查找对GetDlgItemTextAdword_403B3A的引用,位于线程函数ybThread3sub_4016F0中。
输入的8字节字符串分别与0x66亦或,存入全局地址gByte1dword_403C85中,然后
SetEvent gEventArraydword_4032F1。

A8. 查找gEventArraydword_4032F1和gByte1dword_403C85引用。
位于ybThread5sub_4017F9中,先分配60字节的内存空间,异或方式填充字节,即弹出”Good”的代码。


A9.编写代码反推注册码。
根据弹出”Err”的代码:
00401350 | 6A 00            | push 0                       
00401352 | 68 B1 32 40 00   | push crackerme.4032B1        “Err"
00401357 | 68 B1 32 40 00   | push crackerme.4032B1        “Err"
0040135C | FF 35 ED 32 40 00| push dword ptr ds:[4032ED]   
00401362 | E8 13 0C 00 00   | call <crackerme.MessageBoxA>

字符串”Good!”—> 004032B5 aGood           db 'Good!',0 ,所以解密后的代码应该包含
char p[10]={0x68,0xB5,0x32,0x40,0x00,0x68,0xB5,0x32,0x40,0x00};考虑用KMP串匹配算法匹配。
输入字符与0x66异或,A0var_4+ A1var_8==0x32113442,用遍历 62*62*62*62中可能即可获得注册码,遍历过程如下:


A10.得到注册码的前4位,再推出后四位,如下可能:
KAhu  skey
KQhu s沝y
Kahu  sKey
Kqhu s{ey

验证后,正确注册码是    KAhuskey    .

附件: 看雪CTF 2016 第29题分析 29-weigou .rar
上传的附件:
最新回复 (2)
LYQINGYE 2016-12-31 15:57
2
好文,mark
qqsunqiang 2016-12-31 21:36
3
谢谢楼主的分享。
返回