Downloadez l'exemple.
Jusqu'à maintenant, nous avons vu le DOS header, et le PE header. Ce qui reste c'est la table des sections. Une table des sections est en réalité un tableau regroupant plusieurs structures, ce tableau se trouve immédiatement à la suite du PE header. Le nombre de membres du tableau est déterminé par le paramètre NumberOfSections du File Header (IMAGE_FILE_HEADER). Cette structure (la Table des Sections) est appelée 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
Une fois encore, tous les membres ne sont pas utiles. Je décrirai seulement ceux qui sont vraiment importants.
Paramètres | Signification |
---|---|
Name1 | En réalité le nom de ce paramètre est "name" mais le mot "name" est un mot-clé de MASM donc nous devons utiliser "Name1" à la place. Ce membre contient le nom de la section. Notez que la longueur maximale est de 8 octets. Le nom n'est qu'un label, rien plus. Vous pouvez employer n'importe quel nom ou laisser même ce paramètre vide. Notez qu'il n'y a aucun besoin d'un NULL de terminaison. Ce nom n'est pas une chaîne de caractères ASCIIZ donc ne vous attendez pas à ce qu'il se termine par un null. |
VirtualAddress | est le RVA de la section. Le PE Loader examine et emploie la valeur de ce paramètre lorsqu'il Mappe la section en mémoire. Ainsi si la valeur dans ce paramètre est 1000h et que le PE file à été chargé à partir de l'adresse 400000h, alors la section en question sera chargée à partir de 401000h. |
SizeOfRawData | représente la taille des données de la section arrondies au multiple suivant de l'alignement de fichiers. Le PE Loader examine la valeur dans ce paramètre, il sait ainsi combien d'octets de la section doivent êtres mappés en mémoire. |
PointerToRawData | est l'offset(de fichier) où commence la section. Le PE Loader utilise la valeur de ce paramètre pour retrouver où les données de la section sont placées dans le fichier. |
Characteristics | contient des flags de la même façon que si cette section contenait du code exécutable, des données initialisées, des données non initialisées, ils peuvent être accessible en écriture aussi bien qu'en lecture. |
Maintenant que nous connaissons la structure IMAGE_SECTION_HEADER, on va voir comment nous pouvons simuler le travail du PE loader :
Notez qu'on ne s'est pas servit du nom de la section : ce n'est pas vraiment nécessaire.
Cet exemple ouvre un PE file (un fichier Win32 exécutable) et se balade dans sa table des sections, il affiche les informations à propos de ses sections dans un 'listview control' (un objet prédéterminé par Windows (ici une simple page de listes de noms, de tailles, caractéristiques...)).
.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 ? ; adresse de la structure SEH ci-dessus
CurrentHandler dd ? ; adresse du nouvel 'exception handler'
SafeOffset dd ? ; endroit où est sauvegardé l'offset pour continuer l'exécution
PrevEsp dd ? ; l'ancienne valeur dans 'esp'
PrevEbp dd ? ; ancienne valeur dans '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
Cet exemple réutilise le code de l'exemple du Tutorial 2. Après qu'il vérifie que le fichier soit bien un PE valide, il appelle une fonction, 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
Nous nous servons d'edi en tant que pointer de données dans le PE file. D'abord, nous l'initialisons à la valeur 'pMapping' lequel est l'adresse du DOS header. Ensuite nous lui ajoutons la valeur d'e_lfanew, de cette façon il contient maintenant l'adresse du PE header.
mov ax,[edi].FileHeader.NumberOfSections
mov NumberOfSections,ax
Puisque nous avons besoin de la table des sections, il nous faut obtenir le nombre de sections dans le fichier. C'est la valeur du membre NumberOfSections du File header. N'oubliez pas que ce membre est d'une taille d'un WORD.
add edi,sizeof IMAGE_NT_HEADERS
edi contient actuellement l'adresse du PE header. L'addition de la taille du PE header à l'adresse de début du PE Header, fera qu'on pointera sur la table des sections.
invoke DialogBoxParam, hInstance, IDD_SECTIONTABLE,NULL, addr DlgProc, edi
On appelle DialogBoxParam pour afficher la boîte de dialogue contenant le 'listview control'. Remarquez que nous passons l'adresse de la table des sections en tant que son dernier paramètre. Cette valeur sera disponible dans lParam pendant le message WM_INITDIALOG.
Dans la procédure servant à traiter les messages de la DialogBox, en réponse au message WM_INITDIALOG, nous stockons la valeur d'lParam (l'adresse de la table des sections) dans esi, le nombre de sections dans edi et ensuite on crée le 'listview control'. Quand tout est prêt, nous entrons dans une boucle qui insérera les renseignements de chaque section dans le 'listview control'. Cette partie est très simple.
.while edi>0
mov lvi.iSubItem,0
Met cette chaîne de caractères dans la première colonne.
invoke
RtlZeroMemory,addr buffer,9
invoke lstrcpyn,addr buffer,addr
[esi].Name1,8
lea eax,buffer
mov lvi.pszText,eax
Nous affichons le nom de la section, mais nous devons d'abord la convertir en une chaîne de caractères ASCIIZ.
invoke SendDlgItemMessage,hDlg,IDC_SECTIONLIST,LVM_INSERTITEM,0,addr lvi
Ainsi nous l'affichons dans la première colonne.
Nous continuons avec ce schéma tant que la dernière valeur que nous souhaitons montrer concernant cette section soit affichée. Ensuite nous devons nous déplacer jusqu'à la structure suivante.
dec
edi
add esi, sizeof IMAGE_SECTION_HEADER
.endw
On fait décroître la valeur dans edi à chaque fois qu'une section à été traitée. Et nous ajoutons la taille d'IMAGE_SECTION_HEADER à esi, ainsi il contient l'adresse de la structure IMAGE_SECTION_HEADER suivante.
Les étapes de notre petite balade dans la table des sections sont les suivantes:
[Iczelion's Win32 Assembly Homepage]