160CM-023
1. 爆破
首先,拖入PE看一下,这是一个“TASM / MASM / FASM - assembler ”编译的程序,百度一下,就是汇编编译器。
用OD打开程序,搜索一下字符串,关键字都能搜索到,能很快的定位到关键代码段,爆破的话按下图将两处跳转的条件反一下即可。
2. 注册分析
用IDA打开程序,可以看见这个程序的内容不多,用户函数只有6个,其他都是系统函数。
F5依次查看各个函数,过程还比较简单,但是其中有个函数,伪代码很简单,如下图。
int cal_401453()
{
int v1; // [sp+0h] [bp-4h]@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。
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:[0x4031A0],esp ; Chafe_1.0040315E
0040145F |. 8D25 52314000 lea esp,dword ptr ds:[0x403152]
00401465 |. 0FBE05 663140>movsx eax,byte ptr ds:[0x403166]
0040146C |. 03E0 add esp,eax
0040146E \. C3 retn ; 转去401361
00401361 . 8D3D 8C314000 lea edi,dword ptr ds:[0x40318C] ; 取name的地址
00401367 . 0FBE05 683140>movsx eax,byte ptr ds:[0x403168]
0040136E . 03F8 add edi,eax ; 地址++
00401370 . FE05 68314000 inc byte ptr ds:[0x403168]
00401376 . A1 88314000 mov eax,dword ptr ds:[0x403188]
0040137B . 8B25 A0314000 mov esp,dword ptr ds:[0x4031A0]
00401381 . 40 inc eax
00401382 . FF05 88314000 inc dword ptr ds:[0x403188] ; +1
00401388 . 3307 xor eax,dword ptr ds:[edi] ; 异或运算
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 ;
0040139F > C9 leave
004013A0 . C3 retn ; 返回401294的下一行
00401299 |. 0FBE05 663140>movsx eax,byte ptr ds:[0x403166] ; 1294下一行
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
根据上述算法,编写计算代码如下:
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
char name[20];
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[i] = 0;
}
}
for (i = 0; i < 16; i++)
{
sn++;
temp = name[i] + name[i + 1] * 0x100 + name[i + 2] * 0x10000 + name[i + 3] * 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,因此逆向算法代码如下:
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
char name[20];
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[i] = 0;
}
}
for (i = 15; i >=0; i--)
{
temp = name[i] + name[i + 1] * 0x100 + name[i + 2] * 0x10000 + name[i + 3] * 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加不同的值实现的,这个大大增加了分析难度,我只能通过动态跟踪来确定执行过程。如果这个程序复杂一些,再对字符串进行加密处理,感觉很可能就找不到地方了。