在PE文件中添加可执行代码

原理:

  • 将代码添加到目标PE文件中有两种方法,一种是将代码插入到原文件代码的节的空隙中,还有一种办法是在节的末尾新增一个节,当然这两种方法都有局限性,第一个必须得保证节与节之间有足够的空隙插入我们的代码,第二个必须得保证节表末尾有足够的空隙让我们再插入一个节表。
  • 我们既然要插入代码,那肯定就是为了执行。所以我们得让文件先执行我们的代码再去执行原来的代码,因此我们需要将原来的执行入口保存在我们的插入代码中,等我们的插入代码执行完后再跳转到原来的程序的执行入口去执行。
  • 还有一个问题就是我们的插入代码是由另一个进程来装入到目标PE文件中的,而因为函数的地址会随着不同进程中DLL装入位置的不同而不同,所以我们的插入代码中要调用函数的话不能直接调用了,只能用之前博客中讲的动态获取API地址的办法来动态地获取API的地址,然后调用函数。
  • 既然我们要先执行我们的插入代码,那么就要将原来程序执行入口修改
  • 我们插入代码后,原来的PE文件头也要做出相应的修改

下面是插入代码:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;一些函数的原形定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProctoGetProcAddress	typedef proto	:dword,:dword
_ProtoLoadLibrary		typedef	proto	:dword
_ProtoMessageBox		typedef	proto	:dword,:dword,:dword,:dword
_ApiGetProcAddress		typedef	ptr		_ProctoGetProcAddress
_ApiLoadLibrary			typedef	ptr		_ProtoLoadLibrary
_ApiMessageBox			typedef ptr		_ProtoMessageBox
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

APPEND_CODE		equ		this byte
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;被添加到目标文件的代码从这里开始
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include			_GetKernel.asm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
hDllKernel32		dd		?
hDllUser32			dd		?
_GetProcAddress		_ApiGetProcAddress		?
_LoadLibrary		_ApiLoadLibrary			?
_MessageBox			_ApiMessageBox			?
szLoadLibrary		db		'LoadLibraryA',0
szGetProcAddress	db		'GetProcAddress',0
szUser32			db		'user32',0
szMessageBox		db		'MessageBoxA',0
szCaption			db		'问题提示',0
szText				db		'你一定要运行这个程序吗?',0
;************************************************************************************************
;新的入口地址
;************************************************************************************************
_NewEntry:
;************************************************************************************************
;重定位并获取一些API的入口地址
;************************************************************************************************
					call	@F
					@@:
					pop		ebx
					sub		ebx,offset @B				;重定位偏移
;***********************************************************************************************************************
					invoke	_GetKernelBase,[esp]		;获取Kernel32.dll的基址
					.if		! eax
							jmp		_ToOldEntry
					.endif
					mov		[ebx+hDllKernel32],eax		
					lea		eax,[ebx+szGetProcAddress]
					invoke	_GetApi,[ebx+hDllKernel32],eax		;获取GetProcAddress函数的入口地址
					.if		!eax
							jmp		_ToOldEntry
					.endif
					mov		[ebx+_GetProcAddress],eax
;***************************************************************************************************************************
					lea		eax,[ebx+szLoadLibrary]
					invoke	[ebx+_GetProcAddress],[ebx+hDllKernel32],eax		;获取LoadLibrary函数的入口地址
					mov		[ebx+_LoadLibrary],eax
					lea		eax,[ebx+szUser32]
					invoke	[ebx+_LoadLibrary],eax								;获取User32.dll的基址
					mov		[ebx+hDllUser32],eax
					lea		eax,[ebx+szMessageBox]								
					invoke	[ebx+_GetProcAddress],[ebx+hDllUser32],eax							;获取MessageBoxA函数的基地址
					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
;*****************************************************************************************************************************
;执行原来的文件
;*****************************************************************************************************************************
_ToOldEntry:
					db		0e9h			;0e9h是jmp指令的机器码
