Русский
Русский
English
Статистика
Реклама

Winapi

Перевод Как в Runescape ловят пользователей ботов, и почему они не поймали меня

19.04.2021 18:14:05 | Автор: admin

Автоматизация игроков всегда была большой проблемой в глобальных многопользовательских онлайновых ролевых играх (MMORPG), таких как World of Warcraft и Runescape, и этот вид взлома игр значительно отличается от традиционных читов, например в стрелялках. Однажды в выходные я решил взглянуть на системы обнаружения, созданные компанией Jagex для предотвращения автоматизации игроков в Runescape и вот что из этого вышло.


Использование ботов

Последние несколько месяцев игрок с учётной записью sch0u играл в World 67 круглосуточно, выполняя обычные задачи, такие как убийство мобов или сбор ресурсов. На первый взгляд игрок с этой учётной записью выглядит так же, как и любой другой игрок, но есть одно ключевое отличие это бот.

Я запустил этого бота ещё в октябре, чтобы протестировать возможности системы обнаружения ботов. Я попытался найти в Интернете информацию о том, как компания Jagex борется с создателями таких ботов, и нашёл только видео коммерческих ботов, создатели которых хвастались тем, что их системы перемещения мыши неотличимы от людей.

Поэтому единственный вывод, который мне удалось сделать, заключается в том, что движение мыши имеет значение.

Эвристика!

Я начал с анализа клиента Runescape, чтобы подтвердить эту теорию, и быстро заметил глобально вызываемую переменную hhk, которая задаётся вскоре после запуска.

const auto module_handle = GetModuleHandleA(0);hhk = SetWindowsHookExA(WH_MOUSE_LL, rs::mouse_hook_handler, module_handle, 0);

В результате на мышь устанавливается низкоуровневая hook-точка, добавляя её к общесистемной цепочке hook-точек. Это позволяет приложениям в Windows перехватывать все события мыши, независимо от того, связаны они с вашим приложением или нет. Низкоуровневые hook-точки часто используются клавиатурными шпионами, однако есть и законные случаи использования, такие как эвристика, подобная вышеупомянутой hook-точке мыши.

Обработчик мыши Runescape довольно прост по своей сути (следующий псевдокод красиво переписан вручную):

