【文章标题】: 加密解密第二版练习破文
【文章作者】: kanghtta
【作者邮箱】: kanghtta@hotmail.com
【作者主页】: http://kanghtta.cublog.cn
【作者QQ号】: 18381291
【软件名称】: echap518
【软件大小】: 20kb
【下载地址】: 电子书下载
【加壳方式】: 无
【编写语言】: Visual C++6.0
【使用工具】: PEID OllyICE
【操作平台】: Windows XP
【软件介绍】: Name/Serial 中
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
大家好,这是我的第6个练习,虽然练习的少,但还是把到现在的一点心得写一下,主要是提醒自己不要再以后犯同类错误;
1)在软件的信息没有收集充分前,不要轻易动手破解,最好是看看有什么能用得上的信息,根据这些信息能下什么断点,用什么分析方法;
要不,有可能在你分析了一半代码时,会突然感觉断电了,一下子就被扔进深渊,这时一定要回头重新运行下软件,找找看还有没有用得上的信息;
2)如果你在一段代码上重复分析了很多遍,那很有可能是关键,也可能是什么地方你弄错了!
3)千万不要急于找出注册码,尽量尝试分析代码来弄清楚注册码的变换或是置换算法,爆破虽然也能让程序顺利运行,但无论从编程还是从逆向分析上来说,
这对你的进步没有多大帮助; 我在上一遍破文中吃尽苦头,就是因为心急的缘故,最好就是弄清楚你想要的到底是什么?这很重要;
好了,下面转入正题; 今天练习的是加密解密电子书补充的习题,关于系列号的最后两个,嘿嘿。。jmp 正文
正文:
运行程序,这是一个 Name/serial的Crackme ,输入 name: kanghtta serial: kang 然后Check它,嘿嘿,
MessageBox 出来了,不过带来的消息并不是什么好消息哦; one of the details you entered was wrong ,用字串参考;
用PEid 收集信息,软件信息我已经在CTC中写上了;也就是开头部分;
OD载入,在反汇编窗口中,右键—〉查找-〉所有参考文本字串; 看下面;
文本字串参考位于 echap518:.text
地址 反汇编 文本字串
004015C5 push 0040306C ASCII "ERROR"
004015CA push 00403040 ASCII "One of the Details you entered was wrong"
004015DB push 00403034 ASCII "YOU DID IT"
004015E0 push 00403020 ASCII "Well done,Cracker"
00401924 or dword ptr [403170], FFFFFFFF (初始 CPU 选择)
00401A65 push 10000 UNICODE "ALLUSERSPROFILE=C:\Documents and Settings\All Users.WINDOWS"
所有的字串就这些,嘿嘿,在one of 。。。。。。这一行上 Enter键,来到反汇编窗口处;
004015C3 |> \6A 00 push 0
004015C5 |. 68 6C304000 push 0040306C ; ASCII "ERROR" 注册失败的消息框提示
004015CA |. 68 40304000 push 00403040 ; ASCII "One of the Details you entered was wrong"
004015CF |. 8B4D E0 mov ecx, dword ptr [ebp-20]
004015D2 |. E8 BB020000 call <jmp.&MFC42.#4224_CWnd::MessageB>
004015D7 |. EB 14 jmp short 004015ED
004015D9 |> 6A 00 push 0
004015DB |. 68 34304000 push 00403034 ; ASCII "YOU DID IT" 注册成功的消息框提示
004015E0 |. 68 20304000 push 00403020 ; ASCII "Well done,Cracker"
004015E5 |. 8B4D E0 mov ecx, dword ptr [ebp-20]
004015E8 |. E8 A5020000 call <jmp.&MFC42.#4224_CWnd::MessageB>
两个消息框产生的函数,MFC编写,是不是和前几个不同哦,不用担心,OD照样能识别它们;我们上下滚动滚动,在以下两个地址处点击一下:
004015C3 |> \6A 00 push 0 跳转来自00401503 |. /E9 BB000000 jmp 004015C3
004015D9 |> 6A 00 push 0 跳转来自004015C1 |. /EB 16 jmp short 004015D9
在004014B0 处F2 ,下断,F9,输入: name: kanghtta serial: kang 程序被断下;
下面来分析它:
004014B0 /. 55 push ebp
004014B1 |. 8BEC mov ebp, esp
004014B3 |. 6A FF push -1
004014B5 |. 68 C21B4000 push 00401BC2 ; SE 处理程序安装
004014BA |. 64:A1 0000000>mov eax, dword ptr fs:[0]
004014C0 |. 50 push eax
004014C1 |. 64:8925 00000>mov dword ptr fs:[0], esp
004014C8 |. 83EC 14 sub esp, 14
004014CB |. 53 push ebx
004014CC |. 56 push esi
004014CD |. 57 push edi
004014CE |. 894D E0 mov dword ptr [ebp-20], ecx
004014D1 |. 8D4D E4 lea ecx, dword ptr [ebp-1C]
004014D4 |. E8 83030000 call <jmp.&MFC42.#540_CString::CString>
004014D9 |. C745 FC 00000>mov dword ptr [ebp-4], 0
004014E0 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
004014E3 |. E8 74030000 call <jmp.&MFC42.#540_CString::CString>
004014E8 |. C645 FC 01 mov byte ptr [ebp-4], 1
004014EC |. 8B4D E0 mov ecx, dword ptr [ebp-20]
004014EF |. 81C1 A0000000 add ecx, 0A0
004014F5 |. E8 AA030000 call <jmp.&MFC42.#3876_CWnd::GetWindowTextLengthA> 获取Cwnd对象或是控件的文本字串长度
004014FA |. 8945 EC mov dword ptr [ebp-14], eax
004014FD |. 837D EC 05 cmp dword ptr [ebp-14], 5 比较字串的长度是否大于5
00401501 |. 7F 05 jg short 00401508 大于继续,不大于就完蛋;我们的程序在这继续,因为不知道它获取的是什么字符串;F8 跟进;
00401503 |. E9 BB000000 jmp 004015C3
00401508 |> 8B4D E0 mov ecx, dword ptr [ebp-20]
0040150B |. 83C1 60 add ecx, 60
0040150E |. E8 91030000 call <jmp.&MFC42.#3876_CWnd::GetWindowTextLengthA>获取Cwnd对象或是控件的文本字串长度;
00401513 |. 8945 E8 mov dword ptr [ebp-18], eax 比较字串长度
00401516 |. 837D E8 05 cmp dword ptr [ebp-18], 5 不大于程序就完蛋,
我分析的时候在这里跳转后完蛋,而我输入的Serial为:kang 四位;因此判断这两个函数获取的是我们输入的Name和Serial,
因此Ctrl + F2 重新载入后F9 ,输入Serial:为Cracker 反正要大于5位,否则就完蛋, F8 步进,没有跳转,哈哈,说明我们的分析是对的;
0040151A |. 7F 05 jg short 00401521
0040151C |. E9 A2000000 jmp 004015C3
00401521 |> 8B45 E0 mov eax, dword ptr [ebp-20]
00401524 |. 05 E0000000 add eax, 0E0
00401529 |. 50 push eax
0040152A |. 8B4D E0 mov ecx, dword ptr [ebp-20]
0040152D |. 81C1 A0000000 add ecx, 0A0
00401533 |. E8 66030000 call <jmp.&MFC42.#3874_CWnd::GetWindowTextA> 获取name保存在ecx指向的地址中,
ecx上右键,数据窗口跟随;
:00374008 6B 61 6E 67 68 74 74 61 00 65 72 69 66 00 AD kanghtta.erif.
00401538 |. 8B4D E0 mov ecx, dword ptr [ebp-20]
0040153B |. 81C1 E4000000 add ecx, 0E4
00401541 |. 51 push ecx
00401542 |. 8B4D E0 mov ecx, dword ptr [ebp-20]
00401545 |. 83C1 60 add ecx, 60
00401548 |. E8 51030000 call <jmp.&MFC42.#3874_CWnd::GetWindowTextA> 获取Serial : Cracker
0040154D |. 8B55 E0 mov edx, dword ptr [ebp-20]
00401550 |. 81C2 E0000000 add edx, 0E0
00401556 |. 52 push edx
00401557 |. 8D4D E4 lea ecx, dword ptr [ebp-1C]
0040155A |. E8 39030000 call <jmp.&MFC42.#858_CString::operator=>
0040155F |. 8B45 E0 mov eax, dword ptr [ebp-20]
00401562 |. 05 E4000000 add eax, 0E4
00401567 |. 50 push eax
00401568 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
0040156B |. E8 28030000 call <jmp.&MFC42.#858_CString::operator=>
00401570 |. 33C0 xor eax, eax
00401572 |. 33DB xor ebx, ebx
00401574 |. 33C9 xor ecx, ecx eax,ebx,ecx,清零
00401576 |. B9 01000000 mov ecx, 1 初始化ecx 为0
0040157B |. 33D2 xor edx, edx edx清零
0040157D |. 8B45 E4 mov eax, dword ptr [ebp-1C] name送eax
00401580 |> 8A18 /mov bl, byte ptr [eax]
00401582 |. 32D9 |xor bl, cl
00401584 |. 8818 |mov byte ptr [eax], bl
00401586 |. 41 |inc ecx
00401587 |. 40 |inc eax
00401588 |. 8038 00 |cmp byte ptr [eax], 0 对输入的name进行运算,每个字符和0x01异或运算;
0040158B |.^ 75 F3 \jnz short 00401580
0040158D |. 33C0 xor eax, eax
0040158F |. 33DB xor ebx, ebx
00401591 |. 33C9 xor ecx, ecx
00401593 |. B9 0A000000 mov ecx, 0A
00401598 |. 33D2 xor edx, edx
0040159A |. 8B45 F0 mov eax, dword ptr [ebp-10] serial 送eax
0040159D |> 8A18 /mov bl, byte ptr [eax]
0040159F |. 32D9 |xor bl, cl
004015A1 |. 8818 |mov byte ptr [eax], bl
004015A3 |. 41 |inc ecx
004015A4 |. 40 |inc eax
004015A5 |. 8038 00 |cmp byte ptr [eax], 0 对输入的serial进行运算,每个字符和0x0A异或运算
004015A8 |.^ 75 F3 \jnz short 0040159D
004015AA |. 8B45 E4 mov eax, dword ptr [ebp-1C] 运算后的name地址送eax
004015AD |. 8B55 F0 mov edx, dword ptr [ebp-10] 运算后的serial地址送edx
004015B0 |> 33C9 /xor ecx, ecx
004015B2 |. 8A18 |mov bl, byte ptr [eax]
004015B4 |. 8A0A |mov cl, byte ptr [edx]
004015B6 |. 3AD9 |cmp bl, cl 比较运算后的name,heserial,不同就完蛋;
004015B8 |. 75 09 |jnz short 004015C3
004015BA |. 40 |inc eax
004015BB |. 42 |inc edx
004015BC |. 8038 00 |cmp byte ptr [eax], 0
004015BF |.^ 75 EF \jnz short 004015B0
004015C1 |. EB 16 jmp short 004015D9
004015C3 |> 6A 00 push 0
004015C5 |. 68 6C304000 push 0040306C ; ASCII "ERROR"
004015CA |. 68 40304000 push 00403040 ; ASCII "One of the Details you entered was wrong"
004015CF |. 8B4D E0 mov ecx, dword ptr [ebp-20]
004015D2 |. E8 BB020000 call <jmp.&MFC42.#4224_CWnd::MessageBoxA>
004015D7 |. EB 14 jmp short 004015ED
004015D9 |> 6A 00 push 0
004015DB |. 68 34304000 push 00403034 ; ASCII "YOU DID IT"
004015E0 |. 68 20304000 push 00403020 ; ASCII "Well done,Cracker"
004015E5 |. 8B4D E0 mov ecx, dword ptr [ebp-20]
004015E8 |. E8 A5020000 call <jmp.&MFC42.#4224_CWnd::MessageBoxA>
004015ED |> 6A 64 push 64 ; /Timeout = 100. ms
004015EF |. FF15 00204000 call dword ptr [<&KERNEL32.Sleep>] ; \Sleep
004015F5 |. C645 FC 00 mov byte ptr [ebp-4], 0
004015F9 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
004015FC |. E8 65010000 call <jmp.&MFC42.#800_CString::~CString>
00401601 |. C745 FC FFFFF>mov dword ptr [ebp-4], -1
00401608 |. 8D4D E4 lea ecx, dword ptr [ebp-1C]
0040160B |. E8 56010000 call <jmp.&MFC42.#800_CString::~CString>
00401610 |. 8B4D F4 mov ecx, dword ptr [ebp-C]
00401613 |. 64:890D 00000>mov dword ptr fs:[0], ecx
0040161A |. 5F pop edi
0040161B |. 5E pop esi
0040161C |. 5B pop ebx
0040161D |. 8BE5 mov esp, ebp
0040161F |. 5D pop ebp
00401620 \. C3 retn
这个程序相对来说比较简单,算法如下;
1)顺序比较输入name和serial的字符数,如果任何一个字串的字符数小于5位,就完蛋;
2)将输入的name和1做异或运算,即是name第一位取反后作为新的name;
3)将输入的serial异或运算,即是serial的2,4位取反后作为新的serial;
4)顺序比较name和serial的字符,任何一位不同就完蛋;
下面是我写的一个注册机,比较简单,大侠们莫见笑:
#include<iostream.h>
#include<String.h>
void main()
{
char name[20];
char serial[20]={0,0};
cout<<"please input your name:"<<endl;
cin>>name;
int nameLenth=strlen(name);
int middle=1;
for(int i=0;i<nameLenth;i++)
{
name[i]=name[i]^middle;
middle++;
}
middle=0x0A;
for(int j=0;j<nameLenth;j++)
{
serial[j]=name[j]^middle;
middle++;
}
cout<<serial<<'\n';
}
来验证一下: name: kanghtta serial: `hanc}cx
messagebox: well done Cracker
在来一组: name: dingmaoyin serial:o`anfhx`rw
mssagebox: well done Cracker
嘿嘿,到此完;
--------------------------------------------------------------------------------
【经验总结】
这应该是一个相对简单的Crackme,但有个好处就是能让你知道明文和密文之间的转化方式,即明文做运算或是变化后得到密文,本例的算法并没有引入密锁, 算是密码学入门的一个例子吧!
由于写破文和注册机,花了两个小时; 好了,今天就到这里吧!祝大家愉快!
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2008年04月10日 PM 09:02:16
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!