_dwOldEntry:
					dd		?				;原来的入口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
APPEND_CODE_END		equ		this	byte
					
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
					
					

最后两行代码这里解释一下:
 

_ToOldEntry:
					db		0e9h			;0e9h是jmp指令的机器码
_dwOldEntry:
					dd		?				;原来的入口

因为jmp指令的机器码是0e9chxxxxxxxx,后面的叉叉叉就是要跳转的地址减去jmp指令后面一条指令的地址的值。

然后整个插入代码调用程序都是用的动态装入API的方法,程序运行会弹出一个窗口询问是否真的要运行这个程序,是的话,程序将会跳转到原来文件执行入口,不是的话就直接退出。

然后将插入代码插入目标PE文件的程序用的框架还是最开始写PE文件头的博客里面的那个框架,资源也是那里面的资源。功能代码如下:
 

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;_ProcessPeFile.asm 功能: 在PE文件上添加可执行代码
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
			.const
szErrCreate		db	'创建文件错误!',0dh,0ah,0
szErrNoRoom		db	'程序中没有多余地空间可供加入代码!',0dh,0ah,0
szMySection		db	'.adata',0
szExt			db	'_new.exe',0
szSuccess		db	'在文件后附加代码成功,新文件:',0dh,0ah
				db	'%s',0dh,0ah,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
			.code
include		_AddCode.asm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;计算按照指定对齐后的数值
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Align			proc	_dwSize,_dwAlign

				push	edx
				mov		eax,_dwSize
				xor		edx,edx
				div		_dwAlign				;将代码的大小除以文件中的对齐粒度
				.if		edx						;如果有余数则说明没有对齐,则我们要扩充一下
						inc		eax				;EAX中是除法运算后的商,加一之后再乘以对齐粒度就可以了
				.endif
				mul		_dwAlign
				pop		edx
				ret
				
_Align			endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcessPeFile	proc	_lpFile,_lpPeHead,_dwSize
				local	@szNewFile[MAX_PATH]:byte
				local	@hFile,@dwTemp,@dwEntry,@lpMemory
				local	@dwAddCodeBase,@dwAddCodeFile
				local	@szBuffer[256]:byte
				
				pushad
;******************************************************************************************************************
;(Part 1) 准备工作:1-建立新文件,2-打开文件
;******************************************************************************************************************
				invoke	lstrcpy,addr @szNewFile,addr szFileName
				invoke	lstrlen,addr @szNewFile
				lea		ecx,@szNewFile
				mov		byte ptr [ecx+eax-4],0						;将最后一个双字清0
				invoke	lstrcat,addr @szNewFile,addr szExt
				invoke	CopyFile,addr szFileName,addr @szNewFile,FALSE		;复制整个文件
				
				invoke	CreateFile,addr @szNewFile,GENERIC_READ\			;打开文件
						or	GENERIC_WRITE,FILE_SHARE_READ or \
						FILE_SHARE_WRITE,NULL,OPEN_EXISTING,\
						FILE_ATTRIBUTE_ARCHIVE,0
				.if		eax == INVALID_HANDLE_VALUE
						invoke	SetWindowText,hWinEdit,addr szErrCreate
						jmp		_Ret
				.endif
				mov		@hFile,eax	;文件句柄