LRESULT __fastcall rs::mouse_hook_handler(int code, WPARAM wParam, LPARAM lParam){  if ( rs::client::singleton )  {      // Call the internal logging handler      rs::mouse_hook_handler_internal(rs::client::singleton->window_ctx, wParam, lParam);  }  // Pass the information to the next hook on the system  return CallNextHookEx(hhk, code, wParam, lParam);}void __fastcall rs::mouse_hook_handler_internal(rs::window_ctx *window_ctx, __int64 wparam, _DWORD *lparam){  // If the mouse event happens outside of the Runescape window, don't log it.  if (!window_ctx->event_inside_of_window(lparam))  {    return;  }  switch (wparam)  {    case WM_MOUSEMOVE:      rs::heuristics::log_movement(lparam);      break;case WM_LBUTTONDOWN:case WM_LBUTTONDBLCLK:case WM_RBUTTONDOWN:case WM_RBUTTONDBLCLK:case WM_MBUTTONDOWN:case WM_MBUTTONDBLCLK:  rs::heuristics::log_button(lparam);  break;  }}

С учётом пропускной способности эти функции rs::heuristics::log_* используют простые алгоритмы для пропуска данных событий, которые похожи на предыдущие зарегистрированные события.

Эти данные события позже анализируются функцией rs::heuristics::process, которая вызывается каждым фреймом в основном цикле рендеринга.

void __fastcall rs::heuristics::process(rs::heuristic_engine *heuristic_engine){  // Don't process any data if the player is not in a world  auto client = heuristic_engine->client;  if (client->state != STATE_IN_GAME)  {    return;  }  // Make sure the connection object is properly initialised  auto connection = client->network->connection;  if (!connection || connection->server->mode != SERVER_INITIALISED)  {    return;  }  // The following functions parse and pack the event data, and is later sent  // by a different component related to networking that has a queue system for  // packets.  // Process data gathered by internal handlers  rs::heuristics::process_source(&heuristic_engine->event_client_source);  // Process data gathered by the low level mouse hook  rs::heuristics::process_source(&heuristic_engine->event_hook_source);}

Вдали от клавиатуры?

Двигаясь в обратном направлении, я прилагаю усилия, чтобы узнать, насколько релевантна рассматриваемая функция, в первую очередь путём создания и применения hook-точек или патчей для рассматриваемой функции. Обычно заключение о релевантности функции можно получить, сделав её бесполезной и наблюдая за состоянием программного обеспечения. Эта методология приводит к интересному наблюдению.

Запретив игре вызывать функцию rs::heuristics::process, я сразу ничего не заметил, но ровно через пять минут вышел из игры. По-видимому, Runescape принимает решение о неактивности игрока просто по эвристическим данным, отправленным клиентом на сервер, хотя вы можете просто отлично играть в эту игру. Это породило новый вопрос: если сервер не считает, что я играю, то считает ли он, что я использую бота?

В результате я потратил несколько дней на реконструкцию сетевого слоя игры и смог использовать бота для выполнения почти всех действий при помощи только сетевых пакетов.

Чтобы доказать свою теорию, я использовал бот двадцать четыре часа в сутки, семь дней в неделю, даже не шевеля мышкой. Прошли тысячи часов, и я могу с уверенностью заявить, что обнаружение ботов компанией либо опирается на отправленные клиентом эвристические данные событий, либо запускается только тогда, когда игрок не находится в состоянии вдали от клавиатуры. Любого игрока, которому удаётся играть, не двигая мышью, следует немедленно забанить, то есть нынешний контроль имеет смысл пересмотреть.

Уязвимостей полно не только в играх, а с ростом цифровизации различных отраслей, многие недостатки безопасности вышли наружу и требуют серьезного внимания, а значит, растёт востребованность специалистов в сфере информационной безопасности, для обучения которых мы сделали специальный курс Этичный хакер. Если вам по душе искать пробелы в защите и получать за это вознаграждение добро пожаловать.Узнай, как прокачаться и в других областях работы с данными или освоить их с нуля:

Другие профессии и курсы

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Линукс-порт Far Manager прогресс за 4 года

20.10.2020 22:18:35 | Автор: admin
Первая публикация исходников far2l, порта Far Manager под Линукс штуки, которая когда-то считалась принципиально невозможной состоялась 10 августа 2016го. Поскольку главный разработчик поговаривает о переходе проекта в статус беты, решил написать обзорный пост, как там идут дела и чего удалось добиться за прошедшие четыре года.

Консольная версия


Первый же вопрос, который все задавали и здесь, и на опеннете, и на лоре а чего не в консоли? Изначально far2l действительно работал только в графическом режиме, через wxWidgets. Это оказалось самым простым способом быстро получить работающий порт со всеми пользовательскими удобствами: иксовым буфером обмена и всеми сочетаниями клавиш, причем с поддержкой событий не только KeyDown, но и KeyUp.

Сейчас это ограничение в прошлом: far2l прекрасно себя чувствует в консоли. Более того, там появились так называемые расширения терминала far2l, поэтому если запускать консольный far2l внутри графического (например, зайдя куда-нибудь по ssh), они сконнектятсямежду собой, и внутренний far2l тоже будет видеть и буфер обмена (с разрешения пользователя, конечно), и все возможные горячие клавиши. Более того, сделана даже специальная сборка putty, позволяющая наслаждаться всеми этими фишками из Windows.

Вот, смотрите, это far2l в GNOME Terminal


А вот в putty


Русские буквы в .zip'ах с винды


Вы не поверите, но Windows, вплоть, как минимум, до семерки создавала .zip архивы, записывая туда имена файлов в OEM (DOS) кодировке! Совместимость страшная штука. В итоге StackOverflow переполнен вопросами как мне правильно распаковать зип с кракозябрами.

В far2l мы это починили. В процессе родилась демонстрационная утилитка, которая показывает, как правильно работать с кодировками в .zip'ах, чтобы кракозябров не было (пригодится авторам архиваторов), а также патчик к p7zip, применяющий аналогичный алгоритм. p7zip-с-патчиком даже запакован в .deb'ку (ubuntu 20.04+, amd64), установка которой чинит поддержку зипов, например, в Engrampa, используемом в MATE.



Приведение в порядок зависимостей


Изначально порт нёс в себе кучу кода библиотек, и так присутствующих в мире Linux в системных пакетах: pcre, minizip, universal charset detector. Сейчас всё это заменено на грамотное использование зависимостей: системные библиотеки используются везде, где это возможно (исключение: 7z и unrar, там статически компилируются самые свежие библиотеки для поддержки самых свежих особенностей форматов).

Человеческая поддержка .tar.gz


Плагин multiarc, который используется в far2l для доступа к архивам, приехал к нам из мира Windows, и не умел воспринимать .tar.gz как один архив. Он видел .gz, и внутри него .tar. Следовательно, чтобы получить список файлов, приходилось делать полную распаковку. Такой себе экспириенс. Сейчас перешли на libarchive, и эта проблема исчезла.

Красивое консольное окно


В графической версии far2l была проблема: символы рисования рамок показывались раздражающим пунктиром.

image

У нас долго не получалось найти причину или подобрать настройки рендеринга, исправляющие этот бесячий баг, пока я не заглянул в исходники терминалок из GNOME и KDE. А там, оказывается, давным-давно символы рисования рамок рендерятся не из шрифта, а вручную, чтобы линии четкие и красивые получались.

В итоге сделали так же в far2l, только немножко лучше: добавив сглаживание. А то в терминалах KDE и GNOME максимально контрастные линии слишком сильно отвлекают на себя внимание рядом со сглаженным текстом.

В процессе пришлось разобраться в психовизуальных нюансах антиалиасинга, отлавливая несуществующие слишком яркие пиксели, которые упорно видели глаза. Подробности.

image

Пакеты для дистрибутивов


В репозитории дистрибутивов мы пока не попали. Зато давно есть пакеты во всех основных форматах. Даже скрипт сборки для Amazon Linux есть! Ну и конечно есть ppa для *buntu самый удобный способ просто поставить фар для большинства пользователей.

Свежий Colorer


far2l форкнулся от ветки Far 2, так что некоторые плагины там требовали срочного обновления. Недавно такое обновление было проведено: обновили код распаковки 7z и unrar, а также схемы цветовой подсветки синтаксиса Colorer. Остальные портированные плагины не содержат каких-то регулярно обновляющихся штук, а вот старые раскраски и не открывающиеся новые архивы были реальной проблемой. Всё, её больше нет!



NetBoxRocks


Это был второй вопрос, который обычно задавали в каментах: а нетбоооокс буууудеееет?. Нет, нетбокса не будет! Там putty внутри, и тащить в far2l её linux-версию показалось странной затеей. Да и сам код netbox'а не слишком располагал к портированию.

Поэтому автор порта, великолепный elfmz, сделал свою версию сетевого плагина: NetRocks. Там есть всё, что только может понадобиться и ftp[s], и scp, и sftp, и nfs, и webdav, и даже samba! Всё работает на нативных линуксовых библиотеках. В sftp можно даже удаленные команды запускать и в удаленную консоль ходить.

А ещё в NetRocks есть псевдо-сетевой плагин file, который позволяет работать с локальной файловой системой. Зачем? Потому что NetRocks умеет в фоновые операции. А обычное копирование файлов Far не умеет.



А как там вообще с плагинами?


Помимо NetRocks есть в комплекте есть:
colorer (подсветка синтаксиса, свежий!)
multiarc (работа с архивами, доработанный, свежие архиваторы!)
tmppanel (временная панель)
align block (форматирование блоков для редактора)
autowrap (автоперенос слов в редакторе)
drawline (рисование линий в редакторе)
editcase (конвертация регистра в редакторе)
SimpleIndent (работа с отступами в редакторе)
compare (продвинутая версия сравнения папок)
editor autocomplete (автодополнение в редакторе)
filecase (конвертация регистра имен файлов)
incremental search (быстрый поиск в редакторе)
inside (показывает, что внутри ELF и некоторых других форматов)
и даже плагин для написания других плагинов на Python!

Есть парочка сторонних, far2-gvfs и far2l-fuse, но после появления NetRocks они в некоторой степени утратили актуальность.

В общем, базовый набор для комфортной работы с локальными и удаленными файлами и архивами, а также написания кода прямо в редакторе far2l имеется :)

А со стабильностью как? Когда релиз уже?


Со стабильностью всё хорошо: за 4 года использования в работе (webdev + всякое офисное) ни одной потери данных с far2l я не поймал. Автор порта готов понемножку менять статус с альфы на бету, если в течении месяца-двух не вылезет критических ошибок. Тогда можно будет подумать об отправке пакетов в репозитории дистрибутивов, например.

Bonus #1. А там правда Wine под капотом?


Отчасти :) Из Wine были вытащены некоторые кусочки трансляции WinApi в нативный API Linux (конвертация кодировок, например; к слову, этот кусок недавно попробовали переписать на iconv, но выяснилось, что код из Wine делает то же самое в 4 раза быстрее). Со временем обращения к этим функциям, разбросанные по всему коду far2l, можно будет понемножку заменять на прямые вызовы нативных функций. А пока и эта конструкция работает очень даже быстро (самое медленное, на чём пробовали запускать raspbery pi, полет нормальный) и вполне надежно.

Bonus #2. Хватит фигней страдать, консоль учите, дурни!


За время работы над far2l (тестировщиком, а ещё иногда нехитрые патчи шлю) я узнал о линуксовой консоли больше, чем за всю жизнь до этого :) А ещё понемногу осваиваю си, на которых кодить со времен института не приходилось повода не было (заодно, кстати, перешел на ты с git). Так что ждите скоро статью как веб-макака си на опен сорсе учила!

