Перевод: Rob [www.delphiday.blogspot.com]
В этом туториале мы напишем простое приложение, которое использует штатное диалоговое окно выбора, для открытия файлов и выводит информацию о том, является ли выбранный файл правильным PE или нет. Код приложения основан на втором туториале от Iczelion, но я несколько изменил его, для пущей простоты и наглядности и выдрал SEH.
Обычно, для проверки того, что файл является правильным PE, нам необходимо проверить два условия. Необходимо, чтобы текст в начале DOS заголовка имел значение “MZ” и в конце самого DOS заголовка имелся указатель на заголовок PE, который в свою очередь начинается с “PE”, который завершается двумя нулевыми байтами. Эти сигнатуры объявлены в инклуднике windows.inc и имеют следующий вид:
IMAGE_DOS_SIGNATURE equ 5A4Dh ("MZ") IMAGE_NT_SIGNATURE equ 00004550h ("PE" 00 00)
Запустите WinAsm, начните новый проект, добавьте новый диалоговый ресурс и нарисуйте форму похожую на эту:
Кнопка, помеченная тремя точками, предназначена для того, чтобы по клику на ней, выводилось окно, где мы бы могли выбрать нужный нам файл, вместо того, чтобы каждый раз писать новый путь до файла в поле ввода. Теперь наберите следующий код в окне редактирования:
.386 .model flat, stdcall option casemap :none include windows.inc include user32.inc include kernel32.inc include comdlg32.inc includelib user32.lib includelib kernel32.lib includelib comdlg32.lib DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD Validate PROTO :DWORD .data strFilter db "Executable Files (*.exe, *.dll)",0, "*.exe;*.dll",0,"All Files",0,"*.*",0,0 OpenError db "Unable to open target file",0 MsgBoxCap db "Results",0 Invalid db "This is not a valid PE file!!",0 Valid db "This is a valid PE file!!",0 .data? ofn OPENFILENAME <> hInstance HINSTANCE ? hTarget dd ? hMapping dd ? pMapping dd ? TargetName db 512 dup(?) .const IDD_MAIN equ 1001 IDC_TARGET equ 1003 IDC_OPEN equ 1004 IDC_GO equ 1005 IDC_EXIT equ 1006 ARIcon equ 2001 .code start: invoke GetModuleHandle, NULL mov hInstance, eax invoke DialogBoxParam,hInstance,IDD_MAIN,0,addr DlgProc,0 invoke ExitProcess, eax DlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD mov eax,uMsg .if eax==WM_INITDIALOG invoke LoadIcon,hInstance,2001 invoke SendMessage,hWin,WM_SETICON,1,eax .elseif eax==WM_COMMAND mov eax,wParam .if eax==IDC_OPEN mov ofn.lStructSize,SIZEOF ofn mov ofn.lpstrFilter,offset strFilter mov ofn.lpstrFile,offset TargetName mov ofn.nMaxFile,512 mov ofn.Flags,OFN_FILEMUSTEXIST+OFN_PATHMUSTEXIST+\ OFN_LONGNAMES+OFN_EXPLORER+OFN_HIDEREADONLY invoke GetOpenFileName,addr ofn .if eax==TRUE invoke SetDlgItemText,hWin,IDC_TARGET,addr TargetName invoke RtlZeroMemory,addr TargetName,512 .endif .elseif eax==IDC_GO invoke GetDlgItemText,hWin,IDC_TARGET,addr TargetName,512 invoke lstrlen,addr TargetName .if eax!=0 invoke Validate,addr TargetName .endif .elseif eax==IDC_EXIT invoke SendMessage,hWin,WM_CLOSE,0,0 .endif .elseif eax==WM_CLOSE invoke EndDialog,hWin,0 .endif xor eax,eax ret DlgProc endp Validate proc FileName:DWORD invoke CreateFile,FileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 .if eax!=INVALID_HANDLE_VALUE mov hTarget,eax invoke CreateFileMapping,eax,0,PAGE_READ,0,0,0 mov hMapping,eax invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0 mov pMapping,eax .if [eax.IMAGE_DOS_HEADER.e_magic]==IMAGE_DOS_SIGNATURE add eax,[eax.IMAGE_DOS_HEADER.e_lfanew] .if [eax.IMAGE_NT_HEADERS.Signature]==IMAGE_NT_SIGNATURE invoke MessageBox,0,addr Valid,addr MsgBoxCap,MB_ICONASTERISK .endif .else invoke MessageBox,0,addr Invalid,addr MsgBoxCap,MB_ICONASTERISK .endif invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping invoke CloseHandle,hTarget .else invoke MessageBox,0,addr OpenError,0,0 .endif xor eax,eax Ret Validate EndP end start
Нам нужно обсудить три новых переменных. Структура OPENFILENAME описана в файле windows.inc следующим образом:
Это просто шаблон. Чтобы нам использовать его в своём приложении, мы должны объявить её копию (структуры) и дать ей имя, так же как и обычным переменным. Мы назвали её ofn, но не инициализировали ни одного поля. Для этого мы и использовали следующую конструкцию:
Ofn OPENFILENAME <>
Есть только пять полей важных для нас, и они должны быть заполнены, прежде чем мы вызовем функцию GetOpenFileName. Я сделал это в процедуре DlgProc, на приход сообщения WM_COMMAND нашей кнопки.
Оператор SizeOf возвращает размер структурной переменной ofn, которую мы объявили выше. Поле lpstrFilter указывает на переменную strFilter, которая содержит текст, который будет появляться в списке выбора типа файла в нашем диалоге открытия файла. Поле lpstrFile, указывает на переменную буфер, куда запишется имя и путь выбранного файла. Поле nMaxFile содержит максимальную длину буфера.
Функция GetOpenFileName, заполнит буфер TargetName полным путём и именем файла, который мы выберем в диалоге открытия файла, в случаи её успешного завершения. Полное имя файла отобразится в строке редактирования и затем буфер отчистится для следующего раза функцией RtlZeroMemory.
По клику на кнопке Go! Имя файла получается из поля ввода. Делается это для того, чтобы быть уверенным, что если юзер изменит текст в поле ввода, программа пыталась открыть актуальное имя файла (изменённое). Затем, указатель на имя файла передаётся процедуре Validate.
Взгляните ещё раз на код процедуры Validate:
Validate proc FileName:DWORD invoke CreateFile,FileName,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0 .if eax!=INVALID_HANDLE_VALUE mov hTarget,eax invoke CreateFileMapping,eax,0,PAGE_READ,0,0,0 mov hMapping,eax invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0 mov pMapping,eax .if [eax.IMAGE_DOS_HEADER.e_magic]==IMAGE_DOS_SIGNATURE add eax,[eax.IMAGE_DOS_HEADER.e_lfanew] .if [eax.IMAGE_NT_HEADERS.Signature]==IMAGE_NT_SIGNATURE invoke MessageBox,0,addr Valid,addr MsgBoxCap,MB_ICONASTERISK .endif .else invoke MessageBox,0,addr Invalid,addr MsgBoxCap,MB_ICONASTERISK .endif invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping invoke CloseHandle,hTarget .else invoke MessageBox,0,addr OpenError,0,0 .endif xor eax,eax Ret Validate EndP
Целевой файл открывается и проецируется в память. Все эти операции над файлом, как открытие и проецирование, выполняются с флагами “только для чтения”, потому, что нам не нужно ничего записывать, нам хватит и простого чтения. Как я писал раньше, функция MapViewOfFile возвращает указатель на первый байт файла в памяти в регистре EAX.
Следующая часть кода, демонстрирует другой способ работы со структурами, для чтения PE заголовков. Структуры заголовков, уже есть в файле windows.inc и в этот раз, нам не нужно обьявлять новую структурную переменную. Мы просто применяем структуру, как шаблон к области памяти, где спроецирован наш файл.
Для начала нам нужен DOS заголовок. В нём нас интересуют всего два поля. Первое, имя которой e-magic и последнее, e_lfanew:
Для начала, обратимся к значению в поле e_magic. Конструкция для обращения будет следующей: IMAGE_DOS_HEADER.e_magic. Теперь, чтобы применить данный шаблон к нашему файлу в памяти и обратиться к значению поля e_magic, используем такую конструкцию: [EAX.IMAGE_DOS_HEADER.e_magic].
Если значение в поле равно значению IMAGE_DOS_SIGNATURE, мы можем продолжить проверять файл и дальше, получив значение поля e_lfanew и прибавив его, к начальному адресу нашего файла в памяти. Так мы получим указатель на PE заголовок:
Теперь, когда мы знаем адрес PE заголовка, мы можем проверить значение первого поля “Signature”. Если оно равно значению IMAGE_NT_SIGNATURE, тогда можно сказать что наш подопытный файл имеет верный формат PE, ну а если нет, то нет.
Код, который приведён здесь использует структуры, чтобы получать значения полей PE файла, но вы можете встретить исходники, где адреса полей получают путём сложения известных смещений в шестнадцатеричном формате. Например, поле e_lfanew, всегда находится на расстаянии 3Ch от начала файла и для того чтобы получить адрес PE заголовка, вы можете встретить инструкции вида add eax,[eax+3Ch], вместо add eax,[eax.IMAGE_DOS_HEADER.e_lfanew].
В следующем уроке, мы создадим приложение, которое может добавлять новую секцию в исполняемый файл, основываясь на уже написанном здесь коде.
Комментариев нет:
Отправить комментарий