Родные приложения Николай «gorl» Андреев Хакер, номер #082, стр. 082-114-3 #pragma comment(linker, "/SUBSYSTEM:NATIVE") #pragma comment(linker,"/BASE:0x00010000") #pragma comment(lib, "ntdll.lib") Вместо обычной подсистемы WINDOWS я указал NATIVE, чтобы компилятор понял, с чем имеет дело, немного сдвинул вниз виртуальный адрес, по которому будет доступен образ приложения в памяти, и подключил библиотеку для работы с ntdll.dll. Это все можно было сделать и путешествуя по Properties Pages проекта, но, имхо, текстом удобнее. Это, что касалось настроек, теперь плавно погружаемся в кодинг. #define _X86_ #include #include "nt.h" Вот таким незамысловатым образом начинается сам код программы (не считая прагм): два хедера – один из DDK (не забудь, кстати, прописать пути к основным папкам DDK, содержащим хедеры и либы) с описанием основных структур и типов, другой с описаниям функций, экспортируемых ntdll.dll. Определение идентификатора _X86_ нужно для того, чтобы компилятор знал, какой вариант структур в ntddk выбирать, а то они могут различаться в зависимости от платформы. Как программирование, так и выполнения любого более или менее обычного приложения начинается с функции с функции main (WinMain, _tWinMain, в общем, название может быть любым, а смысл остается – точка входа), наш случай не исключение. У родных форточкам программ тоже есть main-функция, однако ее описание несколько отличается. Во-первых, у нее не два параметра, как у консольных приложений, и не 4, как у обычных Win32, а один – указатель на какую-то структуру. И я так бы и забил на его предназначение – ну, PVOID, ну, и черт с ним – если бы не исследования мастера Руссиновича. Этот указатель он окрестил как PSTARTUP_ARGUMENT и ввел несколько интересных определений: // структура, указатель на которую передается точке входа, // к слову, обычно именуемую NtProcessStartup… // я от этого названия отказался typedef struct { ULONG Unknown[3]; PENVIRONMENT_INFORMATION Environment; } STARTUP_ARGUMENT, *PSTARTUP_ARGUMENT; // структура, на которую ссылается STARTUP_ARGUMENT // из интересного содержит командную строку, с которой // было запущено приложение и имя файла typedef struct { ULONG Unknown[21]; UNICODE_STRING CommandLine; UNICODE_STRING ImageFile; } ENVIRONMENT_INFORMATION, *PENVIRONMENT_INFORMATION; Во-вторых, main-функция войдовая, то есть она ничего не возвращает. Я уверен, что ты в курсе, что обычно для завершения работы приложения в main’е используется что-нибудь вроде return 0, но если ты думаешь, что в нашей функции достаточно будет написать просто return, то ты ошибаешься. По ret’у мы ускачем неизвестно куда, так как в стеке может быть любая белиберда. Здесь подход иной: NtTerminateProcess(NtCurrentProcess(), 0); Вот так следует делать, если хочешь, чтобы твоя программа когда-нибудь завершилась. Если собрать все вышесказанное в кучку выйдет вот такая main-функция: void native_main(PSTARTUP_ARGUMENT arg) { NtTerminateProcess(NtCurrentProcess(), 0); } Странное имя для main-функции, не правда ли? Компилятор тоже так подумает, поэтому надо его предупредить добавлением еще одной прагмы. |