Форум программистов, компьютерный форум, киберфорум
Наши страницы
Assembler: MASM64, х64/long mode
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.81/64: Рейтинг темы: голосов - 64, средняя оценка - 4.81
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
#1

Все, что нужно знать, чтобы начать программировать для 64-разрядных версий Windows

28.05.2014, 05:07. Просмотров 11841. Ответов 20
Метки нет (Все метки)

Все, что нужно знать, чтобы начать программировать для 64-разрядных версий Windows

Автор: Мэтт Питрек

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

Честно говоря, мне не слишком комфортно работать с новой операционной системой, если я еще не покопался в ее внутреннем устройстве. А покопаться "под капотом" я люблю. Так что, когда на сцену вышли 64-разрядные версии Windows XP и Windows Server 2003, я был в полном восторге.

Приятная особенность Win64 и процессорной архитектуры x64 заключается в том, что они достаточно сильно отличаются от своих предшественников — как раз настолько, чтобы вызывать интерес, в то же время не требуя основательного переобучения. Хотя мы, разработчики, предпочитаем считать, будто при переходе на платформу x64 удастся отделаться простой перекомпиляцией своих программ, реальность такова, что нам все равно придется долго возиться с ними в отладчике. И здесь хорошее понимание новой программно-аппаратной платформы будет важным подспорьем.

В данной статье я поделюсь квинтэссенцией своих знаний в области Win64 и архитектуры x64 — тем минимумом, который необходим опытному Win32-программисту для перехода на платформу x64. При этом я исхожу из того, что вам известны базовые концепции Win32 и платформы x86 и вы понимаете, зачем ваш код должен работать в Win64. Это позволит мне не отвлекаться от основной тематики. В общем, считайте мою статью обзором, где рассматриваются лишь наиболее важные различия архитектур Win64/x64 и Win32/x86.

Системы x64 хороши еще и тем, что они — в отличие от систем на основе процессоров Itanium — позволяют использовать либо Win32, либо Win64 на одном компьютере без серьезных потерь в производительности. И несмотря на некоторые, весьма туманные различия между реализациями x64 от Intel и AMD x64-совместимая версия Windows должна работать с любой из них. Вам не понадобятся отдельные сборки Windows для x64-процессоров AMD и Intel.

Основное внимание я уделю трем областям: деталям реализации ОС, архитектуре x64 (обязательный минимум) и разработке для x64 с помощью Visual C++.
Операционная система x64

В любом обзоре архитектуры Windows я предпочитаю начинать с рассмотрения адресации и адресного пространства. Хотя 64-разрядный процессор теоретически мог бы адресоваться к 16 экзабайтам памяти (264=18.446.744.073.709.551.616), в настоящее время Win64 поддерживает 16 Тб — значение, которое представлено 44 разрядами (244=17.592.186.044.416). Почему же нельзя задействовать все 64 разряда, чтобы адресоваться к 16 экзабайтам памяти? По целому ряду причин.

Начнем с того, что нынешние процессоры x64 обычно позволяют обращаться лишь к 40-разрядному представлению физической памяти (1 Тб = 240 = 1.099.511.627.776). Сама архитектура (но не современное оборудование) допускает расширение до 52 разрядов (4 петабайтов = 252 = 4.503.599.627.370.496). Даже если бы это ограничение было снято, размеры таблиц страниц, необходимых для проецирования такого громадного объема памяти, оказались бы просто гигантскими.

Как и в Win32, адресуемая память делится на области пользовательского режима и режима ядра. Каждому процессу выделяется собственное уникальное пространство размером 8 Тб в нижней части памяти, а код режима ядра размещается в верхних 8 Тб и разделяется всеми процессами. У разных версий 64-разрядной Windows разные ограничения на объемы физической памяти (табл. 1 и 2).
Табл. 1. Общие ограничения на память
 32-разрядные модели 64-разрядные модели
Виртуальное адресное пространство
(одного процесса)
4 Гб16 Тб
Виртуальное адресное пространство
для каждого 32-разрядного процесса
2 Гб (3 Гб при запуске
системы с ключом /3GB)
4 Гб при компиляции с параметром
/LARGEADDRESSAWARE (иначе 2 Гб)
Виртуальное адресное пространство
для каждого 64-разрядного процесса
Неприменимо 8 Тб
Пул подкачиваемой памяти режима ядра 470 Мб 128 Гб
Пул неподкачиваемой памяти режима ядра 256 Мб 128 Гб
Элемент системной таблицы страниц
(Page Table Entry, PTE)
660-900 Мб 128 Гб

Табл. 2. Ограничения на физическую память в зависимости от процессоров
Операционная система 32-разрядные модели 64-разрядные модели
Windows XP Professional 4 Гб (1-2 процессора) 128 Гб (1-2 процессора)
Windows Server 2003, Standard Edition 4 Гб (1-4 процессора) 32 Гб (1-4 процессора)
Windows Server 2003, Enterprise Edition 64 Гб (1-8 процессоров) 1 Тб (1-8 процессоров)
Windows Server 2003, Datacenter Edition 64 Гб (8-32 процессора) 1 Тб (8-64 процессора)

Так же, как и в Win32, размер страницы на платформе x64 равен 4 Кб. Первые 64 Кб адресного пространства никогда не проецируются на физическую память, поэтому младший допустимый адрес — 0x10000. В отличие от Win32 системные DLL по умолчанию не загружаются по адресу в верхней части адресного пространства пользовательского режима. Вместо этого они загружаются после 4 Гб, обычно по адресам, близким к 0x7FF00000000.

Приятная особенность процессоров x64 — поддержка битового флага No Execute, который в Windows используется для реализации аппаратной защиты от выполнения данных как кода (Data Execution Protection, DEP). Существование многих вирусов и "багов" на платформе x86 как раз и обусловлено тем, что процессор может выполнять данные так, будто это байты кода. Переполнение буфера (намеренное или случайное) может привести к тому, что процессор будет выполнять содержимое области памяти, где должны храниться данные. Благодаря DEP операционная система гораздо четче разграничивает области памяти, в которых находится код, и становится способной перехватывать попытки выполнения кода, выходящие за эти границы. Это уменьшает уязвимость Windows перед атаками.

Для выявления ошибок компоновщик (linker) на платформе x64 по умолчанию присваивает адресам загрузки исполняемых файлов первое значение, большее 32-разрядного числа (4 Гб). Это помогает быстро находить проблемные места в существующем коде после его переноса на Win64. В частности, если указатель хранится как 32-битное значение (например, как DWORD), то при работе в Win64-версии вашей программы он окажется усеченным и станет недопустимым, тут же вызвав нарушение доступа к памяти (access violation). Такой прием резко упрощает отлов ошибок, связанных с указателями.

Затронутый мной вопрос указателей и DWORD-значений позволяет плавно перейти к системе типов в Win64. Какой размер должен иметь указатель? Как насчет LONG? И описателей (handles) наподобие HWND? К счастью, Microsoft, ведя нас по весьма запутанному пути от Win16 к Win32, заодно создала новые модели типов, легко расширяемые и до 64-разрядных. В общем, если не считать нескольких исключений, все типы, отличные от указателей и size_t, совершенно одинаковы, что в старой Win32, что в новой Win64. То есть у 64-битного указателя размер 8 байтов, а у int, long, DWORD и HANDLE остался прежний размер — 4 байта. Подробнее о типах я расскажу позже, когда речь пойдет о разработке для Win64.

Формат файлов в Win64 называется PE32+. С точки зрения структуры, он почти во всем идентичен формату PE в Win32. Лишь некоторые поля вроде ImageBase в заголовке расширены, одно поле удалено и одно изменено так, чтобы оно отражало новый тип процессоров (табл. 3).
Табл. 3. Изменения в полях заголовков PE-файлов
Поле заголовка Изменение
Magic 0x20b вместо 0x10b
BaseOfData Убрано
ImageBase Расширено до 64 битов
SizeOfStackReserve Расширено
SizeOfStackCommit Расширено
SizeOfHeapReserve Расширено
SizeOfHeapCommit Расширено

Помимо заголовка PE, изменений не так уж много. В некоторых структурах, например IMAGE_LOAD_CONFIG и IMAGE_THUNK_DATA, часть полей просто расширена до 64 битов. Больший интерес представляет введение раздела PDATA, так как он высвечивает одно из основных различий между реализациями Win32 и Win64: концепцию обработки исключений.

На платформе x86 обработка исключений базируется на стеке. Когда Win32-функция содержит код try/catch или try/finally, компилятор генерирует инструкции, создающие небольшие блоки данных в стеке. Каждый блок данных try указывает на предыдущую структуру данных try, образуя связанный список, в котором структуры, добавленные последними, помещаются в начало списка. По мере вызова функций и выхода из них начало связанного списка обновляется. Как только возникает исключение, ОС просматривает связанный список блоков в стеке, отыскивая подходящий обработчик. Все детали этого процесса я изложил в своей статье за январь 1997 г. (microsoft.com/msj/0197/Exception/Exception.aspx), так что здесь я не буду вдаваться в подробности.

В Win64 (в версиях для x64 и Itanium) применяется табличная обработка исключений. Никакого связанного списка блоков данных try в стеке не создается. Вместо этого каждый исполняемый файл в Win64 содержит таблицу функций периода выполнения (runtime function table). В каждой записи этой таблицы хранятся начальный и конечный адреса функции, а также местонахождение большого набора данных о коде, обрабатывающем исключения в данной функции, и структура ее фрейма стека. Детальное содержимое этих структур см. в определении IMAGE_RUNTIME_FUNCTION_ENTRY в файле WINNT.H и в x64 SDK.

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

А как быть, если вы сгенерировали код непосредственно в памяти, не используя нижележащий модуль формата PE32+? В Win64 имеется API-функция RtlAddFunctionTable, позволяющая сообщить ОС о динамически генерируемом коде.

Недостаток табличной обработки исключений (в сравнении с x86-моделью на основе стека) заключается в том, что поиск записей в таблице функций по адресам кода занимает больше времени, чем простой просмотр связанного списка. Зато исключаются издержки, связанные с тем, что в x86-модели приходится обновлять блок данных try при каждом выполнении функции.

Не забывайте, что я даю лишь краткое введение, а не полное описание обработки исключений на платформе x64. Более глубокий обзор x64-модели исключений читайте в онлайновом дневнике Кевина Фрая (Kevin Frei) по ссылке blogs.msdn.com/509372.aspx.

В x64-совместимых версиях Windows не появилось слишком уж много новых API-функций — большинство таковых в Win64 добавлено в выпуски Windows для процессоров Itanium. Две наиболее важные API-функции — IsWow64Process и GetNativeSystemInfo — позволяют Win32-приложениям определять, выполняются ли они в Win64, и, если да, выяснять реальные возможности данной системы. Если же 32-разрядный процесс обращается к GetSystemInfo, он видит лишь те возможности, которые свойственны обычной 32-разрядной системе. Так, GetSystemInfo способна сообщать о диапазонах адресов лишь 32-разрядных процессов. В табл. 4 перечислены API-функции для платформы x64, которых не было на платформе x86.
Табл. 4. Изменения в полях заголовков PE-файлов
ФункциональностьAPI-функции
Обработка исключений
RtlAddFunctionTable
RtlDeleteFunctionTable
RtlRestoreContext
RtlLookupFunctionEntry
RtlInstallFunctionTableCallback
Реестр
RegDeleteKeyEx
RegGetValue
RegQueryReflectionKey
NUMA (Non-Uniform Memory Access) GetNumaAvailableMemoryNode
GetNumaHighestNodeNumber
GetNumaNodeProcessorMask
GetNumaProcessorNode
Перенаправление WOW64
Wow64DisableWow64FsRedirection
Wow64Revert
Wow64FsRedirection
RegDisableReflectionKey
RegEnableReflectionKey
РазноеGetLogicalProcessorInformation
QueryWorkingSetEx
SetThreadStackGuarantee
GetSystemFileCacheSize
SetSystemFileCacheSize
EnumSystemFirmwareTables
GetSystemFirmwareTable

Хотя перспективы работы в полностью 64-разрядной Windows-системе выглядят очень радужно, реалии таковы, что какое-то время вам скорее всего понадобится запускать в ней Win32-код. В связи с этим в x64-версиях Windows предусмотрена подсистема WOW64, позволяющая сосуществовать Win32- и Win64-процессам в одной системе. Однако загрузка вашей 32-разрядной DLL в 64-разрядный процесс (или наоборот) не поддерживается. (Поверьте мне, это хорошо.) И наконец-то можно окончательно распрощаться с устаревшим 16-разрядным кодом!

В x64-версиях Windows процесс, запускаемый из 64-разрядного исполняемого файла, например из Explorer.exe, может загружать только Win64 DLL-библиотеки, а процесс, запускаемый из 32-разрядного исполняемого файла, — только Win32 DLL-библиотеки. Когда Win32-процесс вызывает какую-то функцию режима ядра (скажем, для чтения файла), WOW64 автоматически перехватывает этот вызов и переадресует его эквивалентному x64-коду.

Конечно, процессам разных "весовых категорий" (32- и 64-разрядным) нужно как-то взаимодействовать между собой. К счастью, все известные и любимые вами Win32-механизмы межпроцессного взаимодействия работают и в Win64; это относится, в том числе, к разделяемой памяти (shared memory), именованным каналам (named pipes) и именованным синхронизирующим объектам.

Возможно, вас интересует: "А как насчет системного каталога? Не могут же в одном каталоге храниться 32- и 64-разрядные версии системных DLL вроде KERNEL32 или USER32, верно?". WOW64 сама заботится об этом и осуществляет избирательное перенаправление в файловой системе. Файловые операции, выполняемые из Win32-процесса, которые в обычных условиях адресовались бы к каталогу System32, перенаправляются в каталог SysWow64. На внутреннем уровне WOW64 "молча" модифицирует соответствующие запросы так, чтобы они были направлены каталогу SysWow64. По сути, в Win64-системе существуют два каталога \Windows\System32: один — для двоичных файлов x64, а другой — для Win32-эквивалентов.

Как бы гладко ни выглядело это на бумаге, на самом деле легко запутаться. Например, в одном из участков своего кода я использовал окно 32-разрядной командной строки. Выполняя команду DIR применительно к Kernel32.dll в каталоге System32, я получал ровно тот же результат, что и при обращении к каталогу SysWow64. И мне пришлось долго чесать в затылке, пока я не сообразил, что перенаправление в файловой системе работает именно так, как и должно. То есть, даже когда я думал, будто работаю с каталогом \Windows\System32, WOW64 переадресовывала вызовы в каталог SysWow64. Кстати, если вам вдруг понадобится доступ к 32-разрядному каталогу \Windows\System32 из x64-приложения, правильный путь укажет API-функция GetSystemWow64Directory. Только сначала почитайте документацию MSDN, чтобы не упустить какие-нибудь детали.

WOW64 выполняет перенаправление не только в файловой системе, но и в реестре. Вспомните, что я недавно говорил: Win32 DLL нельзя загрузить в Win64-процесс. А теперь подумайте о COM и о том, как она использует реестр для загрузки DLL-библиотек внутреннего (внутрипроцессного) сервера (in-process server). Что будет, если 64-разрядное приложение вызовет CoCreateInstance для создания объекта, реализованного в Win32 DLL? Ведь эту DLL нельзя загрузить, верно? WOW64 вновь спасает положение, перенаправляя запросы из 32-разрядных приложений в \Software\Classes и связанные с ними узлы реестра. В конечном счете Win32-приложения получают отличное от x64-приложений (по большей части параллельное) представление реестра. Как и следовало ожидать, ОС предоставляет возможность 32-разрядным приложениям считывать реальный 64-разрядный параметр из реестра, указывая новые флаговые значения при вызове RegOpenKey и родственных ей функций.

Последние несколько различий в ОС, близкие и дорогие моему сердцу, касаются данных, локальных для потока. В x86-версиях Windows на области памяти, локальные для потоков, в том числе на "последнюю ошибку" и Thread Local Storage (GetLastError и TlsGetValue соответственно), указывал регистр FS. В x64-версиях Windows регистр FS заменен регистром GS. В остальном все работает почти аналогично.

Хотя в этой статье x64 в основном рассматривается с точки зрения пользовательского режима, не могу не обратить ваше внимание на одно важное изменение в архитектуре режима ядра. В Windows для x64 появилась новая технология под названием PatchGuard, предназначенная для повышения как безопасности, так и отказоустойчивости. Если в двух словах, то программы или драйверы пользовательского режима, изменяющие содержимое ключевых структур данных вроде таблиц syscall и таблицы диспетчеризации прерываний (interrupt dispatch table, IDT), создают дыры в защите и потенциальную угрозу стабильности. При разработке архитектуры x64-версий Windows было решено, что допускать модификацию памяти режима ядра такими недокументированными способами больше нельзя. Противодействовать им и призвана технология PatchGuard. Она использует поток режима ядра для мониторинга изменений в критически важных областях памяти режима ядра. Если такое изменение обнаруживается, система останавливается через bugcheck.

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

Основные сведения об архитектуре x64

Теперь рассмотрим архитектуру самих процессоров x64, так как базовые знания набора команд процессора существенно упрощают разработку (особенно отладку).
  • Первое, что вы заметите в x64-коде, сгенерированном компилятором, — насколько он похож на известный и любимый вами x86-код. Это вам не код для Intel IA64!
  • Второе, на что вы обратите внимание, — имена регистров слегка отличаются от привычных и их гораздо больше. Имена регистров общего назначения в процессорах x64 начинаются с буквы R, например RAX, RBX и др. Это развитие старой схемы именования на основе префикса E для 32-битных регистров x86. В далеком прошлом, уже подернутом дымкой забвения, 16-битный регистр AX стал 32-битным EAX, 16-битный BX — 32-битным EBX и т. д. Таким образом, при переходе с 32-разрядных версий на 64-разрядные префикс E во всех именах регистров теперь заменяется префиксом R (скажем, RAX — наследник EAX, RBX продолжает дело EBX, RSI заменяет ESI и т. п.). Кроме того, появилось восемь новых регистров общего назначения (R8-R15).
Список основных 64-разрядных регистров общего назначения
  • RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP
  • R8, R9, R10, R11, R12, R13, R14, R15

Кстати, 32-битный регистр EIP стал регистром RIP. Конечно, 32-разрядные инструкции по-прежнему должны выполняться, поэтому доступны и все остальные версии регистров (EAX, AX, AL, AH и др.).

Чтобы гуру программирования графических и научных приложений не почувствовали себя забытыми, в процессорах x64 также есть 16 128-битных SSE2-регистров с именами от XMM0 до XMM15. Полный набор x64-регистров, поддерживаемых Windows, вы найдете в структуре _CONTEXT, определенной в файле WINNT.H.

Процессор x64 работает либо в 64-разрядном режиме, либо в унаследованном 32-разрядном. В последнем случае процессор x64 декодирует и выполняет инструкции точно так же, как и любой процессор x86. В 64-разрядном режиме процессор слегка меняет свое поведение для поддержки новых регистров и инструкций.

Если вы знакомы со схемами кодирования процессорных операций, то вспомните, что пространство для новых инструкций быстро исчезало и что введение восьми новых регистров — задача не из простых. Один из путей ее решения — исключение некоторых редко применяемых инструкций. Пока что я заметил отсутствие 64-разрядных версий PUSHAD и POPAD, которые соответственно сохраняли все регистры общего назначения в стеке и восстанавливали их из него. Другой способ — полное избавление от сегментов в 64-разрядном режиме. Так что жизнь регистров CS, DS, ES, SS, FS и GS подходит к концу. Но об этом мало кто пожалеет.

Теперь, когда адреса стали 64-битными, вероятно, вас интересует, насколько увеличится размер кода. Вот, например, распространенная 32-разрядная инструкция:
Код
CALL DWORD PTR [XXXXXXXX]
Здесь "иксы" представляют 32-битный адрес. Станет ли он в 64-разрядном режиме 64-битным и превратится ли таким образом из 5-байтовой инструкции в 9-байтовую? К счастью, нет. Эта инструкция останется прежней длины. В 64-разрядном режиме 32-битная часть операнда инструкции интерпретируется как смещение данных относительно текущей инструкции. Небольшой пример поможет прояснить сказанное. Вот что представляет собой инструкция для вызова по 32-битному значению указателя, хранящемуся по адресу 00020000h в 32-разрядном режиме:
Код
00401000: CALL DWORD PTR [00020000h]
В 64-разрядном режиме те же байты кода операции (opcodebytes) делают вызов по 64-битному значению указателя, хранящемуся по адресу 00421000h (4010000h + 20000h). Немного поразмыслив, вы поймете, что этот режим относительной адресации влечет за собой важные последствия в том случае, если вы сами генерируете код. Нельзя просто задать 8-байтовое значение указателя в инструкции. Вместо этого вы должны указать 32-битный относительный адрес участка памяти, в котором хранится реальный 64-битный целевой адрес. То есть предполагается, что 64-битный целевой указатель находится в пределах 2 Гб от использующей его инструкции. Для большинства это не имеет особого значения, но только не для тех, кто динамически генерирует код или модифицирует существующий код в памяти!

Основное преимущество всех x64-регистров в том, что компиляторы наконец-то могут генерировать код, в котором большая часть параметров передается через регистры, а не стек. Заталкивая параметры в стек, неизбежно приходится обращаться по адресам памяти. А мы уже давно крепко усвоили, что обращение по адресам памяти, отсутствующим в кэше процессора, занимает несоизмеримо больше времени.

При разработке соглашений по вызовам (calling conventions) в архитектуре x64 воспользовались возможностью расчистить все завалы, нагороженные в существующих соглашениях Win32 вроде __stdcall, __cdecl, __fastcall, _thiscall и т. д. В Win64 только одно "родное" соглашение по вызовам, и модификаторы наподобие __cdecl игнорируются компилятором. Такое резкое сокращение числа соглашений — великое благо, в том числе для отладки.

Главное, что надо знать о соглашении по вызовам на платформе x64, — оно похоже на x86-соглашение fastcall. В x64-соглашении первые четыре целочисленных аргумента (слева направо) передаются в 64-битных регистрах, предназначенных специально для этой цели:
Код
RCX: 1-й целочисленный аргумент
RDX: 2-й целочисленный аргумент
R8: 3-й целочисленный аргумент
R9: 4-й целочисленный аргумент
Остальные целочисленные аргументы передаются через стек. Указатель this считается целочисленным аргументом, поэтому он всегда помещается в регистр RCX. Что касается параметров со значениями с плавающей точкой, то первые четыре из них передаются в регистрах XMM0-XMM3, а последующие — через стек потока.

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

Рассмотрим пример. Допустим, некая функция передает два целочисленных параметра дочерней функции. Компилятор не только запихнет эти значения в регистры RCX и RDX, но и вычтет 32 байта из регистра RSP (указателя стека). Вызываемая функция может обратиться к параметрам через регистры RCX и RDX. Если же коду этой функции данные регистры понадобятся для какой-то иной цели, он сможет скопировать их содержимое в зарезервированное пространство стека размером 32 байта. На рис. 1 показаны регистры и стек после передачи шести целочисленных параметров.


Рис. 1. Передача целочисленных аргументов

Очистка параметров в стеке несколько необычна в x64-системах. С технической точки зрения, за очистку стека отвечает вызвавшая функция, а не вызываемая. Но, кроме как в прологе и эпилоге, вы редко где увидите изменение регистра RSP. В отличие от компилятора x86, который явно добавляет параметры в стек и удаляет их из него с помощью инструкций PUSH и POP соответственно, компилятор x64 резервирует в стеке пространство, достаточное для вызова функции с самым длинным списком параметров. А потом он снова и снова использует это пространство для задания параметров при вызовах дочерних функций.

Иначе говоря, содержимое регистра RSP меняется крайне редко. Эта закономерность резко отличается от той, которая свойственна x86-коду, где значение регистра ESP варьируется по мере добавления параметров в стек и их удаления из него.

Вновь обратимся к примеру. Возьмем x64-функцию, вызывающую три другие функции. Первая из них принимает четыре параметра (0x20 байтов), вторая — 12 (0x60 байтов), а третья — восемь (0x40 байтов). В прологе сгенерированный код просто резервирует 0x60 байтов в стеке и копирует в соответствующие участки выделенного пространства значения параметров, так что места хватит и для вызова функции с самым длинным списком параметров.

Хорошее и более глубокое описание соглашения по вызовам на платформе x64 можно найти в комментарии Рэймонда Чена (Raymond Chen) по ссылке blogs.msdn.com/58579.aspx. Я не буду тратить драгоценное место в статье на все детали, а лишь подчеркну несколько важных нюансов. Во-первых, целочисленные параметры с размером менее 64 битов дополняются знаком, а затем все равно передаются через соответствующие регистры (если они относятся к первым четырем целочисленным параметрам). Во-вторых, ни при каких условиях ни один параметр не должен оказаться по такому адресу стека, который не кратен 8 байтам; иначе будет нарушено 64-битное выравнивание. Любые аргументы, не равные 1, 2, 4 или 8 байтам (в том числе структуры), передаются по ссылке. И наконец, структуры (struct) и объединения (union) размером 8, 16, 32 или 64 бита передаются так, будто это целые значения того же размера.

