本帖最后由 Double_Z 于 2015-5-28 21:28 编辑
最近一直在练习160CrackMe,也积累了一些东西,一直没时间发,今天可能刚好有些时间就找一些可以发的东西发一下。
这里有 44018723 发的相同的一篇帖子:可以参考一下 重复的内容我就不发了:http://www.52pojie.cn/thread-268511-1-1.html
首先找到关键代码区,查找字符串,或者右键->查找->所有程序间的调用 找到编辑框相关断点下断 运行即可。
[Asm] 纯文本查看 复制代码 00401255 > \3B05 58314000 cmp eax, dword ptr [0x403158]
0040125B . 74 0C je short 00401269
0040125D . 3B05 54314000 cmp eax, dword ptr [0x403154]
00401263 . 0F85 AE000000 jnz 00401317
00401269 > C705 D9124000>mov dword ptr [0x4012D9], 0x584554
00401273 . 6A 00 push 0x0 ; /IsSigned = FALSE
00401275 . 8D45 FC lea eax, dword ptr [ebp-0x4] ; |
00401278 . 50 push eax ; |pSuccess
00401279 . 6A 64 push 0x64 ; |ControlID = 64 (100.)
0040127B . FF35 50314000 push dword ptr [0x403150] ; |hWnd = NULL
00401281 . E8 BC010000 call <jmp.&USER32.GetDlgItemInt> ; \GetDlgItemInt
00401286 . 837D FC 00 cmp dword ptr [ebp-0x4], 0x0
0040128A 74 5F je short 004012EB
0040128C . 50 push eax ; 在此处下F2断点
0040128D . 6A 14 push 0x14 ; /Count = 14 (20.)
0040128F . 68 6C314000 push 0040316C ; |用户名
00401294 . FF35 54314000 push dword ptr [0x403154] ; |hWnd = NULL
0040129A . E8 AF010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
0040129F . 85C0 test eax, eax ; eax = length 用户名长度
004012A1 . 74 48 je short 004012EB
004012A3 . A1 0B304000 mov eax, dword ptr [0x40300B] ; 1480938563 0x58455443
004012A8 . BB 6C314000 mov ebx, 0040316C ; 用户名
004012AD > 0303 add eax, dword ptr [ebx] ; +*((int*)Name)
004012AF . 43 inc ebx
004012B0 . 81FB 7C314000 cmp ebx, 0040317C ; 循环 16次
004012B6 .^ 75 F5 jnz short 004012AD
004012B8 . 5B pop ebx
004012B9 . 03C3 add eax, ebx ; eax = 用户名累加 ebx = serial 注册码
004012BB . 3105 D9124000 xor dword ptr [0x4012D9], eax ; 00584554 ^ eax 对下面的代码进行改写
004012C1 . C1E8 10 shr eax, 0x10 ; 右移 16位
004012C4 . 66:2905 D9124>sub word ptr [0x4012D9], ax ; 0x4012d9 - ax
004012CB . BE EC114000 mov esi, 004011EC
004012D0 . B9 3E000000 mov ecx, 0x3E
004012D5 . 33DB xor ebx, ebx
004012D7 . EB 04 jmp short 004012DD
004012D9 54 push esp ; 改写位置
004012DA 45 inc ebp
004012DB 58 pop eax ; 改写位置
004012DC 00AD 33D84975 add byte ptr [ebp+0x7549D833], ch ; 改写位置
004012E2 ? FA cli
004012E3 . 81FB FBCFFCAF cmp ebx, 0xAFFCCFFB
004012E9 .^ 74 EE je short 004012D9
004012EB > 68 59304000 push 00403059 ; /Your serial is not valid.
004012F0 . FF35 5C314000 push dword ptr [0x40315C] ; |hWnd = NULL
004012F6 . E8 7D010000 call <jmp.&USER32.SetWindowTextA> ; \SetWindowTextA
004012FB . 33C0 xor eax, eax
004012FD . C9 leave ; (Initial CPU selection)
004012FE . C2 1000 retn 0x10
00401301 . 68 73 30 40 0>ascii "hs0@",0 ; YES! You found your serial!!
00401306 . FF35 5C314000 push dword ptr [0x40315C] ; |hWnd = NULL
0040130C . E8 67010000 call <jmp.&USER32.SetWindowTextA> ; \SetWindowTextA
00401311 . 33C0 xor eax, eax
00401313 . C9 leave
00401314 . C2 1000 retn 0x10
这段代码就是将要分析的代码 ,很短,算法也很简单。
首先是 这段代码:
[Asm] 纯文本查看 复制代码 0040128C . 50 push eax ; 在此处下F2断点
0040128D . 6A 14 push 0x14 ; /Count = 14 (20.)
0040128F . 68 6C314000 push 0040316C ; |用户名
00401294 . FF35 54314000 push dword ptr [0x403154] ; |hWnd = NULL
0040129A . E8 AF010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
0040129F . 85C0 test eax, eax ; eax = length 用户名长度
004012A1 . 74 48 je short 004012EB
004012A3 . A1 0B304000 mov eax, dword ptr [0x40300B] ; 1480938563 0x58455443
004012A8 . BB 6C314000 mov ebx, 0040316C ; 用户名
004012AD > 0303 add eax, dword ptr [ebx] ; +*((int*)Name)
004012AF . 43 inc ebx
004012B0 . 81FB 7C314000 cmp ebx, 0040317C ; 循环 16次
004012B6 .^ 75 F5 jnz short 004012AD
004012B8 . 5B pop ebx
004012B9 . 03C3 add eax, ebx ; eax = 用户名累加 ebx = serial 注册码
分析一下,大体意思就是这样滴:
[C] 纯文本查看 复制代码 #include "stdio.h"
#include "string.h"
#include "stdlib.h"
main()
{
char strUsername[20]="doubleZ"; // 用户名
int serial= 12345678; // 注册码
int *pSerial=&serial;
char *pName = strUsername;
int EAX = 0x58455443; //dword ptr [0x40300B]
int i=0;
int nLen=strlen(strUsername);
for( i=nLen;i<20;i++) //字符串后续地址清零
{
*(pName+i)=0;
}
for( i=0;i<16;i++)
{
EAX += *((int*)pName); // add eax, dword ptr [ebx]
pName++;
}
EAX +=serial; //add eax, ebx
printf("EAX:%X\n",EAX);
system("pause");
}
不好意思,我用 DEV C++ 写的,代码比较粗糙。可以用OD调试下程序,看看结果是否正确:运行程序,然后输入用户名,注册码(这个要和C代码中的一致)
然后,找到刚才的代码区,下断点,点击注册框即可断下。
[Asm] 纯文本查看 复制代码 0040128A /74 5F je short 004012EB
0040128C . |50 push eax ; 在此处下F2断点
F8 单步运行,也可直接F4 到此处:
[Asm] 纯文本查看 复制代码 004012B8 . 5B pop ebx
004012B9 . 03C3 add eax, ebx ; eax = 用户名累加 ebx = serial 注册码
查看EAX的值是否与自己写的代码得出的结果是否相同。
然后分析一下下面的代码
[Asm] 纯文本查看 复制代码 004012BB . 3105 D9124000 xor dword ptr [0x4012D9], eax ; 00584554 ^ eax 对下面的代码进行改写
004012C1 . C1E8 10 shr eax, 0x10 ; 右移 16位
004012C4 . 66:2905 D9124>sub word ptr [0x4012D9], ax ; 0x4012d9 - ax
004012CB . BE EC114000 mov esi, 004011EC
004012D0 . B9 3E000000 mov ecx, 0x3E
004012D5 . 33DB xor ebx, ebx
004012D7 . EB 04 jmp short 004012DD
第一句 004012BB . xor dword ptr [0x4012D9], eax ; 其中eax 为前面计算的那个数,0x4012d9 dword 是什么呢,多调试几次你就突然发现下面的代码会莫名的变红。对
其实就是下面的代码区(jmp 后的四个字节)。那为什么 到对这几个字节更改呢?看下面
第二 三句 [Asm] 纯文本查看 复制代码 004012C1 . C1E8 10 shr eax, 0x10 ; 右移 16位
004012C4 . 66:2905 D9124>sub word ptr [0x4012D9], ax ; 0x4012d9 - ax
同样是对0x4012d9 位置的地址进行更改,且是根据用户名和注册码计算出的码进行更改,那猜一下,如果是你,你会改成什么?现在还不知道,待会再猜。
在后面的几句是为JMP后面的循环清零;
看看JMP后面是什么?
[Asm] 纯文本查看 复制代码 004012DD AD lods dword ptr [esi] ; JMP跳到此处
004012DE 33D8 xor ebx, eax
004012E0 . 49 dec ecx
004012E1 .^ 75 FA jnz short 004012DD
004012E3 . 81FB FBCFFCAF cmp ebx, 0xAFFCCFFB
004012E9 .^ 74 EE je short 004012D9
这段程序的意思就是从地址004011EC 到 004011EC+0x3E(注意:这个区间包括0x4012d9这个位置,这点很重要) 取值,然后依次异或,最后的计算结果和0xAFFCCFFB比较,相等则跳转。如果不跳转,下面就是显示注册失败的提示,那么这里就是所谓的关键跳了。那么如果相等的话,那会跳转到哪里呢?按照正常的思路,应该是是提示注册成功的那个位置。
也就是这里:
[Asm] 纯文本查看 复制代码 00401301 . 68 73 30 40 0>ascii "hs0@",0 ; YES! You found your serial!!
00401306 . FF35 5C314000 push dword ptr [0x40315C] ; |hWnd = NULL
0040130C . E8 67010000 call <jmp.&USER32.SetWindowTextA> ; \SetWindowTextA
00401311 . 33C0 xor eax, eax
00401313 . C9 leave
00401314 . C2 1000 retn 0x10
但是,他没有跳到这里(爆破的话可以改跳到这里),而是跳到 0x4012d9这个位置,这个位置的代码是根据咱输入的用户名和注册码计算出来的,所以这里的代码也和咱的
注册码和用户名一样是错的。如何强制改下跳转,你会发现程序会死掉滴。
那么,假若,我们输入的是正确的用户名和注册码,那么0x4012d9 位置的代码是什么? 废话,当然是跳到提示注册成功的哪里喽!
我们将 0x4012d9 出的代码改一下,改成这样。若是这样的话,那么若通过注册码检验,则跳到这里,然后在跳到提示成功!
[Asm] 纯文本查看 复制代码 004012D9 /EB 26 jmp short 00401301 ; 改写位置
004012DB |90 nop ; 改写位置
004012DC |90 nop ; 改写位置
这里有两个关键字节,即EB 26 (jmp short 00401301)。那如何验证我们的猜想呢?往下看。
我们先总结一下作者的思路:首先对用户名和注册码进行一系列的计算,得出Number1,
然后通Number1对0x4012d9 进行异或,加减一系列的操作,总之就是要改变dword [0x4012d9]
这个数。改变这个数的目的,是为了改变0x4012d9处的运行代码(我们猜测含有EB 26 这两个字节)。
最后,对地址004011EC 到 004011EC+0x3E的Dword读取的数进行异或运算,判断是否符合要求。
我们发现地址004011EC 到 004011EC+0x3E 只有两个数是会改变的,即0x4012d8 和 0x4012dc 中的 前者的后三个字节,后者的第一个字节,
这正是被改写的0x4012d9 的 四个字节。 所以最后的验证是验证0x4012d9 是否被改写正确。
如果我们得出0x4012d9的值,那么我们就所有的问题就解决了。那如何得出0x4012d9 处的代码后者说是值呢?
我们在来看一下最后的验证的代码:
[Asm] 纯文本查看 复制代码 004012DD AD lods dword ptr [esi] ; JMP跳到此处
004012DE 33D8 xor ebx, eax
004012E0 . 49 dec ecx
004012E1 .^ 75 FA jnz short 004012DD
004012E3 . 81FB FBCFFCAF cmp ebx, 0xAFFCCFFB
004012E9 .^ 74 EE je short 004012D9
lods dword ptr[esi] 可以这样理解:mov eax,dword ptr[esi]; add esi,4;
即不断取值,然后异或,最后和0xAFFCCFFB 比较相同即可;
即 可以转换为
A xor B xor C ocr D ...... xor E ?= 0xAFFCCFFB ,其中有有两个 F 和 G (F G的一部分)是未知的。那如何求解F,G;
这里有关异或的资料可以参考:http://www.cnblogs.com/suoloveyou/archive/2012/04/25/2470292.html
看完资料我们会发现 A xor B = C ; 则 B = A xor C ;
好了,不废话了,说一下我的思路吧:
我们先计算 0x004012d8 处的值 因为这个值有三个字节是未知的,
把 0x004012d8 之前的 A xor D xor C xor D .... 计算出的值看成一个数 M 后面的看成 N
则 M xor dword [0x004012d8] xor N = 0xAFFCCFFB ;
dword [0x004012d8] = 0xAFFCCFFB xor M xor N ;
那我们如何计算 M 和 N 呢?
嗯,又是一个问题,怎么办呢?我们用现成的工具 ——OD;
再次运行,断下,单步到此处(或F4);
[Asm] 纯文本查看 复制代码 004012DD AD lods dword ptr [esi] ; JMP跳到此处
004012DE 33D8 xor ebx, eax
004012E0 . 49 dec ecx
004012E1 .^ 75 FA jnz short 004012DD
然后调试->设置条件->选择条件为真 填写 esi == 004012d8(注意 ==) 然后跟踪步入,你会发现esi为004012d8,
然后F8一步,此时把ebx的值记下 M =0xA213FC72.
然后F8 单步走到达 xor ebx,ebx 处 此时 eax 为 0x004012d8的值,此时把eax和ebx中的值改为零,当然你也可以只改eax中的值。这里将0x4012dc 中最后一个字节改为00 即D833AD00,
然后在F8,注意jne是否向上跳,不跳的时候把 ebx 记下 N = 0x59C9D849;
则 dword 0x4012d8 = 0xAFFCCFFB xor M xor N ;
计算看下面:
[C] 纯文本查看 复制代码 #include "stdio.h"
#include "stdlib.h"
main()
{
int b = 0xAFFCCFFB ^ 0x59C9D849 ^ 0xA213FC72;//b=0x5426EBC0
printf("%X",b);
system("pause");
}
0x5426EBC0,嗯,果然,有 eb 26 这两个字节,但是因为 0x4012d8 第一个字节是知道的为04
故0x4012d8 = 0x5426EB04;
然后用同样的方法计算出0x4012dc的值,跟踪 到 esi == 0x4012d8,然后F8 等到 0x004012d8 的数值载入到eax 时将eax 改为0x5426EB04这个值
然后 f8,我们用另一个方法,再次f8 到载入 0x4012dc 的值到eax 后,将eax 改为 0,然后f8直到循环结束,记下ebx 的值 S =0x77CF623F
0x4012dc = 0xAFFCCFFB xor S =0xD833ADC4; 明显和原来的数只差一个字节;
0x4012d9 = C45426EB;;//得到这个数的方法很多,同学们可以总结下。
然后我们向上分析:
[Asm] 纯文本查看 复制代码 004012BB . 3105 D9124000 xor dword ptr [0x4012D9], eax ; 00584554 ^ eax 对下面的代码进行改写
004012C1 . C1E8 10 shr eax, 0x10 ; 右移 16位
004012C4 . 66:2905 D9124>sub word ptr [0x4012D9], ax ; 0x4012d9 - ax
很简单: 0x00584554 xor C - C >> 0x10 =0xC41926eb ; 问题又来了,我们如何计算C?
我们转换一下:
0x00584554 xor C = C41926eb + C>>0x10;
方法很笨,但很有效:
[C] 纯文本查看 复制代码 #include "stdio.h"
#include "string.h"
#include "stdlib.h"
main()
{
unsigned int i=0x0;
unsigned int j=0x0;
unsigned int k=0x0;
for( i=0x1;i<=0xFFFFFFFE;i++)
{
j = 0x00584554^i;
k = 0xC45426EB + (i>>0x10);
if(j==k) printf("%X\t%X\t%X\n",i,j,k);
}
//0xC40CAFA3 Number1
//0xC45426EB
system("pause");
}
最终得到 C = 0xC40CAFA3;就是前面我们所说的用用户名和注册码得出的Number1;
最后用户名到注册码的算法前面分析过了,很简单,我把代码放下:
[C] 纯文本查看 复制代码 #include "stdio.h"
#include "string.h"
#include "stdlib.h"
main()
{
char strUsername[20]="doubleZ";
int serial= 0;
int *pSerial=&serial;
char *pName = strUsername;
int i=0;
int nLen=strlen(strUsername);
for( i=nLen;i<20;i++)
{
*(pName+i)=0;
}
serial =0x58455443;
for( i=0;i<16;i++)
{
serial += *((int*)pName);
pName++;
}
serial = 0xC40CAFA3-serial;
printf("Name:%s\n",strUsername);
printf("Serial:%u\n",serial);
//Name:doubleZ
//Serial:3703760779
system("pause");
}
最后得出一组用户名和注册码Name:doubleZ ,Serial:3703760779。但是这个注册码只能在调试时通过,正常情况下不能显示成功,这个好像程序设计时没处理好,
嗯,就这样吧,反正我们的最终的目的达到了——了解CrackMe作者的思路。
这是我第一次发帖,水平有限,莫怪。好吧,我把文件放下赚点外快。
CrackMe_024.rar
(31.35 KB, 下载次数: 34)
|