有个这样的程序(脱壳后,upx壳),用ida静态分析的话,发现从入口开始,全是混乱的指令加jmp,有些call指令,跟进去看还是混乱的指令加jmp,连ret都没有。跟着跳来跳去,又发现这些代码还构成了一个环路,也就是说,静态看的话是死循环。仔细分析,有个别call是用了retn指令返回的,但并非返回原地址,而是代码直接修改了堆栈达到隐含跳转,从而控制代码流程(后来的跟踪发现每次跳出执行完了后又会跳进这个循环)。
脱了壳后,运行显示一个校验错误对话框,然后退出。
用OD打不开,直接退出,隐藏debuger等等也不行。后来没办法拿VC试试,居然可以加载调试,显示对话框("a debuger has found in ....."),然后退出,MessageBox啊,本以为问题好解决了,结果...,这代码太太太复杂了。那个带retn的call确实应该是控制流程的关键点,同时起平衡堆栈的作用,但知道了也没多大用。在这个关键call上加断点,经过艰苦的跟踪,试验,发现加载提示信息是在那个环路执行了3543次后,而显示MessageBox是在2万多次后,正常只要几条语句的事,它这里经过了这么庞大的一个过程,根本无法找到真正检测debuger的代码。同时顺便说一下,这个提示信息在文件里是没加密的常量,但是加载提示信息时该地址是经过动态计算后得到的,计算也经过了多次循环,所以搜引用此地址的指令是搜不到的。
另外,检测debuger应该用了多种方法,我试验过简单的像IsDebuggerPresent法,CloseHandle法都有,可能还有int3法,其他的我的水平就发现不了了。因为代码没法分析。
我的推测,这个程序应该是用了什么代码变形工具,把正常代码拆分成微小的块,每一块再经过变形处理,出来就面目全非了。也许它每次的复杂循环只相当于执行了正常的一条指令?
那个关键的call像这样(这是通用的,执行到显示对话框时就执行了2万多次,跳转到MessageBox也是在这里retn过去的):
push [esp+arg_0]
.nsn1:0056A780 xor edx, 615B3775h
.nsn1:0056A786 push esp
.nsn1:0056A787 mov [esp+8+arg_40], edx
.nsn1:0056A78B pushf
.nsn1:0056A78C mov [esp+0Ch+var_C], bx
.nsn1:0056A790 mov byte ptr [esp+0Ch+var_C], bl
.nsn1:0056A793 push [esp+0Ch+arg_40]
.nsn1:0056A797 retn 54h
上一层调用:
.nsn1:0056BCD0 movsx dx, cl
.nsn1:0056BCD4 mov al, [esi]
.nsn1:0056BCD6 movzx edx, al
.nsn1:0056BCD9 pusha
.nsn1:0056BCDA inc esi
.nsn1:0056BCDB btc dx, 1
.nsn1:0056BCE0 sub al, bl
.nsn1:0056BCE2 bt bp, 1
.nsn1:0056BCE7 pushf
.nsn1:0056BCE8 cmc
.nsn1:0056BCE9 rol al, 4
.nsn1:0056BCEC btr edx, 2
.nsn1:0056BCF0 test cx, si
.nsn1:0056BCF3 btc edx, esi
.nsn1:0056BCF6 xor al, 83h
.nsn1:0056BCF8 movzx dx, al
.nsn1:0056BCFC mov dx, [esp+0C8h+var_B8]
.nsn1:0056BD01 pusha
.nsn1:0056BD02 sub dh, 0CBh
.nsn1:0056BD05 neg al
.nsn1:0056BD07 btc edx, 17h
.nsn1:0056BD0B sub bl, al
.nsn1:0056BD0D cmc
.nsn1:0056BD0E adc dl, al
.nsn1:0056BD10 bt cx, 0Dh
.nsn1:0056BD15 rol dl, 4
.nsn1:0056BD18 movzx eax, al
.nsn1:0056BD1B ror dh, 3
.nsn1:0056BD1E mov edx, dword ptr ds:loc_56A083[eax*4]
.nsn1:0056BD25 call sub_56A77C ;调用上面的代码
初学就遇到这种事,真的很受打击,恳请高手们能分析一下,教教我们怎么应付
[课程]Linux pwn 探索篇!