Возвращаемое значение функции помещается в регистр RAX. Исключение делается для арифметических типов с плавающей точкой, которые возвращаются в XMM0. Между вызовами нужно сохранять содержимое регистров RBX, RBP, RDI, RSI и R12-R15. Значения в регистрах RAX, RCX, RDX и R8-R11 можно изменять и удалять.

Ранее я упоминал, что ОС просматривает фреймы стека в процессе обработки исключений. Если вам когда-нибудь доводилось писать код, "проходящий" по стеку, вы знаете, что из-за произвола в организации фреймов в Win32 такой код весьма нетривиален. В x64-системах ситуация значительно улучшилась. Если некая функция выделяет пространство в стеке, вызывает другие функции, сохраняет значения любых регистров или использует механизм обработки исключений, она должна оперировать строго определенным набором инструкций для генерации стандартных прологов и эпилогов.

Принудительное введение стандарта на создание фрейма стека для функции — один из способов, благодаря которым ОС гарантирует (теоретически), что стек всегда можно будет просмотреть без особых ухищрений. Помимо стандартизированных прологов, компилятор и компоновщик должны создавать соответствующие записи в таблице функций. Для интересующихся сообщу, что эта таблица представляет собой массив элементов IMAGE_FUNCTION_ENTRY64, определенных в winnt.h. Как ее найти? На нее указывает элемент IMAGE_DIRECTORY_ENTRY_EXCEPTION в поле DataDirectory заголовка PE-файла.

Итак, я рассмотрел основные особенности архитектуры, не вдаваясь в детали. Усвоив одни лишь эти основы и зная язык ассемблера на 32-разрядной платформе, вы сможете быстро понять смысл x64-инструкции в отладчике. Ну а мастерство, как всегда, придет с опытом.
Разработка для x64 с помощью Visual C++

Хотя x64-код можно было писать в Microsoft C++ до появления Visual Studio 2005, это было весьма неудобно. Поэтому здесь я исхожу из того, что вы работаете в Visual Studio 2005 и что вы выбрали инструментарий для платформы x64, который по умолчанию не устанавливается. Я также предполагаю, что у вас уже есть какой-то Win32-проект (пользовательского режима) на C++, который вы хотите компилировать для обеих платформ — как x86, так и x64.

Первый шаг в компиляции программы для x64 — создание конфигурации 64-разрядной сборки. Как пользователь Visual Studio, вы прекрасно знаете, что по умолчанию у ваших проектов две конфигурации сборки: Debug и Retail. Поэтому остается создать еще две конфигурации: Debug и Retail для x64.

Начните с загрузки существующего проекта/решения. В меню Build выберите Configuration Manager. В диалоговом окне Configuration Manager в раскрывающемся списке Active Solution Platform выберите New (рис. 2). После этого вы должны увидеть диалог New Solution Platform.

Рис. 2. Создание новой конфигурации сборки

Выберите x64 в качестве новой платформы (рис. 3), прочие параметры оставьте в состоянии по умолчанию и щелкните OK. Вот и все! Теперь у вас должно быть четыре конфигурации сборки: Win32 Debug, Win32 Retail, x64 Debug и x64 Retail. Переключаться между ними вы будете через Configuration Manager.


Рис. 3. Выбор платформы сборки

Теперь посмотрим, насколько совместим с x64 ваш код. Создайте конфигурацию x64 Debug по умолчанию и соберите проект. Если его код не тривиален, все шансы за то, что вы получите при компиляции ошибки, не встречавшиеся в Win32-конфигурации. Но справиться с этими проблемами и сделать код действительно совместимым как с Win32, так и с x64 сравнительно легко, если только вы не нарушали все принципы написания портируемого C++-кода. И не потребуются тонны директив условной компиляции.

Как сделать код совместимым с Win64

Вероятно, при преобразовании Win32-кода в x64-код больше всего усилий понадобится для того, чтобы сохранить корректность ваших определений типов. Помните, что я говорил о системе типов в Win64? Используя Windows-типы, определенные через typedef в заголовочных файлах Windows, а не "родные" для компилятора C++ типы (int, long и др.), вы упростите себе написание чистого Win32-кода, способного работать на платформе x64. Например, если Windows передает вам HWND, не сохраняйте его в FARPROC просто потому, что это легко и удобно.

Вероятно, наиболее частая и легко устранимая ошибка, которую я встречал при переносе кода, вызвана предположением о том, что значение указателя может быть сохранено или перенесено в 32-разрядном типе вроде int, long или даже DWORD. Но вся штука в том, что указатели в Win32 и Win64 имеют разные размеры, а целочисленные типы остались прежней длины.

Здесь помогают типы _PTR, определенные в заголовочных файлах Windows. Такие типы, как DWORD_PTR, INT_PTR и LONG_PTR, позволяют объявлять переменные целочисленного типа, которые всегда имеют достаточный размер для хранения указателя на целевой платформе. Например, переменная, определенная как тип DWORD_PTR, является 32-битной целой при компиляции для Win32 и 64-битной при компиляции для Win64. Немного практики, и использование таких переменных станет вашей второй натурой: объявляя какой-либо тип, вы всегда будете спрашивать себя, нужен здесь DWORD или DWORD_PTR?

Возможны ситуации, где надо точно указывать, сколько именно байтов следует отвести под целый тип. На такие случаи в тех же заголовочных файлах (Basetsd.h и др.), где определяются DWORD_PTR и прочие типы, предлагаются определения целых специфической длины, например INT32, INT64, INT16, UINT32 и DWORD64.

Еще одна проблема, связанная с различиями в размерах типов, относится к форматированию вывода printf и sprintf. Вот я раньше часто грешил конструкциями %X или %08X при форматировании значений указателей и был строго наказан за это при запуске подобного кода в x64-системе. Правильный способ — использование %p, при котором автоматически учитывается размер указателя на целевой платформе. Кроме того, printf и sprintf поддерживают префикс I для типов, размер которых зависит от платформы. Скажем, для вывода значения переменной UINT_PTR можно было бы использовать %Iu. Если же вы точно знаете, что переменная всегда будет 64-битной знаковой, то могли бы указать %I64d.

Вычистив ошибки, вызванные определениями типов, неподходящими для Win64, вы все равно можете остаться с кодом, который работает только на платформе x86. Тогда, вероятно, лучше пойти по простейшему пути и написать две версии функции: одну — для Win32, другую — для x64. И здесь вам очень пригодится набор макросов препроцессора:
Код
_M_IX86
_M_AMD64
_WIN64
Правильное использование макросов препроцессора очень важно для написания корректного кросс-платформенного кода. Макросы _M_IX86 и _M_AMD64 применяются только при компиляции под определенную платформу, а _WIN64 — при компиляции для любой 64-разрядной версии Windows, в том числе выпуска для процессоров Itanium.

Пытаясь применить макрос препроцессора, хорошенько подумайте о том, чего вы добиваетесь. Например, действительно ли ваш код специфичен только для процессоров x64? Если да, пишите:
Код
#ifdef _M_AMD64
С другой стороны, если тот же код мог бы работать и на x64, и на Itanium, лучше сделать так:
Код
#ifdef _WIN64
Для себя я принял полезное правило при использовании любого из этих макросов: всегда создавать явные варианты #else (подчеркиваю, явные!), чтобы можно было быстро понять, не пропущено ли что-то. Для примера возьмем плохо написанный код:
Код
#ifdef _M_AMD64
// Здесь находится x64-код
#else
// Здесь находится x86-код
#endif
Что будет, если я теперь скомпилирую его для третьей процессорной архитектуры? Сам того не желая, я скомпилирую x86-код. Гораздо лучше переделать предыдущий код примерно так:
Код
#ifdef _M_AMD64
// Здесь находится x64-код
#elif defined (_M_IX86)
// Здесь находится x86-код
#else
#error !!! Нужно написать код для этой архитектуры
#endif
Одна из частей моего Win32-кода, которую удалось перенести на платформу x64 лишь с большим трудом, — подставляемый (inline) ассемблерный код, не поддерживаемый Visual C++ для x64. Но не бойтесь, любители ассемблера. Существует 64-разрядный MASM (ML64.exe); его вместе с документацией можно получить через MSDN. ML64.exe и другие инструменты для x64 (в том числе CL.EXE и LINK.EXE) доступны из командной строки. Чтобы настроить нужные переменные окружения, достаточно запустить файл VCVARS64.BAT.

Отладка

