第二篇:开始病毒地狱之旅--编码入门篇
一,定位
一个病毒不知道自己会感染host程序什么地方,所以也就不知道自己的地址,也就不能去引用内存
变领。所以一般一个病毒一上来就是给自己定位。
call GetVirAddr
GetVirAddr:
pop ebp
现在ebp指向GetVirAddr了,要想把变量Var1送入eax,则
mov eax,[ebp+Var1-GetVirAddr]
就可以了。
很多病毒(如Funlove),把上面的定位写成一个单独的sub routine,然后通过减法使ebp总是
指向某一个固定位置。这种方式写起来容易,不必总要定位,但不利于优化代码。
二,获取Kernel32的API
这也是Win32病毒必须做的,因为它要用到API,没有API地址它什么都做不了。
但病毒不能有import table,所以一般的方法是暴力搜索内存空间。
这在我的另外一篇文章《Win32 PE病毒入门教程》里讲得很详细了,故摘抄过来,但改了一点小错误。
注意,这种方法需要你对PE格式非常熟悉。病毒无import table,而且现在已经不时髦去攫取host程
序的import table了。
现在通用的技术是从4G的地址空间中暴力搜索Kernel32的基地址,然后从Kernel32的export
table中找到所需要的API的地址。一旦有了Kernel32的API,想导入其它DLL的API也就容易了,
至少可以用LoadLibraryA和GetProcAddress(呵呵,好基本的方法)。
首先要确定Kernel32的基地址。
这个地址在98,2K,XP下都不同。注意,一个好的病毒不能过分依赖某个OS的特性,所以我们不能
在病毒体内写个死的77E80000。所以我们要搜出来。一般一个程序执行时,Kernel32都被映射到它
的地址空间了,这就是我们为什么可以搜索它的地址的原因。
看看三个OS的基地址,98为BFF70000,2K为77E80000,XP为77E60000,Ok,都在70000000以上,
我们可以就从这里开始。当然如果一个一个字节搜索,那么也太慢了,一般DLL定位都在1M边界,
所以我们可以以10000为跨度。
还有一个问题,4G空间不全是可读的,搜到某个地方就会出现GPE错误。怎么办?hmmmmmmmmmmmm,
M$已经想到了这点,在Win32里提供了一种叫做SEH的技术,可以让你掐死出现的错误,使你的程
序继续执行而不崩溃。具体SEH在汇编中的写法,只要几条指令,大家可以自己去找些病毒看一下就
知道了。
注意到PE文件和内存中的映像很相似,所以我们就可以按下面这个方式来搜索Kernel32
ebx->current address,now is 70000000h
Set SEH frame
#1
ebx = ebx + 10000h
if ebx == 0 then 郁闷中。没找到Kernel32???是崩溃还是返回host随你了,我无话可说。
word ptr [ebx] == 'ZM' ?no,goto #1
eax = [ebx + 3ch] ;另外一篇文章中的这两行写的特别别扭,很像C语言,现在改得更”汇编“些
eax = ebx + eax
word ptr [eax] =='EP' ?no,goto #1 ;另外一篇文章中的这行有错误,把eax写成了ebx,现在
改过来了,看来”狂饮啤酒“和”没喝啤酒“效果真的不一样*^__^*
now we are sure this is a PE image,let's look up whether it's a dll,if not,goto #1
then check the export dll name ,if it's not 'KERNEL32.DLL',goto #1
Now go into it's export table,get the APIs address which we use to start our smart
work,hahahaha.
Remove SEH frame
......
SEH handler:
resume to #1
具体细节问题,大家自己去研究。目前大多数Win32病毒都是这个过程,当然具体实现方法会有不同。“
找到Kernel32的API以后,就可以导入其它dll中要用到的API了。
注意,把API地址存在一个数组里,然后用下面方法调用(过分初级了:()
push large param_n
......
push large param_1
call [ebp+Func-GetVirAddr]
注意,所有的Win32 API都是从右至左压栈的。
三,返回host程序
做完初始化的工作后,要尽快返回host程序,否则容易被发现。
但病毒还要继续运行,所以一般都是分配一块内存(VirtualAlloc),把自己copy进去,用CreateThread
在那里启动一个线程,然后就可以返回host了。
push large hostentry ;here will be filled when infecting
retn
或者
mov eax,hostentry
jmp eax
等等,等等。
hostentry存放的是感染时原程序的entry point。