打开019程序,与018一样的作者,只是要输入用户名和注册码后进行检测:
直接点击“Check it”按钮,程序弹出提示,要求用户名长度要5个以上;
用户名随意输入“11111”,注册码输入“22222”再点击,弹出错误提示:
仍然先查壳,C++ 6.0编写,无壳:
第一步、爆破程序:
用OD加载程序,用“中文搜索引擎”的智能搜索功能,找到了如下字符串:
这次有两个地方要爆破,一个是用户名输入时的限制,一个是爆破注册码:
很容易知道00401582处是用户名长度限制,00401669处是注册成功的提示,那么先双击00401582地址,回到CPU窗口:
随意看了一下代码,很容易就找到了关键跳等信息:
00401579处地址跳过了用户名长度的限制,jge直接改成jmp:
[Asm] 纯文本查看 复制代码 00401572 |. 8945 E4 mov [local.7],eax
00401575 |. 837D E4 05 cmp [local.7],0x5
00401579 |. 7D 43 jge short Brad_Sob.004015BE
0040157B |. 6A 40 push 0x40
0040157D |. 68 20404000 push Brad_Sob.00404020 ; CrackMe
00401582 |. 68 28404000 push Brad_Sob.00404028 ; User Name must have at least 5 characters.
............
004015BE |> C745 E0 00000>mov [local.8],0x0
先保存成可执行文件Brad Soblesky.2-user.nop.exe,试运行一下,不管输不输入,输入什么都不再限制了;
再向下找到00401669处,注册成功的提示,向上查看,到00401642处,命令是jnz Brad_Sob.00401747,跳过成功,走向失败,将这一句nop掉:
[Asm] 纯文本查看 复制代码 00401640 |. 85C0 test eax,eax ; Brad_Sob.00403430
00401642 0F85 FF000000 jnz Brad_Sob.00401747 ; 跳过成功,走向失败
将所有修改保存为可执行文件Brad Soblesky.2-user-Serial.nop.exe,运行Brad Soblesky.2-user-Serial.nop.exe,直接点击“Check“按钮,弹出成功,说明用户名长度限制与注册码都爆破了。
第二步、追注册码:
接下来找注册码,可以先向上翻到“Check”按钮事件的代码段首004014DF处下断,这次我是直接在004015BE处(过了用户名长度限制)下断:
Ctrl+F2键返回程序开始(将刚才修改的两个爆破处还原),
F9运行程序,
输入用户名“52pojie”,注册码“123450”,点击“Check”按钮,程序中断于004015BE处:
查看了一下寄存器窗口,看到EBP-18存入假码,EBP-14存入用户名;
F8单步向下,从004015BE到0040161A之间是生成注册码的代码段,头疼的是一大堆的逻辑与运算、有符号数乘法、异或运算,心里就发怵。
[Asm] 纯文本查看 复制代码 004015BE |> \C745 E0 >mov [local.8],0x0 ; 此时eax=用户名长度,[local.8]首次为0,以后每次+1
004015C5 |. EB 09 jmp short Brad_Sob.004015D0
004015C7 |> 8B55 E0 /mov edx,[local.8] ; edx=[local.8];循环开始,edx开始取值?
004015CA |. 83C2 01 |add edx,0x1 ; edx=edx+1
004015CD |. 8955 E0 |mov [local.8],edx ; [local.8]=edx
004015D0 |> 8B45 E0 mov eax,[local.8] ; eax=[local.8];第一次从上面跳过来,第二次从循环中来
004015D3 |. 3B45 E4 |cmp eax,[local.7] ; eax与长度比较:[local.7]=len(user)
004015D6 |. 7D 42 |jge short Brad_Sob.0040161A ; eax>=len(user)时跳,即循环了用户名长度次数后跳
004015D8 |. 8B4D E0 |mov ecx,[local.8]
004015DB |. 51 |push ecx
004015DC |. 8D4D EC |lea ecx,[local.5] ; ecx=用户名 [local.5]=[ebp-0x14]=user
004015DF |. E8 1C030>|call <Brad_Sob.ASC(Strings(n))> ; al=asc(user(n))
004015E4 |. 0FBED0 |movsx edx,al ; edx=al=ASC(user(n));n为每次循环的当次序数
004015E7 |. 8B45 F0 |mov eax,[local.4] ; [local.4]=[ebp-10],第一次循环前值为0x81276345,固定值
004015EA |. 03C2 |add eax,edx ; eax=eax+edx(16进制)
004015EC |. 8945 F0 |mov [local.4],eax ; [local.4]=[local.4]+hex(asc(user(n)))
004015EF |. 8B4D E0 |mov ecx,[local.8] ; ecx=[local.8]([ebp-20]),ecx(当前循环次数(从0开始))=n
004015F2 |. C1E1 08 |shl ecx,0x8 ; ecx左移8位:ecx=ecx*(2^8)(10进制的算法)
004015F5 |. 8B55 F0 |mov edx,[local.4]
004015F8 |. 33D1 |xor edx,ecx ; 按位'异或'运算,结果送至edx中
004015FA |. 8955 F0 |mov [local.4],edx ; [local.4] = [local.4] xor [local.8]
004015FD |. 8B45 E0 |mov eax,[local.8]
00401600 |. 83C0 01 |add eax,0x1 ; 循环次数+1:n=n+1
00401603 |. 8B4D E4 |mov ecx,[local.7] ; [local.7]=len(user)
00401606 |. 0FAF4D E>|imul ecx,[local.8] ; ecx=[local.7]*[local.8]=len(user)*(n+1)
0040160A |. F7D1 |not ecx ; 对ecx按位求反运算(即0变1,1变0),结果返回ecx?
0040160C |. 0FAFC1 |imul eax,ecx ; eax=eax*ecx=(n+1)*(not ecx)????
0040160F |. 8B55 F0 |mov edx,[local.4]
00401612 |. 0FAFD0 |imul edx,eax ; edx=edx*eax=[local.4]*{(n+1)*[not 【len(user)】*n])????
00401615 |. 8955 F0 |mov [local.4],edx ; [ebp-0x10]存入以上结果(即注册码)
00401618 |.^ EB AD \jmp short Brad_Sob.004015C7
0040161A |> 8B45 F0 mov eax,[local.4] ; 循环运算后得到注册码
运行到004015F2处,命令是shl ecx,0x8,意思是ecx里的数逻辑左移8位?ecx=0,执行命令后,寄存器窗口中ECX 为0不变,下面的标志位中P、A、Z均变成了1,S变成了0;
Shl命令给我难住了,不知道该如何进行下去。再向下看,Not命令后的乘法又不会了!什么意思嘛!从网上疯狂地搜索,搞明白了shl和not这两个命令,基本上ecx=shl ecx,0x8是说ecx中的10进制数值*2的N次方再存入ecx中,Not命令后的结果是10进制数4294967296减去寄存器中的10进制值:
再向下基本上算法出来了:
Result = 0x81276345
For n=0 to len(user)-1
Result = Result + Hex(asc(user(n) ’
Result = Result xor (n shl,0x8)
Result = (n+1) * (not len(user) * n) * Result
Next n
循环7次后,得到注册码,继续向下,对得到的数值进行格式化,是无符号数格式:
[Asm] 纯文本查看 复制代码 0040161D |. 50 push eax
0040161E |. 68 54404>push Brad_Sob.00404054 ; %lu 无符号数格式
00401623 |. 8D4D DC lea ecx,[local.9] ; [local.9]=[ebp-24]:存入计算出的注册码
00401626 |. 51 push ecx
00401627 |. E8 52070>call <jmp.&MFC42.#CString::Format_2818> ; 可能是字符串格式函数,ecx=真码
0040162C |. 83C4 0C add esp,0xC
0040162F |. 8D4D DC lea ecx,[local.9]
00401632 |. E8 79020>call Brad_Sob.004018B0 ; eax=真码,经格式化转变后得到
到0040162C处运行后寄存器窗口中数值如下,感觉“3186769159”这一串数字就是注册码了:试着往程序019中输入,成功了,说明感觉是正确的。
继续到0040163B处,调用检测代码段进行计算:
[Asm] 纯文本查看 复制代码 00401637 |. 50 push eax
00401638 |. 8D4D E8 lea ecx,[local.6] ; [local.6]=[ebp-18]:存入的假码
0040163B |. E8 80020>call <Brad_Sob.IsTrue(Sn)> ; 应该是检测注册码真假,存入eax中
00401640 |. 85C0 test eax,eax
00401642 0F85 FF0>jnz Brad_Sob.00401747 ; 跳过成功,走向失败
到这里,信心满满啊,这个程序也不复杂嘛!开始编写注册机,由于自己不会C语言,仍用VB进行,就在这一步卡了一个多星期!!!
注册算法正确,可是第一次循环过程中,Result+asc(user(n)就开始出错,开始了漫漫的调试修改之路。程序一运行,就是提示6号数值溢出错误,于是将Result定义为双精度型,继续运行,仍然超出了范围!从网上搜索了大量的资料,基本上都是C++及其他语言的,极少有VB的!自己按说明将大数定义为字符,反反复复地修改,几乎要崩溃了!后来,后来,借网上几个大数运算的函数套了进去,然后运行,仍是错误重重,不是首位多了空格,就是结果少了1位,等等。
现在的注册机虽然写出来了,但肯定也会有数据溢出的问题存在,只是精力不足,只能留待以后学习完善,更盼望大神指导!
附件,含CM原程序、爆破后的程序及019注册机。百度链接是:http://pan.baidu.com/s/1skMkJY9密码: 86pm,160个CM、我已练习过的前19个crackme程序(不含012)都在里面。
|