ITEEDU

第1章 Iczelion的Win32汇编教程

第2章 Iczelion的ODBC教程

第3章 Iczelion的VxD教程

第4章 Iczelion的PE教程

第5章 罗云彬的Win32汇编教程

第6章 Win32ASM经验点滴

第7章 X86汇编语言编程

第8章 加密解密

第9章 病毒的分析和防治

PE教程5: Section Table(节表)

 

理论:

到本课为止,我们已经学了许多关于 DOS header 和 PE header 的知识。接下来就该轮到 section table(节表)了。节表其实就是紧挨着 PE header 的一结构数组。该数组成员的数目由 file header (IMAGE_FILE_HEADER) 结构中 NumberOfSections 域的域值来决定。节表结构又命名为 IMAGE_SECTION_HEADER。

 IMAGE_SIZEOF_SHORT_NAME  equ       8

    IMAGE_SECTION_HEADER  STRUCT
       Name1  db        IMAGE_SIZEOF_SHORT_NAME dup(?)
union Misc 
         PhysicalAddress  dd        ?
 VirtualSize  dd        ?
              ends
          VirtualAddress  dd        ?
           SizeOfRawData  dd        ?
        PointerToRawData  dd        ?
    PointerToRelocations  dd        ?
    PointerToLinenumbers  dd        ? 
     NumberOfRelocations  dw        ?
     NumberOfLinenumbers  dw        ?
         Characteristics  dd        ?
    IMAGE_SECTION_HEADER  ENDS

同样,不是所有成员都是很有用的,我们只关心那些真正重要的。

Field Meanings
Name1 事实上本域的名称是"name",只是"name"已被MASM用作关键字,所以我们只能用"Name1"代替。这儿的节名长不超过8字节。记住节名仅仅是个标记而已,我们选择任何名字甚至空着也行,注意这里不用null结束。命名不是一个ASCIIZ字符串,所以不用null结尾。
VirtualAddress 本节的RVA(相对虚拟地址)。PE装载器将节映射至内存时会读取本值,因此如果域值是1000h,而PE文件装在地址400000h处,那么本节就被载到401000h。
SizeOfRawData 经过文件对齐处理后节尺寸,PE装载器提取本域值了解需映射入内存的节字节数。(译者注: 假设一个文件的文件对齐尺寸是0x200,如果前面的 VirtualSize域指示本节长度是0x388字节,则本域值为0x400,表示本节是0x400字节长)。
PointerToRawData 这是节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置。
Characteristics 包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等。

现在我们已知晓 IMAGE_SECTION_HEADER 结构,再来模拟一下 PE装载器的工作吧:

  1. 读取 IMAGE_FILE_HEADER 的 NumberOfSections域,知道文件的节数目。
  2. SizeOfHeaders 域值作为节表的文件偏移量,并以此定位节表。
  3. 遍历整个结构数组检查各成员值。
  4. 对于每个结构,我们读取PointerToRawData域值并定位到该文件偏移量。然后再读取SizeOfRawData域值来决定映射内存的字节数。将VirtualAddress域值加上ImageBase域值等于节起始的虚拟地址。然后就准备把节映射进内存,并根据Characteristics域值设置属性。
  5. 遍历整个数组,直至所有节都已处理完毕。

注意我们并没有使用节名: 这其实并不重要。

示例:

本例程打开一PE文件遍历其节表,并在列表框控件显示各节的信息。

.386
.model        flat,stdcall
option casemap:none 
     include  \masm32\include\windows.inc
     include  \masm32\include\kernel32.inc
     include  \masm32\include\comdlg32.inc
     include  \masm32\include\user32.inc
     include  \masm32\include\comctl32.inc
  includelib  \masm32\lib\comctl32.lib
  includelib  \masm32\lib\user32.lib
  includelib  \masm32\lib\kernel32.lib
  includelib  \masm32\lib\comdlg32.lib

        IDD_SECTIONTABLE  equ       104
         IDC_SECTIONLIST  equ       1001

         SEH  struct


    PrevLink  dd        ?           ; the address of the previous seh structure
          CurrentHandler  dd        ?           ; the address of the new exception handler
  SafeOffset  dd        ?           ; The offset where it's safe to continue execution
     PrevEsp  dd        ?           ; the old value in esp
     PrevEbp  dd        ?           ; The old value in ebp
         SEH  ends

.data
     AppName  db        "PE tutorial no.5",0
ofn OPENFILENAME <> 
            FilterString  db        "Executable Files (*.exe, *.dll)",0,"*.exe      ;*.dll",0
              db        "All Files",0,"*.*",0,0
           FileOpenError  db        "Cannot open the file for reading",0
    FileOpenMappingError  db        "Cannot open the file for memory mapping",0
        FileMappingError  db        "Cannot map the file into memory",0
           FileInValidPE  db        "This file is not a valid PE",0
    template  db        "%08lx",0
 SectionName  db        "Section",0
 VirtualSize  db        "V.Size",0
          VirtualAddress  db        "V.Address",0
           SizeOfRawData  db        "Raw Size",0
   RawOffset  db        "Raw Offset",0
         Characteristics  db        "Characteristics",0

