PE教程5: Section Table(節表)

看雪資料發表於2015-11-15

 

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

同樣,不是所有成員都是很有用的,我們只關心那些真正重要的。

FieldMeanings
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 結構。

翻譯:iamgufeng [Iczelion's Win32 Assembly Homepage][LuoYunBin's Win32 ASM Page]

 

相關文章