Tutorial 5: Section Table
Download the example.
Theory:
Up to this
tutorial, we learned about the DOS header, the PE header. What
remains is the section table. A section table is actually an array
of structure immediately following the PE header. The number of the
array members is determined by NumberOfSections field in the
file header (IMAGE_FILE_HEADER) structure. The structure is
called 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
Again, not all members are
useful. I'll describe only the ones that are really
important.
Field |
Meanings |
Name1 |
Actually the
name of this field is "name" but the word "name" is an MASM
keyword so we have to use "Name1" instead. This member
contains the name of the section. Note that the maximum length
is 8 bytes. The name is just a label, nothing more. You can
use any name or even leave this field blank. Note that there
is no mention of the terminating null. The name is not an ASCIIZ string so don't
expect it to be terminated with a null. |
VirtualAddress |
The RVA of
the section. The PE loader examines and uses the value in this
field when it's mapping the section into memory. Thus if the
value in this field is 1000h and the PE file is loaded at
400000h, the section will be loaded at 401000h. |
SizeOfRawData |
The size of the
section's data rounded up to the next multiple of file
alignment. The PE loader examines the value in this field so
it knows how many bytes in the section it should map into
memory. |
PointerToRawData |
The file offset of the
beginning of the section. The PE loader uses the value in this
field to find where the data in the section is in the
file. |
Characteristics |
Contains flags such as
whether this section contains executable code, initialized
data, uninitialized data, can it be written to or read
from. |
Now that we know
about IMAGE_SECTION_HEADER structure, let's see how we can
emulate the PE loader's job:
- Read
NumberOfSections in IMAGE_FILE_HEADER so we know how
many sections there are in the file.
- Use the value
in SizeOfHeaders as the file offset of the section table
and moves the file pointer to that offset.
- Walk the
structure array, examining each member.
- For each
structure, we obtain the value in PointerToRawData and move
the file pointer to that offset. Then we read the value in
SizeOfRawData so we know how many bytes we should map into
memory. Read the value in VirtualAddress and add the value
in ImageBase to it to get the virtual address the section
should start from. And then we are ready to map the section into
memory and mark the attribute of the memory according to the flags
in Characteristics.
- Walk the
array until all the sections are processed.
Note that we
didn't make use the the name of the section: it's not really
necessary.
Example:
This example opens a PE file
and walks the section table, showing the information about the
sections in a listview control.
.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 C uses
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
Analysis:
This example reuses the code
of the example in PE tutorial 2. After it verifies that the file is
a valid PE, it calls a function, 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
We use edi as the pointer to
the data in the PE file. At first, we initialize it to the value of
pMapping which is the address of the DOS header. Then we add the
value in e_lfanew to it so it now
contains the address of the PE header.
mov
ax,[edi].FileHeader.NumberOfSections mov
NumberOfSections,ax
Since we need to walk the
section table, we must obtain the number of sections in this file.
That's the value in NumberOfSections member of the file header.
Don't forget that this member is of word size.
add edi,sizeof IMAGE_NT_HEADERS
Edi currently contains the
address of the PE header. Adding the size of the PE header to it
will make it point at the section table.
invoke DialogBoxParam,
hInstance, IDD_SECTIONTABLE,NULL, addr DlgProc, edi
Call
DialogBoxParam to show the dialog box containing the
listview control. Note that we pass the address of the section table
as its last parameter. This value will be available in lParam during
WM_INITDIALOG message.
In the dialog box procedure,
in response to WM_INITDIALOG message, we store the value of lParam
(address of the section table) in esi, the number of sections in edi
and then dress up the listview control. When everything is ready, we
enter a loop which will insert the info about each section into the
listview control. This part is very simple.
.while
edi>0 mov
lvi.iSubItem,0
Put this string in the first
column.
invoke RtlZeroMemory,addr buffer,9
invoke
lstrcpyn,addr buffer,addr [esi].Name1,8
lea eax,buffer
mov
lvi.pszText,eax
We will display the name of
the section but we must convert it to an ASCIIZ string
first.
invoke SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTITEM,0,addr
lvi
Then we display it in the
first column. We continue with this scheme until the last value
we want to display for this section is displayed. Then we must move
to the next structure.
dec
edi add esi,
sizeof IMAGE_SECTION_HEADER .endw
We decrement the
value in edi for each section processed. And we add the size of
IMAGE_SECTION_HEADER to esi so it contains the address of the
next IMAGE_SECTION_HEADER structure.
The steps in walking the
section table are:
- Verify that
the file is a valid PE
- Go to the
beginning of the PE header
- Obtain the
number of sections from NumberOfSections field in the file
header.
- Go to the
section table either by adding ImageBase to
SizeOfHeaders or by adding the address of the PE header to
the size of the PE header. (The section table immediately follows
the PE header). If you don't use file mapping, you need to move
the file pointer to the section table using SetFilePointer.
The file offset of the section table is in
SizeOfHeaders.(SizeOfHeaders is a member of
IMAGE_OPTIONAL_HEADER)
- Process each
IMAGE_SECTION_HEADER structure.
[Iczelion's Win32 Assembly
Homepage] |