谢谢楼上的鼓励
无论新旧,补上实现的汇编代码吧,不会的可以学习一下。
首先要说的是因为是基于堆栈初始数据的ANTI,只要在堆栈初始数据下内存或硬件访问断点就可以定位该ANTI了。如果你先ANTI 内存断点和硬件断点,那么这个检查将会非常隐蔽
现在说正题,下面是根据我的结论得出的简单ANTI UNPACK的代码,假如程序被UNPACK了,那么将产生一个0x00000000访问错误
start proc:
call @@1
@@1: pop eax //病毒中常见的获得当前EIP的方法
sub eax,5 //减去第一条代码的机器码长度,即为EP
sub eax,[esp + 34] //校验EP是否等于OEP,如果程序加壳了,EAX不为0(不过看过FLY其中一篇脱文,里面介绍的壳是EP=OEP的)
je eax //这里改变EIP到处理UNPACK的流程或正常流程,随你喜欢了。
ret
end start
把ANTI代码安排在OEP,那么这个ANTI不使用API的隐藏性也就变得意义不大了。而且在程序开始使用了病毒技术,很可能会被杀毒软件误报。下面我将提供一个更加灵活的方法,并把ANTI OD的代码给出。
_start proc:
push ebp
mov ebp,esp //一般程序入口都有的语句,EBP=EP处ESP-4
call save_ebp //这里可以是_START中的任意CALL,但是得保证在这之前EBP不会被改变。
pop ebp
ret
end _start
save_ebp proc:
push ebp
mov ebp,esp //这同样会在一般CALL中出现
mov dword ptr ds:[00ff0000],ebp //在函数的一开始插入汇编代码,将EBP保存在任意全局变量中,好了,现在你可以在任意深度的CALL中访问EP的ESP了,不过要注意访问必须在此处运行之后。
pop ebp
ret
end save_ebp
chk_od proc: //检查OD是否存在
pushad
mov ebp,dword ptr ds:[00ff0000] //EBP=EP处ESP-4
mov edx,7C816D4F
cmp edx,dword ptr ss:[ebp + 4]
je _IsXPsp2 //用结论中提到的方法判断操作系统,下同
mov edx,77E67903
cmp edx,dword ptr ss:[ebp + 4]
je _IsWin2K
mov edx,77E687F5
cmp edx,dword ptr ss:[ebp + 4]
je _IsWin2Ksp4
jmp chk_end
_IsXPsp2:
mov edx,FFFFFFFF
cmp edx,dword ptr ss:[ebp+0c] //对比OD在XPSP2的特征码
je Is_debug //调用你自己处理被OD调试的例程
jmp chk_end
_IsWin2K:
xor edx,edx //在这个版本中OD在该位置的特征码是00000000
cmp edx,dword ptr ss:[ebp+0c]
je Is_debug
jmp chk_end
_IsWin2Ksp4:
mov edx,00000056 //在这个版本中OD在该位置的特征码是00000056
cmp edx,dword ptr ss:[ebp+0c]
je Is_debug
chk_end:
popad
ret
chk_od end
必须要说明的是此方法有很大局限性,经测试,在不同硬件环境相同系统下可用,但是你必须找出所有系统所有SP版本下的特征码。但是我没有测试在不同版本中的OD是否仍然可用,我使用的是flyodbg---ollydbg v1.10中文修改版。
注意到,在相同系统下,由OD加载程序堆栈的初始数据在EP ESP+8处是固定不变的,而由系统加载,此处第一次加载跟第二次加载是不同的,此后加载的初始堆栈都跟第二次一样,直到注销或重启系统。假如你能实现判断是否第一次被系统运行,然后每次启动都将EP ESP+8保存在文件或注册表中,那么你将能写出兼容多个系统的ANTI flyodbg---ollydbg v1.10中文修改版函数。由于实现超过了我的水平,我无法给出实例,不过为了ANTI一个版本的OD是否值得也是一个问题。
下面来看看如何ANTI UNPACK。
chk_unpack proc:
pushad
mov ebp,dword ptr ds:[00ff0000] //EBP=EP处ESP-4
mov eax,dword ptr ss:[ebp + 38] //EAX=EP
call @@1
@@1: pop ebx //得到当前EIP
mov edx,eax //下面将计算EP与当前EIP的差值,由于是32位的无符号加减。我需要处理的是两者之间的绝对值,如果你有更好的方法,可以修改这几行代码。
sub eax,ebx
ja @@2
sub ebx,edx
mov eax,ebx
@@2:
cmp eax,10000h //留到最后解释
ja Is_Unpack //调用你的自定义处理函数
popad
ret
end chk_unpack
为什么是10000h? 此处跟系统内存的页面分配有关,我没看过相关介绍,我是从看雪精华六里面翻译的WIN32病毒教程中,关于如何通过内存搜索获得KERNEL32基址部分得到可以这么做的启发。或者你可以用OD打开任意一个程序,然后打开内存窗口查看模块间CODE段的距离,你会发现,这远大于这个值。当然更好的做法是将此常量替换成未加壳程序的CODE段大小。