首页
社区
课程
招聘
[原创]160个CrackMe之023 Chafe.1.exe
发表于: 2015-10-15 15:46 14974

[原创]160个CrackMe之023 Chafe.1.exe

2015-10-15 15:46
14974
用PEID查一下,发现是TASM / MASM写的程序
用OD打开可以发现基本上都有注释,而且代码很短
状态栏显示Your serial is not valid.
运行程序,填入名字liuyufanny,密码123456789
程序只有一个About按钮,所以我们查找一下字符串,很容易就可以找到

004012B6     /74 16         je short Chafe_1.004012CE
004012B8  |.  68 65304000   push Chafe_1.00403065                    ; /Your serial is not valid.
004012BD  |.  FF35 7C314000 push dword ptr ds:[0x40317C]             ; |hWnd = 0052017C ('Your serial is not valid.',class='Edit',parent=005801E6)
004012C3  |.  E8 66020000   call <jmp.&USER32.SetWindowTextA>        ; \SetWindowTextA
004012C8  |.  33C0          xor eax,eax
004012CA  |.  C9            leave
004012CB  |.  C2 1000       retn 0x10
004012CE  |>  68 7F304000   push Chafe_1.0040307F                    ; /YES! You found your serial!!
004012D3  |.  FF35 7C314000 push dword ptr ds:[0x40317C]             ; |hWnd = 0052017C ('Your serial is not valid.',class='Edit',parent=005801E6)
004012D9  |.  E8 50020000   call <jmp.&USER32.SetWindowTextA>        ; \SetWindowTextA

看到第一行je short Chafe_1.004012CE直接跳到正确的选项,所以在这一行断一个点,断到以后运行,还会继续断到此处,可以确定程序是有循环判断输入的密码是否正确。
再次断到这一行,把je改成jmp并取消断点,发现状态栏变成了YES! You found your serial!!
程序爆破成功。

这个程序的爆破很简单,但我们还需要找出他的密码,才算圆满的变成任务。
先把程序Ctrl+F12重新启动,并运行程序,填入用户名和密码。
下面找到读取输入用户名和密码的代码:

00401475   .  6A 00         push 0x0                                 ; /IsSigned = FALSE
00401477   .  8D45 FC       lea eax,dword ptr ss:[ebp-0x4]           ; |
0040147A   .  50            push eax                                 ; |pSuccess = 00BC614E
0040147B   .  6A 64         push 0x64                                ; |ControlID = 64 (100.)
0040147D   .  FF35 70314000 push dword ptr ds:[0x403170]             ; |hWnd = 00750276 ('TEXme v1.0',class='TEXcls')
00401483   .  E8 64000000   call <jmp.&USER32.GetDlgItemInt>         ; \GetDlgItemInt
00401488   .  A3 88314000   mov dword ptr ds:[0x403188],eax          ;  eax保存密码的十六进行数据,并保存到0x403188
0040148D   .  837D FC 00    cmp dword ptr ss:[ebp-0x4],0x0           ;  如果填入了纯数字,则[ebp-0x4]为1,否则为0
00401491      74 07         je short Chafe_1.0040149A
00401493   .  8005 66314000>add byte ptr ds:[0x403166],0x4           ;  0x403166内存数据加上0x4
0040149A   >  C9            leave
0040149B   .  C3            retn

00401069   .  6A 14         push 0x14                                ; /Count = 14 (20.)
0040106B   .  68 8C314000   push Chafe_1.0040318C                    ; |Buffer = Chafe_1.0040318C
00401070   .  FF35 74314000 push dword ptr ds:[0x403174]             ; |hWnd = 007501F8 (class='Edit',parent=00750276)
00401076   .  E8 7D040000   call <jmp.&USER32.GetWindowTextA>        ; \GetWindowTextA
0040107B   .  B9 14000000   mov ecx,0x14
00401080   .  2BC8          sub ecx,eax
00401082   .  8DB8 8C314000 lea edi,dword ptr ds:[eax+0x40318C]
00401088   >  C607 00       mov byte ptr ds:[edi],0x0                ;  这个循环是保证名字字符串长度为0x14,不足0x14的部分用0填充
0040108B   .  47            inc edi                                  ;  Chafe_1.00403196
0040108C   .  49            dec ecx
0040108D   .^ 75 F9         jnz short Chafe_1.00401088
0040108F   .  85C0          test eax,eax
00401091   .  74 10         je short Chafe_1.004010A3
00401093   .  8005 66314000>add byte ptr ds:[0x403166],0x4           ;  0x403166内存加上0x4
0040109A   .  C605 68314000>mov byte ptr ds:[0x403168],0x0           ;  0x403168设置为0x0
004010A1   .  EB 06         jmp short Chafe_1.004010A9
004010A3   >  8825 66314000 mov byte ptr ds:[0x403166],ah
004010A9   >  C9            leave
004010AA   .  C3            retn

