;好像是11月左右写的,那时正在学习罗大侠的汇编教程,学到PE那里。

对了添加新节,可以先计算然后再添加。这样麻烦点。而是直接把映象加大到一个固定值。

代码:
;*********************************************************************
;添加一个新节,把导入表复制到新节中,更改导入表rva,oep到新加的api函数中然后跳回去
;by onepc/153785587
;添加新节参照玩命的代码,添加api函数参照lordpe手动添加一个api然后用winhex打开观看变化的内容
;然后根据它的变化写出来的。
;想到那里就写那里,写完之后就不想再去分类写子程序了。给初学的参考下。因我也是初学的。有注释
;还有就是修改之后启动慢了好多。map时可以映小的+
;感谢924792768[QQ]
;*********************************************************************
.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

szFilter  db  '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[MAX_PATH]: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 [esi].e_magic!='ZM'
         jmp PeError
     .endif
     add esi,[esi].e_lfanew ;指向NTHEADER
     mov @lpNtHeader,esi
     assume esi:ptr IMAGE_NT_HEADERS
     .if WORD PTR [esi].Signature!='EP'
         jmp PeError
     .endif
    movzx ecx,[esi].FileHeader.NumberOfSections
    mov @dwSectionNum,ecx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   
    push [esi].OptionalHeader.ImageBase  ;基址
    pop @dwImageBase
    push [esi].OptionalHeader.AddressOfEntryPoint ;oep
    pop @dwSaveOep
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 

    inc [esi].FileHeader.NumberOfSections ;节加1

    push [esi].OptionalHeader.SectionAlignment
    pop @dwSectionAlign ;内存对齐值
    push [esi].OptionalHeader.FileAlignment 
    pop @dwFileAlign ;文件对齐值
;    mov ebx,[esi].OptionalHeader.SizeOfImage
;    add ebx,@dwSectionAlign
;    mov [esi].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,[esi].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 [esi].Characteristics
    push dwSectionSize    ;设置内存中节块大小
    pop [esi].Misc.VirtualSize
;***************************************************************************

    
    invoke _FileAlign,dwSectionSize,@dwFileAlign ;返回的是文件对齐后的值
    mov [esi].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,[eax].Misc.VirtualSize ;上节的实际大小
    push eax
    invoke _FileAlign,ecx,@dwSectionAlign ;对齐之后的大小
    mov ecx,eax
    pop eax
    
    add ecx,[eax].VirtualAddress  ;上面ecx是节对齐后的大小 加上 上一节的内存偏移,那么ecx现在的值就是新节的内存偏移地址
     
    mov [esi].VirtualAddress,ecx ;新节内存偏移
    mov @dwNewVaddr,ecx ;保存新节内存偏移
    
    mov ecx,[eax].PointerToRawData ;上一节的文件偏移
    mov ebx,[eax].SizeOfRawData ;上一节的文件对齐的大小
    add ecx,ebx
    mov [esi].PointerToRawData,ecx ;新节文件偏移
    mov @dwNewFaddr,ecx ;保存新节的文件偏移
    push ecx ;新了文件偏移
;***************************************************************************
    mov ecx,[esi].Misc.VirtualSize ;新节的没对齐的大小
    add ecx,[esi].VirtualAddress ;新节内存偏移 
    invoke _FileAlign,ecx,@dwSectionAlign  ;返回对齐之后的值,就是整个镜像的大小
    mov ecx,eax
    mov eax,@lpNtHeader
    assume eax:PTR IMAGE_NT_HEADERS
    mov [eax].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,[esi].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 [edi],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,[edx].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].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 [edx].OriginalFirstThunk || [edx].TimeDateStamp || [edx].ForwarderChain || [edx].FirstThunk || [edx].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 [edx].OriginalFirstThunk,eax ;设成相同
  mov [edx].TimeDateStamp,0 
  mov [edx].ForwarderChain,0
  mov ecx,@dwDllNameAddr
  mov [edx].Name1,ecx  ;指向dll文件的Rva偏移地址。这里的是新节的RVA 
  mov [edx].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 [edx],016Ah
add edx,2
mov WORD PTR [edx],006Ah
add edx,2
mov WORD PTR [edx],006Ah
add edx,2
mov BYTE PTR [edx],68h
add edx,1
mov ecx,@dwNewVaddr;网址在新节0偏移处
add ecx,@dwImageBase ;rva+基址 就是装载到内存中的实际地址
mov DWORD PTR [edx],ecx 
add edx,4
mov WORD PTR [edx],006Ah
add edx,2
mov WORD PTR [edx],006Ah
add edx,2
mov BYTE PTR [edx],0E8h
add edx,1
mov DWORD PTR [edx],00000005h ;因为下边还有一条jmp指令,预长点 ;call
add edx,4
mov BYTE PTR [edx],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 [edx],ecx
add edx,4
;=============================================================
;0000000A5处
mov WORD PTR [edx],25FFh ;这个jmp是跳到dll里去的。后面跟的地址是导入表中API函数的地址
add edx,2
mov eax,@dwShellExecuteAddr ;指向指向API的rva值
add eax,@dwImageBase ;装入内存中的地址
mov DWORD PTR [edx],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 [esi].OptionalHeader.AddressOfEntryPoint,ecx

mov eax,@dwNewVaddr ;新节内存偏移+偏移量
add eax,@dwNewImprotable
mov [esi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress,eax   ;设置新的导入表rva值
;再把IAT清零
mov [esi].OptionalHeader.DataDirectory[12*(sizeof IMAGE_DATA_DIRECTORY)].VirtualAddress,0
mov [esi].OptionalHeader.DataDirectory[12*(sizeof IMAGE_DATA_DIRECTORY)].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,[edi].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,[edi].VirtualAddress
                     add eax,[edi].SizeOfRawData
                 .if (edx>=[edi].VirtualAddress) && (edx<eax)
                     mov ebx,[edi].VirtualAddress
                     sub edx,ebx
                     mov eax,[edi].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,[edi].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,[edi].VirtualAddress
                     add eax,[edi].SizeOfRawData
                 .if (edx>=[edi].VirtualAddress) && (edx<eax)

                    push 0C0000040h
                    pop [edi].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
;*********************************************************************