Bonus #3. А нескучные обои имеются?


Несколько лет назад я бы скорее посмеялся над этим вопросом. Но да, если целый день смотреть в синий цвет, становится как-то не по себе (и прямо чувствуешь, как отрастает борода и свитер с оленями). Поэтому в итоге сделал себе раскраску в духе Monokai, ну и выложил для всех, конечно. У нас opensource ведь :)



Bonus #4. А на маке взлетит?


Взлетит. Половину тикетов в багтрекер с маков пишут. И да, на BSD работает тоже.
Подробнее..

Из песочницы Создаем EXE

13.08.2020 18:07:06 | Автор: admin
Самоизоляция это отличное время приступить к тому, что требует много времени и сил. Поэтому я решил заняться тем, чем всегда хотел написать свой компилятор.

Сейчас он способен собрать Hello World, но в этой статье я хочу рассказать не про парсинг и внутреннее устройство компилятора, а про такую важную часть как побайтовая сборка exe файла.

Начало


Хотите спойлер? Наша программа будет занимать 2048 байт.

Обычно работа с exe файлами заключается в изучении или модификации их структуры. Сами же исполняемые файлы при этом формируют компиляторы, и этот процесс, кажется, немного магическим для разработчиков.

Но сейчас мы с вами попробуем это исправить!

Для сборки нашей программы нам потребуется любой HEX редактор (лично я использовал HxD).

Для старта возьмем псевдокод:

Исходный код
func MessageBoxA(u32 handle, PChar text, PChar caption, u32 type) i32 ['user32.dll']func ExitProcess(u32 code) ['kernel32.dll']func main(){MessageBoxA(0, 'Hello World!', 'MyApp', 64)ExitProcess(0)}


Первые две строки указывают на функции импортируемые из библиотек WinAPI. Функция MessageBoxA выводит диалоговое окно с нашим текстом, а ExitProcess сообщает системе о завершении программы.
Рассматривать отдельно функцию main нет смысла, так как в ней используются функции, описанные выше.

DOS Header


Для начала нам нужно сформировать корректный DOS Header, это заголовок для DOS программ и влиять на запуск exe под Windows не должен.

Более-менее важные поля я отметил, остальные заполнены нулями.