这两个读取名字和密码的地方,有两个特殊的地方,就是对0x403166和0x403168两个地址进行了修改。特别是读取名字以后,把0x403168改成0了。我们先断到读取名字并修改0x403168的下面,地址是0x4010A1。这个时候查看内存地址0x403168,值为0。可以注意到,修改之前,值是为0x10的,所以我们在这里设置一个内存写入断点,然后运行程序,断到了0x401370。
记得把内存断点删了。

00401361   .  8D3D 8C314000 lea edi,dword ptr ds:[0x40318C]          ;  保存着用户名的地址
00401367   .  0FBE05 683140>movsx eax,byte ptr ds:[0x403168]         ;  循环记数i
0040136E   .  03F8          add edi,eax                              ;  取名字的第i个字符 name[i]
00401370   .  FE05 68314000 inc byte ptr ds:[0x403168]               ;  ++i
00401376   .  A1 88314000   mov eax,dword ptr ds:[0x403188]          ;  密码十六进制 num
0040137B   .  8B25 A0314000 mov esp,dword ptr ds:[0x4031A0]
00401381   .  40            inc eax                                  ;  num += 1
00401382   .  FF05 88314000 inc dword ptr ds:[0x403188]              ;  num += 1 这个和上面不同之处在于上面是修改寄存器,下面是修改内存,其实是同一个操作
00401388   .  3307          xor eax,dword ptr ds:[edi]               ;  num ^= name 这个xor是从name+i内存开始,取4个字节转换成整形
0040138A   .  A3 88314000   mov dword ptr ds:[0x403188],eax
0040138F   .  803D 68314000>cmp byte ptr ds:[0x403168],0x10
00401396   .  75 07         jnz short Chafe_1.0040139F
00401398   .  8005 66314000>add byte ptr ds:[0x403166],0x4           ;  0x403166内存加上0x4
0040139F   >  C9            leave
004013A0   .  C3            retn

这里就是通过用户名计算密码的部分代码,为什么说部分呢,下面继续分析。

00401299  |.  0FBE05 663140>movsx eax,byte ptr ds:[0x403166]
004012A0  |.  3A05 67314000 cmp al,byte ptr ds:[0x403167]
004012A6  |.  75 06         jnz short Chafe_1.004012AE
004012A8  |.  33C0          xor eax,eax
004012AA  |.  C9            leave
004012AB  |.  C2 1000       retn 0x10
004012AE  |>  A2 67314000   mov byte ptr ds:[0x403167],al
004012B3  |.  83F8 10       cmp eax,0x10
004012B6  |.  74 16         je short Chafe_1.004012CE

最后一个就是跳到显示正确的关键跳,前面可以看出,取的是0x403166的值,然后与0x10比较,你运行并断点到这几次,你可以发现,0x403166的值从0x4到0x8到0xC就是到不了0x10。看看上面的代码,读取名字的时候加了4,读取密码的时候加了4,计算部分密码的时候加了4,所以可以判断比较密码的时候还会加4。

0040149C   .  A1 88314000   mov eax,dword ptr ds:[0x403188]          ;  计算出来的部分密码
004014A1   .  05 78241109   add eax,0x9112478                        ;  部分密码加上0x9122478
004014A6   .  85C0          test eax,eax
004014A8   .  75 09         jnz short Chafe_1.004014B3               ;  eax为0时,就成功了!
004014AA   .  8005 66314000>add byte ptr ds:[0x403166],0x4           ;  最后一个加4
004014B1   .  EB 07         jmp short Chafe_1.004014BA
004014B3   >  C605 66314000>mov byte ptr ds:[0x403166],0x0
004014BA   >  8B25 A0314000 mov esp,dword ptr ds:[0x4031A0]
004014C0   .  C9            leave
004014C1   .  C3            retn

所以算法就出来了。总结一下算法如下:
取密码的十六进制,从名字的第一个字节开始,每次强制取名字内存四个字节强制转换成uint类型,密码的值加上1,然后与密码xor,并保存为新的密码。重复16次。得到新的密码加上0x9112478最终结果为0x0则正确。
最后得出密码为:2229908868

C++代码如下:
//Chafe.1.exe
int main()
{
    char name[20];
    memset(name, 0, 20);

    std::cout << "input name:";
    std::cin >> name;

    unsigned int answer = 0x00000000 - 0x9112478;

    for (int i = 15; i >= 0; --i)
    {
        unsigned int temp = *reinterpret_cast<unsigned int*>(name + i);
        answer ^= temp;
        answer -= 1;
    }

    std::cout << answer << std::endl;

    return 0;
}

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 193
活跃值: (1220)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错,解释的挺详细的,学习一下
2016-10-19 22:30
0
游客
登录 | 注册 方可回帖
返回
//