第34个CM程序,从160个CM的CHM文件中可以看出采用的是Keyfile验证方式,也就是要有一个验证文件,里面有相应的信息,才能象第32个CM一样正常打开程序。
打开CM程序,标题栏里显示3.0版本,未破解字样。点击File菜单项,只有一个“Exit”退出命令,点击就结束了程序;Help菜单内是“About”命令,没有可以利用的地方。
第一步、查壳:
无壳,ASM汇编程序:
第二步、爆破:
用OD载入Cruehead.3.exe,先中文搜索,很容易看到了各种文本提示内容,和第32个CM一样,看到了CRACKME3.KEY这个keyfile文件名,再向下有一个正确提示和两个错误提示:
和第32个CM一样,先在CM所在目录新建一个CRACKME3.KEY文件,这是一个注册表文件,感觉里面应该要写点什么东西吧。先不管那么多,再打开CM程序,和没有CRACKME3.KEY一样,程序界面及菜单项均无任何改变。双击0040118C这一行进入CPU窗口,上下观察代码,在00401188和0040118A两句应该是关键比较和关键跳,跳过了正确提示 ;
在0040119B处是一个call,跟入查看,这里应该是经计算后得到弹出正确提示框。
返回0040119B处,将0040118A处的跳转命令nop掉试一试,将修改后的可执行文件命名为Cruehead.3.nop.exe,运行一下,主窗口和正确提示框同时出现:
应该是爆破成功了,但主窗口的标题栏中还显示出“Uncracked”的未破解字样,没有完全破解成功。
第三步、追码:
从爆破成功的弹出框中看到内容第一行,应该是说是被某某某破解,所以在“Cracked by:”后面是名字,应该是写在CRACKME3.KEY这个注册表文件中的吧。重载程序,F8单步从头开始向下运行,至00401016到0040102D处要读取“CRACKME3.KEY”的注册表文件,如果不存在,则不弹出注册窗口:
[Asm] 纯文本查看 复制代码 00401016 |. 6A 00 push 0x0 ; /hTemplateFile = NULL 模板文件:为空
00401018 |. 68 80000000 push 0x80 ; |Attributes = NORMAL文件标志和属性:为一般文件
0040101D |. 6A 03 push 0x3 ; |Mode = OPEN_EXISTING创建属性:文件必须存在,如果文件或设备不存在,那么CreateFile调用会失败
0040101F |. 6A 00 push 0x0 ; |pSecurity = NULL安全属性:为空
00401021 |. 6A 03 push 0x3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE 共享模式:允许共享读写
00401023 |. 68 000000C0 push 0xC0000000 ; |Access = GENERIC_READ|GENERIC_WRITE 读取方式:可读、可写
00401028 |. 68 D7204000 push Cruehead.004020D7 ; |CRACKME3.KEY 文件设备名:CRACKME3.KEY
0040102D |. E8 76040000 call <jmp.&KERNEL32.CreateFileA> ; \CreateFileA 返回值:执行成功,则返回文件句柄
到00401032处,因为上一句执行成功,此时eax=0x44,所以00401035处会跳走到读取CRACKME3.KEY这个文件的内容:
[Asm] 纯文本查看 复制代码 00401043 |> \A3 F5204000 mov dword ptr ds:[0x4020F5],eax ; D
00401048 |. B8 12000000 mov eax,0x12
0040104D |. BB 08204000 mov ebx,Cruehead.00402008
00401052 |. 6A 00 push 0x0 ; /pOverlapped = NULL 参数设为NULL表示不是异步读取操作
00401054 |. 68 A0214000 push Cruehead.004021A0 ; |pBytesRead = Cruehead.004021A0从文件中实际读取的字节数
00401059 |. 50 push eax ; |BytesToRead = 12(18.) 要读入的字符数
0040105A |. 53 push ebx ; |Buffer = Cruehead.00402008 用于保存读入数据的缓冲区
0040105B |. FF35 F5204000 push dword ptr ds:[0x4020F5] ; |D
00401061 |. E8 30040000 call <jmp.&KERNEL32.ReadFile> ; \ReadFile 调用成功,返回非0调用不成功,返回为0
00401066 |. 833D A0214000>cmp dword ptr ds:[0x4021A0],0x12 ; 文件中不能少于18个字符
0040106D |.^ 75 C8 jnz short Cruehead.00401037 ; 不等跳向失败
00401059 处ReadFile 的BytesToRead参数为0x12,即要读入的字符数为18个,而在00401066处指令是“cmp dword ptr ds:[0x4021A0],0x12”,也就是说实际读取出的字数与要读取的字数进行比较要相等,不等则跳走,失败了。所以在00401061处下断,重载CM程序,再切换到CM程序所在目录,打开CRACKME3.KEY文件,随意输入不少于18个字符并保存文件(我输入的是“qwertyuiopasdfghjklasdfghjklzxcvbnm”共26个字母)。打开CM原程序,仍然没有任何改变;关闭后再打开Cruehead.3.nop.exe,运行一下,主窗口和正确提示框同时出现,且提示框内容已经改变了:
可以看出后面一串字符肯定不是正确的名字,所以还要切换回OD,F9运行,继续调试:从00401061开始单步向下,到0040106F处,将CRACKME3.KEY文件中的前18个字符“qwertyuiopasdfghjk”压入堆栈;
运行到00401074处时,先查看了一下ds:[0x4020F9]=0,F7跟入00401311中:
先将ecx清零,作为下面循环次数的暂存器;再将eax清零,作为serial字符的暂存器;esi存入CRACK3.KEY文件的前18个字符;bl赋值为0x41;al得到第1个字符,再与bl异或,得到的值累加存入ds:[0x4020F9]中,异或的值转换成的字符再存回原字符的位置(待循环结束,经0040133C子程序验证后作为破解者名字出现在正确提示窗口中);然后字符位置向后1位, bl值加1,循环次数加1,总共循环14次求值,其中如果CRACK3.KEY文件的前14个字符中某1个字符和bl异或后的值为0时就会跳出循环;
运行到00401335处,将ds:[0x4020F9]中的值与0x12345678异或后重新存入ds:[0x4020F9]中备用;下一句retn回00401079处,再查看ds:[0x4020F9]= 0x29E;此时0040106F和00401086处注释变成了“ASCII "05&61?2!&:*?)(ghjk”,00401079处运行后,ds:[0x4020F9]= 0x123454E6;
继续运行到00401086处,将处理过的字符串压栈作为0040108B处call的参数继续进行处理, F7跟入0040133C中:
可以看出这个call的作用是先得到处理过的字符串,然后esi的地址向后0xE(14)位,即来到0x402008+0xE=0x402016处,从CRACK3.KEY文件中第18、17、16、15个字符的16进制ASC值并连接起来赋予eax,然后retn回00401090处:
[Asm] 纯文本查看 复制代码 00401090 |. 83C4 04 add esp,0x4
00401093 |. 3B05 F9204000 cmp eax,dword ptr ds:[0x4020F9] ; 此处必须相等,否则还是失败!!!!
00401099 0f94c0 sete al ; if eax = ds:[0x4020F9] then set al=1 else set al =0 endif
0040109C |. 50 push eax
0040109D |. 84C0 test al,al
0040109F ^ 74 96 je short Cruehead.00401037 ; 跳至主窗口标题后半部分为uncracked的子程序
004010A1 |. 68 0E214000 push Cruehead.0040210E ; CrackMe v3.0
004010A6 |. E8 9B020000 call Cruehead.00401346 ; al=1时,得出主窗口标题后半部分内容为cracked的子程序
004010AB |. 83C4 04 add esp,0x4004010AE |> 6A 00 push 0x0 ; /Title = NULL
004010B0 |. 68 28214000 push Cruehead.00402128 ; |No need to disasm the code!
004010B5 |. E8 9A030000 call <jmp.&USER32.FindWindowA> ; \FindWindowA
004010BA |. 0BC0 or eax,eax
004010BC |. 74 01 je short Cruehead.004010BF
004010BE |. C3 retn
004010BF |> C705 76204000>mov dword ptr ds:[0x402076],0x4003
到00401093处将eax与ds:[0x4020F9]进行比较,结合下面的指令并多次调试后可知此处如果不相等,则运行到0040109F处后跳到00401037处,再call 004012F5子程序,将主窗口标题后半部分变成未注册字样(-Uncracked);eax与ds:[0x4020F9]相等,则到0040109F处不跳走而向下,到004010A6处调用00401346子程序,主窗口标题后半部分变成了已注册字样(-Cracked),然后向下就会按照标准的win32程序启动过程,先是FindWindowA函数获得顶层窗口的句柄,LoadIconA加载图标、LoadCursorA加载鼠标指针、RegisterClassA注册类,再到CreateWindowExA创建主窗体等,再利用作者修改后的win32过程函数Cruehead.WndProc()来接收消息,从而弹出正确提示框。
所以注册算法主要是在00401311和0040133C这两个call中。
从追码的过程中,可以看到爆破过程还是不完全的,没有将主窗口的标题改成“Cracked”,而追码中明白了作者的思路,所以应该在0040109F和0040118A处将跳转命令nop掉,保存为Cruehead.3.nop.nop.exe,运行一下,成功了,主窗口标题是“cracked”,弹出提示框也是正确的。
[Asm] 纯文本查看 复制代码 0040109F ^\74 96 je short Cruehead.00401037 ; 跳至主窗口标题后半部分为uncracked的子程序
0040118A /75 17 jnz short Cruehead.004011A3
注册算法:先读取CRACK3.KEY文件的内容,依次取前14位字符的ASC码与A、B......到N的ASC码分别异或,得到的值再转换成字符,成为破解者名字;异或值累加,再与0x12345678异或后,值的16进制数值形式与第18、17、16、15位字符的16进制ASC值连接起来相同,则破解成功,否则不成功。
注册算法出来了,注册机编写:
[Visual Basic] 纯文本查看 复制代码 Option Explicit
Private Sub Command1_Click()
Dim Serial, se1, Name, Key As String 'Key文件中的前14个字符、第18至15个字符的16进制数值连接形成的新数值、破解者名字、密钥
Dim DS004020F9, Bl, i As Integer 'i为循环数,DS004020F9为CM程序中内存地址缓存,Bl依次为A至K的16进制数值
Dim S() As Integer 'S()为14个字符分别与A至N异或的值
Name = Text1.Text 'Name来自于文本框输入,如果未输入则默认为“[url=http://www.52pojie.cn]www.52pojie.cn[/url]”,Serial为写入注册表文件中的前14位字符
If Name = "" Then
Name = "www.52pojie.cn"
Text1.Text = Name
Text2.Text = "654jpt7'##.b. hS4"
Exit Sub
End If
If Len(Name) > 14 Then Name = Mid(Name, 1, 14) '如果超过14个字符则只取前14个
If Len(Name) < 14 Then Name = Name & String(14 - Len(Name), " ") '如果不够14个字符则后面补空格到14个
'Form1.Print Name
Bl = 65
ReDim S(1 To 14)
For i = 1 To 14 '得到前14个字符
S(i) = Asc(Mid(Name, i, 1)) Xor Bl
Bl = Bl + 1
Serial = Serial & Chr(S(i))
'Form1.Print "Serial(" & i & ") =" & Serial
DS004020F9 = DS004020F9 + Asc(Mid(Name, i, 1))
If S(i) = 0 Then Exit For
Next i
'Serial = Trim(Serial):习惯性思维去空格,却忘记了空格也是一个字符!
If Len(Serial) < 14 Then Serial = Serial & String(14 - Len(Serial), "0")
DS004020F9 = DS004020F9 Xor &H12345678
'Form1.Print "DS004020F9=" & DS004020F9
se1 = Hex(DS004020F9)
'Form1.Print "se1=" & se1
For i = 4 To 1 Step -1 '得出后4位字符
Key = Key & Chr(Val("&H" & Mid(se1, 2 * i - 1, 2)))
'Key = Key & Chr(Val("&H" & Mid(se1, i, 2)))
'Form1.Print "Key=" & Key
Next i
Text2.Text = Serial & Key '一个问题,在Key最前面是空格时连接不到一起来,
Command2.Visible = True
End Sub
Private Sub Command2_Click() '生成注册文件
Open "CRACKME3.KEY" For Output As #1
Print #1, Text2.Text
Close
End Sub
用VB编写注册机过程中却是错了一遍又一遍,郁闷了很久,自己猜测是不是CM程序自身有问题?毕竟异或0x12345678的结果大部分前4位是1234,转换成字符是和4,而后4位就多了,如E6、8A之类,这些用chr()函数得不出正确的结果。而且在Text2.Text = Serial & Key这句代码中如果Key第1个字符是空格,则无法连接,只显示了前面Serial的字符,盼请各位大神指教!!!
终于完成了!
附件
034.rar
(169.3 KB, 下载次数: 5)
,含CM原程序、爆破后的程序、注册机、OD的调试文件等。百度链接是:http://pan.baidu.com/s/1skMkJY9,密码: 86pm,160个CM、我已练习过的前34个crackme程序(012未能破解)都在里面。 |