原理:
- 将代码添加到目标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
编译链接后......程序内火绒给清除了?!
那我们从隔离区恢复并添加信任,然后随便选个程序注入看看:
。。。。。。
那就再恢复一下,发现该文件目录下又多了一个文件:
下面那个就是我们注入后的程序,运行一下看看:
果然,出现了这个对话框,点是:
成功运行了!(这个程序里面的数字请忽略,这个是错误的)。
那么这个是我们自己写的程序被注入了,我们试试其他的程序看看:
运行看看: