WIN32汇编语言教程:第17章 PE文件 · 17.6 应用实例(5)

另外一个源文件_AddCode.asm中包含了被添加到目标PE文件中的代码,内容如下:

;####################################################################
; 一些函数的原形定义
;####################################################################
_ProtoGetProcAddress      typedef proto  :dword,:dword
_ProtoLoadLibrary     typedef proto  :dword
_ProtoMessageBox   typedef proto  :dword,:dword,:dword,:dword
_ApiGetProcAddress    typedef ptr _ProtoGetProcAddress
_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 ;获取GetProcAddress入口
                  lea    eax,[ebx+szGetProcAddress]
                  invoke _GetApi,[ebx+hDllKernel32],eax
                  .if    ! eax
                         jmp    _ToOldEntry
                  .endif
                  mov    [ebx+_GetProcAddress],eax
;********************************************************************
                   lea    eax,[ebx+szLoadLibrary] ;获取LoadLibrary入口
                  invoke [ebx+_GetProcAddress],[ebx+hDllKernel32],eax
                  mov    [ebx+_LoadLibrary],eax
                   lea    eax,[ebx+szUser32] ;获取User32.dll基址
                  invoke [ebx+_LoadLibrary],eax
                  mov    [ebx+hDllUser32],eax
                   lea    eax,[ebx+szMessageBox] ;获取MessageBox入口
                  invoke [ebx+_GetProcAddress],[ebx+hDllUser32],eax
                  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 xxxxxxxx的机器吗
_dwOldEntry:
                   dd     ?      ;用来填入原来的入口地址
;####################################################################
APPEND_CODE_END equ    this byte

这段代码是按照能够自身重定位的方式写的,而且必须按照这种格式书写,因为当它被添加到目标PE文件后,对于不同的PE文件所处的位置肯定是不同的,不进行重定位处理必然无法正常运行。

附加代码实现的功能和17.6.1节的NoImport例子大致相同,也是首先使用17.6.1节中的两个子程序获取Kernel32.dll模块的基址和GetProcAddress函数的入口地址,并由此最后得到MessageBox函数的入口地址以便显示消息框。

程序最后的_ToOldEntry标号处的数据0e9h是jmp xxxxxxxx的机器码的第一个字节,它与下面的_dwOldEntry标号处的双字一起组成整个jmp指令,jmp指令的编码方式是由一个0e9h字节加上指令执行后的EIP的修正值,也就是说,当jmp指令的下一句指令地址是a,而跳转的目标地址是b的话,那么0e9h字节后的双字的值就是b-a,所以主程序中的这几句就是将指令改成“jmp 原入口地址”的样子:

          push      [esi].OptionalHeader.AddressOfEntryPoint ;esi是PE文件头地址

          pop    @dwEntry       ;现在@dwEntry是原来的入口地址

(1)    mov    eax,[ebx].VirtualAddress

(2)    add    eax,(offset _ToOldEntry-offset APPEND_CODE+5)

(3)    sub    @dwEntry,eax

在指令序列执行前,esi指向PE文件头,ebx指向节表,所以由(1)标出的指令执行后,eax的值是新加代码节的起始地址,(2)指令就是将eax修正到_ToOldEntry后面5字节的位置,也就是jmp xxxxxxxx后一条指令的位置,这样,当指令(3)执行后,@dwEntry中的值就是需要填入_dwOldEntry位置的数据。

好!现在回过头来分析一下_ProcessPeFile.asm文件中的代码。Part 1从原始PE文件拷贝一个名为“原始文件名_new.exe”的文件,这个文件将被添加上可执行代码,原来的“原始文件名.exe”文件则不会被改动。当文件成功拷贝后,程序将打开拷贝生成的新文件以便进行修改。

Part 2分配一个等于目标PE文件的文件头大小的内存块,并将文件头拷贝到这个内存块中,所有对PE文件头的修改操作都是在这个内存块中完成的,这个内存块的内容最终将被写到“原始文件名_new.exe”文件中。

Part 3在节表中加入一个新的节表项目,节表项中的VirtualSize,VirtualAddress,SizeOfRawData,PointerToRawData,Characteristics和Name1字段需要被设置。其中Name1中的名称被设置为“.adata”;Characteristics字段中的标志被设置为可执行和可读写,这是因为需要对这个节中的hDllKernel32等变量进行写入操作。其他几个字段值的算法如下(下面的“上一节”指原始PE文件的最后一节):

● PointerToRawData=(上一节的PointerToRawData)+(上一节的SizeOfRawData)

● SizeOfRawData=附加代码的长度按FileAlignMent值对齐

● VirtualAddress=(上一节的VirtualAddress)+(上一节的VirtualSize按SectionAlignMent的对齐值)

● VirtualSize=附加代码的长度按SectionAlignMent值对齐

其中的对齐算法是用_Align子程序来完成的。在这一部分中,程序还修正了文件头中的SizeOfCode和SizeOfImage的值。如果SizeOfImage的值不被修正的话,Windows将无法装入修改后的PE文件,报的错误为“这不是一个有效的Win32可执行文件”。

Part 5将修改后的文件头写入文件并将附加代码写到文件的最后,由于附加代码的长度还没有按FileAlignMent的值对齐,所以程序再次使用SetFilePointer函数将文件指针移动到对齐后的位置并用SetFileEnd函数将文件长度扩展到这里。

Part 6将原始PE文件的入口地址取出,和附加代码的入口地址计算得出“jmp 原入口地址”这条指令中的二进制码值,并将这个值写到附加代码的对应位置中。

Part 7进行扫尾工作,如释放内存、关闭文件和显示成功信息等。至此,程序的所有功能就完成了。

考 文 献

1 Matt Pietrek. Windows 95 System programming SECRETS. IDG Books. 1995

2 Jeffrey Richter. Programming Applications for Windows. Microsoft Press. 1999

3 Charles Petzold. Programming Windows 95. Microsoft Press. 1996

4 Charles Petzold. Programming Windows. Microsoft Press. 1998

5 W.Richard Stevens. TCP/IP详解:卷1 协议. 北京:机械工业出版社

6 侯捷. 深入浅出MFC. 1998年. 华中理工大学出版社

7 施炜,李铮,秦颍. Windows Sockets规范及应用

8 北京科海. 80386系统设计手册. 1990

9 吕晓庆. 80386/486系统编程实践. 浙江大学出版社,1993

10 Art of Assemble. http://www.cs.ucr.edu/~rhyde. 1996

11 Iczlion's Win32asm tutorial. http://win32asm.cjb.net

 

本书的编写过程中还参考了本人长期积累的、来自因特网上的编程资料,出处一时无法考证,在此一并表示感谢!

上页:第17章 PE文件 · 17.6 应用实例(4) 下页:Windows环境下32位汇编语言程序设计

第17章 PE文件

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号