И вот вы наконец добились чистой компиляции Win32- и x64-версий своего кода. Остался последний фрагмент головоломки — выполнение и отладка этого кода. Хотя вы скомпилировали свою x64-версию на компьютере с процессором x64, для отладки в режиме x64 понадобятся средства удаленной отладки, предоставляемые Visual Studio. К счастью, если вы работаете с Visual Studio IDE на 64-разрядной машине, IDE сама позаботится обо всех необходимых операциях. Если по какой-то причине вы не можете использовать удаленную отладку, остается лишь один вариант — взять x64-версию WinDbg (ее можно скачать по ссылке microsoft.com/whdc/devtools/debugging/install64bit.mspx. Однако вы лишитесь многих удобств отладчика Visual Studio.

Если вы никогда не пользовались удаленной отладкой, сильно волноваться не стоит. Как только вы ее настроите, она почти ничем не будет отличаться от локальной отладки.

Первый шаг — установка на целевой компьютер 64-разрядной MSVSMON. Обычно это делается с помощью программы RdbgSetup, поставляемой с Visual Studio. После запуска MSVSMON зайдите в меню Tools для настройки соответствующих параметров защиты соединения между вашей 32-разрядной Visual Studio и экземпляром MSVSMON.

Далее из Visual Studio сконфигурируйте свой проект на применение удаленной отладки x64-кода. И для начала откройте окно свойств проекта (рис. 4).


Рис. 4. Выбор платформы сборки
Убедитесь, что текущей является ваша 64-разрядная конфигурация, и выберите Debugging в разделе Configuration Properties. Ближе к верхней части вы увидите раскрывающийся список Debugger to launch. Его изначальное значение — Local Windows Debugger. Смените его на Remote Windows Debugger. Под этим списком вы можете задать удаленную команду (например, имя программы), которая должна выполняться в момент начала отладки, а также указать имя удаленного компьютера и тип соединения.

Если вы все правильно настроили, можете начинать отладку своего x64-приложения точно так же, как и Win32-программы. О соединении с MSVSMON свидетельствует строка "connected", которая появляется в трассировочном окне этой программы при каждом успешном подключении отладчика. С этого момента большинство операций выполняется так же, как и в хорошо известном вам отладчике Visual Studio. Не забудьте открыть окно регистров, чтобы увидеть все 64-битные регистры, а также заглянуть в окно дизассемблированного кода, чтобы посмотреть на такой знакомый, но все же слегка другой ассемблерный x64-код.

Заметьте, что 64-разрядный минидамп в отличие от 32-разрядного нельзя напрямую загрузить в Visual Studio. Вместо этого нужно использовать Remote Debugging. Кроме того, в настоящий момент Visual Studio 2005 не поддерживает отладку interop-вызовов между управляемым и неуправляемым 64-разрядным кодом.

А как быть с управляемым кодом?

Одно из преимуществ кодирования с применением Microsoft .NET Framework заключается в том, что универсальный код абстрагируется от большей части нижележащей операционной системы. Более того, формат IL-инструкций независим от конкретной процессорной архитектуры. А значит, теоретически двоичный файл .NET-программы, скомпилированной в Win32-системе, можно запускать без модификации в x64-системе. Но на практике все не так гладко.

.NET Framework 2.0 выпускается и в x64-версии. После ее установки на свой компьютер с x64-системой я смог запускать те же исполняемые .NET-файлы, что и в предыдущей Win32-системе. Конечно, нет никакой гарантии, что любая .NET-программа будет одинаково хорошо работать и в Win32, и в x64 без перекомпиляции, но все же мои файлы выполнялись без ошибок.

Если ваш управляемый код явно вызывает неуправляемый (например, через P/Invoke на C# или Visual Basic), вас почти наверняка ждут проблемы при попытке его запуска в среде 64-разрядной CLR. Однако ключ /platform компилятора позволяет указать, на какой платформе должен выполняться ваш код. Так, вы могли бы разрешить выполнение вашего управляемого кода только в WOW64 несмотря на наличие 64-разрядной CLR.
Заключение
В общем, для меня переход на x64-версию Windows был сравнительно безболезненным. Как только вы усвоите основные различия (а их не так уж и много) в архитектурах операционных систем и инструментальных средств, вам будет достаточно легко поддерживать единую кодовую базу, способную работать на обеих платформах. Существенную помощь окажет Visual Studio 2005. К тому же чуть ли не каждый день на сайте http://technet.microsoft.com/sysinternals/default.aspx появляются x64-версии драйверов устройств и инструментов вроде Process Explorer.
to be continued
10
Миниатюры
Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows   Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows   Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  

Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
28.05.2014, 05:07
Ответы с готовыми решениями:

Что означает "Знание архитектуры х86", какие книги почитать, что нужно знать и уметь
Нашёл вакансию junior программиста С++, но нужно знание архитектруы х86. Что...

Хочу начать программировать на iOS, нужно ли знать Cи?
Здравствуйте! Хочу научиться программировать различные программы для Iphone...

Что необходимо для того чтобы начать программировать на Java
Добрый день, господа!!! Небольшая просьба, вашего покорного слуги. Напишите все...

Что надо знать в java, чтобы хорошо программировать под android?
Всем привет. Подскажите пожалуйста какие технологии в java мне необходимо...

C# для фриланса - Что нужно знать чтобы потянуть на Junior'a?
Интересуюсь обучению C# исключительно для зарабатывания денег. имею опыт работы...

20
Убежденный
Ушел с форума
Эксперт С++
15954 / 7266 / 1178
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
28.05.2014, 09:52 #2
Mikl___, спасибо, замечательная статья !
Вот на таких энтузиастах все и держится.

Если позволите, маленькая поправочка:
Цитата Сообщение от Mikl___ Посмотреть сообщение
То есть у 64-битного указателя размер 8 байтов, а у int, long, DWORD и HANDLE остался прежний размер - 4 байта.
Все типы хэндлов (HANDLE, HWND, SC_HANDLE и т.д.) на x64 имеют размер указателя, т.е. 8 байт.
1
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
29.05.2014, 05:02  [ТС] #3
Программные соглашения x64
Перевод статей MSDN
В данном разделе рассматривается методология соглашения о вызовах Visual C++ x64 для 64-битового расширения в архитектуре x86.

Следующий параметр компилятора позволяет оптимизировать приложение для x64:
  • /favor (оптимизация для особенностей архитектуры)

Общие сведения о соглашениях о вызовах для архитектуры x64

Двумя важнейшими различиями между архитектурами x86 и x64 является возможность 64-битной адресации и набор из 16 64-битных регистров общего назначения. Предоставляя расширенный набор регистров, x64 использует только соглашение о вызовах __fastcall и модель RISC-архитектуры обработки исключений. Модель соглашения о вызовах __fastcall использует регистры для первых четырех аргументов, а для передачи других параметров используется кадр стека.

Типы и хранилище
В этом разделе рассматривается перечисление и хранение типов данных архитектуры x64.
Скалярные типы
Несмотря на то, что обращение к данным возможно при любом выравнивании, в целях повышения производительности рекомендуется использовать выравнивание данных в исходном диапазоне. Перечисления представляют собой константы целого типа и обрабатываются как 32-разрядные целые числа. В следующей таблице приводится определение типа и рекомендуемый для него объем памяти в случае выравнивания с использованием следующих значений:
  • Byte — 8 бит
  • Word — 16 бит
  • Double Word — 32 бит
  • Quad Word — 64 бит
  • Octa Word — 128 бит
Скалярный типТип данных C
Объем памяти
(в байтах)
Рекомендуемое
выравнивание
INT8char1Byte
UINT8unsigned char1Byte
INT16short2Word
UINT16unsigned short2Word
INT32int, long4Doubleword
UINT32unsigned int,
unsigned long
4Doubleword
INT64__int648Quadword
UINT64unsigned __int648Quadword
FP32 (одинарной точности)float4Doubleword
FP64 (двойной точности)double8Quadword
pointer*8Quadword
__m64struct __m648Quadword
__m128struct __m12816Octaword
Статические выражения и объединения

К другим типам, таким как массивы, структуры и объединения, предъявляются более строгие требования к выравниванию, обеспечивающие согласованность хранения статистических выражений и объединений и извлечения данных. Далее приведены определения массива, структуры и объединения.
  • Массив
    Содержит упорядоченную группу смежных объектов данных. Каждый объект именуется элементом. Все элементы массива должны быть одного размера и принадлежать одному типу данных.
  • Структура
    Содержит упорядоченную группу объектов данных. В отличие от элементов массива, объекты данных внутри структуры могут принадлежать разным типам и иметь разный размер. Каждый объект данных в структуре называется членом.
  • Union
    Объект, содержащий любое из множества поименованных членов. Члены этого именованного набора могут быть любого типа. Область хранения, выделенная для объединения, равна области хранения, требующейся для члена этого объединения, имеющего наибольший размер, плюс заполнение, необходимое для выравнивания.
В таблице представлены требования по выравниванию для скалярных членов объединений и структур.
Скалярный типТип данных C
Обязательное
выравнивание
INT8charByte
UINT8unsigned charByte
INT16shortWord
UINT16unsigned shortWord
INT32int, longDoubleword
UINT32
unsigned int,
unsigned long
Doubleword
INT64__int64Quadword
UINT64unsigned __int64Quadword
FP32 (одинарной точности)floatDoubleword
FP64 (двойной точности)doubleQuadword
pointer*Quadword
__m64struct __m64Quadword
__m128struct __m128Octaword

Применяются следующие правила выравнивания статистических выражений:
  • Выравнивание массива аналогично выравниванию одного из элементов массива.
  • Выравнивание начальной части структуры или объединения является максимальным выравниванием любого отдельного члена. Каждый член внутри структуры или объединения должен быть размещен в соответствии со своим выравнивании, как определено в предыдущей таблице, для чего может потребоваться неявное внутреннее заполнение в зависимости от предыдущего члена.
  • Размер структуры должен быть целым числом, кратным его выравниванию, для чего может потребоваться заполнение после последнего члена. Поскольку структуры и объединения могут быть сгруппированы в массивы, каждый элемент массива в структуре или объединении должен начинаться и завершаться соответствующим предварительно определенным выравниванием.
  • Возможно выравнивание данных с превышением требований к выравниванию при условии соблюдения ранее установленных правил.
  • Отдельный компилятор может регулировать упаковку структуры из соображений ее размера. Например, /Zp (Выравнивание члена структуры) позволяет регулировать упаковку структур.
Примеры выравнивания структуры

В каждом из следующих примеров содержится объявление выровненной структуры или объединения. Порядок размещения таких структур или объединений в памяти показан на соответствующих рисунках. Каждый столбец на рисунке соответствует байту в памяти. Номер столбца определяет смещение указанного байта. Имя второго столбца на каждом рисунке соответствует имени переменной в объявлении. Затененные столбцы определяют заполнение, необходимое для указанного типа выравнивания.

Пример 1

Пример 2

Пример 3

Пример 4

Разряды

Структура битовых полей ограничивается 64 битами и может быть следующих типов: signed int, unsigned int, int64 или unsigned int64. Битовые поля, которые пересекают границу типов, пропустят биты, чтобы выровнять разряды до уровня следующего типа. Например, разряды целого числа (integer) не могут пересечь 32-разрядную границу.
Конфликты с компилятором x86

Типы данных, размер которых превышает 4 байта, не выравниваются в стеке автоматически при компиляции приложения с помощью компилятора x86. Поскольку архитектура компилятора x86 представляет собой выровненный 4-байтовый стек, что-либо большее, чем 4 байта, например, 64-разрядное целое число, не может автоматически выравниваться по 8-байтовому адресу.

Работа с данными без выравнивания имеет два ограничения.
  • Доступ к невыровненным расположениям может занимать слишком много времени по сравнению с доступом к выровненным расположениям.
  • Невыровненные расположения не могут использоваться в блокируемых операциях.
Если требуется более строгое выравнивание, используйте __declspec(align(N)) при объявлении ваших переменных. Это заставляет компилятор динамически выравнивать стек в соответствии с требованиями. Тем не менее динамическая настройка стека во время выполнения может привести к замедлению выполнения приложения.
Использование регистров

Архитектура x64 поддерживает 16 регистров общего назначения (в дальнейшем называемых целочисленными регистрами), а также 16 регистров XMM, используемых для операций с плавающей запятой. Временные регистры сбрасываются в процессе выполнения вызова. Постоянные регистры должны сохранять свои значения в процессе выполнения функции и должны сохраняться вызываемым объектом в случае использования.

В следующей таблице описываются способы использования каждого регистра в процессе выполнения вызова функции:
РегистрстатусПрименение
RAXВременныйРегистр возвращаемого значения
RCXВременныйПервый целочисленный аргумент
RDXВременныйВторой целочисленный аргумент
R8ВременныйТретий целочисленный аргумент
R9ВременныйЧетвертый целочисленный аргумент
R10:R11Временный
Должен сохраняться вызывающим объектом.
Используется в инструкциях syscall/sysret
R12:R15ПостоянныйДолжен сохраняться вызываемым объектом
RDIПостоянныйДолжен сохраняться вызываемым объектом
RSIПостоянныйДолжен сохраняться вызываемым объектом
RBXПостоянныйДолжен сохраняться вызываемым объектом
RBPПостоянный
Может использоваться как указатель кадра.
Должен сохраняться вызываемым объектом
RSPПостоянныйУказатель стека
XMM0ВременныйПервый аргумент с плавающей запятой
XMM1ВременныйВторой аргумент с плавающей запятой
XMM2ВременныйТретий аргумент с плавающей запятой
XMM3ВременныйЧетвертый аргумент с плавающей запятой
XMM4:XMM5ВременныйДолжен сохраняться вызывающим объектом
XMM6:XMM15ПостоянныйДолжен сохраняться вызываемым объектом
Соглашение о вызовах

Машинный интерфейс для приложений Application Binary Interface (ABI) x64 — это 4 регистра соглашения о вызовах с возможностью последующего возвращения этих регистров в стек. Существует точное однозначное соответствие между аргументами в функции и регистрами для этих аргументов. Любые аргументы, не равные 8 байтам или 1, 2, 4 или 8 байтам, передаются по ссылке. Попыток разместить один аргумент по нескольким регистрам не происходит. Регистровый стек x87 не используется. Его можно использовать, но он должен быть временным при вызове функций. Все операции с плавающей запятой выполняются с помощью 16 регистров XMM. Аргументы передаются в регистрах RCX, RDX, R8 и R9. Если аргументы являются типами float или double, они передаются в регистрах XMM0L, XMM1L, XMM2L и XMM3L. 16-байтовые аргументы передаются по ссылке. Передача параметров подробно описана в разделе Передача параметров. Кроме того, регистры RAX, R10, R11, XMM4 и XMM5 являются временными. Все остальные регистры не являются временными. Использование регистров подробно описано в разделах Использование регистров и Сохраняемые регистры вызываемого и вызывающего объектов.

Вызывающая функция отвечает за выделение пространства для параметров вызываемой функции и должна всегда выделять достаточное пространство для 4 параметров, даже если вызываемая функция не содержит такого количества параметров. Это помогает упростить поддержку функций без прототипов и функций с переменным количеством аргументов (vararg) C/C++. Для функций с переменным количеством аргументов или для функций без прототипов любое значение типа float должно быть продублировано в соответствующем регистре общего назначения. Любые параметры, следующие после первых 4, до вызова должны сохраняться в стеке над резервным хранилищем для первых четырех. Сведения о функции с переменным количеством аргументов представлены в разделе Функции с переменным количеством аргументов (Varargs). Сведения о функции без прототипов представлены в разделе Функции без прототипа.
Выравнивание

Большинство структур выровнены естественным выравниванием. Главными исключениями являются указатели стека и функции распределения памяти malloc или alloca, которые выровнены на 16 байт для сохранения производительности. Выравнивание свыше 16 байт должно выполняться вручную, но начиная с 16 байт выполняется общее выравнивание размера для операций XMM, которого должно хватать для большей части кода. Дополнительные сведения о структуре и выравнивании см. в разделе Типы и хранилище. Дополнительные сведения о стеке см. в разделе Использование стека.
Способность очищаться

Все конечные функции (функции, которые никогда не вызывают функцию, а также никогда не выделяют пространство стека) должны дополняться данными (относится к типам данных xdata или ehdata, на которые есть указатель из pdata), которые объясняют ОС, каким образом выполнять их очищение для сохранения неизменяемых регистров. Прологи и эпилоги строго ограничены, следовательно, они могут быть правильно описаны в xdata. Указатель стека должен быть выровнен на 16 байт, за исключением конечных функций, в любой области кода, которая не является частью эпилога или пролога. Дополнительные сведения о структуре функции пролога и эпилога см. в разделе Пролог и эпилог.


Соглашение о вызовах
Передача параметров

Первые четыре целочисленных аргумента передаются через регистры. Целочисленные значения передаются слева направо в регистры RCX, RDX, R8 и R9. Аргументы начиная с пятого и далее передаются через стек. Все 32-разрядные аргументы получают в регистрах знаковое расширение. Это делается для того, чтобы вызываемый мог игнорировать старшие разряды регистра при необходимости и получить доступ только к необходимой части регистра.

Вещественные 64-разрядные аргументы передаются в регистры XMM0 – XMM3 (до 4) с помощью гнезда целых чисел (RCX, RDX, R8 и R9), которое обычно и используется, в то время как гнездо кардинальных чисел отклоняется (см. пример) и наоборот.

Типы, массивы и строки __m128 никогда не передаются непосредственно, а указатель передается в выделенной памяти вызывающим объектом. Структуры размером в 8, 16, 32 или 64 бита и __m64, передаются так как если бы они были целыми числами того же размера. Структуры или объединения, отличный от этих размеров передаются как указатель на выделенную память вызывающим объектом. Для этих агрегатных типов, передаваемых в качестве указателя (включая __m128), память должна кратна 16.

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

Тот, кто вызывает функцию, несет ответственность за сбрасывание при необходимости параметров регистра в теневое пространство.

В следующей таблице подведены итоги передачи параметров:
Тип параметраКак передается
Вещественные параметры
Первые 4 через регистры от XMM0 к XMM3. Остальные передаются
через стек.
Целочисленные параметры
Первые 4 через регистры RCX, RDX, R8, R9. Остальные передаются
через стек.
Агрегатные параметры (8, 16, 32 или 64-
разрядные) и __m64
Первые 4 через регистры RCX, RDX, R8, R9. Остальные передаются
через стек.
Агрегатные параметры (другие)
__m128
Указатель. Первые 4 параметра, передаются в качестве
указателей через RCX, RDX, R8 и R9
Примеры:
  1. все аргументы — целые числа
    Код
     func1(int a, int b, int c, int d, int e);  
    // a в RCX, b в RDX, c в R8, d в R9, e передается через стек
  2. все аргументы - числа с плавающей запятой
    Код
     func2(float a, double b, float c, double d, float e);  
    // a в XMM0, b в XMM1, c в XMM2, d в XMM3, e передается через стек
  3. аргументы — вперемешку целые и вещественные числа
    Код
     func3(int a, double b, int c, float d);  
    // a в RCX, b в XMM1, c в R8, d в XMM3
  4. аргументаы — __m64, __m128 и агрегатные параметры
    Код
     func4(__m64 a, _m128 b, struct c, float d);
    // a в RCX, указатель на b в RDX, указатель на c в R8, d в XMM3
Функции с переменным количеством аргументов (Varargs)

Если параметры передаются с помощью функции varargs (например, аргументы, задаваемые многоточием), то фактически применяется обычная передача параметра, включая вытеснение пятого и последующих аргументов. Кроме того, вызываемый отвечает за дамп аргументов, которые получают свой адрес. Только для значений с плавающей запятой: целочисленный регистр и регистр с плавающей запятой содержат значение типа float в случае, если вызываемый ожидает значение в целочисленных регистрах.
Функции без прототипа

Для функций без прототипа вызывающий объект передает целые числа в виде значений типа Integer, а значения с плавающей запятой — в виде чисел двойной точности. (Только для значений с плавающей запятой) Если вызываемый объект предполагает наличие значения в регистре операций с целыми числами, в регистрах операций с целыми числами и числами с плавающей запятой одновременно будут содержаться значения с плавающей запятой.
Код
func1();
func2() {   // RCX = 2, RDX = XMM1 = 1.0, R8 = 7
   func1(2, 1.0, 7);
}
Возвращаемые значения

Возвращаемое значение, которое может быть размещен в 64—разрядном регистре RAX это включает типы __m64, но __m128, __m128i, __m128d, расположенном и типы double возвращаются в XMM0. Если возвращаемое значение пользовательского типа, который нельзя разместить в 64—разрядах и вызывающий объект принимает за выделение и передача указателя для возвращаемого значения в качестве первого аргумента. Последующие аргументы перемещают на один аргумент вправо. Тот же самый указатель возвращается вызываемой стороной в RAX. Пользовательские типы, которые должны возвращать непосредственно от 1, 2, 4, 8, 16, 32 и 64 — в длину.

Примеры:
  1. возвращаемое значение 64—разрядный результат
    Код
    __int64 func1 (int a, float b, int c, int d, int e);
    объект вызывающий функцию передает a в RCX, b в XMM1, c в R8, d в R9, e отправляет в стек,
    вызываемая функция возвращает результат __int64 через RAX.
  2. возвращаемое значение — 128-битый результата
    Код
    __m128 func2 (float a, double b, int c, __m64 d); 
    объект вызывающий функцию передает a в XMM0, b в XMM1, c в R8, d в R9, 
    вызываемая функция возвращает результат __m128 в XMM0.
  3. возвращаемое значение — результат пользовательского типа
    Код
    struct1 func3 (int a, double b, int c, float d); 
    объект вызывающий функцию выделяет память для возвращаемой структуры struct1 и передает указатель в RCX, 
    //a в RDX, b в XMM2, c в R9, d отправлен в стек, 
    функция возвращает указатель на struct1 через RAX.
Сохраняемые регистры вызываемого и вызывающего объектов

Регистры RAX, RCX, RDX, R8, R9, R10 и R11 считаются временными и должны уничтожаться при вызове функции (если иное не требуется, исходя из соображений безопасности, например в процессе оптимизации программы).

Регистры RBX, RBP, RDI, RSI, RSP, R12, R13, R14 и R15 считаются защищенными. Значения этих регистров должны сохраняться и восстанавливаться в использующей их функции.
Указатели функций

Указатели функций указывают на метку соответствующей функции. Требования к оглавлению для указателей функций не предусмотрены.
Использование стека

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

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

Область параметров всегда располагается в нижней части стека (даже если используется функция alloca) и всегда примыкает к адресу, возвращаемому при вызове функции. В области параметров содержится как минимум четыре записи и всегда выделяется объем памяти, достаточный для хранения всех параметров любой вызываемой функции. Обратите внимание, что для параметров регистра память выделяется всегда, даже если они никогда не размещаются в стеке. Также гарантированно выделяется память для всех параметров вызываемого объекта. Чтобы обеспечить выделение непрерывной области памяти при вызове функции, принимающей адрес списка аргументов (va_list) или отдельного аргумента, необходимо предоставить внутренние адреса аргументов регистра. В этой области также хранятся аргументы регистра во время выполнения преобразователя. Кроме того, в ней удобно хранить аргументы регистра, используемые в качестве параметров отладки (например поиск аргументов в процессе отладки является более эффективным, если они хранятся по внутреннему адресу в коде пролога). Даже если вызываемая функция принимает менее четырех параметров, для нее выделяется четыре ячейки стека, которые могут использоваться не только для хранения значений регистра параметров, но и для других целей. Таким образом, во время вызова функции вызывающий объект не может сохранять данные в этой области стека.

Если память для функции выделяется динамически (с помощью функции alloca), необходимо использовать защищенный регистр в качестве указателя кадра, с помощью которого помечается основание фиксированной части стека, а также указывается необходимость сохранения и инициализации регистра в прологе. Обратите внимание, что при использовании функции alloca при вызове одного и того же объекта из одного вызывающего объекта для параметров регистра могут использоваться различные внутренние адреса.

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

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


При вызове другой функции из функции B возвращаемый адрес помещается непосредственно под внутренним адресом регистра RCX.

Динамическое создание параметра области стека

Если используется указатель кадров, то параметр используется для динамического создания параметра области стека. В настоящее время в компиляторе x64 это не выполняется.
Типы функций

Существует два основных типа функций. Функция, которой требуется кадр стека, называется функцией с кадром. Функция, которая не требует кадр стека, называется конечной функцией.

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

Если функция с кадром не вызывает другую функцию, то выравнивать стеки не нужно (см. раздел Выделение памяти в стеке).

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

malloc гарантированно возвращает память, которая подходит для хранения любого объекта с базовым выравниванием, который мог бы поместиться в выделенной памяти. Базовое выравнивание — это выравнивание, которое меньше или равно наибольшему выравниванию, которое поддерживается реализацией без задания выравнивания. (В Visual C++ это основное выравнивание, необходимое для double или 8 байт. В коде, который нацелен на 64-разрядные платформы, это 16 байт.) Например, выделение 4 байт будет выровнено по границе, которая поддерживает все любой четырехбайтовый или меньший объект.

Visual C++ допускает типы, имеющие расширенное выравнивание, также известные как сверх-выровненные типы. Например, типы SSE __m128 и __m256, а также типы, объявленные с помощью __declspec(align(n)), где n больше 8, имеют расширенное выравнивание. Выравнивание памяти на границе, которая подходит для объекта, который требует расширенного выравнивания, не гарантированного malloc. Чтобы выделить память для избыточно выровненных типов, используйте _aligned_malloc и соответствующие функции.
alloca

Функции _alloca требуется выравнивание по 16-байтовой границе и использование указателя кадра стека.

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

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

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

При сохранении защищенных регистров рекомендуется перемещать их в стек до выделения фиксированной части стека. Если выделение фиксированной части стека выполняется до сохранения защищенных регистров, для обращения к области сохраненного регистра в большинстве случаев требуется 32-разрядное смещение. Производительность функций помещения и перемещения регистров примерно одинакова и будет оставаться такой в ближайшем будущем, независимо от предполагаемой зависимости между функциями помещения. Защищенные регистры могут сохраняться в любом порядке. Однако в качестве первой операции с защищенным регистром в прологе необходимо выполнять сохранение регистра.

типичный код пролога
Assembler
1
2
3
4
5
6
7
mov       [RSP + 8], RCX
push   R15
push   R14
push   R13
sub      RSP, fixed-allocation-size
lea      R13, [RSP+128]
...

В этом прологе аргумент регистра RCX сохраняется по внутреннему адресу, сохраняются защищенные регистры R13-R15, выделяется кадр фиксированной части кадра стека, а также создается указатель кадра, который указывает на выделенную фиксированную область размером 128 байт. Благодаря использованию смещения обеспечивается обращение к большему числу адресов выделенной фиксированной области с помощью однобайтовых смещений.

Если размер фиксированной области памяти превышает размер одной страницы памяти или равен ему, перед изменением RSP следует вызвать вспомогательную функцию. Вызываемая функция __chkstk обеспечивает проверку подлежащей выделению области стека на предмет допустимости расширения стека. В этом случае приведенный выше пример пролога будет выглядеть следующим образом:
Assembler
1
2
3
4
5
6
7
8
9
mov       [RSP + 8], RCX
push   R15
push   R14
push   R13
mov      RAX,  fixed-allocation-size
call   __chkstk
sub      RSP, RAX
lea      R13, [RSP+128]
...
Вспомогательная функция __chkstk изменяет только регистры R10 и R11. Другие регистры и коды условий не изменяются. В частности, при ее выполнении регистр RAX возвращается без изменений. Все защищенные регистры и регистры передачи аргументов также не изменяются.

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

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

Если в функции не используется указатель кадра, в эпилоге сначала отменяется выделение фиксированной части стека, затем извлекаются сохраненные значения защищенных регистров, после чего управление возвращается вызывающей функции. Например:
Assembler
1
2
3
4
5
add      RSP,fixed-allocation-size
pop      R13
pop      R14
pop      R15
ret
Если в функции используется указатель кадра, перед выполнением эпилога необходимо выполнить усечение стека до размера фиксированной выделяемой области. С технической точки зрения эта операция не входит в состав эпилога. Ниже приведен пример эпилога, который может использоваться для отмены ранее выполненного пролога:
Assembler
1
2
3
4
5
6
7
lea      RSP,[R13-128]
; epilogue proper starts here
add      RSP, fixed-allocation-size
pop      R13
pop      R14
pop      R15
ret
На практике, если используется указатель кадра, не имеет смысла выполнять изменение регистра RSP в два этапа, поэтому вместо приведенного выше можно использовать следующий эпилог:
Assembler
1
2
3
4
5
lea      RSP,[R13128+fixed-allocation-size]
pop      R13
pop      R14
pop      R15
ret
Выше приведены единственно допустимые формы эпилога. Эпилог должен включать выражение add RSP,constant или lea RSP,[FPReg+constant], за которым следуют последовательность из нескольких (или ни одной) команд извлечения 8-байтовых регистров (pop), а также команды return или jmp. В эпилоге допускается использование не всех операторов jmp. К допустимым относятся только операторы jmp со ссылками на память ModRM, в которых значение поля mod ModRM равно 00. Использование в эпилоге операторов jmp, для которых значение поля mod ModRM равно 01 или 10, не допускается. Дополнительные сведения о допустимых ссылках ModRM см. в таблице A-15 в разделе, посвященном инструкциям общего и системного назначения, руководства программиста архитектуры процессора AMD x86-64 (том 3). Использование другого кода в эпилоге не допускается. В частности, в эпилоге не допускается планирование каких-либо задач, в том числе загрузки возвращаемого значения.

Обратите внимание, что если указатель кадра не используется, в эпилоге необходимо использовать выражение add RSP,constant для отмены выделения фиксированной части стека. Использование вместо него выражения lea RSP,[RSP+constant] не допускается. Это ограничение позволяет уменьшить число шаблонов, распознаваемых при поиске эпилогов.

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

В данном разделе рассматриваются структурная обработка исключений и поведение приложений C++ на платформах x64 при обработке исключений.
Данные раскрутки для обработки исключений и поддержки отладчика

В данном подразделе описываются структуры данных, необходимые для обработки исключений, а также поддержки отладчика.
структура RUNTIME_FUNCTION
Для табличной обработки исключений требуется запись в таблице для каждой функции, выделяющей место в стеке или вызывающей другую функцию (например, неконечные функции). Записи в таблице функций имеют следующий формат:
ULONGНачальный адрес функции
ULONGКонечный адрес функции
ULONGАдрес очистки
Структура RUNTIME_FUNCTION должна быть выровнена в памяти по типу DWORD. Все адреса задаются относительно образа, то есть, они представляют собой 32-разрядные смещения относительно стартового адреса образа, содержащего запись в таблице функций. Эти записи сортируются и помещаются в раздел .pdata образа PE32+. Для динамически создаваемых функций [JIT-компиляторов] среда выполнения для поддержки этих функций должна использовать RtlInstallFunctionTableCallback или RtlAddFunctionTable, чтобы предоставлять эти сведения операционной системе. Невыполнение этого требования приведет к ненадежной обработке исключений и отладке процессов.
структура UNWIND_INFO

Информационная структура очищения данных используется для записи эффектов функции на указатель стека и места в стеке, где сохраняются неизменяемые регистры:
UBYTE: 3Версия
UBYTE: 5Флаги
UBYTEРазмер пролога
UBYTEСчетчик кодов очистки
UBYTE: 4Регистр кадра
UBYTE: 4Смещение регистра кадра (масштабированное)
USHORT * nМассив кодов очистки
переменнаяМожет находиться в форме (1) или (2) ниже
(1) Обработчик исключений
ULONGАдрес обработчика исключений
переменнаяДанные языкового обработчика (необязательно)
(2) цепочка Unwind Info
ULONGНачальный адрес функции
ULONGКонечный адрес функции
ULONGАдрес очистки
Структура UNWIND_INFO должна быть выровнена в памяти по DWORD. Каждое поле имеет следующее значение:
  • Версия
    Номер версии данных возврата, текущая версия — 1.
  • Флаги
    В настоящее время определены три флага:
    • UNW_FLAG_EHANDLER функция содержит обработчик исключений, который должен вызываться и функции, которые необходимо проанализировать исключения.
    • UNW_FLAG_UHANDLER функция содержит обработчик завершения, который должен вызываться развертывание исключение.
    • UNW_FLAG_CHAININFO это разматывает структуру нет основные сведения и процедуры. Вместо этого запись информации очистки является содержимым предыдущей записи RUNTIME_FUNCTION. Просмотрите дальнейший текст, поясняющий структуры зависимой информации очистки. Если этот флаг установлен, тогда флаги UNW_FLAG_EHANDLER и UNW_FLAG_UHANDLER должны быть сняты. Кроме того, регистр кадра и фиксированные поля выделения стека должны иметь значения, совпадающие с значениями основной информации очистки.
  • Размер пролога
    Длина пролога функции в байтах.
  • Счетчик кодов очистки
    Количество гнезд в массиве кодов очистки. Обратите внимание на то, что некоторые коды очистки (например, UWOP_SAVE_NONVOL) требуют больше одного гнезда в массиве.
  • Регистр кадра
    Если значение отличается от нулевого, то функция использует указатель кадра, а это поле является номером постоянного регистра, используемого в качестве указателя кадра с той же кодировкой, что и для поля информации об операции узлов UNWIND_CODE.
  • Смещение регистра кадра (масштабированное)
    Если поле регистра кадра отлично от нуля, то это поле содержит масштабированное смещение от RSP, примененного к FP reg после его установки. Действительное значение FP reg задается как RSP + 16 * это число, что делает возможными смещения от 0 до 240. Это разрешает направление FP reg в середину локального выделения стека для динамических кадров стека, обеспечивая лучшую плотность кода за счет использования более коротких инструкций (больше инструкций могут использовать для смещения 8-разрядное знаковое число).
  • Массив кодов очистки
    Это массив элементов, объясняющий степень воздействия пролога на постоянные регистры и RSP. Просмотрите подраздел об использовании UNWIND_CODE для значений индивидуальных элементов. Чтобы выполнить выравнивание, этот массив всегда будет иметь четное количество записей с потенциально неиспользуемой последней записью (в таком случае массив будет на одну запись длиннее, чем указано счетчиком полей кодов очистки).
  • Адрес обработчика исключений
    Это относительный указатель на обработчик языковых исключений или обработчик завершений функции (если флаг UNW_FLAG_CHAININFO снят, а один из флагов UNW_FLAG_EHANDLER или UNW_FLAG_UHANDLER установлен).
  • Данные обработчика определенного языка
    Это данные обработчика исключений выбранного языка функции. Формат этих данных не указан и полностью определяется конкретным используемым обработчиком исключений.
  • Зависимая информация очистки
    Если флаг UNW_FLAG_CHAININFO установлен, тогда структура UNWIND_INFO завершается тремя UWORD. Эти UWORD представляют связанную информацию RUNTIME_FUNCTION для обращаемой функции.
структура UNWIND_CODE

Массив кода раскрутки используется для записи последовательности операций в прологе, оказывающих влияние на энергонезависимые регистры и RSP. Каждый элемент кода имеет следующий формат:
UBYTEСмещение в прологе
UBYTE: 4Код операции очистки
UBYTE: 4Сведения об операции
Элементы массива располагаются в убывающем порядке в соответствии с положением в прологе.
  • Смещение в прологе
    Смещение от начала пролога конца инструкции, которая выполняет данную операцию, плюс 1 (это расположение следующей инструкции).
  • Код завершающей операции
    Примечание. В кодах некоторых операций необходимо использовать смещение без учета знака в качестве значения в локальном кадре стека. Это смещение отсчитывается от начала (нижнего адреса) выделенного фиксированного пространства стека. Если поле регистра кадра стека в структуре UNWIND_INFO является пустым, то смещение отсчитывается от RSP. Если поле регистра кадра стека не является пустым, смещение отсчитывается от расположения RSP в момент установки регистра FP. Оно рассчитывается как регистр FP минус смещение регистра FP (16 * масштабированное смещение регистра кадра стека в UNWIND_INFO). При использовании регистра FP любой код раскрутки со смещением должен использоваться только после установки в прологе регистра FP.

    Для всех кодов операций кроме UWOP_SAVE_XMM128 и UWOP_SAVE_XMM128_FAR смещение будет кратным 8, поскольку хранимые в стеке значения выравниваются до 8 байт (сам стек всегда выравнивается до 16 байт). Для кодов операций с коротким смещением (менее 512 Кбайт) завершающий USHORT в узлах кода содержит смещение, разделенное на 8. Для кодов операций с длинным смещением (от 512 Кбайт до 4 Гбайт) два завершающих узла USHORT кода содержат смещение (с прямым порядком следования байтов).

    Для кодов операций UWOP_SAVE_XMM128 и UWOP_SAVE_XMM128_FAR смещение будет кратным 16, поскольку все 128-битные операции XMM должны выполняться в памяти, выровненной до 16 байт. Поэтому для операции UWOP_SAVE_XMM128 используется масштабный коэффициент 16, который позволяет использовать смещение до 1 МБ.

    Кодом завершающей операции может быть:

    узел UWOP_PUSH_NONVOL (0)1

    Отправка энергонезависимого регистра целых чисел с уменьшением значения RSP в 8 раз. Значение в поле сведений об операции является числом регистра. Обратите внимание, что из-за ограничений, накладываемых на эпилог, коды раскрутки UWOP_PUSH_NONVOL должны использоваться первыми в прологе и, соответственно, последними в массиве кодов раскрутки. Этот относительный порядок применяется ко всем другим кодам раскрутки операций, за исключением UWOP_PUSH_MACHFRAME.

    Узлы UWOP_ALLOC_LARGE (1)2 или 3

    Выделение для стека большого объема памяти. Существуют два варианта. Если в поле сведений об операции содержится значение ноль, в соседнюю ячейку записывается размер выделенной памяти, поделенный на 8. Если в поле сведений об операции содержится значение 1, то в следующие две ячейки записывается размер выделенной памяти без масштабирования с прямым порядком следования байтов. Это позволяет выделять до 4 ГБ - 8.

    узел UWOP_ALLOC_SMALL (2)1

    Выделение для стека небольшого объема памяти. Размер выделения рассчитывается как число в поле сведений об операции * 8 + 8, что позволяет выделять от 8 до 128 байт.

    Код раскрутки должен всегда использовать наиболее короткую кодировку для выделения памяти в стеке.
    Размер выделенияКод раскрутки
    От 8 до 128 байтUWOP_ALLOC_SMALL
    От 136 до 512 КБ - 8 байтUWOP_ALLOC_LARGE, operation info = 0
    От 512 КБ до 4 ГБ – 8 байтUWOP_ALLOC_LARGE, operation info = 1
    узел UWOP_SET_FPREG (3)1

    Установите регистр указателя кадра стека, задав для регистра какое-либо смещение текущего RSP. Это смещение рассчитывается как значение (масштабированное) смещения в поле регистра указателя кадра стека UNWIND_INFO * 16, что разрешает смещение со значением от 0 до 240. Использование смещения позволяет установить указатель кадра стека по центру фиксированного выделения стека, что способствует повышению плотности кода и разрешает использовать большее количество форм коротких инструкций. Обратите внимание, что поле сведений об операции является зарезервированным и не должно использоваться.

    UWOP_SAVE_NONVOL (4)2 узла

    Сохраните энергонезависимый регистр целых чисел в стеке, используя функцию MOV вместо PUSH. Как правило, она применяется для создания изолированного кода, при котором энергонезависимый регистр сохраняется в стеке в том положении, которое было ранее выделено. Значение в поле сведений об операции является числом регистра. Смещение стека (масштабированное по 8) записывается в следующей ячейке кода завершающей операции, как описано выше в примечании.

    Узлы UWOP_SAVE_NONVOL_FAR (5)3

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

    Узлы UWOP_SAVE_XMM128 (8)2

    Сохраните все 128 байт энергонезависимого регистра XMM в стеке. Значение в поле сведений об операции является числом регистра. Смещение стека (масштабированное на 16) записывается в следующую ячейку.

    Узлы UWOP_SAVE_XMM128_FAR (9)3

    Сохраните все 128 байт энергонезависимого регистра XMM в стеке с длинным смещением. Значение в поле сведений об операции является числом регистра. Смещение стека (немасштабированное) записывается в следующие две ячейки.

    узел UWOP_PUSH_MACHFRAME (10)1

    Передача машинного кадра Используется для записи воздействия аппаратного вмешательства или исключения. Существуют два варианта. Если в поле сведений об операции значение ноль, значит в стек были переданы следующие данные:
    RSP+32SS
    RSP+24Old RSP
    RSP+16EFLAGS
    RSP+8CS
    RSPRIP
    Если в поле сведений об операции значение "1", значит в стек были переданы следующие данные:
    RSP+40SS
    RSP+32Old RSP
    RSP+24EFLAGS
    RSP+16CS
    RSP+8RIP
    RSPКод ошибки
    Этот код раскрутки должен всегда присутствовать в фиктивном прологе, который никогда не выполняется, но присутствует перед реальной точкой входа подпрограммы прерывания и существует, только чтобы предоставить место для имитации передачи машинного кадра. UWOP_PUSH_MACHFRAME записывает эту имитацию, что означает, что компьютер концептуально выполнил следующее:
    1. Перемещение обратного адреса RIP с вершины стека в Temp
    2. Передача SS
    3. Передача old RSP
    4. Передача EFLAGS
    5. Передача CS
    6. Передача Temp
    7. Передача кода ошибки (если в поле сведений об операции значение "1")
    Смоделированная операция UWOP_PUSH_MACHFRAME уменьшает RSP на 40 (если в поле сведений об операции значение "0") или 48 (если в поле сведений об операции значение "1")
  • Сведения об операции
    Значение этих 4 байт зависит от кода операции. Чтобы выполнить кодирование регистра (целых чисел) общего назначения, используется следующее сопоставление.
    0RAX
    1RCX
    2RDX
    3RBX
    4RSP
    5RBP
    6RSI
    7RDI
    От 8 до 15Запросы из R8 в R15
структуры связанных данных раскрутки

Если установлен флаг UNW_FLAG_CHAININFO, то структура информации очистки является вторичной и общее поле обработчика исключений/связанных данных содержит первичную информацию раскрутки. Следующий код извлекает основные сведения очистки, при условии что unwindInfo — структура, имеющая установленный флаг UNW_FLAG_CHAININFO.
C
1
PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo-]UnwindCode[( unwindInfo-]CountOfCodes + 1 ) & ~1]);
Связанные данные используются в двух случаях. Во-первых, они используется в несмежных сегментах кода. Используя связанные сведения, можно уменьшить размер требуемой информации раскрутки, поскольку нет необходимости дублировать массив кодов раскрутки из основной информации раскрутки.

