Содержание статьи
WinMain
». И это будет ошибкой. На самом деле первым управление получает стартовый код, скрыто вставляемый компилятором. Выполнив необходимые инициализационные процедуры, в какой-то момент он вызывает WinMain
, а после ее завершения вновь получает управление и выполняет капитальную деинициализацию.Идентификация стартовых функций
В подавляющем большинстве случаев стартовый код не представляет никакого интереса, и первой задачей при анализе становится поиск функции WinMain
. Если компилятор входит в число «знакомых» IDA, она опознает WinMain
автоматически, в противном же случае искать функцию приходится руками и головой.
Обычно в штатную поставку компилятора включают исходные тексты его библиотек, в том числе и процедуры стартового кода. Например, у Microsoft Visual C++ стартовый код расположен в файле \VC\crt\src\vcruntime\mcrtexe.cpp
. В нем содержится код для инициализации ASCII-версий консольных (main) приложений. В этой же папке лежат еще несколько файлов:
mwcrtexe.cpp
— код из этого файла используется при старте консольных приложений с Unicode-символами;mcrtexew.cpp
— вызывается при запуске Windows-приложений (WinMain) с поддержкой ASCII;mwcrtexew.cpp
— служит для запуска Windows-приложений с Юникодом.
После выполнения весьма небольшого блока кода управление из трех последних файлов передается в первый. У Embarcadero C++ Builder 10.3 (в девичестве Borland C++) все файлы со startup-кодом хранятся в отдельной одноименной директории. В частности, файлы, содержащие стартовый код для Windows-приложений, находятся в папке \source\cpprtl\Source\startup\
. Ее содержимое несколько похоже на vcruntime
тем, что имеется главный файл для запуска Win32-приложений — c0nt.asm
. Код из этого файла вызывают другие файлы, содержащие инициализации для подсистем на Win32: DLL, VCL, FMX (приложение FireMonkey, кросс-платформенная графическая подсистема). Если разобраться с исходными текстами, понять дизассемблированный листинг будет намного легче!
![Embarcadero C++Builder. Начиная с этой версии у данной системы программирования появилась Community-редакция, которую можно халявно использовать целый год](/uploads/posts/2022-08/cppbuilder.png)
А как быть, если для компиляции исследуемой программы использовался неизвестный или недоступный тебе компилятор? Прежде чем приступать к утомительному ручному анализу, давай вспомним, какой прототип имеет функция WinMain:
int APIENTRY wWinMain( _In_ HINSTANCE hInstance, // Handle to current instance _In_opt_ HINSTANCE hPrevInstance, // Handle to previous instance _In_ LPWSTR lpCmdLine, // Pointer to command line _In_ int nCmdShow) // Show state of window
Обрати внимание: APIENTRY
замещает WINAPI
. Во-первых, четыре аргумента — это достаточно много, и в большинстве случаев WinMain
оказывается самой «богатой» на аргументы функцией стартового кода. Во-вторых, последний заносимый в стек аргумент — hInstance
— чаще всего вычисляется на лету вызовом функции GetModuleHandleW
. То есть, если встретишь конструкцию типа CALL GetModuleHandleW
, можно с высокой степенью уверенности утверждать, что следующая функция и есть WinMain
. Наконец, вызов WinMain
обычно расположен практически в самом конце кода стартовой функции. За ней бывает не более двух-трех «замыкающих» строй функций, например __cexit
.
Скачать:
Скриншоты:
Важно:
Все статьи и материал на сайте размещаются из свободных источников. Приносим свои глубочайшие извинения, если Ваша статья или материал была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.