练习笔记之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加不同的值实现的,这个大大增加了分析难度,我只能通过动态跟踪来确定执行过程。如果这个程序复杂一些,再对字符串进行加密处理,感觉很可能就找不到地方了。 这个算法其实看起来相对来说是比较简单的{:301_978:} 支持 感谢分享
支持 感谢分享 好好学习,认真总结
谢谢你的ida分析。
页:
[1]