Связанные сведения можно также использовать для группировки сохраненных данных энергозависимых регистров. Компилятор может отложить сохранение некоторых энергозависимых регистров до выхода из пролога записи функции. Они могут быть записаны перед группированным кодом посредством использования основных данных раскрутки для части функции и последующей установки связанных данных с ненулевым размером пролога. При этом коды раскрутки в связанных данных будут отражать сохраненные данных энергозависимых регистров. В этом случае все коды раскрутки являются экземплярами UWOP_SAVE_NONVOL. Команда, которая сохраняет слаболетучие регистры с помощью ПРИНУДИТЕЛЬНО ОТПРАВИТЬ или изменить регистр RSP с помощью дополнительного фиксированного выделение стека не поддерживается.

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

Массив кода очистки сортируется в убывающем порядке. При возникновении исключения полный контекст сохраняется операционной системой в записи контекста. После этого вызывается логика обработки исключений, несколько раз выполняющая следующие операции по поиску обработчика исключения.
  1. Для поиска записи в таблице RUNTIME_FUNCTION, описывающей текущую функцию (или часть функции, в случае связанных записей UNWIND_INFO) следует использовать текущую версию защиты остаточных данных (RIP), сохраняемых в записи контекста.
  2. Если записи в таблице функций не найдено, то она находится в конечной функции, а RSP обращается напрямую к указателю возврата. Указатель возврата в [RSP] сохраняется в обновленном контексте, смоделированный RSP получает приращение на 8, и шаг 1 повторяется.
  3. Если запись в таблице функций найдена, то RIP может лежать в трех областях — a) в заключительной части, b) в прологе или c) в коде, доступном обработчику исключений.
    • Случай a) Если RIP находится в заключительной части, то элемент управления выходит из функции, отсутствует обработчик исключения для этой функции, а результаты заключительной части должны обрабатываться и далее до вычисления контекста вызывающей функции. Чтобы определить, лежит ли RIP в заключительной части, необходимо исследовать поток кода из включенного RIP. Если поток кода может соответствовать конечной части допустимого эпилога, то это будет эпилог, а остальная часть эпилога будет смоделирована, при этом запись контекста будет обновляться при обработке каждой инструкции. После этого повторяется выполнение шага 1.
    • Случай b) Если RIP находится в прологе, то элемент управления не вошел в функцию, отсутствует обработчик исключения для этой функции, а результаты пролога должны быть отменены для вычисления контекста вызывающей функции. RIP лежит в прологе, если расстояние от начала функции до RIP меньше либо равно размеру пролога, закодированному в информации об очистке. Результаты в прологе очищаются при просмотре вперед по массиву кода очистки для первой записи со смещением, меньшим либо равным смещению RIP от начала функции, после чего выполняется отмена результата для всех остальных элементов в массиве кода очистки. После этого повторяется выполнение шага 1.
    • Случай c) Если RIP не лежит в прологе или в заключительной части, и для функции имеется обработчик исключений (установлен флаг UNW_FLAG_EHANDLER), то вызывается обработчик конкретного языка. Обработчик просматривает данные и вызывает соответствующие функции фильтра. Обработчик, специфичный для конкретного языка, может возвращать результат, указывающий на то, что исключение было обработано, либо на то, что поиск следует продолжить. Он может также инициировать очистку напрямую.
  4. Если обработчик, специфичный для конкретного языка, возвращает состояние "обработано", то выполнение продолжается с использованием исходной записи контекста.
  5. Если обработчик, специфичный для конкретного языка, отсутствует, либо если возвращает результат "продолжить поиск", запись контекста должна быть очищена до состояния вызывающего объекта. Эта выполняется путем обработки всех элементов массива кода очистки с отменой результата для каждого элемента. После этого повторяется выполнение шага 1.
Если используются связанные данные очистки, то выполнение этих основных операций продолжается. Единственное отличие заключается в том, что при прохождении массива кода очистки с целью отмены результатов для пролога, как только достигнут конец массива, он присоединяется к главной информации по очистке, и выполняется прохождение по всему обнаруженному массиву очистки. Это присоединение продолжается до состояния очистки без флага UNW_CHAINED_INFO и завершения прохождения по массиву кода очистки.

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

Относительный адрес обработчика языка присутствует в UNWIND_INFO, когда бы ни были установлены флаги UNW_FLAG_EHANDLER или UNW_FLAG_UHANDLER. Как описано в предыдущем разделе, обработчик языка вызывается как часть поиска обработчика исключения или часть раскрутки. Он имеет следующий прототип:
C
1
2
3
4
5
6
typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN ULONG64 EstablisherFrame,
    IN OUT PCONTEXT ContextRecord,
    IN OUT PDISPATCHER_CONTEXT DispatcherContext
);
ExceptionRecord предоставляет указатель на запись исключения, имеющий стандартное определение Win64.

EstablisherFrame представляет собой адрес базы фиксированного расположения стека для данной функции.

ContextRecord указывает на контекст исключения во время его возникновения (в случае если задействован обработчик событий) или текущий контекст "раскрутки" (в случае если задействован обработчик завершения).

DispatcherContext указывает на контекст диспетчера для данной функции. Он имеет следующее определение:
C
1
2
3
4
5
6
7
8
9
10
typedef struct _DISPATCHER_CONTEXT {
    ULONG64 ControlPc;
    ULONG64 ImageBase;
    PRUNTIME_FUNCTION FunctionEntry;
    ULONG64 EstablisherFrame;
    ULONG64 TargetIp;
    PCONTEXT ContextRecord;
    PEXCEPTION_ROUTINE LanguageHandler;
    PVOID HandlerData;
} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;
ControlPc представляет собой значение RIP в рамках данной функции. Это может быть адрес исключения или адрес, на котором элемент прекратил функцию установления. Этот RIP будет использоваться для определения, находится ли элемент управления в рамках защищенной конструкции в данной функции (например, блок __try для __try/__except или __try/__finally).

ImageBase представляет собой основу образа (адрес загрузки) модуля, содержащего данную функцию, которую необходимо добавить в 32-битные смещения, используемые в записи функции, а также в информации раскрутки для записи относительных адресов.

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

EstablisherFrame представляет собой адрес базы фиксированного расположения стека для данной функции.

TargetIp предоставляет адреса выборочных инструкций, указывающие дополнительные адреса раскрутки. Этот адрес пропускается, если не было указано EstablisherFrame.

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

LanguageHandler указывает на подпрограмму вызванного языкового обработчика.

HandlerData указывает на данные языкового обработчика для данной функции.
Завершение вспомогательных процедур для MASM
Для написания правильных подпрограмм ассемблера применяется набор псевдоопераций, которые могут использоваться параллельно с фактическими инструкциями ассемблера для создания соответствующих файлов PDATA и XDATA. Также предусмотрен набор макросов, позволяющих упростить использование псевдоопераций в наиболее распространенных случаях.
Необработанные псевдооперации
В этом разделе перечислены псевдооперации.
ПсевдооперацияОписание
PROC FRAME [:ehandler]
Приводит к тому, что компилятор MASM создает запись в таблице функции в PDATA и раскручивает информацию в XDATA для структурной обработки исключений функции при раскрутке. При наличии обработчика ошибок данная процедура вводится в XDATA как языковой обработчик.
При использовании атрибута FRAME за ним обязательно должна следовать директива .ENDPROLOG. Если функция является конечной (в соответствии с разделом Типы функций), атрибут FRAME является необязательным, так же как и остатки этих псевдоопераций.
.PUSHREG reg
Создает в коде завершения UWOP_PUSH_NONVOL запись для указанного номера регистра с помощью текущего смещения в прологе.
Следует применять эту операцию только к защищенным целочисленным регистрам. Для передачи временных регистров следует использовать ALLOCSTACK 8.
.SETFRAME reg, offsetЗаполняет поле регистра для фреймов и указывает смещение в информации для раскрутки с помощью указанного регистра и смещения. Смещение должно быть кратным 16 и меньшим или равным 240. Данная директива также создает в коде завершения UWOP_SET_FPREG запись для указанного регистра с помощью текущего смещения в прологе.
.ALLOCSTACK size
Создает код UWOP_ALLOC_SMALL или UWOP_ALLOC_LARGE с указанным размером текущего смещения в прологе.
Операнд size должен быть кратным 8.
.SAVEREG reg, offset
Создает запись в коде завершения UWOP_SAVE_NONVOL или UWOP_SAVE_NONVOL_FAR для указанного регистра и смещения, используя текущее смещение в прологе. Компилятор MASM выберет наиболее подходящий способ кодировки.
Смещение должно иметь положительное значение и быть кратным 8. Смещение указывается относительно кадра процедуры (как правило, в RSP) или указателя на кадр (немасштабированный).
.SAVEXMM128 reg, offset
Создает запись в коде завершения UWOP_SAVE_XMM128 или UWOP_SAVE_XMM128_FAR для указанного регистра XMM и смещения, используя текущее смещение в прологе. Компилятор MASM выберет наиболее подходящий способ кодировки.
Смещение должно иметь положительное значение и быть кратным 16. Смещение указывается относительно кадра процедуры (как правило, в RSP) или указателя на кадр (немасштабированный).
.PUSHFRAME [код]Создает запись в коде завершения UWOP_PUSH_MACHFRAME. Если указан дополнительный код, к записи в код завершения добавляется модификатор 1. В противном случае используется модификатор 0.
.ENDPROLOGСообщает об окончании объявлений в прологе. Находится в первых 255 байтах функции.
Ниже представлен пример пролога функции, демонстрирующий допустимое использование большинства кодов операций.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
sample PROC FRAME   
   db      048h; emit a REX prefix, to enable hot-patching
push rbp
.pushreg rbp
sub rsp, 040h
.allocstack 040h   
lea rbp, [rsp+020h]
.setframe rbp, 020h
movdqa [rbp], xmm7
.savexmm128 xmm7, 020h;the offset is from the base of the frame
;not the scaled offset of the frame
mov [rbp+018h], rsi
.savereg rsi, 038h
mov [rsp+010h], rdi
.savereg rdi, 010h; you can still use RSP as the base of the frame
; or any other register you choose
.endprolog
 
; you can modify the stack pointer outside of the prologue (similar to alloca)
; because we have a frame pointer.
; if we didn’t have a frame pointer, this would be illegal
; if we didn’t make this modification,
; there would be no need for a frame pointer
 
sub rsp, 060h
 
; we can unwind from the following AV because of the frame pointer
 
mov rax, 0
mov rax, [rax] ; AV!
 
; restore the registers that weren’t saved with a push
; this isn’t part of the official epilog, as described in section 2.5
 
movdqa xmm7, [rbp]
mov rsi, [rbp+018h]
mov rdi, [rbp-010h]
 
; Here’s the official epilog
 
lea rsp, [rbp-020h]
pop rbp
ret
sample ENDP
Макросы MASM

Для упрощения использования операций, описанных в разделе Необработанные псевдооперации, в файле ksamd64.inc определен набор макросов, которые можно использовать для создания типичных прологов и эпилогов процедур.
МакросОписание
alloc_stack(n)Выделяет кадр стека размером в n байт (с помощью команды "sub rsp, n") и помещает соответствующую информацию для раскрутки (".allocstack b").
save_reg reg, locСохраняет защищенный регистр "reg" в стеке по RSP-смещению "loc" и помещает соответствующую информацию для раскрутки (".savereg reg, loc").
push_reg regСохраняет защищенный регистр "reg" в стеке и помещает соответствующую информацию для раскрутки (".pushreg reg").
rex_push_reg regСохраните слаболетучий регистр в стеке использование внедрения 2 байт, и выведите соответствующее размотайте сведения (reg .pushreg) это должно использоваться, если внедрения первая инструкция в функции убедиться, что функция высокий - patchable.
save_xmm128 reg, locСохраняет защищенный XMM-регистр "reg" в стеке по RSP-смещению "loc" и помещает соответствующую информацию для раскрутки (".savexmm128 reg, loc").
set_frame reg, offsetПрисваивает регистру стекового кадра "reg" значение RSP + offset (с помощью команды mov или lea) и помещает соответствующую информацию для раскрутки (".set_frame reg, offset").
push_eflagsСохраняет регистр "eflags" в стек с помощью команды pushfq и помещает соответствующую информацию для раскрутки (".alloc_stack 8").
Ниже представлен пример пролога функции, в котором должным образом используются описанные макросы.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
SkFrame struct 
Fill    dq ?; fill to 8 mod 16 
SavedRdi dq ?; saved register RDI 
SavedRsi dq ?; saved register RSI 
SkFrame ends
 
sampleFrame struct
Filldq?; fill to 8 mod 16
SavedRdidq?; Saved Register RDI 
SavedRsi  dq?; Saved Register RSI
sampleFrame ends
 
sample2 PROC FRAME
alloc_stack(sizeof sampleFrame)
save_reg rdi, sampleFrame.SavedRdi
save_reg rsi, sampleFrame.SavedRsi
.end_prolog
 
; function body
 
mov rsi, sampleFrame.SavedRsi[rsp]
mov rdi, sampleFrame.SavedRdi[rsp]
 
; Here’s the official epilog
 