.data?
   hInstance  dd        ?
      buffer  db        512 dup(?)
       hFile  dd        ?
    hMapping  dd        ?
    pMapping  dd        ?
     ValidPE  dd        ?
        NumberOfSections  dd        ?

.code
       start  proc
              LOCAL     seh:SEH
              invoke    GetModuleHandle,NULL
              mov       hInstance,eax
              mov       ofn.lStructSize,SIZEOF ofn
              mov       ofn.lpstrFilter, OFFSET FilterString
              mov       ofn.lpstrFile, OFFSET buffer
              mov       ofn.nMaxFile,512
              mov       ofn.Flags, OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
              invoke    GetOpenFileName, ADDR ofn
.if           eax==TRUE
              invoke    CreateFile, addr buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
.if           eax!=INVALID_HANDLE_VALUE
              mov       hFile, eax
              invoke    CreateFileMapping, hFile, NULL, PAGE_READONLY,0,0,0
.if           eax!=NULL
              mov       hMapping, eax
              invoke    MapViewOfFile,hMapping,FILE_MAP_READ,0,0,0
.if           eax!=NULL
              mov       pMapping,eax
              assume    fs:nothing
              push      fs:[0]
              pop       seh.PrevLink
              mov       seh.CurrentHandler,offset SEHHandler
              mov       seh.SafeOffset,offset FinalExit
              lea       eax,seh
              mov       fs:[0], eax
              mov       seh.PrevEsp,esp
              mov       seh.PrevEbp,ebp
              mov       edi, pMapping
              assume    edi:ptr IMAGE_DOS_HEADER
.if           [edi].e_magic==IMAGE_DOS_SIGNATURE
              add       edi, [edi].e_lfanew
              assume    edi:ptr IMAGE_NT_HEADERS
.if           [edi].Signature==IMAGE_NT_SIGNATURE
              mov       ValidPE, TRUE
.else
              mov       ValidPE, FALSE
.endif
.else
              mov       ValidPE,FALSE
.endif
  FinalExit:
              push      seh.PrevLink
              pop       fs:[0]
.if           ValidPE==TRUE
              call      ShowSectionInfo
.else
              invoke    MessageBox, 0, addr FileInValidPE, addr AppName, MB_OK+MB_ICONINFORMATION
.endif
              invoke    UnmapViewOfFile, pMapping
.else
              invoke    MessageBox, 0, addr FileMappingError, addr AppName, MB_OK+MB_ICONERROR
.endif
              invoke    CloseHandle,hMapping
.else
              invoke    MessageBox, 0, addr FileOpenMappingError, addr AppName, MB_OK+MB_ICONERROR
.endif
              invoke    CloseHandle, hFile
.else
              invoke    MessageBox, 0, addr FileOpenError, addr AppName, MB_OK+MB_ICONERROR
.endif
.endif
              invoke    ExitProcess, 0
              invoke    InitCommonControls
       start  endp

  SEHHandler  proc      uses edx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
              mov       edx,pFrame
              assume    edx:ptr SEH
              mov       eax,pContext
              assume    eax:ptr CONTEXT
              push      [edx].SafeOffset
              pop       [eax].regEip
              push      [edx].PrevEsp
              pop       [eax].regEsp
              push      [edx].PrevEbp
              pop       [eax].regEbp
              mov       ValidPE, FALSE
              mov       eax,ExceptionContinueExecution
              ret
  SEHHandler  endp

     DlgProc  proc      uses edi esi hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
              LOCAL     lvc:LV_COLUMN
              LOCAL     lvi:LV_ITEM
.if           uMsg==WM_INITDIALOG
              mov       esi, lParam
              mov       lvc.imask,LVCF_FMT or LVCF_TEXT or LVCF_WIDTH or LVCF_SUBITEM
              mov       lvc.fmt,LVCFMT_LEFT
              mov       lvc.lx,80
              mov       lvc.iSubItem,0
              mov       lvc.pszText,offset SectionName
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTCOLUMN,0,addr lvc inc lvc.iSubItem
              mov       lvc.fmt,LVCFMT_RIGHT
              mov       lvc.pszText,offset VirtualSize
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTCOLUMN,1,addr lvc
              inc       lvc.iSubItem
              mov       lvc.pszText,offset VirtualAddress
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTCOLUMN,2,addr lvc
              inc       lvc.iSubItem
              mov       lvc.pszText,offset SizeOfRawData
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTCOLUMN,3,addr lvc
              inc       lvc.iSubItem
              mov       lvc.pszText,offset RawOffset
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTCOLUMN,4,addr lvc
              inc       lvc.iSubItem
              mov       lvc.pszText,offset Characteristics
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTCOLUMN,5,addr lvc
              mov       ax, NumberOfSections
              movzx     eax,ax
              mov       edi,eax
              mov       lvi.imask,LVIF_TEXT
              mov       lvi.iItem,0
              assume    esi:ptr IMAGE_SECTION_HEADER
.while        edi>0
              mov       lvi.iSubItem,0
              invoke    RtlZeroMemory,addr buffer,9
              invoke    lstrcpyn,addr buffer,addr [esi].Name1,8
              lea       eax,buffer
              mov       lvi.pszText,eax
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTITEM,0,addr lvi
              invoke    wsprintf,addr buffer,addr template,[esi].Misc.VirtualSize
              lea       eax,buffer
              mov       lvi.pszText,eax
              inc       lvi.iSubItem
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_SETITEM,0,addr lvi
              invoke    wsprintf,addr buffer,addr template,[esi].VirtualAddress
              lea       eax,buffer
              mov       lvi.pszText,eax
              inc       lvi.iSubItem
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_SETITEM,0,addr lvi
              invoke    wsprintf,addr buffer,addr template,[esi].SizeOfRawData
              lea       eax,buffer
              mov       lvi.pszText,eax
              inc       lvi.iSubItem
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_SETITEM,0,addr lvi
              invoke    wsprintf,addr buffer,addr template,[esi].PointerToRawData
              lea       eax,buffer
              mov       lvi.pszText,eax
              inc       lvi.iSubItem
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_SETITEM,0,addr lvi
              invoke    wsprintf,addr buffer,addr template,[esi].Characteristics
              lea       eax,buffer
              mov       lvi.pszText,eax
              inc       lvi.iSubItem
              invoke    SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_SETITEM,0,addr lvi
              inc       lvi.iItem
              dec       edi
              add       esi, sizeof IMAGE_SECTION_HEADER
.endw
.elseif
uMsg==WM_CLOSE 
              invoke    EndDialog,hDlg,NULL
.else
              mov       eax,FALSE
              ret
.endif
              mov       eax,TRUE
              ret
     DlgProc  endp

         ShowSectionInfo  proc      uses edi
              mov       edi, pMapping
              assume    edi:ptr IMAGE_DOS_HEADER
              add       edi, [edi].e_lfanew
              assume    edi:ptr IMAGE_NT_HEADERS
              mov       ax,[edi].FileHeader.NumberOfSections
              movzx     eax,ax
              mov       NumberOfSections,eax
              add       edi,sizeof IMAGE_NT_HEADERS
              invoke    DialogBoxParam, hInstance, IDD_SECTIONTABLE,NULL, addr DlgProc, edi
              ret
         ShowSectionInfo  endp
              end       start

分析:

本例重用了PE教程2的代码,校验PE文件的有效性后,继续调用函数ShowSectionInfo显示各节信息。

ShowSectionInfo proc uses edi 
mov edi, pMapping
assume edi:ptr IMAGE_DOS_HEADER
add edi, [edi].e_lfanew
assume edi:ptr IMAGE_NT_HEADERS

我们将edi用作指向PE文件数据的指针。首先,将指向DOS header地址的pMapping赋给edi,再加上e_lfanew域值等于PE header的地址。

mov ax,[edi].FileHeader.NumberOfSections
mov NumberOfSections,ax

因为我们要遍历节表,所以必须先获取文件的节数目。这就得靠file header里的NumberOfSections域了,切记这是个word域。

   add edi,sizeof
IMAGE_NT_HEADERS 

现在edi正指向PE header的起始地址,加上PE header结构大小后恰好指向节表了。

   invoke DialogBoxParam,
hInstance, IDD_SECTIONTABLE,NULL, addr DlgProc, edi

调用 DialogBoxParam 显示列表对话框,注意我们已将节表地址作为最后一个参数传递过去了,该值可从WM_INITDIALOG 消息的lParam参数中提取。

在对话框过程里我们响应WM_INITDIALOG消息,将lParam值 (节表地址)存入esi,节数目赋给edi并设置列表控件。万事俱备后,进入循环将各节信息插入到列表控件中,这部分相当简单。

 .while
              edi>                ;0 
mov lvi.iSubItem,0

字符串置入第一列。

              invoke    RtlZeroMemory,addr buffer,9
              invoke    lstrcpyn,addr buffer,addr [esi].Name1,8
              lea       eax,buffer
              mov       lvi.pszText,eax

要显示节名,当然要将其转换为ASCIIZ字符串先。

        
invoke
SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTITEM,0,addr lvi

然后显示第一列。
继续我们伟大的工程,显示完本节中最后一个欲呈现的值后,立马下一个结构。

        
dec edi 
add esi, sizeof IMAGE_SECTION_HEADER
.endw

每处理完一节就递减edi,然后将esi加上IMAGE_SECTION_HEADER 结构大小,使其指向下一个IMAGE_SECTION_HEADER 结构。

遍历节表的步骤:

  1. PE文件有效性校验。
  2. 定位到 PE header 的起始地址。
  3. 从 file header 的 NumberOfSections域获取节数。
  4. 通过两种方法定位节表: ImageBase+SizeOfHeaders 或者 PE header的起始地址+ PE header结构大小。 (节表紧随 PE header)。如果不是使用文件映射的方法,可以用SetFilePointer 直接将文件指针定位到节表。节表的文件偏移量存放在 SizeOfHeaders域里。(SizeOfHeaders 是 IMAGE_OPTIONAL_HEADER 的结构成员)
  5. 处理每个 IMAGE_SECTION_HEADER 结构。