【文章标题】: 【原创】堆栈初始数据简单分析及其anti od,anti unpack应用
【文章作者】: 笨笨雄
【作者邮箱】: nemo314@gmail.com
【工具名称】: WINDBG OLLYDBG
【作者声明】: 本人见识比较少,不知道这是不是我第一个发现的。如果是老东西,大家可以无视。。。
偶然机会之下,我用windbg跟ollydbg打开同一个程序,发现堆栈的初始数据如下
windbg:
0006ffc4 03 79 e6 77 20 48 08 00-00 00 00 00 00 f0 fd 7f .y.w H..........
0006ffd4 bc f7 13 01 c8 ff 06 00-bc f7 13 01 ff ff ff ff ................
0006ffe4 fd 13 e8 77 08 79 e6 77-00 00 00 00 00 00 00 00 ...w.y.w........
0006fff4 00 00 00 00 20 64 00 01-00 00 00 00 c8 00 00 00 .... d..........
00070004 00 01 00 00 ff ee ff ee-62 00 00 50 60 00 00 40 ........b..P`..@
00070014 00 fe 00 00 00 00 10 00-00 20 00 00 00 02 00 00 ......... ......
00070024 00 20 00 00 d6 02 00 00-ff ef fd 7f 01 00 08 06 . ..............
00070034 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ollydbg:
0006FFC4 77E67903 返回到 KERNEL32.77E67903
0006FFC8 00000000
0006FFCC 00000000
0006FFD0 7FFDF000
0006FFD4 00000000
0006FFD8 0006FFC8
0006FFDC 00000000
0006FFE0 FFFFFFFF SEH 链尾部
0006FFE4 77E813FD SE 句柄
0006FFE8 77E67908 KERNEL32.77E67908
0006FFEC 00000000
0006FFF0 00000000
0006FFF4 00000000
0006FFF8 01006420 test.<ModuleEntryPoint>
0006FFFC 00000000
对比之下,不同的地方有0006FFC8 、0006FFD4、0006FFDC。现在尝试用OD打开某keygenme,得到下面堆栈的初始数据:
0012FFC4 77E67903 返回到 KERNEL32.77E67903
0012FFC8 00000000
0012FFCC 00000000
0012FFD0 7FFDF000
0012FFD4 00000000
0012FFD8 0012FFC8
0012FFDC 00000000
0012FFE0 FFFFFFFF SEH 链尾部
0012FFE4 77E813FD SE 句柄
0012FFE8 77E67908 KERNEL32.77E67908
0012FFEC 00000000
0012FFF0 00000000
0012FFF4 00000000
0012FFF8 004414A0 keygenme.<ModuleEntryPoint>
0012FFFC 00000000
显然,目标程序被OD载入之后,某些位置是不变的。现在来看看WINDBG,载入EXPLORER.EXE得到
0006ffc4 03 79 e6 77 98 97 09 00-00 00 00 00 00 f0 fd 7f .y.w............
0006ffd4 bc f7 17 01 c8 ff 06 00-bc f7 17 01 ff ff ff ff ................
0006ffe4 fd 13 e8 77 08 79 e6 77-00 00 00 00 00 00 00 00 ...w.y.w........
0006fff4 00 00 00 00 a2 c6 40 00-00 00 00 00 c8 00 00 00 ......@.........
00070004 00 01 00 00 ff ee ff ee-62 00 00 50 60 00 00 40 ........b..P`..@
00070014 00 fe 00 00 00 00 10 00-00 20 00 00 00 02 00 00 ......... ......
00070024 00 20 00 00 73 01 00 00-ff ef fd 7f 01 00 08 06 . ..s...........
00070034 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
跟OLLYDBG一样,某些位置是不变的。
先总结一下:
1 经由OLLYDBG与WINDBG加载的同一个程序,堆栈中初始化的数据是不同。
2 不同程序在OLLYDBG中加载,比较堆栈中的初始数据,并不是完全不相同的。相同的初数数据分布在有特殊意义堆栈地址和未知意义的堆栈地址。特殊意义的地址将会被系统写入数据,那么未知意义的地址呢?例如0012FFC8,或许我们可以通过在EP使用ESP+4来访问,假如[esp+4]为0,那么便可以确定程序被OLLYDBG调试
3 假如OLLYDBG堆栈初始数据是正确的,那么[esp+4]不为0程序便是被WINDBG调式了。
也就是说,WINDBG或者OLLYDBG肯定有一个可以通过堆栈的初始数据来判断是否被DEBUG了。现在我们来看看由系统加载的程序,初始堆栈是怎么样的。
用OD随便改一个程序,入口处一开始便是MESSAGEBOX的调用。先用WINDBG加载,记下ESP的值,然后再次运行程序,用WINDBG ATTACH TO A PROCESS,在数据窗口中跟随到刚才记下的ESP。现在我们得到下面堆栈:
0006ffc4 03 79 e6 77 00 00 00 00-f8 a3 0f 00 00 f0 fd 7f .y.w............
0006ffd4 90 dc 1a 04 c8 ff 06 00-90 dc 1a 04 ff ff ff ff ................
0006ffe4 fd 13 e8 77 08 79 e6 77-00 00 00 00 00 00 00 00 ...w.y.w........
0006fff4 00 00 00 00 20 64 00 01-00 00 00 00 c8 00 00 00 .... d..........
00070004 00 01 00 00 ff ee ff ee-02 00 00 00 00 00 00 00 ................
00070014 00 fe 00 00 00 00 10 00-00 20 00 00 00 02 00 00 ......... ......
00070024 00 20 00 00 85 00 00 00-ff ef fd 7f 01 00 08 06 . ..............
00070034 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
EP处的[ESP+8]不为0,而OD加载的程序,在该处是0。发现这个规律之后,我便立刻改了一个程序的入口用以检查OD,能正确检查出OD。但是当我从WIN2K(最初的版本)转到WIN2K SP4的时候,便失效了。我意识到,这种特性与系统是相关的,于是也拿到XP SP2下试验,用OD加载程序后得出下面两组堆栈初始值:
WIN2K SP4:
regedit.exe
0006FFC4 77E687F5 返回到 KERNEL32.77E687F5
0006FFC8 005916A8
0006FFCC 00000056
0006FFD0 7FFDF000
0006FFD4 00000200
0006FFD8 0006FFC8
0006FFDC 00000200
0006FFE0 FFFFFFFF SEH 链尾部
0006FFE4 77E7F0B4 SE 句柄
0006FFE8 77E68EC8 KERNEL32.77E68EC8
0006FFEC 00000000
0006FFF0 00000000
0006FFF4 00000000
0006FFF8 01007345 regedit.<ModuleEntryPoint>
0006FFFC 00000000
explorer.exe
0006FFC4 77E687F5 返回到 KERNEL32.77E687F5
0006FFC8 005916A8
0006FFCC 00000056
0006FFD0 7FFDF000
0006FFD4 00000200
0006FFD8 0006FFC8
0006FFDC 00000200
0006FFE0 FFFFFFFF SEH 链尾部
0006FFE4 77E7F0B4 SE 句柄
0006FFE8 77E68EC8 KERNEL32.77E68EC8
0006FFEC 00000000
0006FFF0 00000000
0006FFF4 00000000
0006FFF8 00408188 explorer.<ModuleEntryPoint>
0006FFFC 00000000
WINXP SP2
notepad.exe
0007FFC4 7C816D4F 返回到 kernel32.7C816D4F
0007FFC8 7C930738 ntdll.7C930738
0007FFCC FFFFFFFF
0007FFD0 7FFD7000
0007FFD4 8054C038
0007FFD8 0007FFC8
0007FFDC 81813D40
0007FFE0 FFFFFFFF SEH 链尾部
0007FFE4 7C8399F3 SE 句柄
0007FFE8 7C816D58 kernel32.7C816D58
0007FFEC 00000000
0007FFF0 00000000
0007FFF4 00000000
0007FFF8 0100739D NOTEPAD.<ModuleEntryPoint>
0007FFFC 00000000
test.exe
0006FFC4 7C816D4F 返回到 kernel32.7C816D4F
0006FFC8 7C930738 ntdll.7C930738
0006FFCC FFFFFFFF
0006FFD0 7FFD4000
0006FFD4 8054C038
0006FFD8 0006FFC8
0006FFDC 816E0B30
0006FFE0 FFFFFFFF SEH 链尾部
0006FFE4 7C8399F3 SE 句柄
0006FFE8 7C816D58 kernel32.7C816D58
0006FFEC 00000000
0006FFF0 00000000
0006FFF4 00000000
0006FFF8 01006420 test.<ModuleEntryPoint>
0006FFFC 00000000
为了看看是否存在硬件相关性,我在另外一台安装了XP SP2的电脑上做了相同的试验,结果如下:
esp.exe
0006FFC4 7C816D4F 返回到 kernel32.7C816D4F
0006FFC8 7C930738 ntdll.7C930738
0006FFCC FFFFFFFF
0006FFD0 7FFD7000
0006FFD4 8054B938
0006FFD8 0006FFC8
0006FFDC 81AA38A0
0006FFE0 FFFFFFFF SEH 链尾部
0006FFE4 7C8399F3 SE 句柄
0006FFE8 7C816D58 kernel32.7C816D58
0006FFEC 00000000
0006FFF0 00000000
0006FFF4 00000000
0006FFF8 01006420 esp.<ModuleEntryPoint>
0006FFFC 00000000
explorer.exe
0007FFC4 7C816D4F 返回到 kernel32.7C816D4F
0007FFC8 7C930738 ntdll.7C930738
0007FFCC FFFFFFFF
0007FFD0 7FFDD000
0007FFD4 8054B938
0007FFD8 0007FFC8
0007FFDC 81CB6DA8
0007FFE0 FFFFFFFF SEH 链尾部
0007FFE4 7C8399F3 SE 句柄
0007FFE8 7C816D58 kernel32.7C816D58
0007FFEC 00000000
0007FFF0 00000000
0007FFF4 00000000
0007FFF8 0101E24E explorer.<ModuleEntryPoint>
0007FFFC 00000000
结论,我们可以通过判断EP中的ESP+8为否为FFFFFFFF或者00000056判断程序是否被OD加载,这里要说明的是[ESP+8]=0的情况。在WIN2K SP4中,由系统加载的程序初始堆栈将会是下面那样
0006ffc4 f5 87 e6 77 00 00 00 00-00 00 00 00 00 f0 fd 7f ...w............
0006ffd4 00 00 00 00 c8 ff 06 00-00 00 00 00 ff ff ff ff ................
0006ffe4 b4 f0 e7 77 c8 8e e6 77-00 00 00 00 00 00 00 00 ...w...w........
0006fff4 00 00 00 00 20 64 00 01-00 00 00 00 c8 00 00 00 .... d..........
00070004 00 01 00 00 ff ee ff ee-02 00 00 00 00 00 00 00 ................
00070014 00 fe 00 00 00 00 10 00-00 20 00 00 00 02 00 00 ......... ......
00070024 00 20 00 00 df 00 00 00-ff ef fd 7f 01 00 08 06 . ..............
00070034 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
[ESP+8]同样为0,所以这个是不能用的,否则会产生误报。要避免程序在WIN2K最初的版本被调试,可以检查系统版本,再检查[ESP+8]是否为0。由于我的同学基本都是用XP SP2的,所以我无法给出除这3个系统之外更多的信息,大家可以用这个方法找出其他系统的特征。
由WINDBG加载的程序,跟系统的还是有差别的,只是无论系统跟WINDBG,在不同的系统,不同的硬件下,没有特别意义的地址一直都在“无规则”的改变。
我估计会有这样的情况出现是因为一般我们释放堆栈的时候只是对ESP进行操作,而不会管仍然存在于堆栈中的数据,我本来想通过分析代码找出那些数据究竟是什么时候被填上去的,可惜我想用WINDBG进行内核调试的时候才发觉我完全不知道应该怎么做,又得看英文了。。。当我熟练掌握WINDBG的时候,我想我可以同时给出WINDBG帮助文件的中文翻译了。。。
回到正题,注意到[esp+34]里面的是EP,我想或者我们可以通过特性来判断在脱壳过程中是否到达OEP。下面是我用NSPACK做了个实验,OEP处的堆栈
0012FFC4 77E687F5 返回到 KERNEL32.77E687F5
0012FFC8 005916A8
0012FFCC 00000056
0012FFD0 7FFDF000
0012FFD4 00000200
0012FFD8 0012FFC8
0012FFDC 00000200
0012FFE0 FFFFFFFF SEH 链尾部
0012FFE4 77E7F0B4 SE 句柄
0012FFE8 77E68EC8 KERNEL32.77E68EC8
0012FFEC 00000000
0012FFF0 00000000
0012FFF4 00000000
0012FFF8 1320536B offset servre.<ModuleEntryPoint>
0012FFFC 00000000
显然这个特性不能在脱壳时应用,反而能成为程序校验自己是否被脱壳,而且重要的是,这种校验,不需要使用API。幸好我们仍然能使用内存断点或硬件断点在入口处ESP+34的地方下断。
最后就是[ESP]了,OD显示返回到 KERNEL32.77E687F5,实际上我们可以通过返回这个地址来达到退出程序的目的。
在做第一个测试的时候,我发现那些返回地址分别对应不同的操作系统
7C816D4F XP SP2
77E67903 WIN2K最初的版本
77E687F5 WIN2K SP4的
另一个不需要API就能判断当前操作系统的方法