;**********************************************************************************************************************
;(Part 2)进行一些准备工作和检测工作
; esi -->原PeHead, edi --> 新的PeHead
; edx -->最后一个节表,ebx --> 新加的节表
;**********************************************************************************************************************
				mov		esi,_lpPeHead
				assume	esi:ptr IMAGE_NT_HEADERS,edi:ptr IMAGE_NT_HEADERS			;edi目前还没指向PE头,待会指向它
				invoke	GlobalAlloc,GPTR,[esi].OptionalHeader.SizeOfHeaders			;分配一块固定内存(所有头加上节表的大小),返回指针
				mov		@lpMemory,eax
				mov		edi,eax
				invoke	RtlMoveMemory,edi,_lpFile,\
						[esi].OptionalHeader.SizeOfHeaders							;将原文件的文件头复制到这个分配的内存中
				add		edi,esi
				sub		edi,_lpFile									;sub esi,_lpFie 减去之后就是DOS头的大小,然后EDI加上它就到了PE头的地方
				movzx	eax,[esi].FileHeader.NumberOfSections
				dec		eax
				mov		ecx,sizeof IMAGE_SECTION_HEADER
				mul		ecx											;得到所有节表减去一个节表的大小
				
				mov		edx,edi
				add		edx,sizeof IMAGE_NT_HEADERS
				add		edx,eax										;到达最后一个节表的起始位置(edx是新分配的内存那里)
				mov		ebx,edx
				add		ebx,sizeof IMAGE_SECTION_HEADER				;节表的末尾位置
				assume	edx:ptr IMAGE_SECTION_HEADER,ebx:ptr IMAGE_SECTION_HEADER				;(此时EDX是最后一个节表的起始位置,EBX是最后一个节表的末尾位置)
;*************************************************************************************************************************
;(Part 2.1)检查是否有空闲位置可供插入节表
;*************************************************************************************************************************
				pushad
				mov		edi,ebx
				xor		eax,eax
				mov		ecx,IMAGE_SECTION_HEADER
				repz	scasb			  		;repz指令是重复执行后面的指令直到CX=0或者ZF标志位为0,scasb就是sub al,[edi],并且如果相等edi会执行后面
												;一个单位。所以整个指令的意思就是在EDI所指向的内存单元中寻找与AL中不同的数据(范围位ECX),如果找到了
												;ZF位置0,没找到置1
				popad
				.if		!ZERO?				;找到了,说明后面没有一个大小为一个节表的全为0的空间
;*************************************************************************************************************************
; (Part 3.1)如果没有新的空间的位置可供插入节表,则查看现存代码节的最后是否
;存在足够的全零空间,如果存在则在此处加入代码
;*******************************************************************************************
						xor		eax,eax
						mov		ebx,edi								;(此时EDI为新创的那块内存的PE头的位置)
						add		ebx,sizeof IMAGE_NT_HEADERS			;加上PE头的大小到达节表的位置
						;assume	ebx:ptr IMAGE_SECTION_HEADER		;(此时ebx为新创的哪个内存的第一个节表的起始位置)
						.while	ax <= [esi].FileHeader.NumberOfSections 
								mov		ecx,[ebx].SizeOfRawData			;查看这个节在文件中的大小,如果是0的话则说明这是一个含未初始化数据的节,就不能插入代码
								.if		ecx && ([ebx].Characteristics & IMAGE_SCN_MEM_EXECUTE)		;节的属性为包含可执行代码
										sub		ecx,[ebx].Misc.VirtualSize
										.if		ecx > offset APPEND_CODE_END - offset APPEND_CODE 	;如果空隙大小可以装下我们的代码
												or		[ebx].Characteristics,IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE		;增加属性可读可写
												jmp		@F
										.endif
								.endif
								add		ebx,IMAGE_SECTION_HEADER		;如果这个节没有空隙就到下一个节表看看
								inc 	ax
						.endw
;***********************************************************************************************************************************
						invoke	CloseHandle,@hFile						;如果都没有空隙就插入不了了
						invoke	DeleteFile,addr @szNewFile
						invoke	SetWindowText,hWinEdit,addr szErrNoRoom
						jmp		_Ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
						@@:
