原帖在
http://bbs.pediy.com/showthread.php?t=106605,我在用OD调试时遇到了两个奇怪的问题,我的解答在21L,为了让大家方便回答,我在这里再贴一份吧。
-------------------------------------------------------------------------------------------------------------------------
先运行一下,用户名输入”xiilin”,序列号输入”1234567”,点击Test,窗口刷的一下不见了……
用peid查一下壳吧,ASPack 2.001 -> Alexey Solodovnikov,直接拿出AspackDie脱掉,再查,MASM32 / TASM32,不过文件大了很多……36KB了,呵呵,运行一下,没问题,那就OD载入吧。
先Ctrl+N查看一下输入表,看到了熟悉的GetDlgItemTextA,哈哈,bp GetDlgItemTextA下断点,F9运行起来,输入用户名”xiilin”和序列号”1234567”,点Test,断下来了,本来想Alt+F9返回用户的领空却失败了……不知跳到哪儿去了……
汗,那换个方法吧,Alt+M打开内存窗口,对用户程序的.text区段下一个内存访问断点,然后F9让程序跑起来,断在了004010FA这里,这里就是我们要找的地方了,看一看:
004010E3 |. 68 FF000000 push 0FF
004010E8 |. 68 54304000 push 00403054
004010ED |. 68 E9030000 push 3E9
004010F2 |. FF75 08 push dword ptr [ebp+8]
004010F5 |. E8 18010000 call <jmp.&user32.GetDlgItemTextA> ;获取用户名
004010FA |. 0BC0 or eax, eax
004010FC |. 75 16 jnz short 00401114 ;用户名不为空就跳
004010FE |. 68 00304000 push 00403000
00401103 |. 68 EA030000 push 3EA
00401108 |. FF75 08 push dword ptr [ebp+8]
0040110B |. E8 1A010000 call <jmp.&user32.SetDlgItemTextA> ;提示输入用户名,这里有个小bug,第二个参数应该是3E9而不是3EA
00401110 |. C9 leave ;可以试一下,如果你不输入用户名,那么提示将会出现在序列号输入框里 :-)
00401111 |. C2 1000 retn 10
00401114 |> 68 FF000000 push 0FF
00401119 |. 68 58304000 push 00403058
0040111E |. 68 EA030000 push 3EA
00401123 |. FF75 08 push dword ptr [ebp+8]
00401126 |. E8 E7000000 call <jmp.&user32.GetDlgItemTextA> ;获取序列号
0040112B |. 0BC0 or eax, eax
0040112D |. 75 16 jnz short 00401145 ;序列号不为空则跳
0040112F |. 68 11304000 push 00403011
00401134 |. 68 EA030000 push 3EA
00401139 |. FF75 08 push dword ptr [ebp+8]
0040113C |. E8 E9000000 call <jmp.&user32.SetDlgItemTextA> ;提示输入序列号
00401141 |. C9 leave
00401142 |. C2 1000 retn 10
00401145 |> 68 54304000 push 00403054
0040114A |. 68 48304000 push 00403048
0040114F |. E8 A0000000 call <jmp.&kernel32.lstrcatA> ;用户名前面加上"pediy"字符串
00401154 |. 68 58304000 push 00403058
00401159 |. 68 48304000 push 00403048
0040115E |. E8 97000000 call <jmp.&kernel32.lstrcmpA> ;在这里进行比较
00401163 |. 0BC0 or eax, eax
00401165 |. 75 1F jnz short 00401186 ;验证失败,跳走
00401167 |. 6A 00 push 0
00401169 |. 68 2B304000 push 0040302B
0040116E |. 68 3A304000 push 0040303A
00401173 |. 6A 00 push 0
00401175 |. E8 A4000000 call <jmp.&user32.MessageBoxA> ;成功
0040117A |. 6A 00 push 0
0040117C |. FF75 08 push dword ptr [ebp+8]
0040117F |. E8 88000000 call <jmp.&user32.EndDialog>
00401184 |. EB 59 jmp short 004011DF
00401186 |> 6A 00 push 0
00401188 |. FF75 08 push dword ptr [ebp+8]
0040118B |. E8 7C000000 call <jmp.&user32.EndDialog> ;失败
00401190 |. EB 4D jmp short 004011DF
如果爆破那就很简单了,将00401165处的jnz改成nop,程序就跳不走了,那么试着追一下注册码吧。
本来我还觉得很简单的,但是真仔细一想,我就蒙了,仔细看一下,00403048这个地址存放的是字符串'pediy'和一个代表结束的0,00403054存放我们输入的用户名,00403058存放我们输入的序列号,那么万一我们输入的用户名不止四个字节呢?例如我输入了xiilin,那么00403058和00403059两个字节就存放了‘i’和‘n’,然后再读取我们的密码,这两个字符就被覆盖掉了,画一下现在数据在内存中的布局吧:
00403044 6F 64 21 00 70 65 64 69 79 00 00 00 00 00 40 00 od!.pediy.....@.
00403054 78 69 69 6c 31 32 33 34 35 36 37 00 00 00 00 00 xiil1234567.....
00403064 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
当我们执行到0040114F这一句的lstrcatA的时候,事实上是将xiil1234567这个字符串0040304D这个地址之后,然后再最后添加一个0作为结束标志,那么内存中就变成了这样:
00403044 6F 64 21 00 70 65 64 69 79 78 69 69 6C 31 32 33 od!.pediyxiil123
00403054 34 35 36 37 00 32 33 34 35 36 37 00 00 00 00 00 4567.234567.....
00403064 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
也就是说在执行lstrcmpA之前,真正的注册码就已经变成了pediyxiil1234567,但是如果我们输入pediyxiil1234567呢?注册码就又变了……因为我们输入的注册码也参加了运算——在计算真正的注册码的时候需要使用我们输入的注册码,那么换一句话来说的话也就是我们永远不可能输入正确的注册码!!当然在某种情况下这样说是不准确的,例如我们的用户名不超过三个字节,那么我们输入的注册码变不参与运算了,那就简单了,例如用户名输入1,那么注册码就是pediy1,用户名输入123,注册码就是pediy123,用户名一旦大于三个字节,那么我们就得不到正确的注册码了……
看看源代码吧,作者在inc文件中是这样写的:
.data
;……
serial db 'pediy'
.data?
hInstance dd ?
id dd ?
code dd ?
这里就是越界的根源了,大概作者也没想到会出现这种诡异的情况吧?修改一下:
.data
;……
serial db 'pediy',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;添加了21个0
.data?
hInstance dd ?
id db 21 dup(?)
code dd ?
这样再编译一下的话,用户名就可以达到20个字节了,试一下,用户名输入xiilin,序列号输入pediyxiilin,验证成功。
顺便总结一下遇到的问题,希望路过的高人给能解答一下:
1、程序停在GetDlgItemTextA的断点处后,用Alt+F9为什么回不到程序的领空了?一定要给.text区段下访问断点才行么?还有什么办法快速回到程序的领空?
2、为什么有些call必须F7跟进去,如果用F8跟过去的话,程序直接就跑丢了……我也不知道跑到哪儿去了,反正肯定不是正常情况时的下一行,例如本例中0040114F处的lstrcatA和下一个lstrcmpA,难道必须每一个call都F7跟进去么?还有什么别的办法可以跳过去这些call而且程序跟不丢?这些call为什么会这样?
-----------------------------------------------------------------------------------------------------------------
麻烦大家给我指点一下到底为什么会出上面这两个问题,谢谢!
补充:
关于第一个问题:我试了试,可以一直Ctrl+F9返回程序……寒一个,当我没说,第一个问题就算解决了吧。
关于第二个问题:我开始时用工具脱的壳,现在换手脱了一下,就没问题了……无语,是脱壳没脱干净的原因?哪位有过类似经历么?
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课