xiaoyu2032 发表于 2022-7-2 09:32

练习笔记之160Crackme-023

# 160CM-023

## 1. 爆破

  首先,拖入PE看一下,这是一个“TASM / MASM / FASM - assembler ”编译的程序,百度一下,就是汇编编译器。

  用OD打开程序,搜索一下字符串,关键字都能搜索到,能很快的定位到关键代码段,爆破的话按下图将两处跳转的条件反一下即可。


  

## 2. 注册分析

  用IDA打开程序,可以看见这个程序的内容不多,用户函数只有6个,其他都是系统函数。



  F5依次查看各个函数,过程还比较简单,但是其中有个函数,伪代码很简单,如下图。

```cpp
int cal_401453()
{
int v1; // @1
dword_4031A0 = &v1;
return byte_403166;
}
```

  但是执行的时候retn后,并不是直接返回到这个函数调用位置的下一行,而是可能跳到==401361==,也可能跳到==40146F==,这个看代码的时候看不大出来,跟踪了好几遍才发现。也就是说401023中有一段代码是该函数执行过程中不会执行的,反而是通过401453进入的。



  跟踪一下可以发现比较重要的两个就是找到name和serial的存放地址,分别是==40318C==和==403188==。

  然后在关键判断处可以发现,有一处eax与0x10的比较,而跟踪可以发现Serial错误的时候eax只有0xC。



  查看40149c可以发现,该函数对某个计算结果==+ 152118392==后判断是否为零,为零则将403166加4,而上图中eax的值就是从403166中取出来的,因此关键就是要找到这个计算结果从哪里来的。

```
int sub_40149C()
{
int result; // eax@1

result = dword_403188 + 152118392;
if ( dword_403188 == -152118392 )
    byte_403166 += 4;
else
    byte_403166 = 0;
return result;
}
```

  跟踪一遍可以发现,==call 401453==执行后,先跳转到==401361==,在里面serial先加1,然后再与name中的四个字节表示的数值进行异或运算,完成后字节地址+1,继续下一次计算,一直到计数值达到0x14。

```asm
0040128D|> \3D 13010000   cmp eax,0x113
00401292|.75 50         jnz short Chafe_1.004012E4
00401294|.E8 BA010000   call Chafe_1.00401453               ;转去cal_401453; Case 113 (WM_TIMER) of switch 0040123F

00401453/$55            push ebp                                          ;cal_401453
00401454|.8BEC          mov ebp,esp                                       ;Chafe_1.0040315E
00401456|.83C4 FC       add esp,-0x4
00401459|.8925 A0314000 mov dword ptr ds:,esp                   ;Chafe_1.0040315E
0040145F|.8D25 52314000 lea esp,dword ptr ds:
00401465|.0FBE05 663140>movsx eax,byte ptr ds:
0040146C|.03E0          add esp,eax
0040146E\.C3            retn                                                                                       ;转去401361

00401361   .8D3D 8C314000 lea edi,dword ptr ds:                   ;取name的地址
00401367   .0FBE05 683140>movsx eax,byte ptr ds:
0040136E   .03F8          add edi,eax                                                                                  ;地址++
00401370   .FE05 68314000 inc byte ptr ds:
00401376   .A1 88314000   mov eax,dword ptr ds:
0040137B   .8B25 A0314000 mov esp,dword ptr ds:
00401381   .40            inc eax
00401382   .FF05 88314000 inc dword ptr ds:                     ;+1
00401388   .3307          xor eax,dword ptr ds:                        ;异或运算
0040138A   .A3 88314000   mov dword ptr ds:,eax                                          
0040138F   .803D 68314000>cmp byte ptr ds:,0x10
00401396   .75 07         jnz short Chafe_1.0040139F
00401398   .8005 66314000>add byte ptr ds:,0x4                  ;
0040139F   >C9            leave
004013A0   .C3            retn                                              ; 返回401294的下一行

00401299|.0FBE05 663140>movsx eax,byte ptr ds:                  ;1294下一行
004012A0|.3A05 67314000 cmp al,byte ptr ds:
004012A6      75 06         jnz short Chafe_1.004012AE
004012A8|.33C0          xor eax,eax
004012AA|.C9            leave
004012AB|.C2 1000       retn 0x10
```

  根据上述算法,编写计算代码如下:

```cpp
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>

int _tmain(int argc, _TCHAR* argv[])
{
        char name;
        int sn;
        int temp,length;
        int i, j;
        printf("请输入用户名(长度20以内):");
        scanf_s("%s", name, 20);
        printf("请输入注册码(数字):");
        scanf_s("%d", &sn);
        length = strlen(name);
        j = 0;
        //正向算法
        for (i = 0; i < 19; i++)
        {
                if (i >= length)
                {
                        name = 0;
                }
        }
        for (i = 0; i < 16; i++)
        {
                sn++;
                temp = name + name * 0x100 + name * 0x10000 + name * 0x1000000;
                sn = sn^temp;
                //printf("SN %d为:%d\n", i, sn);
        }

        printf("SN为:%u\n", sn);
        printf("SN为:%0x\n", sn);
        system("pause");
        return 0;
}
```

  在40149C处设断点,输入任意name和serial,将上述算法计算得到的数值和断点处跟踪到的403188地址的数值进行比较,发现是一致的,说明算法是正确的。



  要逆推算法, 也就是说要将“0x100000000-0x9112478"的十进制数值(4142848904),通过逆推算法得到serial。正向计算过程是先+1,再异或,逆向算法就是先异或,再-1,因此逆向算法代码如下:

```cpp
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>

int _tmain(int argc, _TCHAR* argv[])
{
        char name;
        int sn;
        int temp,temp2,length;
        int i, j;
        printf("请输入用户名(长度20以内):");
        scanf_s("%s", name, 20);
        printf("请输入注册码(数字):");
        scanf_s("%d", &sn);
        length = strlen(name);
        j = 0;
        // 逆向算法
        for (i = 0; i < 19; i++)
        {
                if (i >= length)
                {
                        name = 0;
                }
        }
        for (i = 15; i >=0; i--)
        {
               
                temp = name + name * 0x100 + name * 0x10000 + name * 0x1000000;
                sn = sn^temp;
                sn--;
                //printf("SN %d为:%u\n", i, sn);
        }
        printf("SN为:%u\n", sn);
        printf("SN为:%0x\n", sn);
        system("pause");
        return 0;
}
```

  这里需要注意的是由于程序中输入负数会识别成零,因此需要用无符号整型来显示结果。输入用户名==abcde==,SN输入==4142848904==,计算结果如下:



  运行程序,分别输入abcde和4153203471,注册成功!



## 3. 总结

  这个程序里面比较难分析的就是函数401023中有一段代码是从其他函数进入的,实现方式好像是通过在retn前将esp加不同的值实现的,这个大大增加了分析难度,我只能通过动态跟踪来确定执行过程。如果这个程序复杂一些,再对字符串进行加密处理,感觉很可能就找不到地方了。

Panel 发表于 2022-7-2 13:53

这个算法其实看起来相对来说是比较简单的{:301_978:}

p52p 发表于 2022-7-2 17:15

支持 感谢分享

Junlee 发表于 2022-7-2 20:52


支持 感谢分享

kanxue2018 发表于 2022-7-3 08:56

好好学习,认真总结

冥界3大法王 发表于 2022-7-4 08:07


谢谢你的ida分析。
页: [1]
查看完整版本: 练习笔记之160Crackme-023