WIN32汇编语言教程:第17章 PE文件 · 17.1 PE文件的结构(8)

                                  call      _OpenFile
                          .elseif ax ==  IDM_EXIT
                                  invoke EndDialog,hWnd,NULL
                         .endif
                  .else
                         mov    eax,FALSE
                         ret
                  .endif
                  mov    eax,TRUE
                  ret
 
_ProcDlgMain      endp
;####################################################################
start:
                  invoke LoadLibrary,offset szDllEdit
                  mov    hRichEdit,eax
                  invoke GetModuleHandle,NULL
                  mov    hInstance,eax
                  invoke DialogBoxParam,hInstance,\
                          DLG_MAIN,NULL,offset _ProcDlgMain,NULL
                  invoke FreeLibrary,hRichEdit
                  invoke ExitProcess,NULL
;####################################################################
                  end    start

源代码中没有任何新的概念:程序首先创建一个对话框,并在初始化的时候将RichEdit控件的字体设置为“宋体”。当用户选择“打开”文件菜单后,则显示一个打开文件通用对话框让用户选择一个文件。

最后,程序用内存映射文件的方法将PE文件映射到内存中以供处理,处理使用的代码就是根据具体的情况编写的_ProcessPeFile.asm文件。

在处理文件之前,程序使用第14章中介绍的SEH来设置一个异常处理回调函数,一旦发生异常的话,则将程序转移到_ErrFormat标号处执行并认为文件的格式存在异常。由于PE文件的分析中涉及到很多指针操作,对任何一个指针都进行检测并判断它们是否已经越出了内存映射文件的范围是很麻烦的,使用SEH可以让这方面的工作开销最少。

当一切准备就绪后,程序要简单地判断一下打开的文件是否是一个合法的PE文件,详见下面的一段代码:

       assume esi:ptr IMAGE_DOS_HEADER
       .if    [esi].e_magic != IMAGE_DOS_SIGNATURE
              jmp    _ErrFormat
       .endif
       add    esi,[esi].e_lfanew
       assume esi:ptr IMAGE_NT_HEADERS
       .if    [esi].Signature != IMAGE_NT_SIGNATURE
              jmp    _ErrFormat
       .endif
       invoke _ProcessPeFile,@lpMemory,esi,@dwFileSize

esi一开始被指向文件的头部,程序首先判断DOS文件头中的标识符是否和“MZ”(也就是IMAGE_DOS_SIGNATURE)符合,如果符合的话,那么从003ch处(也就是e_lfanew字段)取出PE文件头的偏移,并比较PE文件头的标识是否为IMAGE_NT_SIGNATURE,这两个步骤都通过的话,那么几乎可以认定这是一个合法的PE文件了,程序就真正开始分析工作——调用_ProcessPeFile.asm 文件中的_ProcessPeFile子程序。

_ProcessPeFile.asm文件的内容如下所示:

                  .const
szMsg  db     '文件名:%s',0dh,0ah
          db    '-------------------------------------------',0dh,0ah
           db     '运行平台:         0x%04X',0dh,0ah
           db     '节区数量:         %d',0dh,0ah
           db     '文件标记:         0x%04X',0dh,0ah
           db     '建议装入地址:     0x%08X',0dh,0ah,0ah,0
szMsgSection db '--------------------------------------------',0dh,0ah
           db     '节区名称 节区大小 虚拟地址 Raw_尺寸 Raw_偏移 节区属性',0dh,0ah
          db '-----------------------------------------------',0dh,0ah,0
szFmtSection db '%s %08X %08X %08X %08X %08X',0dh,0ah,0
 
                  .code
;####################################################################
_ProcessPeFile proc      _lpFile,_lpPeHead,_dwSize
                  local  @szBuffer[1024]:byte,@szSectionName[16]:byte
 
                  pushad
                  mov    edi,_lpPeHead
                  assume edi:ptr IMAGE_NT_HEADERS
;********************************************************************
; 显示 PE 文件头中的一些信息
;********************************************************************
                  movzx  ecx,[edi].FileHeader.Machine
                  movzx  edx,[edi].FileHeader.NumberOfSections
                  movzx  ebx,[edi].FileHeader.Characteristics
                  invoke wsprintf,addr @szBuffer,addr szMsg,\
                          addr szFileName,ecx,edx,ebx,\
                          [edi].OptionalHeader.ImageBase
                  invoke SetWindowText,hWinEdit,addr @szBuffer
;********************************************************************
; 循环显示每个节区的信息
;********************************************************************
                  invoke _AppendInfo,addr szMsgSection
                  movzx  ecx,[edi].FileHeader.NumberOfSections
                  add    edi,sizeof IMAGE_NT_HEADERS
                  assume edi:ptr IMAGE_SECTION_HEADER
                  .repeat
                          push      ecx
;********************************************************************
; 获取节的名称,由于节名称不一定是以0结尾的,所以要进行一些处理
;********************************************************************
                      invoke RtlZeroMemory,addr @szSectionName,\
                             sizeof @szSectionName
                     push      esi
                     push      edi
                      mov    ecx,8
                      mov    esi,edi
                      lea    edi,@szSectionName
                     cld
                      @@:
                     lodsb
                     .if    ! al
                              mov    al,' '
                     .endif
                      stosb
                      loop      @B
                     pop    edi
                      pop    esi
;********************************************************************
                      invoke wsprintf,addr @szBuffer,addr szFmtSection,\
                             addr @szSectionName,[edi].Misc.VirtualSize,\
                             [edi].VirtualAddress,[edi].SizeOfRawData,\
                              [edi].PointerToRawData,[edi].Characteristics
                      invoke _AppendInfo,addr @szBuffer
                      add    edi,sizeof IMAGE_SECTION_HEADER
;********************************************************************
                     pop    ecx
                      .untilcxz
                      assume edi:nothing
                      popad
                      ret
 
_ProcessPeFile    endp
;####################################################################

程序首先显示了PE文件头中一些重要字段的数值,如Machine,NumberOfSections和Characteristics等字段,然后根据NumberOfSections的数值构造一个循环,并在循环中显示每个节的信息。

在子程序开始的地方,edi寄存器被赋值为PE文件头指针,当在循环开始前将edi加上IMAGE_NT_HEADERS结构的长度后,edi指向的就是节表的起始地址了,使用edi作指针就可以将每个节的名称、尺寸、RVA地址、在文件中的偏移以及大小显示出来。

读者可以用这个PEInfo程序打开不同的文件来验证一下本节中叙述的一些内容,并思考下面的问题:

(1)比较EXE文件和DLL文件的Characteristics字段的差异。

(2)EXE文件往往没有重定位节(一般名称为.reloc),而DLL文件中总是有这个节。

(3)看看代码节和数据节的属性有什么不同,再查看第11章中KeyHook例子中的DLL文件,看看包含共享数据的节的属性又有什么不同。

(4)编写一个有.data?段却没有.data段的程序并用PEInfo去查看,可以发现数据节在磁盘文件中的长度为0,但是被映射到内存中以后却不为0。

上页:第17章 PE文件 · 17.1 PE文件的结构(7) 下页:第17章 PE文件 · 17.2 导入表(1)

第17章 PE文件

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