;**************************************************************************************************************************************
;将新增代码加入代码节的空隙中
;**************************************************************************************************************************************
						mov		eax,[ebx].VirtualAddress			;(此时ebx到了可以插入代码的那个节的节表的位置)
						add		eax,[ebx].Misc.VirtualSize			
						mov		@dwAddCodeBase,eax					;@dwAddCodeBase到达内存中这个节的末尾位置(这个是个RVA)
						mov		eax,[ebx].PointerToRawData
						add		eax,[ebx].Misc.VirtualSize			
						mov		@dwAddCodeFile,eax					;@dwAddCodeFile到达文件中我们要插入的位置(这个也是个RVA)
						add		[ebx].Misc.VirtualSize,offset APPEND_CODE_END - offset APPEND_CODE		;更新这个节区在内存中的尺寸
						invoke	SetFilePointer,@hFile,@dwAddCodeFile,\						;设置文件指针,就是指向文件起始位置的指针,现在指向
								NULL,FILE_BEGIN												;我们的代码插入位置
						mov		ecx,offset APPEND_CODE_END - offset APPEND_CODE
						invoke	WriteFile,@hFile,offset APPEND_CODE,ecx,\					;将代码写入文件,因为上面设置了文件指针,所以写入位置就是那里
								addr @dwTemp,NULL											;这个局部变量用来返回实际写入的字节数
				.else
;*********************************************************************************************************************************************
;(Part 3.2)如果有新的节表空间的话,加入一个新的节
;*********************************************************************************************************************************************
						inc		[edi].FileHeader.NumberOfSections							;增加节区的数目
						push	edx
						@@:
						mov		eax,[edx].PointerToRawData									;
;**********************************************************************************************************************************************
;当最后一个节是未初始化数据时,PointerToRawData和SizeOfRawData等于0
;这时应该取前一个节的PointerToRawData和SizeOfRawData数据
;**********************************************************************************************************************************************
						.if		! eax											;如果EAX是0说明这是一个未初始化数据的节,取上一个节的位置和大小
								sub		edx,sizeof IMAGE_SECTION_HEADER
								jmp		@B
						.endif
						add		eax,[edx].SizeOfRawData							;eax为最后一个未含有未初始化数据的节的末尾的RVA
						pop		edx
						mov		[ebx].PointerToRawData,eax						;ebx此时指向最后一个节表的末尾,也就是我们新加入的节表的开始
																				;eax指向最后一个节的末尾也就是我们新加入的节的开始
						mov		ecx,offset APPEND_CODE_END - offset APPEND_CODE
						invoke	_Align,ecx,[esi].OptionalHeader.FileAlignment
						mov		[ebx].SizeOfRawData,eax							;设置节区在文件中对齐后的大小
						invoke	_Align,ecx,[esi].OptionalHeader.SectionAlignment
						add		[edi].OptionalHeader.SizeOfCode,eax				;更新所有含代码的节的总大小(内存中)
						add		[edi].OptionalHeader.SizeOfImage,eax			;更新内存中整个PE映像尺寸
						invoke	_Align,[edx].Misc.VirtualSize,[esi].OptionalHeader.SectionAlignment		;获取最后一个节在内存中的大小(因为可能是未初始
																										;化的数据,所以只能这样来获取
						add		eax,[edx].VirtualAddress												;加上最后一个节的RVA得到这个节的末尾的RVA
						mov		[ebx].VirtualAddress,eax												;这个节的末尾RVA就是我们新加入的节的起始RVA
						mov		[ebx].Misc.VirtualSize,offset APPEND_CODE_END - offset APPEND_CODE
						
						mov		[ebx].Characteristics,IMAGE_SCN_CNT_CODE or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE
						invoke	lstrcpy,addr [ebx].Name1,addr szMySection			;节区的名字
;**************************************************************************************************************************************************
;将新增代码作为一个新的节写到文件尾部
;**************************************************************************************************************************************************
						invoke	SetFilePointer,@hFile,[ebx].PointerToRawData,NULL,FILE_BEGIN		;设置文件指针
						invoke	WriteFile,@hFile,offset APPEND_CODE,[ebx].Misc.VirtualSize,addr @dwTemp,NULL	;将代码写入
						mov		eax,[ebx].PointerToRawData
						add		eax,[ebx].SizeOfRawData												;eax到文件末尾位置(RVA)
						invoke	SetFilePointer,@hFile,eax,NULL,FILE_BEGIN							;设置文件指针
						invoke	SetEndOfFile,@hFile													;该函数设置当前位置为文件末尾
;***************************************************************************************************************************************************
						push	[ebx].VirtualAddress
						pop		@dwAddCodeBase
						push	[ebx].PointerToRawData
						pop		@dwAddCodeFile
				.endif
;***************************************************************************************************************************************************
; (Part 4) 修正文件入口指针并写入新的文件头
;***************************************************************************************************************************************************
				mov		eax,@dwAddCodeBase
				add		eax,(offset _NewEntry - offset APPEND_CODE)					;加上偏移得到新的程序执行入口的RVA
				mov		[edi].OptionalHeader.AddressOfEntryPoint,eax				;新的程序执行入口RVA
				invoke	SetFilePointer,@hFile,0,NULL,FILE_BEGIN						;设置文件指针在文件开头的位置
				invoke	WriteFile,@hFile,@lpMemory,[esi].OptionalHeader.SizeOfHeaders,addr @dwTemp,0		;将新的文件头写入(@hFile指向哪个新分配的内存)
;***************************************************************************************************************************************************
; (Part 5)修正新加代码中的 Jmp oldEntry 指令
;***************************************************************************************************************************************************
				push	[esi].OptionalHeader.AddressOfEntryPoint					;原来的文件执行入口RVA
				pop		@dwEntry													;
				mov		eax,@dwAddCodeBase											;内存中可插入的那个节的缝隙的起始RVA或者新加的节的开始的RVA
				add		eax,(offset _ToOldEntry + 5 - offset APPEND_CODE )			;到达jmp指令后面一条指令的RVA
				sub		@dwEntry,eax												;原程序入口地址RVA减去Jmp指令后面一条指令的位RVA,这个值就是jmp指令后面的值
				mov		ecx,@dwAddCodeFile											;文件中节的缝隙的RVA或者文件中新插入的节的起始RVA
				add		ecx,(offset _dwOldEntry - offset APPEND_CODE)				;ecx到达JMP指令后面要写入数据的地方(RVA)
				invoke	SetFilePointer,@hFile,ecx,NULL,FILE_BEGIN					;设置指针位置
				invoke	WriteFile,@hFile,addr @dwEntry,4,addr @dwTemp,NULL			;将jmp后面的数据写入(其实也算是个偏移)
;****************************************************************************************************************************************
; (Part 6)关闭文件
;****************************************************************************************************************************************
				invoke	GlobalFree,@lpMemory			;释放内存
				invoke	CloseHandle,@hFile				;关闭文件句柄
				invoke	wsprintf,addr @szBuffer,Addr szSuccess,addr @szNewFile
				invoke	SetWindowText,hWinEdit,addr @szBuffer
_Ret:
				assume	esi:nothing
				popad
				ret
				
_ProcessPeFile	endp
						
				
			
			
			

为了防止出现意外导致原来的文件出错,所我们是将代码插入到目标文件的一个拷贝当中。并且因为不管是哪种方法,最后都要修改PE文件头中的某些数据,所以我们先申请一段内存将目标文件的PE文件头复制到这个内存里面,然后再这个内存里面对PE头进行修改,最后将修改好的头再复制回去。

因为用的框架都是之前博客里的,所以这里是拷贝了一份目标文件用内存映射文件函数映射到内存后的代码。然后下面对该功能函数中的一些代码解释一下:

;*************************************************************************************************************************
;(Part 2.1)检查是否有空闲位置可供插入节表
;*************************************************************************************************************************
pushad
mov		edi,ebx
xor		eax,eax
mov		ecx,IMAGE_SECTION_HEADER
repz	        scasb			  		
popad

 这段代码就是检测目标文件头中节表部分是否还有空隙能够让我们再插入一个节表,此时的EDI指向最后一个节表的末尾位置,所以该段代码就是从末尾位置范围为一个节表的大小开始寻找,寻找是否有非0的数据,如果有则ZF标志位置0,如果没有则ZF标志位置1。则下面的代码可以判断,如果ZF标志位置1了,则说明有足够的空隙给我们插入一个节表,如果ZF标志位为0,则说明我们要采取另一种办法了。

; (Part 3.1)如果没有新的空间的位置可供插入节表,则查看现存代码节的最后是否
;存在足够的全零空间,如果存在则在此处加入代码
;*******************************************************************************************
xor		eax,eax
mov		ebx,edi						;(此时EDI为新创的那块内存的PE头的位置)
add		ebx,sizeof IMAGE_NT_HEADERS			;加上PE头的大小到达节表的位置
;(此时ebx为新创的哪个内存的第一个节表的起始位置)
.while	ax <= [esi].FileHeader.NumberOfSections 
	mov		ecx,[ebx].SizeOfRawData			;查看这个节在文件中的大小,如果是0的话则说明这是一个含未初始化数据的节,就不能插入代码
	.if		ecx && ([ebx].Characteristics & IMAGE_SCN_MEM_EXECUTE)		;节的属性为包含可执行代码
			sub		ecx,[ebx].Misc.VirtualSize
			.if		ecx > offset APPEND_CODE_END - offset APPEND_CODE 	;如果空隙大小可以装下我们的代码
					or		[ebx].Characteristics,IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE		;增加属性可读可写
					jmp		@F
			.endif
	.endif
	add		ebx,IMAGE_SECTION_HEADER		;如果这个节没有空隙就到下一个节表看看
	inc 	ax
.endw

这段代码就是判断节之间有没有足够的空隙给我们插入代码,这里我们使用节在文件中的大小减去节中真正的数据部分的大小来判断空隙是否够大,因为我们是将文件映射到内存中来处理的,所以数据的排列以及对齐和在磁盘文件中是一样的,我们插入的地方就是一个节为了对齐而在数据后面补0的那部分,而文件被装载到内存后的对齐粒度更大,所以不用担心装载到内存后会发生错误。

该段代码使用一个循环查看每个节的后面的空隙,如果有某个节空隙够大,那么就开始像这个节中注入代码,如果没有一个空隙够,那就只能提示没有空间给我们插入代码了。

最后我们写入代码都是先用SetFilepointer函数设置文件指针到我们要插入的位置,然后再用WriteFile函数将我们的代码写入,最后,如果是增加节表的方法,那么要将PE文件头中的IMAGE_FILE_HEADER结构中的节的数目修改一下,并且要将增加的节表的数据填好,节的大小要扩充成按指定值对齐后的数值,这个函数也在上面的代码中,代码如下:

_Align			proc	_dwSize,_dwAlign

				push	edx
				mov		eax,_dwSize
				xor		edx,edx
				div		_dwAlign				;将代码的大小除以文件中的对齐粒度
				.if		edx						;如果有余数则说明没有对齐,则我们要扩充一下
						inc		eax				;EAX中是除法运算后的商,加一之后再乘以对齐粒度就可以了
				.endif
				mul		_dwAlign
				pop		edx
				ret
				
_Align			endp

编译链接后......程序内火绒给清除了?!

那我们从隔离区恢复并添加信任,然后随便选个程序注入看看:


。。。。。。

那就再恢复一下,发现该文件目录下又多了一个文件:

下面那个就是我们注入后的程序,运行一下看看:

果然,出现了这个对话框,点是:

       成功运行了!(这个程序里面的数字请忽略,这个是错误的)。

那么这个是我们自己写的程序被注入了,我们试试其他的程序看看:

运行看看:

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值