我们一起来看这个系列的第三题是不是水题..
PEID,没有查到有壳,依旧是一个C++的逆向:
伪码检验:
同样OD中查找文本字符串,定位到该字符串位置:
这三个题目都没有在定位上为难cracker。
于是还是同样的工作,向上找到函数的头部下断点,F9运行,之后,输入伪代码,F8进行代码分析:
这是这之后一部分的代码流程:
004014EC |. 8B4D E0 mov ecx,[local.8]
004014EF |. 81C1 A0000000 add ecx,0xA0
004014F5 |. E8 AA030000 call <jmp.&MFC42.#3876> ; ;获取name字符串的长度
004014FA |. 8945 EC mov [local.5],eax ; cosh_3.004014B0
004014FD |. 837D EC 05 cmp [local.5],0x5 ; ;判断是否大于5
00401501 |. 7F 05 jg short cosh_3.00401508
00401503 |. E9 BB000000 jmp cosh_3.004015C3
00401508 |> 8B4D E0 mov ecx,[local.8]
0040150B |. 83C1 60 add ecx,0x60
0040150E |. E8 91030000 call <jmp.&MFC42.#3876> ; ;获取用户serial字符串的长度
00401513 |. 8945 E8 mov [local.6],eax ; cosh_3.004014B0
00401516 |. 837D E8 05 cmp [local.6],0x5 ; ;判断用户serila长度时候大于5
0040151A |. 7F 05 jg short cosh_3.00401521
0040151C |. E9 A2000000 jmp cosh_3.004015C3
00401521 |> 8B45 E0 mov eax,[local.8]
00401524 |. 05 E0000000 add eax,0xE0
00401529 |. 50 push eax ; cosh_3.004014B0
0040152A |. 8B4D E0 mov ecx,[local.8]
0040152D |. 81C1 A0000000 add ecx,0xA0
00401533 |. E8 66030000 call <jmp.&MFC42.#3874>
00401538 |. 8B4D E0 mov ecx,[local.8]
0040153B |. 81C1 E4000000 add ecx,0xE4
00401541 |. 51 push ecx
00401542 |. 8B4D E0 mov ecx,[local.8]
00401545 |. 83C1 60 add ecx,0x60
00401548 |. E8 51030000 call <jmp.&MFC42.#3874>
0040154D |. 8B55 E0 mov edx,[local.8]
00401550 |. 81C2 E0000000 add edx,0xE0
00401556 |. 52 push edx
00401557 |. 8D4D E4 lea ecx,[local.7]
0040155A |. E8 39030000 call <jmp.&MFC42.#858>
0040155F |. 8B45 E0 mov eax,[local.8]
00401562 |. 05 E4000000 add eax,0xE4
00401567 |. 50 push eax ; cosh_3.004014B0
00401568 |. 8D4D F0 lea ecx,[local.4]
0040156B |. E8 28030000 call <jmp.&MFC42.#858>
00401570 |. 33C0 xor eax,eax ; cosh_3.004014B0
00401572 |. 33DB xor ebx,ebx
00401574 |. 33C9 xor ecx,ecx
00401576 |. B9 01000000 mov ecx,0x1
0040157B |. 33D2 xor edx,edx
依旧是在判断输入的name和serial字符串的长度。
这之后就进入了两个循环中,分别是name的算法和serial的算法:
0040157D |. 8B45 E4 mov eax,[local.7] ; ;name字符串的算法
00401580 |> 8A18 /mov bl,byte ptr ds:[eax]
00401582 |. 32D9 |xor bl,cl
00401584 |. 8818 |mov byte ptr ds:[eax],bl
00401586 |. 41 |inc ecx
00401587 |. 40 |inc eax ; cosh_3.004014B0
00401588 |. 8038 00 |cmp byte ptr ds:[eax],0x0
0040158B |.^ 75 F3 \jnz short cosh_3.00401580
0040158D |. 33C0 xor eax,eax ; cosh_3.004014B0
0040158F |. 33DB xor ebx,ebx
00401591 |. 33C9 xor ecx,ecx
00401593 |. B9 0A000000 mov ecx,0xA
00401598 |. 33D2 xor edx,edx
0040159A |. 8B45 F0 mov eax,[local.4]
0040159D |> 8A18 /mov bl,byte ptr ds:[eax] ; ;serial字符串改变算法
0040159F |. 32D9 |xor bl,cl
004015A1 |. 8818 |mov byte ptr ds:[eax],bl
004015A3 |. 41 |inc ecx
004015A4 |. 40 |inc eax ; cosh_3.004014B0
004015A5 |. 8038 00 |cmp byte ptr ds:[eax],0x0
这Hex窗口中跟随会好一些:
熟悉汇编循环的童鞋很容易就能理解在做什么,举例来说:以C语言:
int c1=0x1;
for(int i=0;i<strlen(name);i++)
{
name[i]=c1^(int)name[i];
c1++;
}
同样对seial_false做了同样的操作,只是初始值变了:
int c1=0xA;
for(int i=0;i<strlen(seial);i++)
{
serial[i]=c1^(int)serial[i];
c1++;
}
之后,分析代码:
004015A4 |. 40 |inc eax
004015A5 |. 8038 00 |cmp byte ptr ds:[eax],0x0
004015A8 |.^ 75 F3 \jnz short cosh_3.0040159D
004015AA |. 8B45 E4 mov eax,[local.7]
004015AD |. 8B55 F0 mov edx,[local.4]
004015B0 |> 33C9 /xor ecx,ecx
004015B2 |. 8A18 |mov bl,byte ptr ds:[eax]
004015B4 |. 8A0A |mov cl,byte ptr ds:[edx]
004015B6 |. 3AD9 |cmp bl,cl
004015B8 |. 75 09 |jnz short cosh_3.004015C3 ; ;改变后的name字符串与改变后的serial逐位比较,失败就失败
004015BA |. 40 |inc eax
004015BB |. 42 |inc edx
004015BC |. 8038 00 |cmp byte ptr ds:[eax],0x0
004015BF |.^ 75 EF \jnz short cosh_3.004015B0
004015C1 |. EB 16 jmp short cosh_3.004015D9 ; ;比较成功跳转成功信息
中间部分看似多,实际上就是对每一位的name和serial进行了比较:
这是我输入的伪码所生成的两个字符串:
明显不相等,第一位比较就会错误...
我们彻底清楚了算法的流程,要注意,两个字符串经过各自的算法变换之后的比较中,仅仅比较name字符串的长度的个数的位数。
通过算法用C语言写逆向算法的功力:
#include <iostream>
#include <cstring>
using namespace std;
#define N 100
int main()
{
char name[N];
char serial_true[N];
char serial_false[N];
cout<<"Please input your name:"<<endl;
cin>>name;
cout<<"Please input your serial:"<<endl;
cin>>serial_false;
if(strlen(name)<=5||strlen(serial_false)<=5)
{
cout<<"Error!"<<endl;
return 0;
}
int c1=0x1;
for(int i=0;i<strlen(name);i++)
{
name[i]=c1^(int)name[i];
c1++;
}
//for(int i=0;i<strlen(name);i++)
//cout<<name[i];
int c2=0xA;
for(int i=0;i<strlen(name);i++)
{
serial_true[i]=c2^name[i];
c2++;
}
for(int i=0;i<strlen(name);i++)
cout<<serial_true[i];
return 0;
}
结果检验:
最近的生活啊,我在用命crackME...