本帖最后由 海天一色001 于 2019-8-3 15:51 编辑
第33个CM程序,打开程序,选择“Help—Register”命令,输入name/serial=“52pojie/1234567890”,点击“OK”按钮,注册窗口消失,弹出两次错误提示框:
第一步、查壳:
无壳,ASM汇编程序:第二步、爆破:用OD载入Cruehead.1.exe,先中文搜索,很容易看到了各种文本提示内容,有一个正确提示和两个错误提示:
双击“Good work!”这一行进入CPU窗口:
向下查看,0040134D到00401361处是弹出正确提示框的子程序代码,00401362到0040137D处是错误提示框的子程序代码,再向下004013AD到004013BC处又是错误提示框的代码。从上图看到正确提示框最后一行代码是retn,说明这一段应该是一段子程序,要有调用的地址。点击0040134D这一行,信息栏中出现“本地调用来自 0040124C”的提示;
再向下点击第一段错误提示框的子程序开始行00401362处,信息栏中出现“本地调用来自 00401245”的提示:
第二段错误提示框的代码被包含于0040137E到004013C1处的子程序中,点击子程序首行0040137E处,信息栏中出现含“本地调用来自 0040122D”的提示。
注意看了一下这三个调用来源,地址基本上是在一起的,所以在任一信息栏中右键点击“本地调用来自 XXXXXXXX”这一行,选择“转到CALL来自XXXXXXXX”命令,均可来到调用来源处查看相应代码,
这里我返回第二段错误提示的代码调用地址0040122D处:仔细观察上下文代码,0040122D、00401245两处调用错误子程序,那么将其改为调用正确提示的子程序地址0040134D,那么不就行了吗?下图为已修改过的代码,存为可执行文件Cruehead.1.call.sucess.exe,运行一下,如果不输入name/serial,点击OK按钮,注册窗口消失,不弹出正确提示;随意输入name/serial,则弹出两次正确提示,还没有完全爆破成功。
将修改的两处call 正确提示子程序处撤消修改,再来看这一段代码:00401228处得到name字符串,0040122D调用第二个错误子程序(这里注释成第二个错误子程序是错误的),在0040122D处下断,F9运行CM程序,选择“Help—Register”命令,输入name/serial=“52pojie/1234567890”,点击“OK”按钮,程序中断,F7跟入0040137E处:
F8单步向下,运行到0040138B处,跳转到004013AC处成立,会弹出失败提示框,也就是说name字符的最小值是大写字母A,所以在寄存器窗口将双击标志位“C”的值“1”,将其改变为“0”,使跳转不成立,然后继续单步向下,一直到004013C1处,retn到00401232处。可以看出这段代码的作用是先判断name输入框中的每一个字符的ASC值是否不小于0x41,如果小于则弹出失败提示框,这就是在注册窗口输入name/serial=“52pojie/1234567890”后会出现两次错误提示的原因了,第一次是name字符串不合要求,第二次是serial不匹配name。如果字符的ASC值大于0x5A则减去0x20,然后在004013C2处对name字符串进行运算处理。所以要爆破,可以在0040137E处直接retn掉,或者在0040122D处nop掉,根本不对name进行任何判断和处理,就不会弹出第一次错误提示框了。F8继续向下,可以看出00401241处为关键比较,00401243处是关键跳,跳到正确提示框。
所以爆破的话,将0040122D处nop掉,将00401243处je改成jmp,将修改后的文件保存为可执行文件Cruehead.1.nop_jmp.exe,运行一下,随意在name/serial文本框中输入任意字符,点击“OK”按钮,弹出正确提示,爆破成功。第三步,追码:再来查找注册算法:Ctrl+F2重载CM程序,F9运行,因为爆破中已经对0040137E处进行了大致分析,知道name\字符串的要求,所以在注册界面输入name/serial=“wapojie/1234567890”,点击“OK”按钮,程序中断于0040122D处。上下观察代码,00401228处,将存储name字符串的地址压栈,作为下一行指令中0040137E这个call的参数,得到结果存入eax中;00401233处,将存储serial字符串的地址压栈,调用004013D8处的子程序;继续向下将结果存入ebx中,比较eax和ebx,相等则跳向正确,不等则失败。可知0040122D和00401238处的调用的两个call分别对name/serial进行运算,这两个call内就是注册算法了。
F7跟入0040137E这个call中,F8向下:前面已经对0040137E至0040139A处进行了分析,直接到0040139C处,将esi(name.text)压栈到作为下一行004013C2这个call的参数;F8向下,F72进入004013C中,
这是一个小循环,以name字符串的长度作为循环次数,作用是将name字符串得到的每一个ASC值进行累加,值存入edi中,循环结束返回004013A2处,继续F8运行,004013A2到004013C1处,edi = edi xor 0x5678,再存入eax中,返回00401232处,此时eax=0x5477;F8向下,到00401238处F7进入004013D8:
这里也有一个循环,作用是将每一个serial中的字符ASC值减去0x30,乘以0xA,再加上下一个字符ASC值减去0x30,得到新的结果,这个结果再乘以0xA,这样循环次数到serial字符串长度值时结束。如果注册码只是数字的话,其实就是将注册码的数字所有的0全部去掉得到的值再乘以10,再与4660(0x60)异或得到ebx的值。如果注册码中含有其他字符,暂时不考虑。程序验证注册码的算法出来了,用name字符串的ASC值累加,得到的值与0x5678异或,最终存储到eax中;将注册码中所有的0全部去掉得到的值再乘以10,再与4660(0x60)异或得到ebx的值;如果eax=ebx,则注册码正确。所以注册机算法就是先得到name字符串,将每个字符的ASC值相加,得到的值分别与0x5678、0x1234异或后转换成10进制数即可(不知道serial用字母行不行,没试验这一点):VB形式的代码如下:【刚开始时没有“If Len(name) > 11 Then name = Mid(name, 1, 11)”这一句,所以在name字符串长度超过11位之后生成的注册码中出现了错误,在OD中自己将API函数基本上都注释了一遍,所以发现了在004012C4和004012E4这两个API函数GetDlgItemTextA中对文本长度进行了限制,最多只有11位】
[Visual Basic] 纯文本查看 复制代码 Option Explicit
Private Sub Command1_Click()
Dim name As String
Dim NameInt, n(), serial, i As Integer
name = Text1.Text
If name = "" Then
Text1.Text = "WAPOJIE"
Text2.Text = "17987"
Exit Sub
End If
If Len(name) > 11 Then name = Mid(name, 1, 11)
ReDim n(1 To Len(name))
For i = 1 To Len(name)
n(i) = Asc(Mid(name, i, 1))
If n(i) > 90 Then n(i) = n(i) - 32
NameInt = NameInt + n(i)
Next i
serial = NameInt Xor 17484
Text1.Text = name
Text2.Text = serial
End Sub
Private Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii < 65 Or KeyAscii > 122 Then KeyAscii = 0
End Sub 附件
033.rar
(213.18 KB, 下载次数: 8)
,含CM原程序、爆破后的程序、注册机、OD的调试文件等。百度链接是:http://pan.baidu.com/s/1skMkJY9,密码: 86pm,160个CM、我已练习过的前33个crackme程序(不含012)都在里面。 |