硬盘爱好者 发表于 2012-4-13 22:54:05

修改磁盘中的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
;*********************************************************************

丁刚 发表于 2012-4-14 00:54:17

我真想学习一下

JK徐 发表于 2012-4-14 09:22:42

看不懂 、、、、、

丁刚 发表于 2012-4-14 14:30:09

看不懂啊啊,

1683 发表于 2014-3-1 12:15:35

参考学习 总结分享

s44580761 发表于 2017-9-12 18:28:07

好难啊   
页: [1]
查看完整版本: 修改磁盘中的PE文件,增加一个API函数!