Стуктура IMAGE_DOS_HEADER
Struct IMAGE_DOS_HEADER{     u16 e_magic// 0x5A4D"MZ"     u16 e_cblp// 0x0080128     u16 e_cp// 0x00011     u16 e_crlc     u16 e_cparhdr// 0x00044     u16 e_minalloc// 0x001016     u16 e_maxalloc// 0xFFFF65535     u16 e_ss     u16 e_sp// 0x0140320     u16 e_csum     u16 e_ip     u16 e_cs     u16 e_lfarlc// 0x004064     u16 e_ovno     u16[4] e_res     u16 e_oemid     u16 e_oeminfo     u16[10] e_res2     u32 e_lfanew// 0x0080128}


Самое главное, что этот заголовок содержит поле e_magic означающее, что это исполняемый файл, и e_lfanew указывающее на смещение PE-заголовка от начала файла (в нашем файле это смещение равно 0x80 = 128 байт).

Отлично, теперь, когда нам известна структура заголовка DOS Header запишем ее в наш файл.

(1) RAW DOS Header (Offset 0x00000000)
4D 5A 80 00 01 00 00 00  04 00 10 00 FF FF 00 0040 01 00 00 00 00 00 00  40 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 80 00 00 00



Уточнение

Сначала я использовал левую колонку как на скриншоте для указания смещения внутри файла, но тогда неудобно копировать исходный текст, приходится обрезать каждую строку.

Поэтому для удобства в первой скобке каждого блока указан порядок добавления в файл, а в последней смещение в файле (Offset) по которому должен располагаться данный блок.

Например, первый блок мы вставляем по смещению 0x00000000, и он займет 64 байта (0x40 в 16-ричной системе), следующий блок мы будем вставлять уже по этому смещению 0x00000040 и т.д.
Готово, первые 64 байта записали. Теперь нужно добавить еще 64, это так называемый DOS Stub (Заглушка). Во время запуска из-под DOS, она должна уведомить пользователя что программа не предназначена для работы в этом режиме.

Но в целом, это маленькая программа под DOS которая выводит строку и выходит из программы.
Запишем наш Stub в файл и рассмотрим его детальнее.

(2) RAW DOS Stub (Offset 0x00000040)
0E 1F BA 0E 00 B4 09 CD  21 B8 01 4C CD 21 54 6869 73 20 70 72 6F 67 72  61 6D 20 63 61 6E 6E 6F74 20 62 65 20 72 75 6E  20 69 6E 20 44 4F 53 206D 6F 64 65 2E 0D 0A 24  00 00 00 00 00 00 00 00



А теперь этот же код, но уже в дизассемблированном виде

Asm DOS Stub
0000push cs; Запоминаем Code Segment(CS) (где мы находимся в памяти)0001pop ds; Указываем что Data Segment(DS) = CS0002mov dx, 0x0E; Указываем адрес начала строки DS+DX, которая будет выводится до символа $(Конец строки) 0005mov ah, 0x09; Номер инструкции (Вывод строки)0007int 0x21; Вызов системного прерывания 0x210009mov ax, 0x4C01; Номер инструкции 0x4C (Выход из программы) ; Код выхода из программы 0x01 (Неудача)000cint 0x21; Вызов системного прерывания 0x21000e"This program cannot be run in DOS mode.\x0D\x0A$" ; Выводимая строка


Это работает так: сначала заглушка выводит строку о том, что программа не может быть запущена, а затем выходит из программы с кодом 1. Что отличается от нормального завершения (Код 0).

Код заглушки может немного отличатся (от компилятора к компилятору) я сравнивал gcc и delphi, но общий смысл одинаковый.

А еще забавно, что строка заглушки заканчивается как \x0D\x0D\x0A$. Скорее всего причина такого поведения в том, что c++ по умолчанию открывает файл в текстовом режиме. В результате символ \x0A заменяется на последовательность \x0D\x0A. В результате получаем 3 байта: 2 байта возврата каретки Carriage Return (0x0D) что бессмысленно, и 1 на перевод строки Line Feed (0x0A). В бинарном режиме записи (std::ios::binary) такой подмены не происходит.

Для проверки корректности записи значений я буду использовать Far с плагином ImpEx:



NT Header


Спустя 128 (0x80) байт мы добрались до NT заголовка (IMAGE_NT_HEADERS64), который содержит в себе и PE заголовок (IMAGE_OPTIONAL_HEADER64). Несмотря на название IMAGE_OPTIONAL_HEADER64 является обязательным, но различным для архитектур x64 и x86.

Структура IMAGE_NT_HEADERS64
Struct IMAGE_NT_HEADERS64{u32 Signature// 0x4550 "PE"Struct IMAGE_FILE_HEADER {u16 Machine// 0x8664 архитектура x86-64u16 NumberOfSections// 0x03 Количество секций в файле u32 TimeDateStamp// Дата создания файлаu32 PointerToSymbolTableu32 NumberOfSymbolsu16 SizeOfOptionalHeader // Размер IMAGE_OPTIONAL_HEADER64 (Ниже)u16 Characteristics// 0x2F }Struct IMAGE_OPTIONAL_HEADER64{u16 Magic// 0x020B Указывает что наш заголовок для PE64u8 MajorLinkerVersionu8 MinorLinkerVersionu32 SizeOfCodeu32 SizeOfInitializedDatau32 SizeOfUninitializedDatau32 AddressOfEntryPoint// 0x1000 u32 BaseOfCode// 0x1000 u64 ImageBase// 0x400000 u32 SectionAlignment// 0x1000 (4096 байт)u32 FileAlignment// 0x200u16 MajorOperatingSystemVersion// 0x05Windows XPu16 MinorOperatingSystemVersion// 0x02Windows XPu16 MajorImageVersionu16 MinorImageVersionu16 MajorSubsystemVersion// 0x05Windows XPu16 MinorSubsystemVersion// 0x02Windows XPu32 Win32VersionValueu32 SizeOfImage// 0x4000u32 SizeOfHeaders // 0x200 (512 байт)u32 CheckSumu16 Subsystem// 0x02 (GUI) или 0x03 (Console)u16 DllCharacteristicsu64 SizeOfStackReserve// 0x100000u64 SizeOfStackCommit// 0x1000u64 SizeOfHeapReserve// 0x100000u64 SizeOfHeapCommit// 0x1000u32 LoaderFlagsu32 NumberOfRvaAndSizes // 0x16 Struct IMAGE_DATA_DIRECTORY [16] {u32 VirtualAddressu32 Size}}}


Разберемся что хранится в этой структуре:

Описание IMAGE_NT_HEADERS64
Signature Указывает на начало структуры PE заголовка

Далее идет заголовок IMAGE_FILE_HEADER общий для архитектур x86 и x64.

Machine Указывает для какой архитектуры предназначен код в нашем случае для x64
NumberOfSections Количество секции в файле (О секциях чуть ниже)
TimeDateStamp Дата создания файла
SizeOfOptionalHeader Указывает размер следующего заголовка IMAGE_OPTIONAL_HEADER64, ведь он может быть заголовком IMAGE_OPTIONAL_HEADER32.

Characteristics Здесь мы указываем некоторые атрибуты нашего приложения, например, что оно является исполняемым (EXECUTABLE_IMAGE) и может работать более чем с 2 Гб RAM (LARGE_ADDRESS_AWARE), а также что некоторая информация была удалена (на самом деле даже не была добавлена) в файл (RELOCS_STRIPPED | LINE_NUMS_STRIPPED | LOCAL_SYMS_STRIPPED).

SizeOfCode Размер исполняемого кода в байтах (секция .text)
SizeOfInitializedData Размер инициализированных данных (секция .rodata)
SizeOfUninitializedData Размер не инициализированных данных (секция .bss)
BaseOfCode указывает на начало секции кода блок
SectionAlignment Размер по которому нужно выровнять секции в памяти
FileAlignment Размер по которому нужно выровнять секции внутри файла
SizeOfImage Размер всех секций программы
SizeOfHeaders Размер всех заголовков вместе (IMAGE_DOS_HEADER, DOS Stub, IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER[IMAGE_FILE_HEADER.NumberOfSections]) выровненный по FileAlignment
Subsystem Указывает тип нашей программы GUI или Console
MajorOperatingSystemVersion, MinorOperatingSystemVersion, MajorSubsystemVersion, MinorSubsystemVersion Говорят о том на какой системе можно запускать данный exe, и что он может поддерживать. В нашем случае мы берем значение 5.2 от Windows XP (x64).
SizeOfStackReserve Указывает сколько приложению нужно зарезервировать памяти под стек. Этот параметр по умолчанию составляет 1 Мб, максимально можно указать 1Гб. Вроде как умные программы на Rust умеют считать необходимый размер стека, в отличии от программ на C++ где этот размер нужно править вручную.
SizeOfStackCommit Размер по умолчанию составляет 4 Кб. Как должен работать данный параметр пока не разобрался.
SizeOfHeapReserve Указывает сколько резервировать памяти под кучу. Равен 1 Мб по умолчанию.
SizeOfHeapCommit Размер по умолчанию равен 4 Кб. Подозреваю что работает аналогично SizeOfStackCommit, то есть пока неизвестно как.

IMAGE_DATA_DIRECTORY массив записей о каталогах. В теории его можно уменьшить, сэкономив пару байт, но вроде как все описывают все 16 полей даже если они не нужны. А теперь чуть подробнее.

У каждого каталога есть свой номер, который описывает, где хранится его содержимое. Пример:
Export(0) Содержит ссылку на сегмент который хранит экспортируемые функции. Для нас это было бы актуально если бы мы создавали DLL. Как это примерно должно работать можно посмотреть на примере следующего каталога.

Import(1) Этот каталог указывает на сегмент с импортируемыми функциями из других DLL. В нашем случае значения VirtualAddress = 0x3000 и Size = 0xB8. Это единственный каталог, который мы опишем.

Resource(2) Каталог с ресурсами программы (Изображения, Текст, Файлы и т.д.)
Значения других каталогов можно посмотреть в документации.

Теперь когда мы посмотрели из чего состоит NT-заголовок запишем и его в файл по аналогии с остальными по адресу 0x80.

(3) RAW NT-Header (Offset 0x00000080)
50 45 00 00 64 86 03 00  F4 70 E8 5E 00 00 00 0000 00 00 00 F0 00 2F 00  0B 02 00 00 3D 00 00 0013 00 00 00 00 00 00 00  00 10 00 00 00 10 00 0000 00 40 00 00 00 00 00  00 10 00 00 00 02 00 0005 00 02 00 00 00 00 00  05 00 02 00 00 00 00 0000 40 00 00 00 02 00 00  00 00 00 00 02 00 00 0000 00 10 00 00 00 00 00  00 10 00 00 00 00 00 0000 00 10 00 00 00 00 00  00 10 00 00 00 00 00 0000 00 00 00 10 00 00 00  00 00 00 00 00 00 00 0000 30 00 00 B8 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00


В результате получаем вот такой вид IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER64 и IMAGE_DATA_DIRECTORY заголовков:







Далее описываем все секции нашего приложения согласно структуре IMAGE_SECTION_HEADER

Структура IMAGE_SECTION_HEADER
Struct IMAGE_SECTION_HEADER{i8[8] Nameu32 VirtualSizeu32 VirtualAddressu32 SizeOfRawDatau32 PointerToRawDatau32 PointerToRelocationsu32 PointerToLinenumbersu16 NumberOfRelocationsu16 NumberOfLinenumbersu32 Characteristics}


Описание IMAGE_SECTION_HEADER
Name имя секции из 8 байт, может быть любым
VirtualSize сколько байт копировать из файла в память
VirtualAddress адрес секции в памяти выровненный по SectionAlignment
SizeOfRawData размер сырых данных выровненных по FileAlignment
PointerToRawData адрес секции в файле выровненный по FileAlignment
Characteristics Указывает какие данные хранит секция (Код, инициализированные или нет данные, для чтения, для записи, для исполнения и др.)

В нашем случае у нaс будет 3 секции.

Почему Virtual Address (VA) начинается с 1000, а не с нуля я не знаю, но так делают все компиляторы, которые я рассматривал. В результате 1000 + 3 секции * 1000 (SectionAlignment) = 4000 что мы и записали в SizeOfImage. Это полный размер нашей программы в виртуальной памяти. Вероятно, используется для выделения места под программу в памяти.

 Name| RAW Addr| RAW Size| VA| VA Size | Attr--------+---------------+---------------+-------+---------+--------.text| 200| 200| 1000| 3D  |   CER.rdata| 400| 200| 2000| 13  | I   R.idata| 600| 200| 3000| B8  | I   R

Расшифровка атрибутов:

I Initialized data, инициализированные данные
U Uninitialized data, не инициализированные данные
C Code, содержит исполняемый код
E Execute, позволяет исполнять код
R Read, позволяет читать данные из секции
W Write, позволяет записывать данные в секцию

.text (.code) хранит в себе исполняемый код (саму программу), атрибуты CE
.rdata (.rodata) хранит в себе данные только для чтения, например константы, строки и т.п., атрибуты IR
.data хранит данные которые можно читать и записывать, такие как статические или глобальные переменные. Атрибуты IRW
.bss хранит не инициализированные данные, такие как статические или глобальные переменные. Кроме того, данная секция обычно имеет нулевой RAW размер и ненулевой VA Size, благодаря чему не занимает места в файле. Атрибуты URW
.idata секция содержащая в себе импортируемые из других библиотек функции. Атрибуты IR

Важный момент, секции должны следовать друг за другом. При чем как в файле, так и в памяти. По крайней мере когда я менял их порядок произвольно программа переставала запускаться.

Теперь, когда нам известно какие секции будет содержать наша программа запишем их в наш файл. Тут смещение оканчивается на 8 и запись будет начинаться с середины файла.

(4) RAW Sections (Offset 0x00000188)
                         2E 74 65 78 74 00 00 003D 00 00 00 00 10 00 00  00 02 00 00 00 02 00 0000 00 00 00 00 00 00 00  00 00 00 00 20 00 00 602E 72 64 61 74 61 00 00  13 00 00 00 00 20 00 0000 02 00 00 00 04 00 00  00 00 00 00 00 00 00 0000 00 00 00 40 00 00 40  2E 69 64 61 74 61 00 00B8 00 00 00 00 30 00 00  00 02 00 00 00 06 00 0000 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40



Следующий адрес для записи будет 00000200 что соответствует полю SizeOfHeaders PE-Заголовка. Если бы мы добавили еще одну секцию, а это плюс 40 байт, то наши заголовки не уложились бы в 512 (0x200) байт и пришлось бы использовать уже 512+40 = 552 байта выровненные по FileAlignment, то есть 1024 (0x400) байта. А все что останется от 0x228 (552) до адреса 0x400 нужно чем-то заполнить, лучше конечно нулями.

Взглянем как выглядит блок секций в Far:



Далее мы запишем в наш файл сами секции, но тут есть один нюанс.

Как вы могли заметить на примере SizeOfHeaders, мы не можем просто записать заголовок и перейти к записи следующего раздела. Так как что бы записать заголовок мы должны знать сколько займут все заголовки вместе. В результате нам нужно либо посчитать заранее сколько понадобиться места, либо записать пустые (нулевые) значения, а после записи всех заголовков вернуться и записать уже их реальный размер.

Поэтому программы компилируются в несколько проходов. Например секция .rdata идет после секции .text, при этом мы не можем узнать виртуальный адрес переменной в .rdata, ведь если секция .text разрастется больше чем на 0x1000 (SectionAlignment) байт, она займет адреса 0x2000 диапазона. И соответственно секция .rdata будет находиться уже не в адресе 0x2000, а в адресе 0x3000. И нам будет необходимо вернуться и пересчитать адреса всех переменных в секции .text которая идет перед .rdata.

Но в данном случае я уже все рассчитал, поэтому будем сразу записывать блоки кода.

Секция .text


Asm segment .text
0000push rbp0001mov rbp, rsp0004sub rsp, 0x200008mov rcx, 0x0000Fmov rdx, 0x4020000016mov r8, 0x40200D001Dmov r9, 0x400024call QWORD PTR [rip + 0x203E]002Amov rcx, 0x00031call QWORD PTR [rip + 0x2061]0037add rsp, 0x20003Bpop rbp003Cret


Конкретно для этой программы первые 3 строки, ровно, как и 3 последние не обязательны.
Последние 3 даже не будут исполнены, так как выход из программы произойдет еще на второй функции call.

Но скажем так, если бы это была не функция main, а подфункция следовало бы сделать именно так.

А вот первые 3 в данном случае хоть и не обязательны, но желательны. Например, если бы мы использовали не MessageBoxA, а printf то без этих строк получили бы ошибку.

Согласно соглашению о вызовах для 64-разрядных систем MSDN, первые 4 параметра передаются в регистрах RCX, RDX, R8, R9. Если они туда помещаются и не являются, например числом с плавающей точкой. А остальные передаются через стек.

По идее если мы передаем 2 аргумента функции, то должны передать их через регистры и зарезервировать под них два места в стеке, что бы при необходимости функция могла скинуть регистры в стек. Так же мы не должны рассчитывать, что нам вернут эти регистры в исходном состоянии.

Так вот проблема функции printf заключается в том, что, если мы передаем ей всего 1 аргумент, она все равно перезапишет все 4 места в стеке, хотя вроде бы должна перезаписать только одно, по количеству аргументов.

Поэтому если не хотите, чтобы программа себя странно вела, всегда резервируйте как минимум 8 байт * 4 аргумента = 32(0x20) байт, если передаете функции хотя бы 1 аргумент.

Рассмотрим блок кода с вызовами функций

MessageBoxA(0, 'Hello World!', 'MyApp', 64)ExitProcess(0)

Сначала мы передаем наши аргументы:

rcx = 0
rdx = абсолютный адрес строки в памяти ImageBase + Sections[".rdata"].VirtualAddress + Смещение строки от начала секции, строка читается до нулевого байта
r8 = аналогично предыдущему
r9 = 64(0x40) MB_ICONINFORMATION, значок информации

А далее идет вызов функции MessageBoxA, с которым не все так просто. Дело в том, что компиляторы стараются использовать как можно более короткие команды. Чем меньше размер команды, тем больше таких команд влезет в кэш процессора, соответственно, будет меньше промахов кэша, подзагрузок и выше скорость работы программы. Для более подробной информации по командам и внутренней работе процессора можно обратится к документации Intel 64 and IA-32 Architectures Software Developers Manuals.

Мы могли бы вызвать функцию по полному адресу, но это заняло бы как минимум (1 опкод + 8 адрес = 9 байт), а с относительным адресом команда call занимает всего 6 байт.

Давайте взглянем на эту магию поближе: rip + 0x203E, это ни что иное, как вызов функции по адресу, указанному нашим смещением.

Я подсмотрел немного вперед и узнал адреса нужных нам смещений. Для MessageBoxA это 0x3068, а для ExitProcess это 0x3098.

Пора превратить магию в науку. Каждый раз, когда опкод попадает в процессор, он высчитывает его длину и прибавляет к текущему адресу инструкции (RIP). Поэтому, когда мы используем RIP внутри инструкции, этот адрес указывает на конец текущей инструкции / начало следующей.
Для первого call смещение будет указывать на конец команды call это 002A не забываем что в памяти этот адрес будет по смещению Sections[".text"].VirtualAddress, т.е. 0x1000. Следовательно, RIP для нашего call будет равен 102A. Нужный нам адрес для MessageBoxA находится по адресу 0x3068. Считаем 0x3068 0x102A = 0x203E. Для второго адреса все аналогично 0x1000 + 0x0037 = 0x1037, 0x3098 0x1037 = 0x2061.

Именно эти смещения мы и видели в командах ассемблера.

0024call QWORD PTR [rip + 0x203E]002Amov rcx, 0x00031call QWORD PTR [rip + 0x2061]0037add rsp, 0x20

Запишем в наш файл секцию .text, дополнив нулями до адреса 0x400:

(5) RAW .text section (Offset 0x00000200-0x00000400)
55 48 89 E5 48 83 EC 20  48 C7 C1 00 00 00 00 48C7 C2 00 20 40 00 49 C7  C0 0D 20 40 00 49 C7 C140 00 00 00 FF 15 3E 20  00 00 48 C7 C1 00 00 0000 FF 15 61 20 00 00 48  83 C4 20 5D C3 00 00 00........00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00

Хочется отметить что всего лишь 4 строки реального кода содержат весь наш код на ассемблере. А все остальное нули что бы набрать FileAlignment. Последней строкой заполненной нулями будет 0x000003F0, после идет 0x00000400, но это будет уже следующий блок. Итого в файле уже 1024 байта, наша программа весит уже целый Килобайт! Осталось совсем немного и ее можно будет запустить.


Секция .rdata


Это, пожалуй, самая простая секция. Мы просто положим сюда две строки добив нулями до 512 байт.

.rdata
0400"Hello World!\0"040D"MyApp\0"


(6) RAW .rdata section (Offset 0x00000400-0x00000600)
48 65 6C 6C 6F 20 57 6F  72 6C 64 21 00 4D 79 4170 70 00 00 00 00 00 00  00 00 00 00 00 00 00 00........00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00


Секция .idata


Ну вот осталась последняя секция, которая описывает импортируемые функции из библиотек.

Первое что нас ждет новая структура IMAGE_IMPORT_DESCRIPTOR

Структура IMAGE_IMPORT_DESCRIPTOR
Struct IMAGE_IMPORT_DESCRIPTOR{u32 OriginalFirstThunk (INT)u32 TimeDateStampu32 ForwarderChainu32 Nameu32 FirstThunk (IAT)}


Описание IMAGE_IMPORT_DESCRIPTOR
OriginalFirstThunk Адрес указывает на список имен импортируемых функций, он же Import Name Table (INT)
Name Адрес, указывающий на название библиотеки
FirstThunk Адрес указывает на список адресов импортируемых функций, он же Import Address Table (IAT)

Для начала нам нужно добавить 2 импортируемых библиотеки. Напомним:

func MessageBoxA(u32 handle, PChar text, PChar caption, u32 type) i32 ['user32.dll']func ExitProcess(u32 code) ['kernel32.dll']

(7) RAW IMAGE_IMPORT_DESCRIPTOR (Offset 0x00000600)
58 30 00 00 00 00 00 00  00 00 00 00 3C 30 00 0068 30 00 00 88 30 00 00  00 00 00 00 00 00 00 0048 30 00 00 98 30 00 00  00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 00 00


У нас используется 2 библиотеки, а что бы сказать что мы закончили их перечислять. Последняя структура заполняется нулями.

 INT| Time | Forward  | Name   | IAT--------+--------+----------+--------+--------0x3058| 0x0    | 0x0      | 0x303C | 0x30680x3088| 0x0    | 0x0      | 0x3048 | 0x30980x0000| 0x0    | 0x0      | 0x0000 | 0x0000

Теперь добавим имена самих библиотек:

Имена библиотек
063С"user32.dll\0"0648"kernel32.dll\0"


(8) RAW имена библиотек (Offset 0x0000063С)
                                     75 73 65 7233 32 2E 64 6C 6C 00 00  6B 65 72 6E 65 6C 33 322E 64 6C 6C 00 00 00 00


Далее опишем библиотеку user32:

(9) RAW user32.dll (Offset 0x00000658)
                         78 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  78 30 00 00 00 00 00 0000 00 00 00 00 00 00 00  00 00 4D 65 73 73 61 67 65 42 6F 78 41 00 00 00


Поле Name первой библиотеки указывает на 0x303C если мы посмотрим чуть выше, то увидим что по адресу 0x063C находится библиотека user32.dll\0.

Подсказка, вспомните что секция .idata соответствует смещению в файле 0x0600, а в памяти 0x3000. Для первой библиотеки INT равен 3058, значит в файле это будет смещение 0x0658. По этому адресу видим запись 0x3078 и вторую нулевую. Означающую конец списка. 3078 ссылается на 0x0678 это RAW-строка

00 00 4D 65 73 73 61 67 65 42 6F 78 41 00 00 00

Первые 2 байта нас не интересуют и равны нулю. А вот дальше идет строка с названием функции, заканчивающаяся нулем. То есть мы можем представить её как "\0\0MessageBoxA\0".

При этом IAT ссылается на аналогичную таблице IAT структуру, но только в нее при запуске программы будут загружены адреса функций. Например, для первой записи 0x3068 в памяти будет значение отличное от значения 0x0668 в файле. Там будет адрес функции MessageBoxA загруженный системой к которому мы и будем обращаться через вызов call в коде программы.

И последний кусочек пазла, библиотека kernel32. И не забываем добить нулями до SectionAlignment.

(10) RAW kernel32.dll (Offset 0x00000688-0x00000800)
                         A8 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  A8 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00  00 00 45 78 69 74 50 72 6F 63 65 73 73 00 00 00  00 00 00 00 00 00 00 00 ........00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00



Проверяем что Far смог корректно определить какие функции мы импортировали:



Отлично! Все нормально определилось, значит теперь наш файл готов к запуску.
Барабанная дробь

Финал




Поздравляю мы справились!

Файл занимает 2 Кб = Заголовки 512 байт + 3 секции по 512 байт.

Число 512(0x200) не что иное как FileAlignment который мы указали в заголовке нашей программы.

Дополнительно:
Если хочется вникнуть чуть глубже, можно заменить надпись Hello World! на что-нибудь другое, только не забудьте изменить адрес строки в коде программы (секция .text). Адрес в памяти 0x00402000, но в файле будет обратный порядок байт 00 20 40 00.

Или квест чуть сложнее. Добавить в код вызов ещё одного MessageBox. Для этого придется скопировать предыдущий вызов, и пересчитать в нем относительный адрес (0x3068 RIP).

Заключение


Статья получилась достаточно скомканной, ей бы, конечно, состоять из 3 отдельных частей: Заголовки, Программа, Таблица импорта.

Если кто-то собрал свой exe значит мой труд был не напрасен.

Думаю в скором времени создать ELF файл похожим образом, интересна ли будет такая статья?)

Ссылки:

Подробнее..

Туториал по FASM (Windows x32 APIWin32API), Hello world!

29.05.2021 00:21:04 | Автор: admin

Коротко о FASM, ассемблере, WinAPI

  • Что такое FASM? - Это компилятор ассемблера (flat assembler).

  • Что такое ассемблер? - это машинные инструкции, то есть команды что делать процессору.

  • Что такое Windows API/WinAPI? - Это функции Windows, без них нельзя работать с Windows.

    Что дают WinAPI функции? - Очень много чего:

  • Работа с файлами.

  • Работа с окнами, отрисовка картинок, OpenGL, DirectX, GDI, и все в таком духе.

  • Взаимодействие с другими процессами.

  • Работа с портами.

  • Работа с консолью Windows

  • И еще очень много интересных функций.

Зачем нужен ассемблер?

На нем можно сделать все что угодно, от ОС до 3D игр.

Вот плюсы ассемблера:

  • Он очень быстрый.

  • На нем можно сделать любую программу.

А вот минусы ассемблера:

  • Долго делать программу. (относительно)

  • Сложен в освоении.

Что нужно для программирования на ассемблере (FASM)?

Установка компонентов (если можно так назвать)

Архив FASM-а распаковуем в C:\\FASM\ или любой другой, но потом не забудьте настроить FASMEditor.

Архив FASMEdit-a распаковуем куда-то, в моем случае C:\\FASM Editor 2.0\

Архив OlyDbg распаковуем тоже куда-то, в моем случае C:\\Users\****\Documents\FasmEditorProjects\

Настройка FASM Editor-a

Для этого его нужно запустить.

Сразу вас приветствует FASM Editor соей заставкой.

Теперь вам нужно зайти в вкладку "Сервис" (на картинке выделил синим) -> "Настройки..."

Жмем на кнопку с названием "..." и выбираем путь к файлам или папкам.

Теперь мы полностью готовы. К началу.

Пишем "Hello world!" на FASM

В Fasm Editor нужно нажать на кнопку слева сверху или "файл" -> "новый". Выбираем любое, но можно выбрать "Console"

По началу вас это может напугать, но не боимся и разбираемся.

format PE Console ; говорим компилятору FASM какой файл делатьentry start ; говорим windows-у где из этой каши стартовать программу.include 'win32a.inc' ; подключаем библиотеку FASM-а;можно и без нее но будет очень сложно.section '.data' data readable writeable ; секция данныхhello db 'hello world!',0 ; наша строка которую нужно вывестиsection '.code' code readable writeable executable ; секция кодаstart: ; метка стартаinvoke printf, hello ; вызываем функцию printf    invoke getch ; вызываем её для того чтоб программа не схлопнулась  ;то есть не закрылась сразу.    invoke ExitProcess, 0 ; говорим windows-у что у нас программа закончилась  ; то есть нужно программу закрыть (завершить)section '.idata' data import readable ; секция импорта        library kernel, 'kernel32.dll',\ ; тут немного сложней, объясню чуть позже                msvcrt, 'msvcrt.dll'    import kernel,\  ExitProcess, 'ExitProcess'            import msvcrt,\  printf, 'printf',\          getch, '_getch'

На самом деле из всей этой каши текста, команд всего 3: на 16, 18, 21 строках. (и то это не команды, а макросы. Мы к командам даже не подобрались)

Все остальное это просто подготовка программы к запуску.

Программа при запуске должна выглядеть так:

Самое интересное то что программа весит 2КБ. (Можно сократить и до 1КБ, но для упрощения и так пойдет)

Разбор: что значат этот весь текст?

На 1 строчке: "format PE Console" - это строчка говорит FASM-у какой файл скомпилировать, точнее 1 слово, все остальные слова это аргументы (можно так сказать).

PE - EXE файл, программа.

Console - говорим что это у нас консольная программа, но вам некто не мешает сделать из консольной программы оконную и наоборот.

Но есть кроме это остальные:

  • format MZ - EXE-файл НО под MS-DOS

  • format PE - EXE-файл под Windows, аналогично format PE GUI 4.0

  • format PE64 - EXE-файл под Windows, 64 битное приложение.

  • format PE GUI 4.0 - EXE-файл под Windows, графическое приложение.

  • format PE Console - EXE-файл под Windows, консольная программа. (просто подключается заранее консоль)

  • format PE Native - драйвер

  • format PE DLL - DLL-файл Windows, поясню позднее.

  • format COFF - OBJ-файл Linux

  • format MS COFF - аналогично предыдущему

  • format ELF - OBJ-файл для gcc (Linux)

  • format ELF64 - OBJ-файл для gcc (Linux), 64-bit

Сразу за командой (для компилятора) format PE Console идет ; это значит комментарий. К сожалению он есть только однострочный.

3 строка: entry start

  • Говорим windows-у где\в каком месте стартовать. "start" это метка, но о метках чуть позже.

5 строка: include 'win32a.inc'

  • Подключает к проекту файл, в данном случае "win32a.inc" он находиться в папке INCLUDE (в папке с FASM). этот файл создает константы и создает макросы для облегчения программирования.

8 строка: section '.data' data readable writeable

  • Секция данных, то есть программа делиться на секции (части), к этим секциям мы можем дать разрешение, имя.

Флаг "data" (Флаг это бит\байт\аргумент хранившей в себе какую-то информацию) говорит то что эта секция данных.

Флаги "readable writeable" говорят то что эта секция может читаться кем-то и записываться кем-то.

Текст '.data' - имя секции

10 строка: hello db 'hello world!',0

hello - это метка, она может быть любого имени (почти, есть некоторые зарезервированные имена), эта метка хранит в себе адрес строки, это не переменная, а просто адрес, но чтобы не запоминать адреса в ручную, помогает FASM он запоминает адрес и потом когда видит эту метку снова, то он заменяет слово на адрес.

db - говорит то что под каждый символ резервируем 1 байт. То есть 1 символ храниться в одном байте.

'hello world!' - наша строка в кодировке ASCII

Что значит ",0" в конце строки? - это символ с номером 0 (или просто ноль), у вас на клавиатуре нет клавиши которая имела символ с номером 0, по этому этот символ используют как показатель конца строки. То есть это значит конец строки. Просто ноль записываем в байт после строки.

12 строка: section '.code' code readable writeable executable

Флаг "code" - говорит то что это секция кода.

Флаг "executable" - говорит то что эта секция исполняема, то есть в этой секции может выполняться код.

Все остальное уже разобрали.

14 строка: start:

Это второй вид меток. Просто эта метка указывает на следующую команду. Обратите внимание на то что в 3 строке мы указали start как метку входа в программу, это она и есть. Может иметь эта метка любое имя, главное не забудьте ваше новое имя метки вписать в entry

15 строка: invoke printf, hello

  • Функция printf - выводит текст\число в консоль. В данном случае текст по адресу "hello"

Это штото на подобие команды, но это и близко не команда ассемблера, а просто макрос.

Макрос - Это макро команда для компилятора, то есть вместо имени макроса подставляется что-то другое.

Например, макро команда invoke делиться на такие команды: (взят в пример команда с 15 строки)

push hellocall [printf]

Не переживайте если нечего не поняли.

17 строка: invoke getch

  • getch - функция получения нажатой кнопки, то есть просто ждет нажатия кнопки и потом возвращает нажатую кнопку.

20 строка: invoke ExitProcess, 0

  • ExitProcess - WinAPI функция, она завершает программу. Она принимает значение, с которым завершиться, то есть код ошибки, ноль это нет ошибок.

23 строка: section '.idata' data import readable

Флаг "import" - говорит то что это секция импорта библиотек.

24-25 строки:

library kernel, 'kernel32.dll',\  msvcrt, 'msvcrt.dll'
  • Макро команда "library" загружает DLL библиотеки в виртуальную память (не в ОЗУ, вам ОЗУ не хватит чтоб хранить всю виртуальную память).

Что такое DLL объясню позже.

kernel - имя которое привязывается к библиотеке, оно может быть любым.

Следующий текст после запятой: 'kernel32.dll' - это имя DLL библиотеки который вы хотите подключить.

Дальше есть знак \ это значит что текст на следующей строке нужно подставить в эту строку.

То есть код:

library kernel, 'kernel32.dll',\  msvcrt, 'msvcrt.dll'

Заменяется на:

library kernel, 'kernel32.dll', msvcrt, 'msvcrt.dll'

Это нужно потому что у ассемблера 1 строка это 1 команда.

27-28 строка:

import kernel,\  ExitProcess, 'ExitProcess'

import - Макро команда, которая загружает функции из DLL.

kernel - Имя к которой привязана DLL, может быть любым.

ExitProcess - Как будет называться функция в программе, это имя будет только в вашей программе, и по этому имени вы будете вызывать функцию. (WinAPI функция)

'ExitProcess' - Это имя функции которое будет загружено из DLL, то есть это имя функции которое прописано в DLL.

Дальше думаю не стоит объяснять, вроде все понятно.

Что такое DLL библиотека?

Это файл с расширением DLL. В этом файле прописаны функции (какие не будь). Это обычная программа, но которая не запускается по двойному щелчку, а загружается к программе в виртуальную память, и потом вызываются функции находящиеся в этой DLL.

Подводим итог

На ассемблере писать можно не зная самого языка, а используя всего лишь макро команды компилятора. За всю статью я упомянул всего 2 команды ассемблера это push hello и call [printf] . Что это значит расскажу в следующей статье.

Подробнее..
Категории: Assembler , Туториал , Уроки , Winapi , Fasm , Windowsapi

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru