160个CrackMe 029 Cosh.3
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了160个CrackMe 029 Cosh.3相关的知识,希望对你有一定的参考价值。
我们一起来看这个系列的第三题是不是水题..
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...
以上是关于160个CrackMe 029 Cosh.3的主要内容,如果未能解决你的问题,请参考以下文章