【文章标题】: 加密解密习题破文
【文章作者】: kanghtta
【作者邮箱】: kanghtta@hotmail.com
【作者主页】:
b93K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3E0S2L8X3N6Z5N6s2c8S2i4K6u0W2j5%4g2T1L8r3!0Y4i4K6u0W2j5$3^5`.
【作者QQ号】: 18381291
【软件名称】: echap512
【软件大小】: 4kb
【下载地址】: 加密解密第二版光盘
【加壳方式】: 无壳
【编写语言】: masm32
【使用工具】: peid OllyICE
【操作平台】: windows
【软件介绍】: 难度--稍难(不是我说的,出题人给的)
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
大家好,有一就有二嘛!昨天做了个我的第一个练习,本想自己练练,但是觉得把自己破解的思路记下来,以后也好温习温习,
还是写一写好,本来昨晚就写得差不多了,可由于在宿舍住,阿姨关电是不是从来不看时间啊,唉。。我写了2小时的文章
就那样灰飞烟灭了,也不想抱怨什么了,这个社会上抱怨是没用的。 (我废话真多,嘿嘿。。jmp short 正文)。
由于题目给的是稍难,但是不用管这个,难不难都试一试吧!
首先,还是老格调,拿到一个程序呢,先运行一下看看,找找提示信息,熟悉熟悉软件环境。好了,看到一个编辑框,
直接 OK它, 熟悉的Messagebox出来了,不过不用高兴,它带来的不是什么好消息: Wrong S/N #
现在,用peid载入看看,主要是看有没有壳,(乌龟盖),还有软件的一些信息。
(其它的不用说了吧。其实也不知道怎么说,你用一下就知道了)
刚才说过了,messagebox跳出来说了句:Wrong S/N #,一般的思路都是用字串参考,不过还是用OD载入看看。
一开始就是GetModuleHandle ,获取程序模块句柄,好了,先不用字串参考了,上下拖拖看。(指滚动条)
窗口产生过程的流程都差不多,获取模块句柄,注册窗口类,创建窗口,更新客户区,消息循环。和昨天不同的是,
昨天的用对话框,今天用CreateWidnowEx 用了三次,分别产生:窗口,编辑框,按钮,(后两个窗口用系统预定义类)
在往下拉拉看,找到关键,嘿嘿,,,
0040118F |. /75 5E jnz short 004011EF
00401191 |. |6A 28 push 28 ; /Count = 28 (40.)
00401193 |. |68 A6204000 push 004020A6 ; |Buffer = echap512.004020A6
00401198 |. |FF35 4C204000 push dword ptr [40204C] ; |hWnd = NULL
0040119E |. |E8 00010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
004011A3 |. |FF35 44204000 push dword ptr [402044] ; /Arg1 = 00000000
004011A9 |. |E8 5D000000 call 0040120B ; \echap512.0040120B
004011AE |. |83F8 01 cmp eax, 1
004011B1 |. |75 1E jnz short 004011D1
004011B3 |. |6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004011B5 |. |68 81204000 push 00402081 ; |Title = "Good For U!"
004011BA |. |68 8D204000 push 0040208D ; |Text = "U Did It!!"
004011BF |. |FF35 48204000 push dword ptr [402048] ; |hOwner = NULL
004011C5 |. |E8 EB000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004011CA |. |5E pop esi
004011CB |. |5F pop edi
004011CC |. |5B pop ebx
004011CD |. |C9 leave
004011CE |. |C2 1000 retn 10
004011D1 |> |6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004011D3 |. |68 98204000 push 00402098 ; |Title = "=)"
004011D8 |. |68 9B204000 push 0040209B ; |Text = "Wrong S/N#"
004011DD |. |FF35 48204000 push dword ptr [402048] ; |hOwner = NULL
004011E3 |. |E8 CD000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004011E8 |. |5E pop esi
004011E9 |. |5F pop edi
004011EA |. |5B pop ebx
004011EB |. |C9 leave
004011EC |. |C2 1000 retn 10
建议大家如果程序比较大的时候,还是用字串参考,这里先看两个地址, 如果可能,请记下它们.
004011E3 |. E8 CD000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004011C5 |. E8 EB000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
分别是不成功和成功注册给的提示信息。接下来,我们上下拉拉看,找找有没有Call Jxx 阿什么的跳到这两个地址。
结果不容乐观。但不用担心,如果字串找到了,你一般留心下函数。
看到这个没有
0040119E |. E8 00010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
GetWindowText函数复制一个指定的窗口标题文本(如果它有的话)到一缓冲区中。如果指定的窗口是控件,那么控件文本
被复制。然而,GetWindowText不能获得另一应用程序的控件文本。
原型如下:
int GetWindowText(
HWND hWnd, // handle to window or control with text 有文本的窗口或是控件的句柄。
LPTSTR lpString, // address of buffer for text 文本缓冲区地址
int nMaxCount // maximum number of characters to copy能复制的最大字符数。
);
0040119E |. E8 00010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
004011A3 |. FF35 44204000 push dword ptr [402044] ; /Arg1 = 00000000
004011A9 |. E8 5D000000 call 0040120B ; \echap512.0040120B
004011AE |. 83F8 01 cmp eax, 1
004011B1 |. 75 1E jnz short 004011D1
004011B3 |. 6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004011A9 |. E8 5D000000Call 0040120B 初步断定为关键,获取完文本,接下来一般都是对文本做写处理或者是变换。
废话少说 在0040119E处 F2,然后Ctrl +F2 重新载入后,按F9 运行:
窗口出来了,我输入的是 : kanghtta (当然,你可以输入你喜欢的任何字符,要是直接输入注册码,你就神了,嘿嘿。。)
现在我们F8来到004011A9 处F7跟进,哈哈,看下面
0040120B /$ C8 000000 enter 0, 0
0040120F |. 53 push ebx
00401210 |. 52 push edx
00401211 |. 33C0 xor eax, eax
00401213 |. B8 A6204000 mov eax, 004020A6 ; ASCII "kanghtta"( 熟悉吧,我是很熟悉的)
00401218 |. 8038 00 cmp byte ptr [eax], 0
0040121B |. 74 60 je short 0040127D 是否输入的是空字符,如果是,跳走,估计程序也完蛋了,这么说的原因是,我不知道它跳到什么地方,干了什么事
0040121D |. 33DB xor ebx, ebx
0040121F |. 33D2 xor edx, edx ; 接下来两句是:寄存器清0
00401221 |> 8A18 /mov bl, byte ptr [eax]
00401223 |. C1C3 08 |rol ebx, 8
00401226 |. 03D3 |add edx, ebx
00401228 |. 40 |inc eax
00401229 |. 8038 00 |cmp byte ptr [eax], 0
0040122C |.^ 75 F3 \jnz short 00401221 这一段的功能是循环计算输入字符,算法一会下面给出
0040122E |. 52 push edx ; /<%lX> = 7FF45409
0040122F |. 68 54204000 push 00402054 ; |Format = "%lX"
00401234 |. 68 BF204000 push 004020BF ; |s = echap512.004020BF
00401239 |. E8 8F000000 call <jmp.&USER32.wsprintfA> ; \wsprintfA
0040123E |. BB BF204000 mov ebx, 004020BF ;ebx 为74746168
00401243 |. 803B 38 cmp byte ptr [ebx], 38 第一字节和38比较
00401246 |. 75 35 jnz short 0040127D 结果不为0则跳转,F8跟进,结果程序完蛋.Wrong....
00401248 |. 807B 01 44 cmp byte ptr [ebx+1], 44
0040124C |. 75 2F jnz short 0040127D
0040124E |. 807B 02 43 cmp byte ptr [ebx+2], 43
00401252 |. 75 29 jnz short 0040127D
00401254 |. 807B 03 41 cmp byte ptr [ebx+3], 41
00401258 |. 75 23 jnz short 0040127D
0040125A |. 807B 04 46 cmp byte ptr [ebx+4], 46
0040125E |. 75 1D jnz short 0040127D
00401260 |. 807B 05 33 cmp byte ptr [ebx+5], 33
00401264 |. 75 17 jnz short 0040127D
00401266 |. 807B 06 36 cmp byte ptr [ebx+6], 36
0040126A |. 75 11 jnz short 0040127D
0040126C |. 807B 07 38 cmp byte ptr [ebx+7], 38
00401270 |. 75 0B jnz short 0040127D
00401272 |. B8 01000000 mov eax, 1
00401277 |. 5A pop edx
00401278 |. 5B pop ebx
00401279 |. C9 leave
0040127A |. C2 0400 retn 4
将运算的结果和 38 44 43 41 46 33 36 38 依次比较,任何一个不等,就跳到0040127D现在我们看看此处代码:
0040127D |> 33C0 xor eax, eax eax 清0
0040127F |. 5A pop edx 恢复现场
00401280 |. 5B pop ebx
00401281 |. C9 leave 恢复现场,和堆栈维护
00401282 \. C2 0400 retn 4
返回到
004011AE |. 83F8 01 cmp eax, 1 eax 和1比 不等则完蛋.
;而eax在040127D处被清0 这就是说,只要跳转到0040127D处,程序就完蛋
004011B1 |. 75 1E jnz short 004011D1
004011B3 |. 6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
下面我们来分析注册的算法:
这里先留意一个API函数: wsprintf
The wsprintf function formats and stores a series of characters and values in a buffer.
Wsprintf在缓冲区中格式化并储存一系列的字符和值,任何自变量都能依照在格式串中指定的格式被修改和复制到输出缓冲区中.
这个函数在输入的字符串后面附加一个NUll终结符,但是返回值中并不包含这个终结符的计数.
它可以将数值按指定的格式翻译成字符串:
int wsprintf(
LPTSTR lpOut, // pointer to buffer for output 指向输出缓冲区的指针
LPCTSTR lpFmt, // pointer to format-control string 指向格式控制字符串的指针
... // optional arguments 自变量选项,这个参数的类型和大小依赖以在lpFmt参数中指定的格式控制规格.
);
lx, lX A long unsigned hexadecimal integer in lowercase or uppercase
程序算法:
1.将由GetWindowText获得的文本从左到右输入一个字符到bl中.
2,bl循环右移8位,
3,将右移后得到的值和edx相加,edx初值为0,结果保存在edx中
4,取下一字符
5,比较,看看取得的字符是否为NULL ,如果是,说明字符串结束,如果不是,回到第1步
6,将计算的到的edx值格式化后输出到缓冲区中
7,缓冲区的字符串依次和38 44 43 41 46 33 36 38比较.,字符串翻译为: 8DCAF368
ebx=00006B00
ebx=006B6100
ebx=6B616E00
ebx=616E676B
ebx=6E676861
ebx=6768746E
ebx=68747467
ebx=74746168
--------------------------
edx=7FF45409
0040123E |. BB BF204000 mov ebx, 004020BF 取缓冲区地址 ; ASCII "7FF45409"
00401243 |. 803B 38 cmp byte ptr [ebx], 38 缓冲区数据比较
00401246 |. 75 35 jnz short 0040127D
004020BF=004020BF (ASCII "7FF45409")
ebx=74746168
我们知道最后比较的字符串为:8D CA F3 68,现在考虑如何逆向它:
ebx=00006B00 00k0
ebx=006B6100 0ka0
ebx=6B616E00 kan0
ebx=616E676B angk
ebx=6E676861 ngha
ebx=6768746E ghtn
ebx=68747467 httg
ebx=74746168 ttah
--------------------------
edx=7FF45409
也就是 最后的edx的结果要是8DCAF368
n个字符经循环移位变换后得n/2个字符,
我们找一些ASIIC小的字符作为被减数,
我们取4字符: ! " # % 作为被减数
由于开始取四字符,从后向前减,往往后面不够减,故从前向后推.
取 k = ! 减去 00002100
我们的字符依次减,! - # " ! 可到第6步步够减,
因此缩短注册码的数目减为6位
ebx=00006B00 0 0 k 0
ebx=006B6100 0 k a 0
ebx=6B616E00 k a n 0
ebx=616E676B a n g k
ebx=6E676861 n g h a
ebx=6768746E g h t n
给出算法:
假如我们的注册码是: kanght ( 6位)
所以输入超过6位你就不好算了
k+a+n+g == 8D
k+a+n+g+h == CA
k+a+n+g+h+t == F3
k+a+n == 68
按顺序来,求这个多项式的值,满足就可以.注册码应该有很多.( 有点像高中的解多项式,哈哈,不过变量是多元的)
大家观察一下,应该从变量少的加到变量多的,就是先求3个变量的,在求四个的.为什么呢? 好计算阿,所以说学数学很重要。
我最后求的注册码是: " $ " % = ) 注意输入的时候切换成英文,要不出错那就不怪我了。
22 24 22 25 3D 29
下面我们来写一个计算注册号的程序;
伪算法:
其中涉及到进制转换,那就是你编程的水平了。
char k,a,n,g,h,t;
cout<<"请输入注册号的前两位:(注意,输入两数之和不大于0x68 " <<endl;
cin>>k>>a;
n=0x68-k-a;
g=0x8D-k-a-n;
h=0xCA-k-a-n-g;
t=0xF3-k-a-n-g-h;
--------------------------------------------------------------------------------
【经验总结】
分析的关键部分没花多少时间,可是算法花了我4小时,由于刚开始学习,不过还是自己动手练出来了。嘿嘿。。
以后得花时间学数学了, 现在明白它真的很重要。。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2008年03月28日 下午 05:15:34
[培训]科锐逆向工程师培训第53期2025年7月8日开班!
上传的附件: