修改磁盘中的PE文件,增加一个API函数!
添加新节,可以先计算然后再添加。这样麻烦点。而是直接把映象加大到一个固定值。;*********************************************************************;添加一个新节,把导入表复制到新节中,更改导入表rva,oep到新加的api函数中然后跳回去
;by onepc/153785587
;添加新节参照玩命的代码,添加api函数参照lordpe手动添加一个api然后用winhex打开观看变化的内容
;然后根据它的变化写出来的。
;想到那里就写那里,写完之后就不想再去分类写子程序了。给初学的参考下。因我也是初学的。有注释
;还有就是修改之后启动慢了好多。map时可以映小的+
;感谢924792768
;*********************************************************************
.386
.model flat,stdcall
option casemap:none
;*********************************************************************
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
include comdlg32.inc
includelib comdlg32.lib
;*********************************************************************
_OpenFile proto ;打开文件并映射文件到内存
_FileAlign proto dwVirSize : DWORD, dwAlign : DWORD
_RvaToOffset proto _lpNtHeader:DWORD,_RvaAddr:DWORD
_CheckPeInfo proto _lpMem:DWORD ;检查是否是PE文件
_SetSection proto_lpNtHeader:DWORD,_RvaAddr:DWORD
;*********************************************************************
dwSectionSize equ 100h
.data
szCreateFileError db '打开文件失败',0
szCreateFileMappingError db '创建文件映射失败',0
szMapViewOfFileError db '映射文件到内存失败',0
szError db 'ERROR',0
szInvalPe db '无效PE格式',0
szNewSectionName db 'onepc',0
szWz db 'http://www.baidu.com/index.php?tn=onepc_pg',0
szShell32 db 'Shell32.dll',0
szShellExecuteA db 'ShellExecuteA',0
szFmat db '%08x',0
szFilterdb'Text Files(*.exe)',0,'*.exe',0,'Dll Files(*.dll)',0,'*.dll',0,0
.data?
hInstance dd ?
szBuffer db MAX_PATH dup (?)
.code
;*********************************************************************
_OpenFile proc
local @hFile:DWORD,@hFileMap:DWORD
local @lpMem:DWORD
local @szFileNameBuf:BYTE
local @stFile:OPENFILENAME
pushad
invoke RtlZeroMemory,addr @szFileNameBuf,sizeof @szFileNameBuf
invoke RtlZeroMemory,addr @stFile,sizeof @stFile
mov @stFile.lStructSize,sizeof @stFile
;push hMain
;pop @stFile.hwndOwner
;mov @stFile.hwndOwner,hMin
mov @stFile.lpstrFilter,offset szFilter
lea eax,@szFileNameBuf
mov @stFile.lpstrFile,eax
mov @stFile.nMaxFile,MAX_PATH
mov @stFile.Flags,OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST
invoke GetOpenFileName,addr @stFile
.if !eax ;getopenfilename打开成功的话返回非0值
popad
ret
.endif
invoke CreateFile,addr @szFileNameBuf,GENERIC_READ+GENERIC_WRITE,\
FILE_SHARE_READ+FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
.if eax==INVALID_HANDLE_VALUE
jmp CreateFileError
.endif
mov @hFile,eax
invoke GetFileSize,@hFile,NULL
.if !eax ;等于0失败
jmp CreateFileError
.endif
add eax,2000h
xchg eax,ecx
invoke CreateFileMapping,@hFile,NULL,PAGE_READWRITE,0,ecx,NULL ; 这里若是都设0表示是以原文件的尺寸映射到内存中
.if !eax
jmp CreateFileMapError
.endif
mov @hFileMap,eax
invoke MapViewOfFile,@hFileMap,FILE_MAP_READ+FILE_MAP_WRITE+FILE_MAP_COPY,NULL,NULL,NULL
.if !eax
jmp MapViewOfFileError
.endif
mov @lpMem,eax
;*********************************************************************
invoke _CheckPeInfo,@lpMem;检查PEINFO
;*********************************************************************
;*********************************************************************
;*********************************************************************
ExitOpenFile:
invoke UnmapViewOfFile, @lpMem
invoke CloseHandle, @hFileMap
invoke CloseHandle, @hFile
popad
ret
;*********************************************************************
CreateFileError:
lea eax,szCreateFileError
jmp ShowError
CreateFileMapError:
lea eax,szCreateFileMappingError
jmp ShowError
MapViewOfFileError:
lea eax,szMapViewOfFileError
jmp ShowError
ShowError:
invoke MessageBox,NULL,eax,addr szError,MB_OK
jmp ExitOpenFile
;*********************************************************************
_OpenFile endp
;*********************************************************************
_CheckPeInfo proc _lpMem:DWORD ;起始地址
local @lpNtHeader:DWORD ;NTHEADER
local @dwSectionNum:DWORD ;原来节数目
local @dwSectionAlign:DWORD ;节对齐值
local @dwFileAlign:DWORD ;文件对齐值
local @dwStartSection:DWORD ;原始节的起始位置
local @dwNewStartSection:DWORD ;新节超始位置
local @dwNewVaddr:DWORD ;新节的内存偏移值
local @dwNewFaddr:DWORD ;新节的文件偏移值
local @dwShell32:DWORD ;Shell32.dll名所在的文件的文件偏移 含lpMem
local @dwShellExecuteA:DWORD ;指向shellexecuteA的地址
local @dwDllNameAddr:DWORD ;
local @dwShellExecuteAddr:DWORD
local @dwNewImprotable:DWORD ;新的导入表位置
local @dwSaveOep:DWORD ;旧入口点
local @dwNewOep:DWORD ;新入口点
local @dwImageBase:DWORD ;基址
mov esi,_lpMem
assume esi:ptr IMAGE_DOS_HEADER
.if .e_magic!='ZM'
jmp PeError
.endif
add esi,.e_lfanew ;指向NTHEADER
mov @lpNtHeader,esi
assume esi:ptr IMAGE_NT_HEADERS
.if WORD PTR .Signature!='EP'
jmp PeError
.endif
movzx ecx,.FileHeader.NumberOfSections
mov @dwSectionNum,ecx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
push .OptionalHeader.ImageBase;基址
pop @dwImageBase
push .OptionalHeader.AddressOfEntryPoint ;oep
pop @dwSaveOep
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
inc .FileHeader.NumberOfSections ;节加1
push .OptionalHeader.SectionAlignment
pop @dwSectionAlign ;内存对齐值
push .OptionalHeader.FileAlignment
pop @dwFileAlign ;文件对齐值
; mov ebx,.OptionalHeader.SizeOfImage
; add ebx,@dwSectionAlign
; mov .OptionalHeader.SizeOfImage,ebx
add esi,sizeof IMAGE_NT_HEADERS ;esi指向节表
assume esi:PTR IMAGE_SECTION_HEADER
mov @dwStartSection,esi ;保存第一个节表值
mov ebx,sizeof IMAGE_SECTION_HEADER ;一个节表大小
mov eax,@dwSectionNum ;有N个节表
mul ebx ;eax的值就是N个节表的大小
add esi,eax ;esi指向是要新加的节表的起始位置
mov @dwNewStartSection,esi ;新节表起始位置
lea edi,.Name1
lea esi,szNewSectionName
SetSectionValue:
lodsb ;把esi指向的字节一个一个装入al
test al,al
jz ExitSetSectionName
stosb ;从al里一个一个地传到edi中
jmp SetSectionValue
ExitSetSectionName:
mov esi,@dwNewStartSection
push 0E00000E0h ;设置节块属性为可读可写可执行 C0000040
pop .Characteristics
push dwSectionSize ;设置内存中节块大小
pop .Misc.VirtualSize
;***************************************************************************
invoke _FileAlign,dwSectionSize,@dwFileAlign ;返回的是文件对齐后的值
mov .SizeOfRawData,eax;文件对齐值
; invoke _FileAlign,dwSectionSize,@dwSectionAlign ;实际大小除于节对齐,那么返回的是节对齐之后的值
; mov ecx,eax ;节对齐之后的值保存在ecx中
sub @dwNewStartSection,sizeof IMAGE_SECTION_HEADER ;上一节的偏移
mov eax,@dwNewStartSection
assume eax:PTR IMAGE_SECTION_HEADER
mov ecx,.Misc.VirtualSize ;上节的实际大小
push eax
invoke _FileAlign,ecx,@dwSectionAlign ;对齐之后的大小
mov ecx,eax
pop eax
add ecx,.VirtualAddress;上面ecx是节对齐后的大小 加上 上一节的内存偏移,那么ecx现在的值就是新节的内存偏移地址
mov .VirtualAddress,ecx ;新节内存偏移
mov @dwNewVaddr,ecx ;保存新节内存偏移
mov ecx,.PointerToRawData ;上一节的文件偏移
mov ebx,.SizeOfRawData ;上一节的文件对齐的大小
add ecx,ebx
mov .PointerToRawData,ecx ;新节文件偏移
mov @dwNewFaddr,ecx ;保存新节的文件偏移
push ecx ;新了文件偏移
;***************************************************************************
mov ecx,.Misc.VirtualSize ;新节的没对齐的大小
add ecx,.VirtualAddress ;新节内存偏移
invoke _FileAlign,ecx,@dwSectionAlign;返回对齐之后的值,就是整个镜像的大小
mov ecx,eax
mov eax,@lpNtHeader
assume eax:PTR IMAGE_NT_HEADERS
mov .OptionalHeader.SizeOfImage,ecx ;镜像大小
;***************************************************************************
pop ecx;新了文件偏移
mov edi,ecx;;;;;;;
; invoke wsprintf,addr szBuffer,addr szFmat,edi
; invoke MessageBox,NULL,addr szBuffer,addr szBuffer,0
add edi,_lpMem ;在内存中的新节的文件偏移的位置
; invoke wsprintf,addr szBuffer,addr szFmat,edi
; invoke MessageBox,NULL,addr szBuffer,addr szBuffer,0
push edi
mov ecx,.SizeOfRawData ;文件对齐后的大小给ecx , 目的是要把它填零
xor eax, eax
cld
rep stosb
;s:stosb
;loop s
;***************************************************************************
;***************************************************************************
;添加字符串
invoke lstrlen,addr szWz ;不包含终止符的长度
;invoke wsprintf,addr szBuffer,addr szFmat,eax
;invoke MessageBox,NULL,addr szBuffer,addr szBuffer,0
pop edi ;这个是新节的文件偏移起如位置
mov ecx,eax ;长度
lea esi,szWz
cld
rep movsb
;edi已加上了szWz的长度的位置了。
;invoke wsprintf,addr szBuffer,addr szFmat,edi
;invoke MessageBox,NULL,addr szBuffer,addr szBuffer,0
;***************************************************************************
;复制导入表结构到此
;***************************************************************************
add edi,6 ;其实已算出它多长
;**********************************************************************
;**********************************************************************
mov eax,edi
sub eax,_lpMem ;
sub eax,@dwNewFaddr ;对节头的偏移量
mov @dwDllNameAddr,eax ;对节头的偏移量
mov eax,@dwNewVaddr ;新节内存偏移+偏移量
add eax,@dwDllNameAddr
xchg eax,@dwDllNameAddr
;**********************************************************************
;**********************************************************************
mov @dwShell32,edi;指向shell32.dll的起始位置
invoke lstrlen,addr szShell32
mov ecx,eax ;长度
lea esi,szShell32
cld
rep movsb
inc edi ;加一个0字节shell32.dll结尾 edi的文件偏移是加上镜像地址的
;**********************************************************************
;**********************************************************************
mov eax,edi
sub eax,_lpMem ;shellexecutea文件偏移量
sub eax,@dwNewFaddr ;对节头的偏移量
mov @dwShellExecuteAddr,eax ;对节头的偏移量
;mov @dwShellExecuteA,edi ;指向ShellExecuteA的起始位置
;**********************************************************************
;**********************************************************************
add edi,2 ;这二个值是Hint,填零
invoke lstrlen,addr szShellExecuteA
mov ecx,eax ;长度
lea esi,szShellExecuteA
cld
rep movsb
;add edi,5 ;指向下一行 准备放导入表
inc edi ;加一个0 shellexecutea以0结尾空字符
mov eax,@dwNewVaddr ;新节内存偏移+偏移量=指向shellexecute的前二位 即hitn
add eax,@dwShellExecuteAddr ;比如内存偏移是4000 + 6 那么rva就是4006了
mov DWORD PTR ,eax
;**********************************************************************
;**********************************************************************
mov eax,edi ;edi现在是指向指向shellexeute偏移的偏移地址
sub eax,_lpMem ;文件偏移量
sub eax,@dwNewFaddr ;对节头的偏移量
mov @dwShellExecuteAddr,eax ;对节头的偏移量 注:这里只是用同一个变量。
;**********************************************************************
;**********************************************************************
add edi,8
;;;;;;;;;;;;;;;;; 注意这里
;;还有前面的变量偏移加上四就是到导入表的偏移变量
add eax,8
mov @dwNewImprotable,eax ;保存偏移量
mov edx,@lpNtHeader
assume edx:ptr IMAGE_NT_HEADERS
mov ebx,.OptionalHeader.DataDirectory.VirtualAddress ;导入表RVA
invoke _SetSection,@lpNtHeader,ebx
invoke _RvaToOffset,@lpNtHeader,ebx ;把RVA转为文件偏移,返回的eax的值就是文件偏移
push eax ;导入表文件偏移
add eax,_lpMem ;eax是导入表的文件偏移,所以加上_lpMem,那么现在eax就是指向导入表
;;;;;;;;;;;;;;;;;
mov esi,eax ;源
;;;;;;;;;;;;;;;;; 注意这里
mov edx,eax
assume edx:ptr IMAGE_IMPORT_DESCRIPTOR ;IMAGE_IMPORT_DESCRIPTOR最后一个是全0的结构
;***
.while .OriginalFirstThunk || .TimeDateStamp || .ForwarderChain || .FirstThunk || .Name1
add edx,sizeof IMAGE_IMPORT_DESCRIPTOR
.endw ;全0时退出
; add edx,20 ;全0的结构 一个导入表的结构是20字节 不要加
sub edx,_lpMem ;导入表结尾的文件偏移
pop eax ;导入表文件偏移
sub edx,eax ;edx导入表大小
mov ecx,edx ;长度
cld
rep movsb
;复制之前的导入表到新节中完成
;edi现在的位置就可以写入新的导入表结构
mov edx,edi
assume edx:ptr IMAGE_IMPORT_DESCRIPTOR
mov eax,@dwNewVaddr ;新节内存偏移+偏移量
add eax,@dwShellExecuteAddr ;这里指向-指向shellexecuteaddr的rva
;;;;;;;
mov @dwShellExecuteAddr,eax ;存回去,下面用
;;;;;;;
mov .OriginalFirstThunk,eax ;设成相同
mov .TimeDateStamp,0
mov .ForwarderChain,0
mov ecx,@dwDllNameAddr
mov .Name1,ecx;指向dll文件的Rva偏移地址。这里的是新节的RVA
mov .FirstThunk,eax ;设成相同
;这里设置新增导入表完成
;***************************************************************************
;***************************************************************************
;***************************************************************************
;***************************************************************************
;更改oep
add edi,50;20*2=40 +10 预多
mov edx,edi;用edx保存文件里的起始位置
sub edi,_lpMem
sub edi,@dwNewFaddr ;对节头的偏移量
add edi,@dwNewVaddr ;加上新节的RVA 那就是入口点了
mov @dwNewOep,edi
mov WORD PTR ,016Ah
add edx,2
mov WORD PTR ,006Ah
add edx,2
mov WORD PTR ,006Ah
add edx,2
mov BYTE PTR ,68h
add edx,1
mov ecx,@dwNewVaddr;网址在新节0偏移处
add ecx,@dwImageBase ;rva+基址 就是装载到内存中的实际地址
mov DWORD PTR ,ecx
add edx,4
mov WORD PTR ,006Ah
add edx,2
mov WORD PTR ,006Ah
add edx,2
mov BYTE PTR ,0E8h
add edx,1
mov DWORD PTR ,00000005h ;因为下边还有一条jmp指令,预长点 ;call
add edx,4
mov BYTE PTR ,0E9h ;jmp oldoep
add edx,1
;=============================================================
mov ecx,@dwSaveOep ;旧的oep
add ecx,@dwImageBase ;ecx装入内存后的地址要跳到的目的地址
mov eax,edx
add eax,4 ;这里是已加上jmp指令的长度了
sub eax,_lpMem
sub eax,@dwNewFaddr ;对新节的偏移值
add eax,@dwNewVaddr ;rva值
add eax,@dwImageBase ;装入内存中的地址本身jmp地址+指令长度
sub ecx,eax
;=============================================================
mov DWORD PTR ,ecx
add edx,4
;=============================================================
;0000000A5处
mov WORD PTR ,25FFh ;这个jmp是跳到dll里去的。后面跟的地址是导入表中API函数的地址
add edx,2
mov eax,@dwShellExecuteAddr ;指向指向API的rva值
add eax,@dwImageBase ;装入内存中的地址
mov DWORD PTR ,eax
;jmp计算:目的地址-(本身jmp的起始地址+本身指令长度)=jmp后面的数据
;call xxxxxxx变成机器码的话这个是在文件里存放的。这个表标的是
;比如当前的call代码的起始地址是00401041 汇编这个CALL 0040104A
;那么机器码就是:目标地址(0040104A)-起始地址+5字节(call指令长度)=00000004
;就变成了call E8 04000000 这个机器码了
;***************************************************************************
;***************************************************************************
;***************************************************************************
mov esi,@lpNtHeader
assume esi:ptr IMAGE_NT_HEADERS
;;;;;;更改oep
mov ecx,@dwNewOep
mov .OptionalHeader.AddressOfEntryPoint,ecx
mov eax,@dwNewVaddr ;新节内存偏移+偏移量
add eax,@dwNewImprotable
mov .OptionalHeader.DataDirectory.VirtualAddress,eax ;设置新的导入表rva值
;再把IAT清零
mov .OptionalHeader.DataDirectory.VirtualAddress,0
mov .OptionalHeader.DataDirectory.isize,0
;***************************************************************************
ExitPeInfo:
assume edx:nothing
assume eax:nothing
assume esi:nothing
ret
PeError:
lea eax,szInvalPe
invoke MessageBox,NULL,eax,addr szError,MB_OK
jmp ExitPeInfo
_CheckPeInfo endp
;*********************************************************************
;*********************************************************************
_RvaToOffset proc uses edi ebx _lpNtHeader:DWORD,_RvaAddr:DWORD
local @dwSetionNum
mov edi,_lpNtHeader ;***********
assume edi:ptr IMAGE_NT_HEADERS
movzx eax,.FileHeader.NumberOfSections
mov @dwSetionNum,eax
add edi,sizeof IMAGE_NT_HEADERS
assume edi:ptr IMAGE_SECTION_HEADER
mov edx,_RvaAddr
.while @dwSetionNum>0
mov eax,.VirtualAddress
add eax,.SizeOfRawData
.if (edx>=.VirtualAddress) && (edx<eax)
mov ebx,.VirtualAddress
sub edx,ebx
mov eax,.PointerToRawData ;节头的文件偏移
add eax,edx ;节头的文件偏移+对节头的偏移 即已把RVA转换成了文件偏移
jmp @F
.endif
dec @dwSetionNum
add edi,sizeof IMAGE_SECTION_HEADER ;指向下一个节头
.endw
@@:assume edi:nothing
ret
_RvaToOffset endp
;*********************************************************************
;*********************************************************************
;设置可写可读
_SetSection proc uses edi edx eax ebx _lpNtHeader:DWORD,_RvaAddr:DWORD
local @dwSetionNum
mov edi,_lpNtHeader ;***********
assume edi:ptr IMAGE_NT_HEADERS
movzx eax,.FileHeader.NumberOfSections
mov @dwSetionNum,eax
add edi,sizeof IMAGE_NT_HEADERS
assume edi:ptr IMAGE_SECTION_HEADER
mov edx,_RvaAddr
.while @dwSetionNum>0
mov eax,.VirtualAddress
add eax,.SizeOfRawData
.if (edx>=.VirtualAddress) && (edx<eax)
push 0C0000040h
pop .Characteristics
jmp @F
.endif
dec @dwSetionNum
add edi,sizeof IMAGE_SECTION_HEADER ;指向下一个节头
.endw
@@:assume edi:nothing
ret
_SetSection endp
_FileAlign proc uses ecx edx dwVirSize : DWORD, dwAlign : DWORD
mov ecx, dwAlign;对齐值
mov eax, dwVirSize ;写入的实值大小
xor edx, edx
div ecx ;用没有对齐的实际值除以对齐值,若余数(放在edx中)等于0,能整除说明它的大小刚好就用 商*对齐值 =这个就是大小
cmp edx, 0
jz AlreadyAligned
inc eax ;若余数不等于0,说明(商+1) *对齐值就可以放值对齐后的大小
AlreadyAligned:
mul ecx ;对齐后的值放在eax中
ret
_FileAlign endp
;例:没对齐的大小是100 对齐值是200 那么 100/200 edx存的余数不为0 则 要商(eax+1)*对齐值就可以存放这个100的大小
;即是(0+1) *200=200即经对齐后的大小是200
;*********************************************************************
;*********************************************************************
;添加API函数
;用lordpe添加了导入表结构看了下,里面的导入表大小无相关
;IAT那里可以清零
;*********************************************************************
;*********************************************************************
;_AddImportTableAPI proc
;
;
;
;_AddImportTableAPI endp
;*********************************************************************
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke _OpenFile
invoke ExitProcess,NULL
end start
;*********************************************************************
我真想学习一下 看不懂 、、、、、 看不懂啊啊, 参考学习 总结分享 好难啊
页:
[1]