Таблица секций
Скачайте пример.
Теория:
Мы изучили DOS-заголовок и PE-заголовок. Осталась таблица секций. Таблица секций - это массив структур, следующий непосредственно за PE-заголовком. Размер массива определен в поле NumberOfSections структуры IMAGE_FILE_HEADER. Структура секции называется 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
Как обычно, не все члены полезны. Я расскажу только о тех, которые действительно полезны.
Name1 | Очевидно, что имя этого поля - "name", но так как слово "name" является ключевым словом в MASM'е, мы используем вместо него "Name1". Заметьте, что максимальная длина этого поля 8 байтов. Имя - это всего лишь название, ничего больше. Вы можете использовать любое имя или даже оставить это поле пустым. Заметьте, что null в конце может и не находиться. |
VirtualAddress | RVA секции. PE-загрузчик использует значение в этом поле, когда мэппирует секцию в память. То есть, если значение в этом поел равняется 1000h и файл загружен в 400000h, секция будет загружена в 401000h. |
SizeOfRawData | Размер секции, выравненный согласно установкам в PE-заголовке. PE-загрузчик проверяет значение в этом поле, чтобы знать, сколько байтов он должен загрузить в память. |
PointerToRawData | Файловое смещение на начало секции. PE-загрузчик использует это значение для того, чтобы найти где она начинается. |
Characteristics | Содержит флаги, такие как содержит ли эта секция исполняемый код, инициализированные данные, неинициализированные данные, можно читать и писать в секцию. |
Теперь когда мы знаем о структуре IMAGE_SECTION_HEADER, давайте посмотрим, как мы можем эмулировать работу, выполняемую PE-загрузчиком:
- Пpочитаем NumberOfSections в IMAGE_FILE_HEADER, чтобы узнать, сколько секций в файле.
- Используем значение в SizeOfHeaders в качестве файлового смещения таблицы секций и передвинем файловый указатель на это смещение.
- Проходим по массиву структур, изучая каждый элемент.
- Просматривая каждую структуру, мы получаем значение PointerToRawData и перемещаем файловый указатель на это смещение. Затем мы читаем значение в SizeOfRawData, чтобы узнать сколько байтов мы должны загрузить в память. Читаем значение в VirtualAddress и добавляем значение к ImageBase, чтобы получить виртуальный адрес секции, с которого ей следует начинаться. И тогда мы готовы промэппировать секцию в память и пометить атрибут памяти согласно флагам, указанным в Characteristics.
- Идем по массиву, пока все секции не будут обработаны.
Заметьте, что мы не используем имя секции: оно не необходимо.
Пpимеp:
Этот пример открывает PE-файл и проходит по таблице секций, показывая информацию о секциях в listview-контроле.
.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 ? ; адрес предыдущей seh-структуры CurrentHandler dd ? ; адрес нового обработчика исключения SafeOffset dd ? ; смещение с которого безопасно продолжить выполнение программы PrevEsр dd ? ; старое значение esр PrevEbр dd ? ; старое значение ebр 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, а затем вызывается функция 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-файле. Во-первых, мы присваиваем ему значение рMaрing, которое является адресом DOS-заголовка. Затем мы добавляем к нему значение e_lfanew - теперь edi содержит адрес PE-заголовка.
mov ax,[edi].FileHeader.NumberOfSections mov NumberOfSections,ax
Так как нам нужно перейти к таблице секций, мы должны получить количество секций в этом файле. Это значение параметра NumberOfSections в заголовке файла. Hе забудьте, что этот параметр размером в слово.
add edi,sizeof IMAGE_NT_HEADERS
Сейчас edi содержит адрес PE-заголовка. Последний мы добавляем к первому, и в результате edi будет создержать адрес таблицы секций.
invoke DialogBoxParam, hInstance, IDD_SECTIONTABLE,NULL, addr DlgProc, edi
Вызываем DialogBoxParam, чтобы показать диалоговое окно, содержащее listview-контрол. Отметьте, что мы передаем адрес таблицы секций в качестве последнего параметра. Это значение будет доступно в lParam во время обработки сообщения WM_INITDIALOG.
Во время этого мы сохраняем значение lParam (адрес таблицы секций) в esi, номер секций в edi, а затем выводим listview-контрол. Когда все готово, мы входим в цикл, который должен вставить информацию о каждой секции в этот контрол. Эта часть очень проста.
.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 после обработки каждой из секций. И добавляем размер IMAGE_SECTION_HEADER к esi, что он содержал адрес следующей структуры IMAGE_SECTION_HEADER.
Вот, что необходимо сделать, чтобы обработать таблицу секций:
- Убедиться, что файл является верным PE.
- Перейти к началу PE-заголовка.
- Получить количество секций из поля NumberOfSections в файловом заголовке.
- Перейти к таблице секций либо добавив ImageBase к SizeOfHeaders или добавив адрес PE-заголовка к размеру DOS-заголовка (таблица секций идет непосредственно за PE-заголовком). Если вы не хотите использовать file maрing, вам нужно переместить файловый указатель на таблицу секций посредством SetFilePointer. Файловое смещение таблицы секций находится в SizeOfHeaders (это один из параметров IMAGE_OPTIONAL_HEADER).
- Обработать каждую структуру IMAGE_SECTION_HEADER.
[C] Iczelion, пер. Aquila.