add rsp, (sizeof sampleFrame)
ret
sample2 ENDP
Описание раскрутки данных в языке C
Далее следует описание раскрутки данных в языке С.
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
typedef enum _UNWIND_OP_CODES {
    UWOP_PUSH_NONVOL = 0, /* info == register number */
    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
    UWOP_SAVE_XMM128,     /* info == XMM reg number, offset in next slot */
    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;
 
typedef union _UNWIND_CODE {
    struct {
        UBYTE CodeOffset;
        UBYTE UnwindOp : 4;
        UBYTE OpInfo   : 4;
    };
    USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
 
#define UNW_FLAG_EHANDLER  0x01
#define UNW_FLAG_UHANDLER  0x02
#define UNW_FLAG_CHAININFO 0x04
 
typedef struct _UNWIND_INFO {
    UBYTE Version       : 3;
    UBYTE Flags         : 5;
    UBYTE SizeOfProlog;
    UBYTE CountOfCodes;
    UBYTE FrameRegister : 4;
    UBYTE FrameOffset   : 4;
    UNWIND_CODE UnwindCode[1];
/*  UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
*   union {
*       OPTIONAL ULONG ExceptionHandler;
*       OPTIONAL ULONG FunctionEntry;
*   };
*   OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, *PUNWIND_INFO;
 
typedef struct _RUNTIME_FUNCTION {
    ULONG BeginAddress;
    ULONG EndAddress;
    ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
 
#define GetUnwindCodeEntry(info, index) \
    ((info)-]UnwindCode[index])
 
#define GetLanguageSpecificDataPtr(info) \
    ((PVOID)&GetUnwindCodeEntry((info),((info)-]CountOfCodes + 1) & ~1))
 
#define GetExceptionHandler(base, info) \
    ((PEXCEPTION_HANDLER)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))
 
#define GetChainedFunctionEntry(base, info) \
    ((PRUNTIME_FUNCTION)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))
 
#define GetExceptionDataPtr(info) \
    ((PVOID)((PULONG)GetLanguageSpecificData(info) + 1)
to be continued
3
Миниатюры
Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows   Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows   Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  

Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows   Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
29.05.2014, 06:16  [ТС] #4
Где взять 64-разрядный компилятор?

64-разрядный ассемблерный транслятор не распространяется отдельно, но ml64.exe и link.exe можно получить бесплатно (пожалуйста, прочитайте и соблюдайте "Лицензионное соглашение" ) если скачать комплект C++ компиляторов от Microsoft. Вам потребуется
После установки WDK ml64.exe и link.exe содержатся в папках C:\WinDDK\7600.16385.1\bin\x86\amd\

После установки SDK ml64.exe и link.exe содержатся в папках C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\

Обычно используют 64-битный link из папки amd64, а ml64 уже 32-битный из папки x86_amd64. В папке amd64 есть 64-битная версия ml64, но при работе с большим количеством макросов он почему-то зависает или просто медленно работает. В конце концов выбор за Вами

На сайте http://dsmhelp.narod.ru/environment.htm можно найти набор lib- и inc-файлов для создания 64-разрядных приложений на ассемблере, а также кучу полезных макросов
to be continued
3
Вложения
Тип файла: zip masm64.zip (3.83 Мб, 219 просмотров)
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
08.06.2014, 10:44  [ТС] #5
Простейшее приложение для Win64
Итак базовые сведения о Win64 мы получили, теперь напишем простейшее приложение выводящее на экран фразу "Win64 Assembly is Great!" и посмотрим, как это сделать на разных диалектах ассемблера
MessageBox на nasm
исходный текст
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%include 'nasmx.inc'
%include 'windows.inc'
%include 'kernel32.inc'
%include 'user32.inc'
 
entry    demo1
 
[section .text]
MsgCaption: db "Iczelion's tutorial #2", 0x0
MsgBoxText: db "Win64 Assembly with NASM is Great!", 0x0
 
proc     demo1
    invoke    MessageBoxA, NULL, MsgBoxText, MsgCaption, MB_OK
    invoke    ExitProcess, NULL
 
endproc

текст под оттладчиком
Код
	push rsp
	push q[rsp]
	and spl,0F0h
	xor ecx,ecx
	lea rdx,[401017];"Win64 Assembly is Great!"
	lea r8,[401000];"Iczelion's tutorial #2"
	xor r9d,r9d
	sub rsp,20h
	call MessageBoxA
	add rsp,28h
	pop rsp
	push rsp
	push q,[rsp]
	and spl,0F0h
	xor ecx,ecx
	sub rsp,20h
	call ExitProcess
	add rsp,28h
	pop rsp

текст bat-файла, которым собиралось приложение
Код
cls
set file="demo1"
nasm -f win64 %file%.asm -o %file%.obj
GoLink.exe /entry _main %file%.obj kernel32.dll user32.dll
del %file%.obj

MessageBox на fasm
исходный текст программы
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
format PE64 GUI 5.0
entry start
include 'win64a.inc'
section '.text' code readable executable
 
  start:
   invoke MessageBoxA,0,msgBoxText,msgBoxCaption,0
   invoke ExitProcess,0
 
section '.data' data readable writeable
 
msgBoxText  db "Win64 Assembly with FASM is Great!",0
msgBoxCaption db "Iczelion Tutorial #2:MessageBox",0
 
section '.idata' import data readable writeable
 
  dd 0,0,0,RVA kernel_name,RVA kernel_table
  dd 0,0,0,RVA user_name,RVA user_table
  dd 0,0,0,0,0
 
  kernel_table:
    ExitProcess dq RVA _ExitProcess
    dq 0
  user_table:
    MessageBoxA dq RVA _MessageBoxA
    dq 0
 
  kernel_name db 'KERNEL32.DLL',0
  user_name db 'USER32.DLL',0
 
  _ExitProcess dw 0
    db 'ExitProcess',0
  _MessageBoxA dw 0
    db 'MessageBoxA',0

текст под оттладчиком
Код
	sub rsp,20h
	mov rcx,0
	mov rdx,402000;"Win64 Assembly with FASM is Great!"
	mov r8,402023;"Iczelion Tutorial #2:MessageBox"
	mov r9,0
	call MessageBoxA
	add rsp,20h
	sub rsp,20h
	mov rcx,0
	call ExitProcess
	add rsp,20h

MessageBox на masm
исходный текст
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
OPTION DOTNAME
include temphls.inc
include win64.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
.data
MsgCaption      db "Iczelion's tutorial #2",0
MsgBoxText      db "Win64 Assembly with MASM is Great!",0
.code
WinMain proc 
    invoke MessageBox,0,&MsgBoxText,&MsgCaption,0
        invoke ExitProcess,0
WinMain endp
end

текст bat-файла, которым собиралось приложение
Код
set file=msgbox
set path=\ml64\masm64\
%path%bin\ml64 /Cp /I"%path%Include" %file%.asm /link /subsystem:windows ^
/LIBPATH:"path%Lib" /entry:WinMain
del %file%.obj
del mllink$.lnk

текст под оттладчиком
Код
	sub rsp,28h
	xor rcx,rcx
	xor r9d,r9d
	lea rdx,[140000287];"Win64 Assembly with MASM is Great!"
	lea r8,[140000270];"Iczelion Tutorial #2:MessageBox"
	call MessageBoxA
	xor ecx,ecx
	call ExitProcess

MessageBox на JWasm
текст bat-файла, которым собиралось приложение
Код
JWasm -win64 Win64_1.asm
link /subsystem:windows /entry:WinMain Win64_1.obj

исходный текст
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    option casemap:none
 
    includelib kernel32.lib
    includelib user32.lib
 
externdef MessageBoxA : near
externdef ExitProcess : near
 
    .data
 
text    db 'Win64 Assembly with JWasm is Great!', 0
caption db 'Iczelion Tutorial #2:MessageBox', 0
 
    .code
 
WinMain proc
    sub rsp, 28h        ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d        ; 4. argument: r9d = uType = 0
    lea r8, [caption]   ; 3. argument: r8  = caption
    lea rdx, [text]     ; 2. argument: edx = window text
    xor rcx, rcx        ; 1. argument: rcx = hWnd = NULL
    call MessageBoxA
    xor ecx, ecx        ; ecx = exit code
    call ExitProcess
WinMain endp
 
    end
to be continued
3
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
11.06.2014, 10:38  [ТС] #6
Уменьшаем размер приложения для Win64
Простейшее приложение выводящее на экран фразу "Win64 Assembly is Great!" на диалекте MASM
исходный текст
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OPTION DOTNAME
option casemap:none
include \x64\include\temphls.inc
include \x64\include\win64.inc
include \x64\include\kernel32.inc
include \x64\include\user32.inc
includelib \x64\lib\kernel32.lib
includelib \x64\lib\user32.lib
.data
MsgCaption      db "Iczelion's tutorial #2",0
MsgBoxText      db "Win64 Assembly is Great!",0
.code
WinMain proc 
    invoke MessageBox,0,&MsgBoxText,&MsgCaption,0
        invoke ExitProcess,0
WinMain endp
end
текст bat-файла которым собиралось приложение
Код
set file=msgbox
set path=\x64\
%path%bin\ml64 %file%.asm /link /subsystem:windows /entry:WinMain
del %file%.obj
del mllink$.lnk

текст под оттладчиком
Код
	sub rsp,28h
	xor rcx,rcx
	xor r9d,r9d
	lea rdx,[140000287];"Win64 Assembly is Great!"
	lea r8,[140000270];"Iczelion Tutorial #2:MessageBox"
	call MessageBoxA
	xor ecx,ecx
	call ExitProcess

В результате получаем ехе-файл в 2560 байт. При просмотре внутренностей ехе-файла программой hiew32 видно, что 90% содержимого нули. Возникает законный вопрос, а как уменьшить размер программы, но чтобы при этом не терялась ее функциональность?
Шаг первый ― уменьшение размера выравнивания сегментов
  • Программа-компановщик link.exe имеет ключ /ALIGN:размер. Ключ /ALIGN[MENT] ― указывает компановщику на необходимость выравнивания сегментов в исполняемом файле на границу, кратную значению размер. Здесь размер ― это число равное степени двойки (20=1, 21=2, 22=4, 23=8, ..., 29=512 до 64 К включительно).
    Если об этом ключе не упоминать, то выравнивание равно 512 байт для совместимости с программами созданными для Windows 95/98. Посмотреть значение выравнивания можно при помощи любимого редактора бинарных файлов hiew32 в заголовке программы в поле «File alignment» (Открываем наш exe программой hiew32, нажимаем F4 (Mode) и выбираем Hex, нажимаем F8 (Header) в поле «File alignment» видим число 200h=512).
Кроме ключа /ALIGN добавим в файл makeit.bat еще некоторые ключи компиляции и линковки, которые позволят нам уменьшить количество служебной информации в asm-файлах.
  • Добавление ключа компиляции /Cp ― «сохранить регистр символов во всех идентификаторах» позволяет не писать каждый раз в тексте ассемблерного файла «option casemap:none».
  • Добавление ключа компиляции /I:путь ― «установить путь для включаемых файлов» позволяет нам не указывать каждый раз полный путь к inc-файлам.
  • Ключ /LIBPATH:путь говорит линкеру, где находятся библиотеки импорта, что позволяет нам не указывать каждый раз полный путь к lib-файлам.
Так выглядит содержимое файла makeit.bat до изменений:
Код
set file=msgbox
set path=\x64\
%path%bin\ml64 %file%.asm /link /subsystem:windows /entry:WinMain
del %file%.obj
del mllink$.lnk

а так после:
Код
set file=msgbox
set path=\x64\
%path%bin\ml64 /Cp /I%path%include %file%.asm /link /subsystem:windows ^
/LIBPATH:%path%lib /ALIGN:256 /entry:WinMain
del %file%.obj
del mllink$.lnk

при компиляции получаем сообщение
LINK : warring LNK4108: /ALIGN specified without /DRIVER; image may not run
размер файла msgbox.exe меняется с 2560 на 1536 байт и, не смотря, на предупреждение image may not run файл msgbox.exe благополучно запускается
Продолжаем эксперимент, изменяя значение ALIGN, наблюдаем за уменьшением размера файла msgbox.exe. При этом каждый раз запускаем получившийся файл msgbox.exe на исполнение и убеждаемся в работоспособности получившегося файла.
/ALIGN:размерзаголовоккодданныеимпортобщий размер msgbox.exe
не указан 1024 5125125122560 байт
256 768 2562562561536 байт
128 640 1281282561152 байт
64 576 6464192896 байт
32 576 6464192896 байт
16 576 4848192864 байт
8ошибка при создании файла
при /ALIGN:8 получаем сообщение об ошибке
msgbox.obj : fatal error LNK1164: section 0x1 alignment (16) greater then /ALIGN value
похоже, что в этом направлении мы достигли предела, хотя от исходной программы в 2560 байт пришли к программе в 848 байт, а это согласитесь не плохо!
Шаг второй ― объединяем сегмент кода и сегмент данных.
Наша программа использует два сегмента, сегмент кода и сегмент данных, посмотрите внимательно через hiew32 ― между этими сегментами прослойка из нулей, от которых мы и пытаемся избавится. А помните во времена DOS'а можно было создавать COM-файлы, которые в единственном сегменте содержал и код, и стек, и данные? А нельзя ли и здесь создать, что-то подобное?
  • Объединения сегмента кода и сегмента данных можно добиться если использовать опцию линкера /SECTION.
    Опция командной строки компановщика link.exe /SECTION:name,[[!]{DEKPRSW}][,ALIGN=#] позволяет принудительно назначать атрибуты секциям PE-файла. Для секции можно задать один или несколько атрибутов. Следует задавать все атрибуты, которые должна иметь секция; если какой-либо знак атрибута не указан, то его бит будет отключен. Если не указан атрибут R, W или E, то существующее состояние чтения, записи или исполнения остается неизмененным. Чтобы инвертировать атрибут, перед его символом указывают знак «!». С помощью параметра ALIGN=# можно задать значение выравнивания для конкретной секции. Значения знаков атрибутов приведены в следующей таблице.
    буква атрибут значениеперевод
    DDiscardableMarks the section as discardableСекция помечается как выгружаемая
    EExecuteThe section is executableСекция является выполняемой
    KCacheableMarks the section as not cacheableСекция помечается как некэшируемая
    PPageableMarks the section as not pageableСекция помечается как секция без страничной организации
    RReadAllows read operations on dataДопускаются операции чтения данных
    SSharedShares the section among all processes that load the imageСекция совместно используется всеми процессами, загружающими образ
    WWriteAllows write operations on dataДопускаются операции записи данных
    В данном случае секции с именем «.text» (содержащей код программы) и уже имеющей атрибуты R (доступна для чтения) и E (исполнимая), устанавливается атрибут W (доступна для записи)
    Код
    /SECTION:.text,W
Вносим изменения в текст программы
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
include win64.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
 
.code
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!", 0
 
WinMain proc
    sub rsp, 28h        ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d        ; 4. argument: r9d = uType = 0
    lea r8, MsgCaption  ; 3. argument: r8  = caption
    lea rdx,MsgBoxText  ; 2. argument: edx = window text
    xor ecx, ecx        ; 1. argument: rcx = hWnd = NULL
    call MessageBox
    xor ecx, ecx        ; ecx = exit code
    call ExitProcess
WinMain endp
вносим изменения в файл makeit.bat, создающий msgbox.exe
Код
set file=msgbox
set path=\x64\
%path%bin\ml64 /Cp /I"%path%include" %file%.asm /link /subsystem:windows ^
/LIBPATH:"%path%lib" /ALIGN:16 /SECTION:.text,W /entry:WinMain
del %file%.obj
del mllink$.lnk
В результате получаем msgbox.exe в 832 байт
заголовоккод и данныеимпортобщий размер
54496192832 байт
Шаг третий ― «хирургический» ― ампутируем у файла «хвост».
hiew32 показывает, что в хвосте нашего файла, сразу за строкой «kernel32.dll» целых двенадцать байтов содержащих нули. А не удалить ли их нам вручную?
Код
.40000300:   E2 01 4D 65 73 73 61 67  65 42 6F 78 41 00 75 73 т MessageBoxA us
.40000310:   65 72 33 32 2E 64 6C 6C  00 00 BC 00 45 78 69 74 er32.dll    Exit
.40000320:   50 72 6F 63 65 73 73 00  6B 65 72 6E 65 6C 33 32 Process kernel32
.40000330:   2E 64 6C 6C 00 00 00 00  00 00 00 00 00 00 00 00 .dll
Сказано ― сделано! В far'е нажимаем F4 открываем файл на редактирование, нажимем Ctrl+End и переходим в конец файла. Далее, нажимая на Backspace, удаляем лишнее, жмем на F2 и сохраняем изменения в файле, нажимаем на F10 и выходим из файла ― размер msgbox.exe стал 816 байт. Нажимаем на него и запускаем msgbox.exe ― запускается нормально! Стоп-стоп! 832-816=16 байт, а должно быть 12! Смотрим в hiew32 ― о, боже!, мы отрезали вместе с нулями от «kernel32.dll» кусок «.dll», но файл-то всё равно работает! Запомним на будущее, что от DLL достаточно только названия, а точку и расширение файла система подставит сама.
заголовоккод и данныеимпортобщий размер
54496176816 байт
Шаг четвертый ― уменьшаем DOS-stub.
Код нашей программы начинается с 1F8h=504 байта. Всё, что выше ― это заголовок нашего EXE-файла ― вот бы его уменьшить! Заголовок нашего файла состоит из двух частей. От строки «MZ» до строки «PE» DOS-заголовок (DOS-stub) (0C0h=192 байта) и от строки «PE» до 21Fh PE-заголовок (352 байта). Адрес строки «PE» содержится в DOS-заголовке в двойном слове по смещению 3Ch.
Начнем с уменьшения DOS-stub'а. Возьмем hiew32.exe и создадим с его помощью вот такой файл:
Код
.000000:   4D 5A 00 00 00 00 00 00  00 00 00 00 00 00 00 00 MZ
.000010:   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
.000020:   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
.000030:   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
Присвоим ему имя stub.exe и используем в качестве stub-программы в очередной раз, внеся изменения в makeit.bat.
Код
set file=msgbox
set path=\x64\
%path%bin\ml64 /Cp /I"%path%include" %file%.asm /link /subsystem:windows ^
/LIBPATH:"%path%lib" /ALIGN:16 /SECTION:.text,W /STUB:stubby.exe /entry:WinMain
del %file%.obj
del mllink$.lnk
Запускаем makeit.bat и получаем msgbox.exe в 768 байт, если еще отрезать конечные нулевые байты и «.dll», тогда его размер равен 752 байт. Хотя через hiew32 видно, что размер DOS-заголовка не 64 байта, как мы предполагали, а 128 байт, но это не 192 байта, как было в начале.
заголовоккод и данныеимпортобщий размер
48096176752 байта
to be continued
1
Изображения
  
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
12.06.2014, 12:13  [ТС] #7
Уменьшаем размер приложения для Win64
продолжение
Шаг пятый. Так как код программы и данные нам сократить не удастся ― поэтому сократим размеры заголовка и импорта PE-файла. Для этой цели стандартные компиляторы и линкеры не подходят. Нам потребуется создать bin-файл, заголовок, которого мы напишем самостоятельно. Для этого требуется ассемблер, который может создавать бинарные файлы, например, FASM, учтем, что у него немного другой синтаксис.
  1. за основу бинарного файла возьмем msgbox.exe размером 832 байта
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    
    IMAGE_DOS_SIGNATURE     equ 5A4Dh
    IMAGE_NT_SIGNATURE      equ 00004550h
    PROCESSOR_AMD_X8664     equ 8664h
    IMAGE_SCN_CNT_CODE      equ 00000020h
    IMAGE_SCN_MEM_WRITE     equ 80000000h
    IMAGE_SCN_MEM_READ      equ 40000000h
    IMAGE_SCN_CNT_INITIALIZED_DATA  equ 00000040h
    IMAGE_SUBSYSTEM_WINDOWS_GUI equ 2
    IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 20Bh
    IMAGE_FILE_RELOCS_STRIPPED  equ 1
    IMAGE_FILE_EXECUTABLE_IMAGE equ 2
     
    include 'win64a.inc'
    use16
    org 0
    ;--------DOS-stub-------------------------------
    Signature       dw IMAGE_DOS_SIGNATURE
    Bytes_on_last_page  dw 0x90
    Page_in_line        dw 3
    Relocations_count   dw 0
    Paragraphs_in_header    dw 4
    Minimum_memory      dw 0
    Maximum_memory      dw 0xFFFF
    SS_SP_setting       dw 0,0xB8
    Checksum        dw 0
    CS_IP_setting       dw 0,0
    Relocations_table_address dw 0x40
    Overlay_number      dw 0    
    times 4 dq 0
    NewExe_offset       dd ntHeader
        push cs
        pop ds
        mov dx,string
        mov ah,9
        int 21h
        mov ax,0x4C01
        int 21h
    use64
    string db 'This program cannot be run in DOS mode.',0xD,0xD,0xA,'$'
    times 17*4+3 db 0
    ;-------PE-заголовок--------------------------------------------------
    ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    ;image_header----Файловый заголовок
    Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    Count_of_section    dw 2;Количество секций
    TimeStump       dd 0;Информация о времени, когда был собран данный PE-файл
    Symbol_table_offset dd 0;Указатель на размер отладочной информации
    Symbol_table_count  dd 0;Указатель на COFF-таблицу символов PE-формата
    Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or \
    IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    ;-------Стандартные поля NT
    optional_header:
    Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл. 
    Size_of_code        dd 0x60;Суммарный размер секций кода
    Size_of_init_data   dd 0xC0;Суммарный размер инициализированных данных
    Size_of_uninit_data dd 0;Суммарный размер неинициализированных данных
    entry_point     dd start;Относительный адрес точки входа, отсчитываемый 
    ;от начала Image Base. Из всех стандартных полей, поле entry_point является наиболее 
    ;интересным для формата PE файлов. Это поле содержит адрес точки входа приложения, 
    ;и, что, вероятно, более важно для для хакеров, местоположение конца Import Address 
    ;Table (таблицы импортируемых адресов - IAT).
    base_of_code        dd begin;Относительные базовые адреса кодовой секции. 
    ;Относительное смещение сегмента кода («.text» сегмент) в загруженном файле.
    ;------Дополнительные поля NT-----------------------------------------------
    image_base      dq 0x140000000;Базовый адрес загрузки страничного имиджа. 
    ;Предпочтительный адрес в адресном пространстве процесса для загрузки исполнимого 
    ;файла. Линковщик подставляет «значение по умолчанию», но это значение можно 
    ;изменить с помощью опции линковщика BASE:.
    section_alignment   dd 0x10;Кратность выравнивания секций в памяти. Сегменты 
    ;загружаются в адресное пространство процесса последовательно, начиная с ImageBase. 
    ;section_alignment предписывает минимальный размер, который сегмент может занять 
    ;при загрузке - так что сегменты оказываются выровненными по границе section_alignment. 
    ;Выравнивание сегмента не может быть меньше размера страницы (в настоящий момент 4096 
    ;байт на платформе x86), и должно быть кратно размеру страницы, как предписывает 
    ;поведение менеджера виртуальной памяти Windows NT. 4096 (=1000h) байт являются 
    ;«значением по умолчанию», но может быть установлено любое другое значение, при 
    ;использовании опции линковщика ALIGN:
    file_alignment      dd 0x10;
    OS_version_major_minor  dw 5,2
    image_version_major_minor dd 0
    subsystem_version_major_minor dw 5,2
    Win32_version       dd 0
    size_of_image       dd end_import
    size_of_header      dd begin
    checksum        dd 0
    subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    Stack_allocation    dq 0x100000
    Stack_commit        dq 0x1000
    Heap_allocation     dq 0x100000
    Heap_commit     dq 0x1000
    loader_flag     dd 0
    number_of_dirs      dd 16
    export_RVA_size     dq 0
    import_RVA_size     dd _import,0x3C;end_import-import
    resurce         dq 0
    exception       dq 0
    security        dq 0
    fixups_         dq 0
    debug           dq 0
    description     dq 0
    MIPS_GP         dq 0
    TLS         dq 0
    Load_config     dq 0
    Bound_import        dq 0
    import_table1       dd Import_Table,0x20
    delay_import        dq 0
    com_runtime     dq 0
    reserved        dq 0
    ;------------------------------------------------
    section_table       dq '.text'
    .virtual_size       dd 0x55
    .virtual_address    dd begin
    .Physical_size      dd 0x60;end_import-start
    .Physical_offset    dd begin
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    ;------------------------------------------------
    section_table1      dq '.rdata'
    .virtual_size       dd 0xB6
    .virtual_address    dd Import_Table
    .Physical_size      dd 0xC0;end_import-start
    .Physical_offset    dd Import_Table
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    ;--------данные и код-----------------------------------------
    dq 0
    begin:
    MsgCaption db "Iczelion's tutorial #2", 0
    MsgBoxText db "Win64 Assembly is Great!", 0
    start:
        sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
        xor r9d, r9d    ; 4. argument: r9d = uType = 0
        lea r8, [MsgCaption]  ; 3. argument: r8  = caption
        lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
        xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
        call [MessageBox]
        xor ecx, ecx    ; ecx = exit code
        call [ExitProcess]
    ;---------секция импорта---------------------------------------
    times 11 db 0
    Import_Table:
    kernel32_table:
    ExitProcess dq _ExitProcess,0
    user32_table:
    MessageBox  dq _MessageBox,0
    _import:
    dd user32_table2,0,0,user32_dll,user32_table
    dd kernel32_table2,0,0,kernel32_dll,kernel32_table
    dq 0,0,0
    kernel32_table2:
    dq _ExitProcess,0
    user32_table2:
    dq _MessageBox,0
    _MessageBox     db 0xE2,1,"MessageBoxA",0
    user32_dll db "user32.dll",0,0
    _ExitProcess        db 0xBC,0,"ExitProcess",0
    kernel32_dll    db "kernel32.dll"
    dd 0,0,0
    end_import:
    заголовоккод и данныеимпортобщий размер
    54496192832 байт
  2. сократим DOS-stub до 64 байт, уменьшение таблицы разделов, по умолчанию количество разделов должно быть равным 16, нам же требуется только секция импорта. В таблице разделов секция импорта идет сразу после секции экспорта.
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    
    include 'win64a.inc'
    use64
    org 0
    ;--------DOS-stub-------------------------------
    Signature       dw IMAGE_DOS_SIGNATURE
    ;так как мы не собираемся наше приложение запускать под DOS поэтому названия
    ;и содержимое полей DOS-стаба нам не интересно, для нас представляют интерес
    ;только два поля Signature и NewExe_offset, остальные поля заполним нулями
    times 58 db 0
    NewExe_offset       dd ntHeader
    ;-------PE-заголовок------------------------------
    ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    ;image_header--------------------------
    Machine         dw PROCESSOR_AMD_X8664
    Count_of_section    dw 2
    times 3 dd 0
    Size_of_optional_header dw section_table-optional_header;
    Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or \
    IMAGE_FILE_EXECUTABLE_IMAGE
    ;20h  - Handle >2Gb addresses
    ;-------------------------------------
    optional_header:
    Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
    Linker_version_major_and_minor dw 9 
    Size_of_code        dd 0x60;end_import-start
    Size_of_init_data   dd 0xC0
    Size_of_uninit_data dd 0
    entry_point     dd start
    base_of_code        dd begin
    image_base      dq 0x140000000
    section_alignment   dd 0x10
    file_alignment      dd 0x10
    OS_version_major_minor  dw 5,2
    image_version_major_minor dd 0
    subsystem_version_major_minor dw 5,2
    Win32_version       dd 0
    size_of_image       dd end_import
    size_of_header      dd begin
    checksum        dd 0
    subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    Stack_allocation    dq 0x100000
    Stack_commit        dq 0x1000
    Heap_allocation     dq 0x100000
    Heap_commit     dq 0x1000
    loader_flag     dd 0
    number_of_dirs      dd (section_table-export_RVA)/8
    export_RVA_size     dq 0
    import_RVA_size     dd _import,0x3C;end_import-import
    ;------------------------------------------------
    section_table       dq '.text'
    .virtual_size       dd 0x55
    .virtual_address    dd begin
    .Physical_size      dd 0x60;end_import-start
    .Physical_offset    dd begin
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    ;------------------------------------------------
    section_table1      dq '.rdata'
    .virtual_size       dd 0xB6
    .virtual_address    dd Import_Table
    .Physical_size      dd 0xC0;end_import-start
    .Physical_offset    dd Import_Table
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    ;-------------------------------------------------
    dq 0
    begin:
    MsgCaption db "Iczelion's tutorial #2", 0
    MsgBoxText db "Win64 Assembly is Great!", 0
    start:
        sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
        xor r9d, r9d    ; 4. argument: r9d = uType = 0
        lea r8, [MsgCaption]  ; 3. argument: r8  = caption
        lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
        xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
        call [MessageBox]
        xor ecx, ecx    ; ecx = exit code
        call [ExitProcess]
    ;---------секция импорта---------------------------------------
    times 11 db 0
    Import_Table:
    kernel32_table:
    ExitProcess dq _ExitProcess,0
    user32_table:
    MessageBox  dq _MessageBox,0
    _import:
    dd user32_table2,0,0,user32_dll,user32_table
    dd kernel32_table2,0,0,kernel32_dll,kernel32_table
    dq 0,0,0
    kernel32_table2:
    dq _ExitProcess,0
    user32_table2:
    dq _MessageBox,0
    _MessageBox     db 0xE2,1,"MessageBoxA",0
    user32_dll db "user32.dll",0,0
    _ExitProcess        db 0xBC,0,"ExitProcess",0
    kernel32_dll    db "kernel32.dll"
    dd 0,0,0
    end_import:
    Приложение сократилось до размера 592 байт
    заголовоккод и данныеимпортобщий размер
    30496192592 байт
  3. Что можно сократить в секции импорта? Секция импорта содержит две одинаковые таблицы (Import LookUp Table и Import Address Table), содержащие ссылки на названия импортируемых функций. Можно ли обойтись только таблицей Import Address Table? Удаляем Import LookUp Table и обнуляем ссылки на kernel32_table2 и user32_table2
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    
    include 'win64a.inc'
     
    use64
    org 0
     
    ;--------DOS-stub-------------------------------
    Signature       dw IMAGE_DOS_SIGNATURE
    times 58 db 0
    NewExe_offset       dd ntHeader
    ;-------PE-заголовок------------------------------
    ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    ;image_header--------------------------
    Machine         dw PROCESSOR_AMD_X8664
    Count_of_section    dw 2
    times 3 dd 0
    Size_of_optional_header dw section_table-optional_header;
    Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or \
    IMAGE_FILE_EXECUTABLE_IMAGE
    ;20h Handle >2Gb addresses
    ;-------------------------------------
    optional_header:
    Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
    Linker_version_major_and_minor dw 9 
    Size_of_code        dd 0x60;end_import-start
    Size_of_init_data   dd 0xC0
    Size_of_uninit_data dd 0
    entry_point     dd start
    base_of_code        dd begin
    image_base      dq 0x140000000
    section_alignment   dd 0x10
    file_alignment      dd 0x10
    OS_version_major_minor  dw 5,2
    image_version_major_minor dd 0
    subsystem_version_major_minor dw 5,2
    Win32_version       dd 0
    size_of_image       dd end_import
    size_of_header      dd begin
    checksum        dd 0
    subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    Stack_allocation    dq 0x100000
    Stack_commit        dq 0x1000
    Heap_allocation     dq 0x100000
    Heap_commit     dq 0x1000
    loader_flag     dd 0
    number_of_dirs      dd (section_table-export_RVA)/8
    export_RVA_size     dq 0
    import_RVA_size     dd _import,0x3C;end_import-import
    ;------------------------------------------------
    section_table       dd 'xet.','t';'.text'
    .virtual_size       dd 0x55
    .virtual_address    dd begin
    .Physical_size      dd 0x60;end_import-start
    .Physical_offset    dd begin
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    ;------------------------------------------------
    section_table1      dd 'adr.','at';'.rdata'
    .virtual_size       dd 0xB6
    .virtual_address    dd Import_Table
    .Physical_size      dd 0xC0;end_import-start
    .Physical_offset    dd Import_Table
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    ;-------------------------------------------------
    dq 0
    begin:
    MsgCaption db "Iczelion's tutorial #2", 0
    MsgBoxText db "Win64 Assembly is Great!", 0
    start:
        sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
        xor r9d, r9d    ; 4. argument: r9d = uType = 0
        lea r8, [MsgCaption]  ; 3. argument: r8  = caption
        lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
        xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
        call [MessageBox]
        xor ecx, ecx    ; ecx = exit code
        call [ExitProcess]
    ;---------секция импорта---------------------------------------
    times 11 db 0
    Import_Table:
    kernel32_table:
    ExitProcess dq _ExitProcess,0
    user32_table:
    MessageBox  dq _MessageBox,0
    _import:
    dd 0,0,0,user32_dll,user32_table
    dd 0,0,0,kernel32_dll,kernel32_table
    dq 0,0,0
    _MessageBox     db 0xE2,1,"MessageBoxA",0
    user32_dll db "user32.dll",0,0
    _ExitProcess        db 0xBC,0,"ExitProcess",0
    kernel32_dll    db "kernel32.dll"
    dd 0,0,0
    end_import:
    Приложение сократилось до размера 560 байт
    заголовоккод и данныеимпортобщий размер
    30496160560 байт
  4. удаляем 12 нулевых байтов в конце файла и окончания ".dll"
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    
    include 'win64a.inc'
    use64
    org 0
    ;--------DOS-stub-------------------------------
    Signature       dw IMAGE_DOS_SIGNATURE;'MZ'
    times 58 db 0
    NewExe_offset       dd ntHeader
    ;-------PE-заголовок------------------------------
    ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    ;image_header--------------------------
    Machine         dw PROCESSOR_AMD_X8664
    Count_of_section    dw 2
    TimeStump       dd 0
    Symbol_table_offset dd 0
    Symbol_table_count  dd 0
    Size_of_optional_header dw section_table-optional_header;
    Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or \
    IMAGE_FILE_EXECUTABLE_IMAGE
    ;20h Handle >2Gb addresses
    ;-------------------------------------
    optional_header:
    Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
    Linker_version_major_and_minor dw 9 
    Size_of_code        dd 0x60;end_import-start
    Size_of_init_data   dd 0xC0
    Size_of_uninit_data dd 0
    entry_point     dd start
    base_of_code        dd begin
    image_base      dq 0x140000000
    section_alignment   dd 0x10
    file_alignment      dd 0x10
    OS_version_major_minor  dw 5,2
    image_version_major_minor dd 0
    subsystem_version_major_minor dw 5,2
    Win32_version       dd 0
    size_of_image       dd end_import
    size_of_header      dd begin
    checksum        dd 0
    subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    Stack_allocation    dq 0x100000
    Stack_commit        dq 0x1000
    Heap_allocation     dq 0x100000
    Heap_commit     dq 0x1000
    loader_flag     dd 0
    number_of_dirs      dd (section_table-export_RVA)/8
    export_RVA_size     dq 0
    import_RVA_size     dd _import,0x3C;end_import-import
    ;------------------------------------------------
    section_table       dd 'xet.','t';'.text'
    .virtual_size       dd 0x55
    .virtual_address    dd begin
    .Physical_size      dd 0x60;end_import-start
    .Physical_offset    dd begin
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    ;------------------------------------------------
    section_table1      dd 'adr.','at';'.rdata'
    .virtual_size       dd 0xB6
    .virtual_address    dd Import_Table
    .Physical_size      dd 0xC0;end_import-start
    .Physical_offset    dd Import_Table
    .Relocations_and_Linenumbers dq 0
    .Relocations_and_Linenumbers_count dd 0
    .Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    ;-------------------------------------------------
    dq 0
    begin:
    MsgCaption db "Iczelion's tutorial #2", 0
    MsgBoxText db "Win64 Assembly is Great!", 0
    start:
        sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
        xor r9d, r9d    ; 4. argument: r9d = uType = 0
        lea r8, [MsgCaption]  ; 3. argument: r8  = caption
        lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
        xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
        call [MessageBox]
        xor ecx, ecx    ; ecx = exit code
        call [ExitProcess]
    ;---------секция импорта---------------------------------------
    times 11 db 0
    Import_Table:
    kernel32_table:
    ExitProcess dq _ExitProcess,0
    user32_table:
    MessageBox  dq _MessageBox,0
    _import:
    dd 0,0,0,user32_dll,user32_table
    dd 0,0,0,kernel32_dll,kernel32_table
    dq 0,0,0
    _MessageBox     db 0xE2,1,"MessageBoxA",0
    user32_dll db "user32",0,0
    _ExitProcess        db 0xBC,0,"ExitProcess",0
    kernel32_dll    db "kernel32"
    end_import:
    Приложение сократилось до размера 540 байт
    заголовоккод и данныеимпортобщий размер
    30496140540 байт
3
Dmitrinik
535 / 391 / 99
Регистрация: 18.08.2012
Сообщений: 1,014
12.06.2014, 15:56 #8
Mikl___! Откуда столько энергии? Завидую, по-хорошему
1
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
08.07.2014, 11:27  [ТС] #9
5.
сокращаем DOS-stub на 16 байтов, смещение e_lfanew оказывается внутри PE-заголовка. Так как у программы со стабом короче 64 байт смещение от начала файла 3Ch (поле e_lfanew) попадает уже внутрь PE-заголовка, то нужно, чтобы он не попал на поле PE-заголовка имеющее критическое значение при загрузке файла. Помещаем указатель на ntHeader в поле Symbol_table_offset. При размещении в нем числа 30h получаем работоспособное приложение.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
include 'win64a.inc'
use64
org 0
;--------DOS-stub-------------------------------
Signature       dw IMAGE_DOS_SIGNATURE;'MZ'
times 46 db 0
;-------PE-заголовок------------------------------
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
Machine         dw PROCESSOR_AMD_X8664
Count_of_section    dw 2
TimeStump       dd 0
Symbol_table_offset dd ntHeader
Symbol_table_count  dd 0
Size_of_optional_header dw section_table-optional_header;
Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h Handle >2Gb addresses
;-------------------------------------
optional_header:
Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
Linker_version_major_and_minor dw 9 
Size_of_code        dd 0x60;end_import-start
Size_of_init_data   dd 0xC0
Size_of_uninit_data dd 0
entry_point     dd start
base_of_code        dd begin
image_base      dq 0x140000000
section_alignment   dd 0x10
file_alignment      dd 0x10
OS_version_major_minor  dw 5,2
image_version_major_minor dd 0
subsystem_version_major_minor dw 5,2
Win32_version       dd 0
size_of_image       dd end_import
size_of_header      dd begin
checksum        dd 0
subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
Stack_allocation    dq 0x100000
Stack_commit        dq 0x1000
Heap_allocation     dq 0x100000
Heap_commit     dq 0x1000
loader_flag     dd 0
number_of_dirs      dd (section_table-export_RVA_size)/8
export_RVA_size     dq 0
import_RVA_size     dd _import,0x3C;end_import-import
;------------------------------------------------
section_table       dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd 0x60;end_import-start
.Physical_offset    dd begin
.Relocations_and_Linenumbers dq 0
.Relocations_and_Linenumbers_count dd 0
.Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;------------------------------------------------
section_table1      dq '.rdata'
.virtual_size       dd 0xB6
.virtual_address    dd Import_Table
.Physical_size      dd 0xC0;end_import-start
.Physical_offset    dd Import_Table
.Relocations_and_Linenumbers dq 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
;-------------------------------------------------
dq 0
begin:
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!", 0
start:
    sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d    ; 4. argument: r9d = uType = 0
    lea r8, [MsgCaption]  ; 3. argument: r8  = caption
    lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
    xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
    call [MessageBox]
    xor ecx, ecx    ; ecx = exit code
    call [ExitProcess]
;------------секция импорта------------------------------------
times 11 db 0
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
MessageBox  dq _MessageBox,0
_import:
dd 0,0,0,user32_dll,user32_table
dd 0,0,0,kernel32_dll,kernel32_table
dq 0,0,0
_MessageBox     db 0xE2,1,"MessageBoxA",0
user32_dll db "user32",0,0
_ExitProcess        db 0xBC,0,"ExitProcess",0
kernel32_dll    db "kernel32"
end_import:
Приложение сократилось до размера 524 байт
заголовоккод и данныеимпортобщий размер
28896140524 байт
6.
сокращаем DOS-stub еще на 16 байтов, смещение e_lfanew оказывается в поле Size_of_code. По одним источникам ― это поле используется для первичного отведения памяти под приложение. По другим ― не используются вообще. Если поместить туда число 20h, то практическая проверка показывает, что приложение с таким stub'ом работает нормально.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
include 'win64a.inc'
use64
org 0
;--------DOS-stub-------------------------------
Signature       dw IMAGE_DOS_SIGNATURE;'MZ'
times 30 db 0
;-------PE-заголовок------------------------------
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
Machine         dw PROCESSOR_AMD_X8664
Count_of_section    dw 2
TimeStump       dd 0
Symbol_table_offset dd 0
Symbol_table_count  dd 0
Size_of_optional_header dw section_table-optional_header;
Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or \
IMAGE_FILE_EXECUTABLE_IMAGE
;20h Handle ]2Gb addresses
;-------------------------------------
optional_header:
Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
Linker_version_major_and_minor dw 9 
Size_of_code        dd ntHeader;0x60;end_import-start
Size_of_init_data   dd 0xC0
Size_of_uninit_data dd 0
entry_point     dd start
base_of_code        dd begin
image_base      dq 0x140000000
section_alignment   dd 0x10
file_alignment      dd 0x10
OS_version_major_minor  dw 5,2
image_version_major_minor dd 0
subsystem_version_major_minor dw 5,2
Win32_version       dd 0
size_of_image       dd end_import
size_of_header      dd begin
checksum        dd 0
subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
Stack_allocation    dq 0x100000
Stack_commit        dq 0x1000
Heap_allocation     dq 0x100000
Heap_commit     dq 0x1000
loader_flag     dd 0
number_of_dirs      dd (section_table-export_RVA)/8
export_RVA_size     dq 0
import_RVA_size     dd _import,0x3C;end_import-import
;------------------------------------------------
section_table       dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd 0x60;end_import-start
.Physical_offset    dd begin
.Relocations_and_Linenumbers dq 0
.Relocations_and_Linenumbers_count dd 0
.Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;------------------------------------------------
section_table1      dq '.rdata'
.virtual_size       dd 0xB6
.virtual_address    dd Import_Table
.Physical_size      dd 0xC0;end_import-start
.Physical_offset    dd Import_Table
.Relocations_and_Linenumbers dq 0
.Relocations_and_Linenumbers_count1 dd 0
.Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
;-------------------------------------------------
dq 0
begin:
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!", 0
start:
    sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d    ; 4. argument: r9d = uType = 0
    lea r8, [MsgCaption]  ; 3. argument: r8  = caption
    lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
    xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
    call [MessageBox]
    xor ecx, ecx    ; ecx = exit code
    call [ExitProcess]
;---------секция импорта---------------------------------------
times 11 db 0
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
MessageBox  dq _MessageBox,0
_import:
dd 0,0,0,user32_dll,user32_table
dd 0,0,0,kernel32_dll,kernel32_table
dq 0,0,0
_MessageBox     db 0xE2,1,"MessageBoxA",0
user32_dll db "user32",0,0
_ExitProcess        db 0xBC,0,"ExitProcess",0
kernel32_dll    db "kernel32"
end_import:
Приложение сократилось до размера 508 байт
заголовоккод и данныеимпортобщий размер
27296140508 байт
7.
сокращаем DOS-stub до 16 байт, смещение e_lfanew оказывается в поле base_of_code. При размещении в нем числа 10h получаем работоспособное приложение.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
include 'win64a.inc'
use64
org 0
;--------DOS-stub-------------------------------
Signature       dw IMAGE_DOS_SIGNATURE;'MZ'
times 14 db 0
;-------PE-заголовок------------------------------
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
Machine         dw PROCESSOR_AMD_X8664
Count_of_section    dw 2
times 3 dd 0
Size_of_optional_header dw section_table-optional_header;
Characteristics     dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h Handle >2Gb addresses
;-------------------------------------
optional_header:
Magic_optional_header   dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
Linker_version_major_and_minor dw 9 
Size_of_code        dd 0x60;end_import-start
Size_of_init_data   dd 0xC0
Size_of_uninit_data dd 0
entry_point     dd start
base_of_code        dd ntHeader
image_base      dq 0x140000000
section_alignment   dd 0x10
file_alignment      dd 0x10
OS_version_major_minor  dw 5,2
image_version_major_minor dd 0
subsystem_version_major_minor dw 5,2
Win32_version       dd 0
size_of_image       dd end_import
size_of_header      dd begin
checksum        dd 0
subsystem       dw IMAGE_SUBSYSTEM_WINDOWS_GUI
DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
Stack_allocation    dq 0x100000
Stack_commit        dq 0x1000
Heap_allocation     dq 0x100000
Heap_commit     dq 0x1000
loader_flag     dd 0
number_of_dirs      dd (section_table-export_RVA)/8
export_RVA_size     dq 0
import_RVA_size     dd _import,0x3C;end_import-import
;------------------------------------------------
section_table       dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd 0x60;end_import-start
.Physical_offset    dd begin
.Relocations_and_Linenumbers dq 0
.Relocations_and_Linenumbers_count dd 0
.Attributes     dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;------------------------------------------------
section_table1      dq '.rdata'
.virtual_size       dd 0xB6
.virtual_address    dd Import_Table
.Physical_size      dd 0xC0;end_import-start
.Physical_offset    dd Import_Table
.Relocations_and_Linenumbers dq 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
;-------------------------------------------------
dq 0
begin:
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!", 0
start:
    sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d    ; 4. argument: r9d = uType = 0
    lea r8, [MsgCaption]  ; 3. argument: r8  = caption
    lea rdx,[MsgBoxText]  ; 2. argument: edx = window text
    xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
    call [MessageBox]
    xor ecx, ecx    ; ecx = exit code
    call [ExitProcess]
;---------секция импорта---------------------------------------
times 11 db 0
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
MessageBox  dq _MessageBox,0
_import:
dd 0,0,0,user32_dll,user32_table
dd 0,0,0,kernel32_dll,kernel32_table
dq 0,0,0
_MessageBox     db 0xE2,1,"MessageBoxA",0
user32_dll db "user32",0,0
_ExitProcess        db 0xBC,0,"ExitProcess",0
kernel32_dll    db "kernel32"
end_import:
Приложение сократилось до размера 492 байт
заголовоккод и данныеимпортобщий размер
25696140492 байт
8.
В начале секции импорта убрали 8 байт выравнивания.
Мы удалили таблицу, которую используют для импорта по ординалам. Внимание, вопрос ― А зачем нам в таком случае ординалы перед названием функций? Обнуляем ординалы и задаем себе следующий вопрос ― Если ординал нулевой ― нужен ли нулевой байт на конце строки? Удаляем нуль-терминаторы, а заодно и нулевые байты, которые делали адреса названий функции кратными двум, так как импортируемые функции теперь у нас заканчиваются двумя нулевыми байтами (а это расточительно!) ― помещаем в поле ординала последний символ названия функции или названия dll и нуль-терминатор
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
include 'win64a.inc'
org 0
use64
 
Signature:      dq IMAGE_DOS_SIGNATURE,0
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine        dw PROCESSOR_AMD_X8664
.Count_of_section   dw 1
.TimeStump      dd 0
.Symbol_table_offset    dd 0;ntHeader
.Symbol_table_count dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics    dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header  dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_and_minor dw 9 
.Size_of_code       dd 0
.Size_of_init_data  dd 0xC0
.Size_of_uninit_data    dd 0
.entry_point        dd begin
.base_of_code       dd ntHeader
.image_base     dq 0x140000000
.section_alignment  dd 0x10
.file_alignment     dd 0x10
.OS_version_major_minor dw 5,2
.image_version_major_minor dd 0
.subsystem_version_major_minor dw 5,2
.Win32_version      dd 0
.size_of_image      dd end_import
.size_of_header     dd begin
.checksum       dd 0
.subsystem      dw IMAGE_SUBSYSTEM_WINDOWS_GUI
.DLL_flag       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation   dq 0x100000
.Stack_commit       dq 0x1000
.Heap_allocation    dq 0x100000
.Heap_commit        dq 0x1000
.loader_flag        dd 0
.number_of_dirs     dd (section_table-export_RVA_size)/8;16
export_RVA_size     dq 0
.import_RVA     dd import_
.import_size        dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;------------------------------------------------
begin:
    sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d    ; 4. argument: r9d = uType = 0
    lea r8, [MsgCaption]; 3. argument: r8  = caption
    lea rdx,[MsgBoxText]; 2. argument: edx = window text
    xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
    call [MessageBox]
    xor ecx, ecx    ; ecx = exit code
    call [ExitProcess]
;------------------------------------------------
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!",0
;------------------------------------------------
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
MessageBox  dq _MessageBox 
import_:
dd 0,0,0,user32_dll,  user32_table
dd 0,0,0,kernel32_dll,kernel32_table
user32_dll  db "user32",0,0
kernel32_dll    db "kernel32"
dw 0
_MessageBox db 0,0,"MessageBox"
_ExitProcess    db "A",0,"ExitProcess"
end_import:
Приложение сократилось до размера 454 байт
заголовоккод и данныеимпортобщий размер
24888118454 байт
9.
количество секций делаем равным 1, удаляем секцию '.rdata' и размещаем импорт в секции кода, помещаю данные за вызовом функции ExitProcess, удаляю 3 нулевых байта перед секцией import.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
include 'win64a.inc'
org 0
use64
Signature:      dq IMAGE_DOS_SIGNATURE,0
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine        dw PROCESSOR_AMD_X8664
.Count_of_section   dw 1
.TimeStump      dd 0
.Symbol_table_offset    dd 0;ntHeader
.Symbol_table_count dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics    dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header  dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_and_minor dw 9 
.Size_of_code       dd 0
.Size_of_init_data  dd 0xC0
.Size_of_uninit_data    dd 0
.entry_point        dd begin
.base_of_code       dd ntHeader
.image_base     dq 0x140000000
.section_alignment  dd 0x10
.file_alignment     dd 0x10
.OS_version_major_minor dw 5,2
.image_version_major_minor dd 0
.subsystem_version_major_minor dw 5,2
.Win32_version      dd 0
.size_of_image      dd end_import
.size_of_header     dd begin
.checksum       dd 0
.subsystem      dw IMAGE_SUBSYSTEM_WINDOWS_GUI
.DLL_flag       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation   dq 0x100000
.Stack_commit       dq 0x1000
.Heap_allocation    dq 0x100000
.Heap_commit        dq 0x1000
.loader_flag        dd 0
.number_of_dirs     dd (section_table-export_RVA_size)/8;16
export_RVA_size     dq 0
.import_RVA     dd import_
.import_size        dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;------------------------------------------------
begin:
    sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d    ; 4. argument: r9d = uType = 0
    lea r8, [MsgCaption]; 3. argument: r8  = caption
    lea rdx,[MsgBoxText]; 2. argument: edx = window text
    xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
    call [MessageBox]
    xor ecx, ecx    ; ecx = exit code
    call [ExitProcess]
;------------------------------------------------
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!",0
;------------------------------------------------
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
MessageBox  dq _MessageBox
 
import_:
dd 0,0,0,user32_dll,  user32_table
dd 0,0,0,kernel32_dll,kernel32_table
user32_dll  db "user32",0,0
kernel32_dll    db "kernel32"
dw 0
_MessageBox db 0,0,"MessageBox"
_ExitProcess    db "A",0,"ExitProcess"
end_import:
Приложение сократилось до размера 400 байт
заголовоккод и данныеимпортобщий размер
20885107400 байт
1
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
08.07.2014, 12:51  [ТС] #10
Шаг шестой — алгоритмическая оптимизация. На сайте CodeProject я нашел статью Daniel Pistelli от 3 августа 2009 "Moving to Windows Vista x64" где предлагалось следующее простейшее предложение
C
1
2
3
4
5
6
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR szCmdLine, int iCmdShow)
{
    MessageBox(NULL, _T("Hello World!"), _T("My First x64 Application"), 0);
    return 0;
}
под дизассемблером оно выглядит вот так:
Код
.text:0000000000401220 sub_401220 proc near       ; CODE XREF: start+10E p

.text:0000000000401220
.text:0000000000401220 arg_0  = qword ptr 8
.text:0000000000401220 arg_8  = qword ptr 10h
.text:0000000000401220 arg_10 = qword ptr 18h
.text:0000000000401220 arg_18 = dword ptr 20h
.text:0000000000401220
.text:0000000000401220    mov [rsp+arg_18], r9d
.text:0000000000401225    mov [rsp+arg_10], r8
.text:000000000040122A    mov [rsp+arg_8], rdx
.text:000000000040122F    mov [rsp+arg_0], rcx

.text:0000000000401234    sub rsp, 28h
.text:0000000000401238    xor r9d, r9d            ; uType
.text:000000000040123B    lea r8, Caption         ; "My First x64 Application"
.text:0000000000401242    lea rdx, Text           ; "Hello World!"
.text:0000000000401249    xor ecx, ecx            ; hWnd
.text:000000000040124B    call cs:MessageBoxA
.text:0000000000401251    xor eax, eax
.text:0000000000401253    add rsp, 28h
.text:0000000000401257    retn
.text:0000000000401257 sub_401220 endp
Приложение нормально запускается и нормально закрывается. Делаем вывод ― без вызова функции ExitProcess можно вполне обойтись ― нужно всего лишь вернуть указатель стека на место.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
include 'win64a.inc'
org 0
use64
 
Signature:      dq IMAGE_DOS_SIGNATURE,0
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine        dw PROCESSOR_AMD_X8664
.Count_of_section   dw 1
.TimeStump      dd 0
.Symbol_table_offset    dd 0
.Symbol_table_count dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics    dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header  dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_and_minor dw 9 
.Size_of_code       dd 0
.Size_of_init_data  dd 0xC0
.Size_of_uninit_data    dd 0
.entry_point        dd begin
.base_of_code       dd ntHeader
.image_base     dq 0x140000000
.section_alignment  dd 0x10
.file_alignment     dd 0x10
.OS_version_major_minor dw 5,2
.image_version_major_minor dd 0
.subsystem_version_major_minor dw 5,2
.Win32_version      dd 0
.size_of_image      dd end_import
.size_of_header     dd begin
.checksum       dd 0
.subsystem      dw IMAGE_SUBSYSTEM_WINDOWS_GUI
.DLL_flag       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation   dq 0x100000
.Stack_commit       dq 0x1000
.Heap_allocation    dq 0x100000
.Heap_commit        dq 0x1000
.loader_flag        dd 0
.number_of_dirs     dd (section_table-export_RVA_size)/8
export_RVA_size     dq 0
.import_RVA     dd import_
.import_size        dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;-------------------------------------------------
begin:
    sub rsp, 28h    ; space for 4 arguments + 16byte aligned stack
    xor r9d, r9d    ; 4. argument: r9d = uType = 0
    lea r8, [MsgCaption]; 3. argument: r8  = caption
    lea rdx,[MsgBoxText]; 2. argument: edx = window text
    xor ecx, ecx    ; 1. argument: rcx = hWnd = NULL
    call [MessageBox]
    add rsp, 28h
    ret
;------------------------------------------------
MsgCaption db "Iczelion's tutorial #2", 0
MsgBoxText db "Win64 Assembly is Great!",0
;------------------------------------------------
Import_Table:
user32_table:
MessageBox  dq _MessageBox
import_:
dd 0,0,0,user32_dll,user32_table
dd 0
user32_dll    db "user32",0,0
dw 0
_MessageBox db 0,0,"MessageBoxA"
end_import:
Приложение сократилось до размера 345 байт.
заголовоккод и данныеимпортобщий размер
2088255345 байт
Итог: размер приложения удалось сократить примерно на порядок с 2560 до 345 байт
1
Миниатюры
Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
09.07.2014, 04:45  [ТС] #11
Шаблон оконного приложения для Win64
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
IMAGE_DOS_SIGNATURE     equ 5A4Dh
IMAGE_NT_SIGNATURE      equ 00004550h
PROCESSOR_AMD_X8664     equ 8664h
IMAGE_SCN_CNT_CODE      equ 00000020h
IMAGE_SCN_MEM_READ      equ 40000000h
IMAGE_SCN_MEM_WRITE     equ 80000000h
IMAGE_SCN_CNT_INITIALIZED_DATA  equ 00000040h
IMAGE_SUBSYSTEM_WINDOWS_GUI equ 2
IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 20Bh
IMAGE_FILE_RELOCS_STRIPPED  equ 1
IMAGE_FILE_EXECUTABLE_IMAGE equ 2
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
exebase             equ 400000h
include 'win64a.inc'
org 0
use64
;----------------------------------------------------
Signature:      dq IMAGE_DOS_SIGNATURE,0
ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine        dw PROCESSOR_AMD_X8664
.Count_of_section   dw 1
.TimeStump      dd 0
.Symbol_table_offset    dd 0
.Symbol_table_count dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics    dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header  dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_and_minor dw 9 
.Size_of_code       dd 0
.Size_of_init_data  dd 0xC0
.Size_of_uninit_data    dd 0
.entry_point        dd begin
.base_of_code       dd ntHeader
.image_base     dq exebase
.section_alignment  dd 0x10
.file_alignment     dd 0x10
.OS_version_major_minor dw 5,2
.image_version_major_minor dd 0
.subsystem_version_major_minor dw 5,2
.Win32_version      dd 0
.size_of_image      dd end_import
.size_of_header     dd begin
.checksum       dd 0
.subsystem      dw IMAGE_SUBSYSTEM_WINDOWS_GUI
.DLL_flag       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation   dq 0x100000
.Stack_commit       dq 0x1000
.Heap_allocation    dq 0x100000
.Heap_commit        dq 0x1000
.loader_flag        dd 0
.number_of_dirs     dd (section_table-export_RVA_size)/8
export_RVA_size     dq 0
.import_RVA     dd import_
.import_size        dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;-------------------------------------------------
begin:  enter ((8*12+sizeof.MSG+7) and 0FFFFFFF8h),0
;12 аргументов для функции CreateWindowEx, место под структуру MSG и делаем адрес кратным 8
        mov ecx,wc+exebase
        call [RegisterClass]
          ; +--------------------------+
          ; | creating the main window |
          ; +--------------------------+
        mov eax,exebase
        mov [rbp-40h],eax;дескриптор приложения
        shr eax,7;Special CreateWindow position value CW_USEDEFAULT=8000h
        xor ecx,ecx
        mov edx,_class+exebase;имя класса
        mov r8d,_title+exebase;заголовок
        mov r9d,WS_OVERLAPPEDWINDOW+WS_VISIBLE;стиль окна
        mov [rbp-70h],eax;X-координата левого верхнего угла окна, определяется Windows по умолчанию
        mov [rbp-68h],eax;Y-координата левого верхнего угла окна, определяется Windows по умолчанию
        mov [rbp-60h],eax;ширина окна, определяется Windows по умолчанию
        mov [rbp-58h],eax;высота окна, определяется Windows по умолчанию
        mov [rbp-50h],ecx;нет родительского окна
        mov [rbp-48h],ecx;нет меню
        mov [rbp-38h],ecx;без дополнительных аргументов
        call [CreateWindowEx]
          ; +---------------------------+
          ; | entering the message loop |
          ; +---------------------------+
        lea rdi,[rbp-((sizeof.MSG+7)and 0FFFFFFF8h)]
window_message_loop_start:
        mov rcx,rdi    ; lpMsg 
        xor edx,edx    ; hWnd 
        mov r8,rdx     ; wMsgFilterMin 
        mov r9,rdx     ; wMsgFilterMax                  
        call [GetMessage]
        mov rcx,rdi    ; lpMsg 
        call [DispatchMessage]
        jmp  window_message_loop_start
          ; +----------------------+
          ; | the window procedure |
          ; +----------------------+
WindowProc:;,hwnd,wmsg,wparam,lparam
        cmp edx,WM_DESTROY
        je wmDESTROY
        jmp [DefWindowProc]
wmDESTROY:xor ecx,ecx
        call [ExitProcess]
;-----------------------------------------------
  _title TCHAR "Iczelion's Tutorial #3:The Simplest Window in FASM",0 ;name of our window
  _class TCHAR 'FASMWIN64',0;name of class
  wc WNDCLASS 0,0,WindowProc+exebase,0,0,400000h,0,10003h,COLOR_WINDOW,NULL,_class+exebase
;-------------------------------------------------
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
RegisterClass   dq _RegisterClass
CreateWindowEx  dq _CreateWindowEx
DefWindowProc   dq _DefWindowProc
GetMessage      dq _GetMessage
DispatchMessage dq _DispatchMessage
import_:
dd 0,0,0,user32_dll,  user32_table
dd 0,0,0,kernel32_dll,kernel32_table
user32_dll      db "user32",0,0
kernel32_dll    db "kernel3",0,0
;Если IMAGE_IMPORT_DESCRIPTOR.ForwarderChain в замыкающей структуре не равняется 
;нулю, не работает.
_RegisterClass          db 0,0,'RegisterClass'
_CreateWindowEx         db 'A',0,'CreateWindowEx'
_DefWindowProc          db 'A',0,'DefWindowProc'
_GetMessage             db 'A',0,'GetMessage'
_DispatchMessage        db 'A',0,'DispatchMessage'
_ExitProcess            db 'A',0,"ExitProcess"
end_import:

Размер ехе-файла 665 байт.
Почему не используется макрос invoke, а вместо этого вызов WinAPI-функций записан "вручную"? Использование макроса порождает слишком много избыточного кода, например, sub rsp,N перед вызовом WinAPI-функции и add rsp,N после. Хотя это можно сделать только один раз для функции с самым большим количеством параметров.
1
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
10.07.2014, 06:59  [ТС] #12
Отрисовка текста в Win64
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_NT_SIGNATURE  equ 00004550h
PROCESSOR_AMD_X8664 equ 8664h
IMAGE_SCN_CNT_CODE  equ 00000020h
IMAGE_SCN_MEM_READ  equ 40000000h
IMAGE_SCN_MEM_WRITE equ 80000000h
IMAGE_SCN_CNT_INITIALIZED_DATA  equ 00000040h
IMAGE_SUBSYSTEM_WINDOWS_GUI equ 2
IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 20Bh
IMAGE_FILE_RELOCS_STRIPPED  equ 1
IMAGE_FILE_EXECUTABLE_IMAGE equ 2
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
exebase         equ 400000h
include 'win64a.inc'
org 0
use64
;----------------------------------------------------
Signature:  dq IMAGE_DOS_SIGNATURE,0
ntHeader    dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine    dw PROCESSOR_AMD_X8664
.Count_of_section   dw 1
.TimeStump  dd 0
.Symbol_table_offset    dd 0
.Symbol_table_count dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics    dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header  dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_and_minor dw 9 
.Size_of_code       dd 0
.Size_of_init_data  dd 0xC0
.Size_of_uninit_data    dd 0
.entry_point        dd begin
.base_of_code       dd ntHeader
.image_base dq exebase
.section_alignment  dd 0x10
.file_alignment     dd 0x10
.OS_version_major_minor dw 5,2
.image_version_major_minor dd 0
.subsystem_version_major_minor dw 5,2
.Win32_version      dd 0
.size_of_image      dd end_import
.size_of_header     dd begin
.checksum   dd 0
.subsystem  dw IMAGE_SUBSYSTEM_WINDOWS_GUI
.DLL_flag   dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation   dq 0x100000
.Stack_commit       dq 0x1000
.Heap_allocation    dq 0x100000
.Heap_commit        dq 0x1000
.loader_flag        dd 0
.number_of_dirs     dd (section_table-export_RVA_size)/8
export_RVA_size     dq 0
.import_RVA dd import_
.import_size        dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes  dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;-------------------------------------------------
begin:  enter 90h,0
    mov ecx,wc+exebase
    call [RegisterClassEx]
      ; +--------------------------+
      ; | creating the main window |
      ; +--------------------------+
     mov eax,exebase
    mov [rsp+50h],eax
    shr eax,7;eax=8000h=CW_USEDEFAULT Special CreateWindow position value in user64.inc
    xor ecx,ecx
    mov edx,_class+exebase
    mov r8d,_title+exebase
    mov r9d,WS_OVERLAPPEDWINDOW+WS_VISIBLE
    mov [rsp+20h],eax
    mov [rsp+28h],eax
    mov [rsp+30h],eax
    mov [rsp+38h],eax
    mov [rsp+40h],ecx
    mov [rsp+48h],ecx
    mov [rsp+58h],ecx
    call [CreateWindowEx]
      ; +---------------------------+
      ; | entering the message loop |
      ; +---------------------------+
      lea rdi,[rbp-30h]
      window_message_loop_start:
      mov rcx,rdi
    xor edx,edx
    mov r8,rdx
    mov r9,rdx  
    call [GetMessage]
    mov rcx,rdi
    call [DispatchMessage]
             jmp  window_message_loop_start
      ; +----------------------+
      ; | the window procedure |
      ; +----------------------+
proc WindowProc
hwnd      equ rbp+10h
;local ps:PAINTSTRUCT 8+4+16+4+4+36=72=48h
;local expRect:RECT 16=10h
        enter 90h,0
          mov  [hwnd],rcx
          cmp  edx,WM_PAINT
          je   wmPAINT
          cmp  edx,WM_DESTROY
          je   wmDESTROY
          cmp  edx,WM_SIZE
          je   wmPAINT
          leave
          jmp [DefWindowProc]
wmDESTROY:    xor ecx,ecx
          call [ExitProcess]
wmSIZE:  
wmPAINT:;rcx=hwnd
          lea rdx,[rbp-60h];addr ps
          call [BeginPaint]
          mov rcx,[hwnd]
          lea rdx,[rbp-18h];addr expRect
          call [GetClientRect]
          mov rcx,[rbp-60h];[ps.hdc]
          mov rdx,expTxt+exebase
          or r8d,-1
          lea r9,[rbp-18h];addr expRect
          mov dword [rsp+20h],DT_SINGLELINE or DT_CENTER or DT_VCENTER
          call [DrawText]
          mov rcx,[hwnd]
          lea rdx,[rbp-60h];addr ps
          call [EndPaint]
wmBYE:   leave
          retn
          endp
;------------------------------------------
  _title TCHAR "Iczelion's Tutorial #4:Painting with Text in FASM",0 ;name of our window
  _class TCHAR 'FASMWIN64',0;name of class
  wc WNDCLASSEX sizeof.WNDCLASSEX,0,WindowProc+exebase,0,0,exebase,0,10003h,COLOR_WINDOW,NULL,_class+exebase,NULL
  expTxt    db   'Win64 assembly with FASM is great and easy',0
;-------------------------------------------------------
Import_Table:
kernel32_table:
ExitProcess dq _ExitProcess,0
user32_table:
RegisterClassEx dq _RegisterClassEx
CreateWindowEx  dq _CreateWindowEx
DefWindowProc   dq _DefWindowProc
GetMessage  dq _GetMessage
DispatchMessage dq _DispatchMessage
BeginPaint  dq _BeginPaint
GetClientRect   dq _GetClientRect
DrawText    dq _DrawText
EndPaint    dq _EndPaint
;InvalidateRect  dq _InvalidateRect
import_:
dd 0,0,0,user32_dll,  user32_table
dd 0,0,0,kernel32_dll,kernel32_table
user32_dll  db "user32",0,0
kernel32_dll    db "kernel32",0,0
;Если IMAGE_IMPORT_DESCRIPTOR.ForwarderChain в замыкающей структуре не равняется 
;нулю, не работает.
_RegisterClassEx    db 0,0,'RegisterClassEx'
_CreateWindowEx     db 'A',0,'CreateWindowEx'
_DefWindowProc      db 'A',0,'DefWindowProc'
_GetMessage     db 'A',0,'GetMessage'
_DispatchMessage    db 'A',0,'DispatchMessage'
_ExitProcess        db 'A',0,"ExitProces"
_BeginPaint     db 's',0,'BeginPain'
_GetClientRect      db 't',0,'GetClientRec'
_DrawText       db 't',0,'DrawText'
_EndPaint       db 'A',0,'EndPaint'
end_import:

Размер ехе-файла 894 байта
2
Миниатюры
Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  
NoNaMe
Эксперт по компьютерным сетям
660 / 310 / 51
Регистрация: 10.06.2009
Сообщений: 1,388
25.03.2015, 05:53 #13
Пришлось побаловаться немного, но я добился своего!
Вот консольный вариант на тему FASM64.

Размер ехе-файла 320 байт.

Hellow World Windows CUI
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_NT_SIGNATURE  equ 00004550h
 
IMAGE_MACHINE_UNKNOWN   equ 0
IMAGE_MACHINE_I386      equ 014Ch  ; Intel 386
IMAGE_MACHINE_I860      equ 014Dh  ; Intel i860
IMAGE_MACHINE_R3000B    equ 0160h  ; MIPS R3000 big-endian
IMAGE_MACHINE_R3000L    equ 0162h  ; MIPS R3000 little-endian
IMAGE_MACHINE_R4000     equ 0166h  ; MIPS little-endian
IMAGE_MACHINE_R10000    equ 0168h  ; MIPS little-endian
IMAGE_MACHINE_WCEMIPSV2 equ 0169h  ; MIPS little-endian WCE v2
IMAGE_MACHINE_OLD_ALPHA equ 0184h  ; old Alpha AXP
IMAGE_MACHINE_ALPHA     equ 0184h  ; Alpha AXP
IMAGE_MACHINE_SH3       equ 01A2h  ; Hitachi SH3 little-endian
IMAGE_MACHINE_SH3DSP    equ 01A3h  ; Hitachi SH3 little-endian DSP
IMAGE_MACHINE_SH3E      equ 01A4h  ; Hitachi SH3E little-endian
IMAGE_MACHINE_SH4       equ 01A6h  ; Hitachi SH4 little-endian
IMAGE_MACHINE_SH5       equ 01A8h  ; Hitachi SH5
IMAGE_MACHINE_ARM       equ 01C0h  ; ARM Little-Endian
IMAGE_MACHINE_THUMB     equ 01C2h  ; Thumb
IMAGE_MACHINE_AM33      equ 01D3h  ; Matsushita AM33
IMAGE_MACHINE_POWERPC   equ 01F0h  ; IBM PowerPC Little-Endian
IMAGE_MACHINE_POWERPCFP equ 01F1h  ; IBM PowerPC with floating point support
IMAGE_MACHINE_IA64      equ 0200h  ; Intel IA64
IMAGE_MACHINE_MIPS16    equ 0266h  ; MIPS16
IMAGE_MACHINE_ALPHA64   equ 0284h  ; ALPHA64
IMAGE_MACHINE_MIPSFPU   equ 0366h  ; MIPS with FPU
IMAGE_MACHINE_MIPSFPU16 equ 0466h  ; MIPS16 with FPU
IMAGE_MACHINE_TRICORE   equ 0520h  ; Infineon
IMAGE_MACHINE_CEF       equ 0CEFh                   ;UNKNOWN
IMAGE_MACHINE_EBC       equ 0EBCh  ; EFI Byte Code
IMAGE_MACHINE_AMD64     equ 8664h  ; AMD AMD64 (K8)
IMAGE_MACHINE_M32R      equ 9041h  ; Mitsubishi M32R little-endian
IMAGE_MACHINE_CEE       equ C0EEh                   ;UNKNOWN
 
;IMAGE_SCN_TYPE_REG                  equ 00000000h  ; Reserved.
;IMAGE_SCN_TYPE_DSECT                equ 00000001h  ; Reserved.
;IMAGE_SCN_TYPE_NOLOAD               equ 00000002h  ; Reserved.
;IMAGE_SCN_TYPE_GROUP                equ 00000004h  ; Reserved.
IMAGE_SCN_TYPE_NO_PAD                equ 00000008h  ; Reserved.
;IMAGE_SCN_TYPE_COPY                 equ 00000010h  ; Reserved.
 
IMAGE_SCN_CNT_CODE                   equ 00000020h  ; Section contains code.
IMAGE_SCN_CNT_INITIALIZED_DATA       equ 00000040h  ; Section contains initialized data.
IMAGE_SCN_CNT_UNINITIALIZED_DATA     equ 00000080h  ; Section contains uninitialized data.
 
IMAGE_SCN_LNK_OTHER                  equ 00000100h  ; Reserved.
IMAGE_SCN_LNK_INFO                   equ 00000200h  ; Section contains comments or some  other type of information.
;IMAGE_SCN_TYPE_OVER                 equ 00000400h  ; Reserved.
IMAGE_SCN_LNK_REMOVE                 equ 00000800h  ; Section contents will not become part of image.
IMAGE_SCN_LNK_COMDAT                 equ 00001000h  ; Section contents comdat.
;                                    equ 00002000h  ; Reserved
;IMAGE_SCN_MEM_PROTECTED             equ 00004000h  ; Reserved IMAGE_SCN_MEM_PROTECTED - Obsolete
IMAGE_SCN_NO_DEFER_SPEC_EXC          equ 00004000h  ; Reset speculative exceptions handling bits in the TLB entries for this section.
IMAGE_SCN_GPREL                      equ 00008000h  ; Section content can be accessed relative to GP
IMAGE_SCN_MEM_FARDATA                equ 000080000  ;
;IMAGE_SCN_MEM_SYSHEAP               equ 00010000h  ; Reserved IMAGE_SCN_MEM_SYSHEAP  - Obsolete
IMAGE_SCN_MEM_PURGEABLE              equ 000200000  ;
IMAGE_SCN_MEM_16BIT                  equ 000200000  ;
IMAGE_SCN_MEM_LOCKED                 equ 000400000  ;
IMAGE_SCN_MEM_PRELOAD                equ 000800000  ;
 
IMAGE_SCN_ALIGN_1BYTES               equ 00100000h  ;
IMAGE_SCN_ALIGN_2BYTES               equ 00200000h  ;
IMAGE_SCN_ALIGN_4BYTES               equ 00300000h  ;
IMAGE_SCN_ALIGN_8BYTES               equ 00400000h  ;
IMAGE_SCN_ALIGN_16BYTES              equ 00500000h  ; Default alignment if no others are specified.
IMAGE_SCN_ALIGN_32BYTES              equ 00600000h  ;
IMAGE_SCN_ALIGN_64BYTES              equ 00700000h  ;
IMAGE_SCN_ALIGN_128BYTES             equ 00800000h  ;
IMAGE_SCN_ALIGN_256BYTES             equ 00900000h  ;
IMAGE_SCN_ALIGN_512BYTES             equ 00A00000h  ;
IMAGE_SCN_ALIGN_1024BYTES            equ 00B00000h  ;
IMAGE_SCN_ALIGN_2048BYTES            equ 00C00000h  ;
IMAGE_SCN_ALIGN_4096BYTES            equ 00D00000h  ;
IMAGE_SCN_ALIGN_8192BYTES            equ 00E00000h  ;
 
;Unused                               equ 00F00000h  ;
IMAGE_SCN_ALIGN_MASK                 equ 00F00000h  ;
 
IMAGE_SCN_LNK_NRELOC_OVFL            equ 01000000h  ; Section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE            equ 02000000h  ; Section can be discarded.
IMAGE_SCN_MEM_NOT_CACHED             equ 04000000h  ; Section is not cachable.
IMAGE_SCN_MEM_NOT_PAGED              equ 08000000h  ; Section is not pageable.
IMAGE_SCN_MEM_SHARED                 equ 10000000h  ; Section is shareable.
IMAGE_SCN_MEM_EXECUTE                equ 20000000h  ; Section is executable.
IMAGE_SCN_MEM_READ                   equ 40000000h  ; Section is readable.
IMAGE_SCN_MEM_WRITE                  equ 80000000h  ; Section is writeable.
 
IMAGE_SUBSYSTEM_UNKNOWN                  equ 0    ;  Unknown subsystem.
IMAGE_SUBSYSTEM_NATIVE                   equ 1    ;  Image doesn't require a subsystem.
IMAGE_SUBSYSTEM_WINDOWS_GUI              equ 2    ;  Image runs in the Windows GUI subsystem.
IMAGE_SUBSYSTEM_WINDOWS_CUI              equ 3    ;  Image runs in the Windows character subsystem.
IMAGE_SUBSYSTEM_OS2_CUI                  equ 5    ;  image runs in the OS/2 character subsystem.
IMAGE_SUBSYSTEM_POSIX_CUI                equ 7    ;  image runs in the Posix character subsystem.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS           equ 8    ;  image is a native Win9x driver.
IMAGE_SUBSYSTEM_WINDOWS_CE               equ 9                          
IMAGE_SUBSYSTEM_EFI_APPLICATION          equ 10
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  equ 11
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER       equ 12
IMAGE_SUBSYSTEM_EFI_ROM                  equ 13
IMAGE_SUBSYSTEM_XBOX                     equ 14
IMAGE_SUBSYSTEM_UNKNOWN2                 equ 15
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION equ 16
 
IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 523  ;20Bh    ; NT MAGIC translation code x64
IMAGE_NT_OPTIONAL_HDR32_MAGIC   equ 267  ;10Bh    ; NT MAGIC translation code x32
 
IMAGE_FILE_RELOCS_STRIPPED  equ 1
IMAGE_FILE_EXECUTABLE_IMAGE equ 2
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
exebase         equ 400000h
include 'include\WIN64A.INC'
org 0
use64
 
Signature:               dq IMAGE_DOS_SIGNATURE,0
ntHeader                 dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine                 dw IMAGE_MACHINE_AMD64
.Count_of_section        dw 1
.TimeStump               dd 0
.Symbol_table_offset     dd 0
.Symbol_table_count      dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics         dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header          dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_minor     db 9,0
.Size_of_code                   dd 0x60
.Size_of_init_data              dd 0x00
.Size_of_uninit_data            dd 0
.entry_point                    dd begin
.base_of_code                   dd ntHeader
.image_base                     dq 0x140000000
.section_alignment              dd 0x10
.file_alignment                 dd 0x10
.OS_version_major_minor         dw 5,2
.image_version_major_minor      dd 0
.subsystem_version_major_minor  dw 5,2
.Win32_version                  dd 0
.size_of_image                  dd end_import-1h
.size_of_header                 dd begin
.checksum                       dd 0x8C1B  ; calc checksum
.subsystem                      dw IMAGE_SUBSYSTEM_WINDOWS_CUI
.DLL_flag                       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation               dq 0x100000
.Stack_commit                   dq 0x1000
.Heap_allocation                dq 0x100000
.Heap_commit                    dq 0x1000
.loader_flag                    dd 0
.number_of_dirs                 dd (section_table-export_RVA_size)/8
export_RVA_size                 dq 0
.import_RVA                     dd import_
.import_size                    dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;-------------------------------------------------
begin:
    mov ecx, 7
    lea rdx, [MsgText]
    mov r8d, 18h
    xor r9d, r9d
    call [WriteConsole]
    ret
;------------------------------------------------
MsgText db "Win64 Assembly is Great!",0
;------------------------------------------------
Import_Table:
kernel32_table:
WriteConsole  dq _WriteConsole
import_:
dd 0,0,0,kernel32_dll,kernel32_table
dd 0
kernel32_dll    db "kernel32",0,0
dw 0
_WriteConsole db 0,0,"WriteConsoleA"
end_import:
1
NoNaMe
Эксперт по компьютерным сетям
660 / 310 / 51
Регистрация: 10.06.2009
Сообщений: 1,388
06.04.2015, 19:14 #14
Я думал что меньше некуда.
Итог готовый бинарник размером 313байт
Hellow World Windows CUI WriteFile
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_NT_SIGNATURE  equ 00004550h
 
IMAGE_MACHINE_UNKNOWN   equ 0
IMAGE_MACHINE_I386      equ 014Ch  ; Intel 386
IMAGE_MACHINE_I860      equ 014Dh  ; Intel i860
IMAGE_MACHINE_R3000B    equ 0160h  ; MIPS R3000 big-endian
IMAGE_MACHINE_R3000L    equ 0162h  ; MIPS R3000 little-endian
IMAGE_MACHINE_R4000     equ 0166h  ; MIPS little-endian
IMAGE_MACHINE_R10000    equ 0168h  ; MIPS little-endian
IMAGE_MACHINE_WCEMIPSV2 equ 0169h  ; MIPS little-endian WCE v2
IMAGE_MACHINE_OLD_ALPHA equ 0184h  ; old Alpha AXP
IMAGE_MACHINE_ALPHA     equ 0184h  ; Alpha AXP
IMAGE_MACHINE_SH3       equ 01A2h  ; Hitachi SH3 little-endian
IMAGE_MACHINE_SH3DSP    equ 01A3h  ; Hitachi SH3 little-endian DSP
IMAGE_MACHINE_SH3E      equ 01A4h  ; Hitachi SH3E little-endian
IMAGE_MACHINE_SH4       equ 01A6h  ; Hitachi SH4 little-endian
IMAGE_MACHINE_SH5       equ 01A8h  ; Hitachi SH5
IMAGE_MACHINE_ARM       equ 01C0h  ; ARM Little-Endian
IMAGE_MACHINE_THUMB     equ 01C2h  ; Thumb
IMAGE_MACHINE_AM33      equ 01D3h  ; Matsushita AM33
IMAGE_MACHINE_POWERPC   equ 01F0h  ; IBM PowerPC Little-Endian
IMAGE_MACHINE_POWERPCFP equ 01F1h  ; IBM PowerPC with floating point support
IMAGE_MACHINE_IA64      equ 0200h  ; Intel IA64
IMAGE_MACHINE_MIPS16    equ 0266h  ; MIPS16
IMAGE_MACHINE_ALPHA64   equ 0284h  ; ALPHA64
IMAGE_MACHINE_MIPSFPU   equ 0366h  ; MIPS with FPU
IMAGE_MACHINE_MIPSFPU16 equ 0466h  ; MIPS16 with FPU
IMAGE_MACHINE_TRICORE   equ 0520h  ; Infineon
IMAGE_MACHINE_CEF       equ 0CEFh                   ;UNKNOWN
IMAGE_MACHINE_EBC       equ 0EBCh  ; EFI Byte Code
IMAGE_MACHINE_AMD64     equ 8664h  ; AMD AMD64 (K8)
IMAGE_MACHINE_M32R      equ 9041h  ; Mitsubishi M32R little-endian
IMAGE_MACHINE_CEE       equ C0EEh                   ;UNKNOWN
 
;IMAGE_SCN_TYPE_REG                  equ 00000000h  ; Reserved.
;IMAGE_SCN_TYPE_DSECT                equ 00000001h  ; Reserved.
;IMAGE_SCN_TYPE_NOLOAD               equ 00000002h  ; Reserved.
;IMAGE_SCN_TYPE_GROUP                equ 00000004h  ; Reserved.
IMAGE_SCN_TYPE_NO_PAD                equ 00000008h  ; Reserved.
;IMAGE_SCN_TYPE_COPY                 equ 00000010h  ; Reserved.
 
IMAGE_SCN_CNT_CODE                   equ 00000020h  ; Section contains code.
IMAGE_SCN_CNT_INITIALIZED_DATA       equ 00000040h  ; Section contains initialized data.
IMAGE_SCN_CNT_UNINITIALIZED_DATA     equ 00000080h  ; Section contains uninitialized data.
 
IMAGE_SCN_LNK_OTHER                  equ 00000100h  ; Reserved.
IMAGE_SCN_LNK_INFO                   equ 00000200h  ; Section contains comments or some  other type of information.
;IMAGE_SCN_TYPE_OVER                 equ 00000400h  ; Reserved.
IMAGE_SCN_LNK_REMOVE                 equ 00000800h  ; Section contents will not become part of image.
IMAGE_SCN_LNK_COMDAT                 equ 00001000h  ; Section contents comdat.
;                                    equ 00002000h  ; Reserved
;IMAGE_SCN_MEM_PROTECTED             equ 00004000h  ; Reserved IMAGE_SCN_MEM_PROTECTED - Obsolete
IMAGE_SCN_NO_DEFER_SPEC_EXC          equ 00004000h  ; Reset speculative exceptions handling bits in the TLB entries for this section.
IMAGE_SCN_GPREL                      equ 00008000h  ; Section content can be accessed relative to GP
IMAGE_SCN_MEM_FARDATA                equ 000080000  ;
;IMAGE_SCN_MEM_SYSHEAP               equ 00010000h  ; Reserved IMAGE_SCN_MEM_SYSHEAP  - Obsolete
IMAGE_SCN_MEM_PURGEABLE              equ 000200000  ;
IMAGE_SCN_MEM_16BIT                  equ 000200000  ;
IMAGE_SCN_MEM_LOCKED                 equ 000400000  ;
IMAGE_SCN_MEM_PRELOAD                equ 000800000  ;
 
IMAGE_SCN_ALIGN_1BYTES               equ 00100000h  ;
IMAGE_SCN_ALIGN_2BYTES               equ 00200000h  ;
IMAGE_SCN_ALIGN_4BYTES               equ 00300000h  ;
IMAGE_SCN_ALIGN_8BYTES               equ 00400000h  ;
IMAGE_SCN_ALIGN_16BYTES              equ 00500000h  ; Default alignment if no others are specified.
IMAGE_SCN_ALIGN_32BYTES              equ 00600000h  ;
IMAGE_SCN_ALIGN_64BYTES              equ 00700000h  ;
IMAGE_SCN_ALIGN_128BYTES             equ 00800000h  ;
IMAGE_SCN_ALIGN_256BYTES             equ 00900000h  ;
IMAGE_SCN_ALIGN_512BYTES             equ 00A00000h  ;
IMAGE_SCN_ALIGN_1024BYTES            equ 00B00000h  ;
IMAGE_SCN_ALIGN_2048BYTES            equ 00C00000h  ;
IMAGE_SCN_ALIGN_4096BYTES            equ 00D00000h  ;
IMAGE_SCN_ALIGN_8192BYTES            equ 00E00000h  ;
 
;Unused                               equ 00F00000h  ;
IMAGE_SCN_ALIGN_MASK                 equ 00F00000h  ;
 
IMAGE_SCN_LNK_NRELOC_OVFL            equ 01000000h  ; Section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE            equ 02000000h  ; Section can be discarded.
IMAGE_SCN_MEM_NOT_CACHED             equ 04000000h  ; Section is not cachable.
IMAGE_SCN_MEM_NOT_PAGED              equ 08000000h  ; Section is not pageable.
IMAGE_SCN_MEM_SHARED                 equ 10000000h  ; Section is shareable.
IMAGE_SCN_MEM_EXECUTE                equ 20000000h  ; Section is executable.
IMAGE_SCN_MEM_READ                   equ 40000000h  ; Section is readable.
IMAGE_SCN_MEM_WRITE                  equ 80000000h  ; Section is writeable.
 
IMAGE_SUBSYSTEM_UNKNOWN                  equ 0    ;  Unknown subsystem.
IMAGE_SUBSYSTEM_NATIVE                   equ 1    ;  Image doesn't require a subsystem.
IMAGE_SUBSYSTEM_WINDOWS_GUI              equ 2    ;  Image runs in the Windows GUI subsystem.
IMAGE_SUBSYSTEM_WINDOWS_CUI              equ 3    ;  Image runs in the Windows character subsystem.
IMAGE_SUBSYSTEM_OS2_CUI                  equ 5    ;  image runs in the OS/2 character subsystem.
IMAGE_SUBSYSTEM_POSIX_CUI                equ 7    ;  image runs in the Posix character subsystem.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS           equ 8    ;  image is a native Win9x driver.
IMAGE_SUBSYSTEM_WINDOWS_CE               equ 9                          
IMAGE_SUBSYSTEM_EFI_APPLICATION          equ 10
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  equ 11
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER       equ 12
IMAGE_SUBSYSTEM_EFI_ROM                  equ 13
IMAGE_SUBSYSTEM_XBOX                     equ 14
IMAGE_SUBSYSTEM_UNKNOWN2                 equ 15
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION equ 16
 
IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 523  ;20Bh    ; NT MAGIC translation code x64
IMAGE_NT_OPTIONAL_HDR32_MAGIC   equ 267  ;10Bh    ; NT MAGIC translation code x32
 
IMAGE_FILE_RELOCS_STRIPPED  equ 1
IMAGE_FILE_EXECUTABLE_IMAGE equ 2
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
exebase         equ 400000h
include 'include\WIN64A.INC'
org 0
use64
 
Signature:               dq IMAGE_DOS_SIGNATURE,0
ntHeader                 dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine                 dw IMAGE_MACHINE_AMD64
.Count_of_section        dw 1
.TimeStump               dd 0
.Symbol_table_offset     dd 0
.Symbol_table_count      dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics         dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header          dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_minor     db 9,0
.Size_of_code                   dd 0x60
.Size_of_init_data              dd 0x00
.Size_of_uninit_data            dd 0
.entry_point                    dd begin
.base_of_code                   dd ntHeader
.image_base                     dq 0x140000000
.section_alignment              dd 0x10
.file_alignment                 dd 0x10
.OS_version_major_minor         dw 5,2
.image_version_major_minor      dd 0
.subsystem_version_major_minor  dw 5,2
.Win32_version                  dd 0
.size_of_image                  dd end_import-1h
.size_of_header                 dd begin
.checksum                       dd 0x8C1B  ; calc checksum
.subsystem                      dw IMAGE_SUBSYSTEM_WINDOWS_CUI
.DLL_flag                       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation               dq 0x100000
.Stack_commit                   dq 0x1000
.Heap_allocation                dq 0x100000
.Heap_commit                    dq 0x1000
.loader_flag                    dd 0
.number_of_dirs                 dd (section_table-export_RVA_size)/8
export_RVA_size                 dq 0
.import_RVA                     dd import_
.import_size                    dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;-------------------------------------------------
begin:
    mov ecx, 7
    lea rdx, [MsgText]
    mov r8d, 18h
    call [WriteFile]
ret;-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-
;------------------------------------------------
MsgText db "Win64 Assembly is Great!",0
;------------------------------------------------
Import_Table:
kernel32_table:
WriteFile  dq _WriteFile
import_:
dd 0,0,0,kernel32_dll,kernel32_table
dd 0
kernel32_dll    db "kernel32",0,0
dw 0
_WriteFile db 0,0,"WriteFile"
end_import:
1
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
07.04.2015, 06:05  [ТС] #15
NoNaMe,
вот вариант "окна с меню", который я написал на FASM,
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
format PE64 GUI 5.0
entry WinMain 
include 'win64a.inc' 
ZZZ_TEST equ 0 
ZZZ_OPEN equ 1 
ZZZ_SAVE equ 2 
ZZZ_EXIT equ 3 
 
section '.text' code readable writeable executable 
  _title TCHAR 'Iczelion Tutorial #8',0 ;name of our window 
  _class TCHAR 'FASMWIN64',0;name of class 
  wc WNDCLASSEX sizeof.WNDCLASSEX,0,WindowProc,0,0,IMAGE_BASE,0,10005h,COLOR_WINDOW,NULL,_class,NULL
 
     menu_name  db  'ZZZ_Menu',0 
test_msg    db  'You select menu item TEST',0 
open_msg    db  'You select menu item OPEN',0 
save_msg    db  'You select menu item SAVE',0 
menu_handlers dq test_msg, open_msg, save_msg 
 
proc WinMain
IMAGE_BASE = $-rva $
local msg:MSG
      ; +------------------------------+ 
      ; | registering the window class | 
      ; +------------------------------+
      sub rsp,20h
      xor ebx,ebx
      lea ecx,[wc]
      call [RegisterClassEx]
      mov edx,30
      mov ecx,IMAGE_BASE
      call [LoadMenu]
      ; +--------------------------+ 
      ; | creating the main window | 
      ; +--------------------------+
      sub rsp,40h
      xor ecx,ecx
      lea edx,[_class]
      lea r8,[_title]
      mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE
      mov [rsp+58h],rbx
      mov qword [rsp+50h],IMAGE_BASE
      mov [rsp+48h],rax
      mov [rsp+40h],rbx
      mov eax,CW_USEDEFAULT
      mov [rsp+38h],rax
      mov [rsp+30h],rax
      mov [rsp+28h],rax
      mov [rsp+20h],rax
      call [CreateWindowEx]
      add rsp,40h
      lea edi,[msg]
      ; +---------------------------+ 
      ; | entering the message loop | 
      ; +---------------------------+ 
window_message_loop_start:
      mov ecx,edi
      xor edx,edx
      mov r8,rbx
      mov r9,rbx
      call [GetMessage]
      mov ecx,edi
      call [DispatchMessage]
      jmp  window_message_loop_start
endp 
 
 
 
      ; +----------------------+ 
      ; | the window procedure | 
      ; +----------------------+ 
      proc WindowProc,hWnd,uMsg,wParam,lParam 
           cmp  edx,WM_COMMAND
           je   wmCOMMAND 
           cmp  edx,WM_DESTROY
           je   wmDESTROY 
wmDEFAULT:     leave 
           jmp [DefWindowProc] 
wmDESTROY:     xor ecx,ecx
           call [ExitProcess]
wmCOMMAND:     cmp r8,ZZZ_EXIT 
           je   wmDESTROY 
show_msg:      sub rsp,20h 
           mov r9,rbx;r9=MB_OK
           mov rdx,[menu_handlers+r8*8] 
           lea r8,[menu_name] 
           call [MessageBox] 
           add rsp,20h 
wmBYE:         ret 
      endp 
 
section '.idata' import data readable writeable 
     library   KERNEL32, 'KERNEL32.DLL',\ 
           USER32,   'USER32.DLL' 
 
     import    KERNEL32,\ 
           ExitProcess,    'ExitProcess' 
 
     import    USER32,\ 
           RegisterClassEx,    'RegisterClassExA',\ 
           CreateWindowEx,     'CreateWindowExA',\ 
           DefWindowProc,      'DefWindowProcA',\ 
           LoadMenu,       'LoadMenuA',\ 
           GetMessage,     'GetMessageA',\ 
           MessageBox,     'MessageBoxA',\ 
           DispatchMessage,    'DispatchMessageA' 
 
section '.rsrc' resource data readable 
     directory RT_MENU,appMenu 
 
     resource  appMenu,\ 
           30,LANG_ENGLISH,menuMain 
 
     menu menuMain 
      menuitem '&File',0,MFR_POPUP 
      menuitem '&Test',ZZZ_TEST,MFT_STRING 
      menuitem '&Open',ZZZ_OPEN,MFT_STRING 
      menuitem '&Save',ZZZ_SAVE,MFT_STRING 
      menuseparator 
      menuitem '&Exit',ZZZ_EXIT,MFR_END 
 
      menuitem '&Exit',ZZZ_EXIT,MFR_END
помоги что-либо подобное сделать на MASM с использованием ml64, link и rc
0
Миниатюры
Все, что нужно знать, чтобы начать программировать для 64-разрядных версий  Windows  
Вложения
Тип файла: zip tut_08-2.zip (2.2 Кб, 12 просмотров)
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
08.04.2015, 10:05  [ТС] #16
Цитата Сообщение от NoNaMe Посмотреть сообщение
Я думал что меньше некуда.
Итог готовый бинарник размером 313байт
309
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
format binary as 'exe'
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_NT_SIGNATURE  equ 00004550h
 
IMAGE_MACHINE_UNKNOWN   equ 0
IMAGE_MACHINE_I386      equ 014Ch  ; Intel 386
IMAGE_MACHINE_I860      equ 014Dh  ; Intel i860
IMAGE_MACHINE_R3000B    equ 0160h  ; MIPS R3000 big-endian
IMAGE_MACHINE_R3000L    equ 0162h  ; MIPS R3000 little-endian
IMAGE_MACHINE_R4000     equ 0166h  ; MIPS little-endian
IMAGE_MACHINE_R10000    equ 0168h  ; MIPS little-endian
IMAGE_MACHINE_WCEMIPSV2 equ 0169h  ; MIPS little-endian WCE v2
IMAGE_MACHINE_OLD_ALPHA equ 0184h  ; old Alpha AXP
IMAGE_MACHINE_ALPHA     equ 0184h  ; Alpha AXP
IMAGE_MACHINE_SH3       equ 01A2h  ; Hitachi SH3 little-endian
IMAGE_MACHINE_SH3DSP    equ 01A3h  ; Hitachi SH3 little-endian DSP
IMAGE_MACHINE_SH3E      equ 01A4h  ; Hitachi SH3E little-endian
IMAGE_MACHINE_SH4       equ 01A6h  ; Hitachi SH4 little-endian
IMAGE_MACHINE_SH5       equ 01A8h  ; Hitachi SH5
IMAGE_MACHINE_ARM       equ 01C0h  ; ARM Little-Endian
IMAGE_MACHINE_THUMB     equ 01C2h  ; Thumb
IMAGE_MACHINE_AM33      equ 01D3h  ; Matsushita AM33
IMAGE_MACHINE_POWERPC   equ 01F0h  ; IBM PowerPC Little-Endian
IMAGE_MACHINE_POWERPCFP equ 01F1h  ; IBM PowerPC with floating point support
IMAGE_MACHINE_IA64      equ 0200h  ; Intel IA64
IMAGE_MACHINE_MIPS16    equ 0266h  ; MIPS16
IMAGE_MACHINE_ALPHA64   equ 0284h  ; ALPHA64
IMAGE_MACHINE_MIPSFPU   equ 0366h  ; MIPS with FPU
IMAGE_MACHINE_MIPSFPU16 equ 0466h  ; MIPS16 with FPU
IMAGE_MACHINE_TRICORE   equ 0520h  ; Infineon
IMAGE_MACHINE_CEF       equ 0CEFh                   ;UNKNOWN
IMAGE_MACHINE_EBC       equ 0EBCh  ; EFI Byte Code
IMAGE_MACHINE_AMD64     equ 8664h  ; AMD AMD64 (K8)
IMAGE_MACHINE_M32R      equ 9041h  ; Mitsubishi M32R little-endian
IMAGE_MACHINE_CEE       equ C0EEh                   ;UNKNOWN
 
;IMAGE_SCN_TYPE_REG                  equ 00000000h  ; Reserved.
;IMAGE_SCN_TYPE_DSECT                equ 00000001h  ; Reserved.
;IMAGE_SCN_TYPE_NOLOAD               equ 00000002h  ; Reserved.
;IMAGE_SCN_TYPE_GROUP                equ 00000004h  ; Reserved.
IMAGE_SCN_TYPE_NO_PAD                equ 00000008h  ; Reserved.
;IMAGE_SCN_TYPE_COPY                 equ 00000010h  ; Reserved.
 
IMAGE_SCN_CNT_CODE                   equ 00000020h  ; Section contains code.
IMAGE_SCN_CNT_INITIALIZED_DATA       equ 00000040h  ; Section contains initialized data.
IMAGE_SCN_CNT_UNINITIALIZED_DATA     equ 00000080h  ; Section contains uninitialized data.
 
IMAGE_SCN_LNK_OTHER                  equ 00000100h  ; Reserved.
IMAGE_SCN_LNK_INFO                   equ 00000200h  ; Section contains comments or some  other type of information.
;IMAGE_SCN_TYPE_OVER                 equ 00000400h  ; Reserved.
IMAGE_SCN_LNK_REMOVE                 equ 00000800h  ; Section contents will not become part of image.
IMAGE_SCN_LNK_COMDAT                 equ 00001000h  ; Section contents comdat.
;                                    equ 00002000h  ; Reserved
;IMAGE_SCN_MEM_PROTECTED             equ 00004000h  ; Reserved IMAGE_SCN_MEM_PROTECTED - Obsolete
IMAGE_SCN_NO_DEFER_SPEC_EXC          equ 00004000h  ; Reset speculative exceptions handling bits in the TLB entries for this section.
IMAGE_SCN_GPREL                      equ 00008000h  ; Section content can be accessed relative to GP
IMAGE_SCN_MEM_FARDATA                equ 000080000  ;
;IMAGE_SCN_MEM_SYSHEAP               equ 00010000h  ; Reserved IMAGE_SCN_MEM_SYSHEAP  - Obsolete
IMAGE_SCN_MEM_PURGEABLE              equ 000200000  ;
IMAGE_SCN_MEM_16BIT                  equ 000200000  ;
IMAGE_SCN_MEM_LOCKED                 equ 000400000  ;
IMAGE_SCN_MEM_PRELOAD                equ 000800000  ;
 
IMAGE_SCN_ALIGN_1BYTES               equ 00100000h  ;
IMAGE_SCN_ALIGN_2BYTES               equ 00200000h  ;
IMAGE_SCN_ALIGN_4BYTES               equ 00300000h  ;
IMAGE_SCN_ALIGN_8BYTES               equ 00400000h  ;
IMAGE_SCN_ALIGN_16BYTES              equ 00500000h  ; Default alignment if no others are specified.
IMAGE_SCN_ALIGN_32BYTES              equ 00600000h  ;
IMAGE_SCN_ALIGN_64BYTES              equ 00700000h  ;
IMAGE_SCN_ALIGN_128BYTES             equ 00800000h  ;
IMAGE_SCN_ALIGN_256BYTES             equ 00900000h  ;
IMAGE_SCN_ALIGN_512BYTES             equ 00A00000h  ;
IMAGE_SCN_ALIGN_1024BYTES            equ 00B00000h  ;
IMAGE_SCN_ALIGN_2048BYTES            equ 00C00000h  ;
IMAGE_SCN_ALIGN_4096BYTES            equ 00D00000h  ;
IMAGE_SCN_ALIGN_8192BYTES            equ 00E00000h  ;
 
;Unused                               equ 00F00000h  ;
IMAGE_SCN_ALIGN_MASK                 equ 00F00000h  ;
 
IMAGE_SCN_LNK_NRELOC_OVFL            equ 01000000h  ; Section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE            equ 02000000h  ; Section can be discarded.
IMAGE_SCN_MEM_NOT_CACHED             equ 04000000h  ; Section is not cachable.
IMAGE_SCN_MEM_NOT_PAGED              equ 08000000h  ; Section is not pageable.
IMAGE_SCN_MEM_SHARED                 equ 10000000h  ; Section is shareable.
IMAGE_SCN_MEM_EXECUTE                equ 20000000h  ; Section is executable.
IMAGE_SCN_MEM_READ                   equ 40000000h  ; Section is readable.
IMAGE_SCN_MEM_WRITE                  equ 80000000h  ; Section is writeable.
 
IMAGE_SUBSYSTEM_UNKNOWN                  equ 0    ;  Unknown subsystem.
IMAGE_SUBSYSTEM_NATIVE                   equ 1    ;  Image doesn't require a subsystem.
IMAGE_SUBSYSTEM_WINDOWS_GUI              equ 2    ;  Image runs in the Windows GUI subsystem.
IMAGE_SUBSYSTEM_WINDOWS_CUI              equ 3    ;  Image runs in the Windows character subsystem.
IMAGE_SUBSYSTEM_OS2_CUI                  equ 5    ;  image runs in the OS/2 character subsystem.
IMAGE_SUBSYSTEM_POSIX_CUI                equ 7    ;  image runs in the Posix character subsystem.
IMAGE_SUBSYSTEM_NATIVE_WINDOWS           equ 8    ;  image is a native Win9x driver.
IMAGE_SUBSYSTEM_WINDOWS_CE               equ 9                          
IMAGE_SUBSYSTEM_EFI_APPLICATION          equ 10
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  equ 11
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER       equ 12
IMAGE_SUBSYSTEM_EFI_ROM                  equ 13
IMAGE_SUBSYSTEM_XBOX                     equ 14
IMAGE_SUBSYSTEM_UNKNOWN2                 equ 15
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION equ 16
 
IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 523  ;20Bh    ; NT MAGIC translation code x64
IMAGE_NT_OPTIONAL_HDR32_MAGIC   equ 267  ;10Bh    ; NT MAGIC translation code x32
 
IMAGE_FILE_RELOCS_STRIPPED  equ 1
IMAGE_FILE_EXECUTABLE_IMAGE equ 2
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
exebase         equ 400000h
include 'WIN64A.INC'
org 0
use64
 
Signature:               dq IMAGE_DOS_SIGNATURE,0
ntHeader                 dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine                 dw IMAGE_MACHINE_AMD64
.Count_of_section        dw 1
.TimeStump               dd 0
.Symbol_table_offset     dd 0
.Symbol_table_count      dd 0
.Size_of_optional_header dw section_table-optional_header
.Characteristics         dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h = Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header          dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_minor     db 9,0
.Size_of_code                   dd 0x60
.Size_of_init_data              dd 0x00
.Size_of_uninit_data            dd 0
.entry_point                    dd begin
.base_of_code                   dd ntHeader
.image_base                     dq 0x140000000
.section_alignment              dd 0x10
.file_alignment                 dd 0x10
.OS_version_major_minor         dw 5,2
.image_version_major_minor      dd 0
.subsystem_version_major_minor  dw 5,2
.Win32_version                  dd 0
.size_of_image                  dd end_import-1h
.size_of_header                 dd begin
.checksum                       dd 0x8C1B  ; calc checksum
.subsystem                      dw IMAGE_SUBSYSTEM_WINDOWS_CUI
.DLL_flag                       dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation               dq 0x100000
.Stack_commit                   dq 0x1000
.Heap_allocation                dq 0x100000
.Heap_commit                    dq 0x1000
.loader_flag                    dd 0
.number_of_dirs                 dd (section_table-export_RVA_size)/8
export_RVA_size                 dq 0
.import_RVA                     dd import_
.import_size                    dd end_import-import_
;------------------------------------------------
section_table:      dq '.text'
.virtual_size       dd 0x55
.virtual_address    dd begin
.Physical_size      dd end_import-begin
.Physical_offset    dd begin
.Relocations        dd 0
.Linenumbers        dd 0
.Relocations_and_Linenumbers_count dd 0
.Attributes      dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
;-------------------------------------------------
begin: push 7
    pop rcx
    lea rdx,[MsgText]
    push 18h
    pop r8
    call [WriteFile]
ret;-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-
;------------------------------------------------
MsgText db "Win64 Assembly is Great!",0
;------------------------------------------------
Import_Table:
kernel32_table:
WriteFile  dq _WriteFile
import_:
dd 0,0,0,kernel32_dll,kernel32_table
dd 0
kernel32_dll    db "kernel32",0,0
dw 0
_WriteFile db 0,0,"WriteFile"
end_import:
0
maxillion
273 / 183 / 52
Регистрация: 25.12.2012
Сообщений: 616
09.04.2015, 01:20 #17
Цитата Сообщение от Mikl___ Посмотреть сообщение
Так же, как и в Win32, размер страницы на платформе x64 равен 4 Кб
В ядре страницы могут быть и по 4 Mb.
Цитата Сообщение от Mikl___ Посмотреть сообщение
Первые 64 Кб адресного пространства никогда не проецируются на физическую память
ZwAllocateVirtualMemory
Цитата Сообщение от Mikl___ Посмотреть сообщение
Приятная особенность процессоров x64 — поддержка битового флага No Execute, который в Windows используется для реализации аппаратной защиты от выполнения данных как кода (Data Execution Protection, DEP). Существование многих вирусов и "багов" на платформе x86 как раз и обусловлено тем, что процессор может выполнять данные так, будто это байты кода.
На windows xp x32 при желании можно включить NoExecute (boot.ini) .На Win 7 и выше не зависимо от разрядности NoExecute работает всегда.

Ещё здесь не написали что с появление x64, код стал по настоящему база-независим.
2
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
08.06.2015, 11:56  [ТС] #18
Минимальный по размеру ЕХЕ-файл для х64

Размер ЕХЕ-файла 268 байт, меньше система не дает создать, пришлось в конце файла добавлять нули. DOS-stub =4 байта, section_alignment = file_alignment = 4, содержит секцию импорта, поэтому будет запускаться на любой х64 Windows
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
format binary as 'exe'
 
IMAGE_DOS_SIGNATURE             equ 5A4Dh
IMAGE_NT_SIGNATURE              equ 00004550h
PROCESSOR_AMD_X8664             equ 8664h
IMAGE_SCN_CNT_CODE              equ 00000020h
IMAGE_SCN_MEM_READ              equ 40000000h
IMAGE_SCN_MEM_WRITE             equ 80000000h
IMAGE_SCN_CNT_INITIALIZED_DATA  equ 00000040h
IMAGE_SUBSYSTEM_WINDOWS_GUI     equ 2
IMAGE_NT_OPTIONAL_HDR64_MAGIC   equ 20Bh
IMAGE_FILE_RELOCS_STRIPPED      equ 1
IMAGE_FILE_EXECUTABLE_IMAGE     equ 2
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
 
include 'win64a.inc'
org 0
use64
IMAGE_BASE = 400000h
Signature:              dw IMAGE_DOS_SIGNATURE,0
ntHeader                dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
.Machine                dw PROCESSOR_AMD_X8664
.Count_of_section       dw 0;2
.TimeStump              dd 0
.Symbol_table_offset    dd 0;ntHeader
.Symbol_table_count     dd 0
.Size_of_optional_header dw EntryPoint-optional_header
.Characteristics        dw 0x20 or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE
;20h Handle >2Gb addresses
;-------------------------------------
optional_header:
.Magic_optional_header  dw IMAGE_NT_OPTIONAL_HDR64_MAGIC
.Linker_version_major_and_minor dw 9 
.Size_of_code           dd 0
.Size_of_init_data      dd 0;xC0
.Size_of_uninit_data    dd 0
.entry_point            dd EntryPoint
.base_of_code           dd ntHeader
.image_base             dq IMAGE_BASE
.section_alignment      dd 4
.file_alignment         dd 4
.OS_version_major_minor dw 5,2
.image_version_major_minor dd 0
.subsystem_version_major_minor dw 5,2
.Win32_version          dd 0
.size_of_image          dd EndOfImage
.size_of_header         dd EntryPoint
.checksum               dd 0
.subsystem              dw IMAGE_SUBSYSTEM_WINDOWS_GUI
.DLL_flag               dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
.Stack_allocation       dq 0x100000
.Stack_commit           dq 0x1000
.Heap_allocation        dq 0x100000
.Heap_commit            dq 0x1000
.loader_flag            dd 0
.number_of_dirs         dd (EntryPoint-export_RVA_size)/8
export_RVA_size        dq 0
.import_RVA             dd import_
.import_size            dd end_import-import_
;------------------------------------------------
EntryPoint:
   enter 20h,0        ; space for 4 arguments + 16byte aligned stack
   xor ecx, ecx                   ; 1. argument: rcx = hWnd = NULL
   mov r9, rcx                    ; 4. argument: r9d = uType = MB_OK = 0
   mov edx,MsgCaption+IMAGE_BASE  ; 2. argument: edx = window text
   mov r8,rdx                     ; 3. argument: r8  = caption
   call [MessageBox]
   leave
   ret
;------------------------------------------------
MsgCaption      db "Iczelion's tutorial #2a",0
;-------------------------------------------------
Import_Table:
user32_table:
MessageBox  dq _MessageBox
import_:
dd 0,0,0,user32_dll,user32_table
dd 0
user32_dll    db "user32",0,0
dw 0
_MessageBox     db 0,0,"MessageBoxA"
end_import:
times 268-end_import db 0  ;filling up to 268 bytes
EndOfImage:
1
Изображения
 
Вложения
Тип файла: zip tinyPEx64.zip (1.5 Кб, 12 просмотров)
George_
0 / 0 / 0
Регистрация: 01.08.2015
Сообщений: 32
18.08.2015, 11:20 #19
А что можно сказать о работе с 10-ти байтным long double в x64?
0
Mikl___
Автор FAQ
11730 / 6042 / 543
Регистрация: 11.11.2010
Сообщений: 11,027
18.08.2015, 13:44  [ТС] #20
George_,
то же, что и о работе с 10 байтовым числом в 8087, ничего не изменилось...
1
18.08.2015, 13:44
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
18.08.2015, 13:44

Что нужно знать для того чтобы устроиться на работу?
Я студент вуза 1 курс... Очень хотел бы начать работать пораньше... Знаний...

Что скачать и установить, чтобы начать программировать?
Решил изучить Яву. Хочу научиться создавать оконные приложения для Windows....

Какие темы нужно знать, чтобы начать работать с DirectX
День добрый! Прочитал уже почти 3 книги "для начинающих", множество статей,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru