Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
articles:ida2asm-link [2017/03/09 18:36]
root удалено
articles:ida2asm-link [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
-====== ida2asm ====== +====== ida2asm-link ====== 
-<​sub>​{{ida2asm.odt|Original file}}</​sub>​+<​sub>​{{ida2asm-link.odt|Original file}}</​sub>​
  
-====== ​секреты ассемблирования ​дизассемблерных листингов ======+====== ​obj файлы на топчане или\\ ​линковка дизассемблерных ​файлов ======
  
-крис касперскиака мыщъх, no-email+крис касперски ака мыщъх, no-email
  
-**дизассемблер ****IDA Pro**** (как и любой другой) умеет генерировать ассемблерные листинги, однако, их непосредственная ​трансляция невозможна и прежде ​чем ассемблер ​проглотит наживку, приходится ​совершить ​немало телодвижений, о самых значимых из которых мыщъх и рассказывает в этой статье**+**в прошлой статье этого цикла мы подошли к тому, что ассемблировали дизассемблерный листинг, ​поборов все ошибки ​транслятора и получив в итоге… неработоспособный ****obj**** файл, вызывающий у ликера хроническое ​недержание ****error****'​ов. сегодня мы продолжим заниматься извращенным сексом ама-сутра отдыхает), обогащаясь новыми знаниями и пополняя ​свой ​словарный запас матерных слов**
  
 ===== введение ===== ===== введение =====
  
-Обычно дизассемблер используется для ​реконструкции ​алгоритма подопытной программы, ​который после этого ​переписывается на Си/Си++ или в двоичном ​файле правится тот нехороший jx, который не дает приложению работать, ​если не найден ключевой файл или демонстрационный период давно истек.+Освежая в памяти события давно минувших ​дней (уже листья успели облететь за это время), напомним, что исправив кучу багофичей IDA Pro (перечисление ​которых заняло бы слишком много ​места) мы дошли до файла ​demo_3.asm, который нас удалось ассемблировать ​MASM'​омсо следующими ​ключам:
  
-Значительно реже дизассемблированную программу требуется оттранслировать зановоНапример,​ хочется исправить множественные ошибки разработчиков,​ нарастить функционал или внести другие изменения… Конечно,​ все это можно сделать непосредственно в двоичном коде, наложив на программу "​заплатку",​ присобаченную с помощью jump'​овВ большинстве случаев это самый короткий и самый _надежный_ пусть. Нет никаких гарантий,​ что программа дизассемблирована правильно. Существует по меньшей мере три фундаментальные проблемы дизассемблирования:​ а) синтаксическая неразличимость смещений от констант;​ б) неоднозначность соответствия ассемблерных мнемоник машинным командам;​ в) код ошибочно принятый за данные и данные,​ ошибочно принятые за код.+ML.EXE /coff /I/c /Cp /Zp1 /Zm demo_3.asm
  
-Как следствие,​ откомпилированный дизассемблерный листинг ​в лучшем случае вообще не работает,​ зависая при запуске,​ в худшем же — периодически падать в разных местах. Но до этих проблем нам — как до Луны, а, может, еще ​и дальше. Для начала необходимо протащить дизассемблерный листинг ​сквозь ​ассемблер, устранив явные ошибки трансляции,​ а со всем остальным мы разберемся как ​ни будь потом (быть может, даже в следующей статье).+Листинг 1 ключи для транслирования ассемблирования дизассемблерного ​листингасгенерированного IDA Pro
  
-===== первое боевое ​крещение ​=====+Здесь: **/​****coff** – создавать оbj-файл в формате coff (иные форматы ms link не поддерживает,​ а искать другие линкеры нам ​в лом), **/​****I****.** – искать включаемые файлы в текущей директории,​ **/​****c** – только ассемблировать, не линковать (линковать мы будем вручную, с помощью мата, кувалды и, конечно, хвоста),​ **/​****Cp** – учитывать регистр символов,​ **/​****Zp****1** –выравнивание для структур,​ **/​****Zm** – режим совместимости с MASM 5.10, в формате которого IDA Pro и создает листинги.
  
-Давайте создадим _простейшую_ консольную программку типа "​hello,​ world!",​ откомпилируем ее, а затем дизассемблируем с помощью IDA Pro и попытаемся ассемблировать полученный листинг.+===== битва за API =====
  
-Исходный текст в нашем случае выглядит так:+Транслятор MASM (входящий, в частности, в состав NTDDK) ​не выдает не единой ошибки (ну да! еще бы!!! чуть хвост не треснул, пока мы их вылавливали) и генерирует obj-файл. Наступает волнующее время линковки:
  
-#include <stdio.h>+$link.exe demo_3.obj
  
-main()+Microsoft ​(RIncremental Linker Version 5.12.8181
  
-{+Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
  
-printf("​hello,​world!\n"​);​+LINK:​**fatal error LNK1221: a subsystem can't be inferred and must be defined**
  
-}+Листинг 2 попытка линковки только что ассемблированного файла
  
-Листинг 1 исходный текст программы demo_comsole.c+Линкер материться, что подсистема ​не задана ​и ни хвоста линковать не хочет. Ну это даже ​не вопрос! Подсистема задается через ​ключ **/​****SUBSYSTEM** за которым ​следует одно из следующих ключевых слов: NATIVE — для драйверов,​ WINDOWS — для GUI приложений, CONSOLE — для консольных приложений,​ WINDOWSCE — для платформы Windows CE,​ POSIX – э… ну… это эдакая пародия на UNIX, но все равно ни хрена не работающая
  
-Компилируем ее компилятором ​MicrosoftVisualC++ 6.0 с настойками ​по умолчанию ("​cl.exe demo_console.c"​) ​и загружаем полученный exe-файл в IDA Pro 4.7. Естественно, можно использовать и другие ​версии продуктов, но тогда результат ​будет несколько отличаться, что, впрочемна ход повествования практически ​никак не повлияет.+Фактически выбирать приходится ​между WINDOWS и CONSOLE. Чем они отличаются? С точки зрения PE-формата, одним битом в заголовке, указывающему системному ​загрузчику создавать или не создавать консоль при запуске файла. Попытка линковки консольного ​файла как GUI заканчивается фатально онсоль ​не создается и весь ввод/вывод обламывается). Обратное не столь плачевно,​ но пустое ​консольное окно на фоне GUI выглядит как-то странно. Но мы-то знаем, что наше ​приложение — ​консольного типа, следовательно пишем:
  
-{{ida2asm-link_Image_0.png}}+**$link /​SUBSYSTEM:​CONSOLE demo_3.obj**
  
-Рисунок 1 успешно дизассемблированный файл+Microsoft (R) Incremental Linker Version 5.12.8181
  
-Дождавшись завершения дизассемблирования файла ​(когда экран IDA Pro будет выглядеть приблизительно как показано на рис. 1), попросим ее сгенерировать ассемблированный листинг. Порядочные дизассемблеры поддерживают несколько популярных синтаксисов:​ TASM, MASM и, учитывая,​ что IDA Pro недавно была перенесена на Linux, неплохо бы добавить к этому списку еще и AT&T, но… увы! В меню "​Options"​  "​Targetassembler"​ значиться только какой-то загадочный "​GenericforIntel 80x86",​ не совместимый ни MASM'​ом,​ ни с TASM'​ом (во всяком случае не с их последними версиями)В IDA Pro 5.0 в этом отношении сделан огромный шаг вперед и теперь нам предлагают выбор между "​GenericforIntel 80x86" и "​BorlandTASMinIdealmode"​ (см. рис. 2).+Copyright ​(CMicrosoft Corp 1992-1998All rights reserved.
  
-{{ida2asm-link_Image_1.png}}+demo_3.obj:error LNK2001: unresolved external symbol _WriteFile
  
-Рисунок 2 ассемблеры,​ поддерживаемые IDA Pro 4.7 (слева),​ и IDA Pro 5.0 (справа) +demo_3.obj:​error ​LNK2001unresolved external ​symbol ​_GetVersion
- +
-Очень своевременное решение,​ особенно в свете того, что TASM давно мертв — не "​переваривает"​ новых инструкций,​ не обновляется,​ не поддерживается и официально не распространяется. Borland уже давно забила на этот проект. И хотя есть несколько некоммерческих TASM-совместимых ассемблеров (см. статью "​обзор ассемблерных трансляторов"​) всех проблем они не решают и дизассемблерные листинги транслируются только после существенной переделки,​ а раз так — лучше остановить свой выбор на пакете MASM, входящим в состав NTDDK. +
- +
-Решено! Выбираем "​GenericforIntel 80x86" и говорим "​File"​  "​Produceoutputfile"​  "​ProduceASMfile"​ или просто нажимаем горячую клавишу <​Alt-F10>​. Даем файлу имя (например,​ "​demo_1.asm"​) и через несколько минут шуршания диском у нас образуется… нечто по имени ничто. +
- +
-Скармливаем эту штуку ассемблеру "​ml.exe /​c demo_1.asm"​ (версия 6.13.8204) для справки. Транслятор выдает свыше сотни ошибок,​ после чего прекращает свою работу,​ не видя никакого смысла ее продолжать (см. рис. 3). +
- +
- +
- +
-{{ida2asm-link_Image_2.png}} +
- +
-Рисунок 3 результат непосредственной трансляции дизассемблерного листинга +
- +
-Анализ показывает,​ что 90% ошибок связаны с неверным определением типа процессора "​instructionorregisternotacceptedincurrentCPUmode"​. Ах, да! По умолчанию IDA Pro выбирает "​MetaPC (disassembleall 32-bitopcodes)",​ но забывает поместить соответствующую директиву в дизассемблерный листинг,​ а транслятор по умолчанию устанавливает 8086 ЦП, совершенно не совместимый с 32-разрядным режимом. +
- +
-Материмся,​ лезем в начало листинга,​ вставляем директиву "​**.386**",​ после чего повторяет сеанс трансляции заново. И опять куча ошибок (правда,​ на этот раз чуть меньше ста, что не может не радовать). Смотрим,​ что не понравилось транслятору:​ "​demo_1.asm(34):​errorA2008:​syntaxerror:​flat"​. Хм?! Открываем demo_1.asm, переходим к строке 34 и видим: "model flat". А точка где?! Кто ее будет ставить?​ Абель что ли? Возвращаем точку на место, заодно добавляя квалификатор языка Си: "​.model flat,​C"​ и вновь прогоняем программу через транслятор. На этот раз MASM едет крышей настолько,​ что выпадает в soft-ice (если тот был предварительно запущен) или выбрасывает знаменитое сообщение о критической ошибке.  +
- +
-{{ida2asm-link_Image_3.png}} +
- +
-Рисунок 4 критическая ошибка при попытке ассемблирования листинга,​ сгенерированного IDA Pro +
- +
-Ладно, положим,​ это ошибка самого транслятора,​ легко обходимая добавлением волшебного ключика "/​coff"​ к командной строке и следующая попытка трансляции проходит уже без ошибок:​ "​ml.exe /​c /​coff demo_1.asm"​. В смысле без _критических_ ошибок самого транслятора,​ а ошибок в листинге по прежнему предостаточно. +
- +
-Большинство из них относится к невозможности определения имен библиотечных функций,​ имен и меток:​ +
- +
-demo_1.asm(53) : error A2006: undefined symbol : _printf +
- +
-demo_1.asm(64) : error A2006: undefined symbol : __exit +
- +
-demo_1.asm(285) : error A2006: undefined symbol : _fclose +
- +
-demo_1.asm(297) : error A2006: undefined symbol : _free +
- +
-demo_1.asm(453) : error A2006: undefined symbol : off_403450 +
- +
-demo_1.asm(490) : error A2006: undefined symbol : off_403450 +
- +
-Листинг 2 транслятор не может найти имена библиотечных функций в листинге +
- +
-Черт! Как же мы могли забыть,​ что хитрая IDA Pro коллапсирует библиотечные функции,​ стремясь расчистить листинг от бесполезного мусора,​ не несущего никакой полезной нагрузки. Вернемся к рисунку 1 и сравним его со следующим фрагментом сгенерированного ассемблерного листинга:​ +
- +
-; [00000031 BYTES: COLLAPSED FUNCTION _printf. PRESS KEYPAD "​+"​TO EXPAND] +
- +
-; [000000D4 BYTES: COLLAPSED FUNCTION start. PRESS KEYPAD "​+"​TO EXPAND] +
- +
-Листинг 3 сколлапсированные функции остаются сколлапсированными и в ассемблерном листинге! +
- +
-Это же какую ума палату нужно уметь, чтобы допустить такое?​! Интересно,​ тестировался ли ассемблерный генератор вообще или был написан в расчете на авось?​! Матерясь,​ возвращаемся в IDA Pro, в меню "​View"​ выбираем пункт "​Unhideall",​ наблюдая за тем как "​раскрываются"​ библиотечные функции. +
- +
-Генерируем новый ассемблерный файл, на этот раз "​demo_2.asm",​ не забыв вставить в его начало директивы "​.386"​ и "​.model flat,​C"​. Повторяем трансляцию. Просматривая протокол ошибок (ну куда же IDA Pro без ошибок) с удивлением обнаруживаем множественные ругательства на неопределенные символы StartupInfo и CPInfo, представляющие собой легко узнаваемые структуры:​ +
- +
-demo_2.asm(2533) : error A2006: undefined symbol : _STARTUPINFOA +
- +
-demo_2.asm(4276) : error A2006: undefined symbol : _cpinfo +
- +
-Листинг 4 реакция транслятора на отсутствие объявления структур +
- +
-Куда же они могли подеваться?​! Открываем ассемблерный листинг в текстовом редакторе и… нет, в русском языке просто не существует подходящих слов, чтобы адекватно выразить наше состояние:​ +
- +
-; [00000012 BYTES. COLLAPSED STRUCT _cpinfo. PRESS KEYPAD "​+"​ TO EXPAND] +
- +
-; [00000044 BYTES. COLLAPSED STRUCT _STARTUPINFOA. PRESS KEYPAD"​+"​ TO EXPAND] +
- +
-Листинг 5 сколлапсированные структуры в ассемблерном файле +
- +
-Ассемблерный генератор IDA Pro поместил структуры в целевой файл, даже не удосужившись их автоматически развернуть! Что же, придется это сделать самостоятельно. Возвращаемся в IDA Pro, в меню "​View"​ находим пункт "​Open Subview",​ а там — "​Structures"​ или просто жмем горячую клавишу <​Shift-F9>​. Перед нами появляется окно с перечнем всех структур и для их разворота достаточно дать команду "​View"​  "​Unhide all",​ после чего можно повторить генерацию ассемблерного файла, назвав его "demo_3.asm" (про директивы .386/.model flat мы не забываем,​ да?). +
- +
-Поразительно,​ но количество ошибок трансляции совсем не уменьшается,​ а даже возрастает. И ассемблер по прежнему не может найти "​развернутые"​ структуры. Что же ему мешает?​ Присмотревшись к логу ошибок повнимательнее,​ мы видим, что ругательству на неопределенный символ предшествует ошибка типа "​operandmustbeamemoryexpression"​ (операнд должен быть выражением,​ адресующим память):​ +
- +
-demo_3.asm(2561) ​: error A2027operand must be a memory expression +
- +
-demo_3.asm(2596) : error A2006: undefined ​symbol ​: StartupInfo +
- +
-demo_3.asm(2599) : error A2006: undefined symbol : StartupInfo +
- +
-demo_3.asm(2601) : error A2006: undefined symbol : StartupInfo +
- +
-Листинг 6 транслятор по прежнему не может определить развернутые структуры +
- +
-Открываем ассемблерный файл в редакторе,​ переходим к строке 2561 и видим следующую картину маслом:​ +
- +
-__ioinitproc near; CODE XREF: start+6Fp +
- +
-**StartupInfo= _STARTUPINFOAptr -44h**+
  
  
  
-cmp[esp+54h+StartupInfo.cbReserved2],​ 0+demo_3.obj:error LNK2001: unresolved external symbol _SetStdHandle
  
-jzloc_4022E6+demo_3.obj:​error LNK2001: unresolved external symbol _CloseHandle
  
-moveax, [esp+54h+StartupInfo.lpReserved2]+demo_3.exe:fatal error LNK1120: 40 unresolved externals
  
-Листинг 7 камень преткновения всех структур+Листинг 3 подсистема определена, ​но линкер не может найти API-функции
  
-Мыщъх ​не уверен на счет "​GenericforIntel 80x86", ​но транслятор MASM, начиная с версии >​5.1, ​такого способа объявлений ​структур уже не поддерживает, ​и чтобы откомпилировать программу у нас есть по меньшей мере два пути: разрушить все структуры на хрен (все равно в ассемблерном листинге ​они нам несильно понадобятся),​ либо ​же использовать ключ ​командной строки **/​Zm**, ​обеспечивающим ​обратную ​совместимость с MASM 5.1. Вот ​такнаверноемы и поступим: "​ml.exe /​c /​coff /​Zmdemo_3.asm".+Хорошая ​новость — линкер заглатывает наживку и пытается переварить файл. Плохая новость — это у него не получается. А не получается потому, что он не распознает ​имена ​API функций, которых в нашем демонстрационном примере аж целых 40 штук! В переводе с английского ругательство "error LNK2001: unresolvedexternalsymbol _WriteFile"​ звучит как "​Егор:​ LNK2001: ​неразрешимый внешний символ _WriteFile".
  
-Количество ошибок сразу же уменьшается ​чуть ли не в три ​раза и они свободно помешаются на экранчто не может не радовать!+Сразу же возникает вопрос: откуда взялся ​знак прочерка и почему это WriteFile вдруг стала неразрешимым ​символом?! Смотрим в ассемблерный листинг. Контекстный поиск по "​_WriteFile" ​ничего не дает! API-функция там объявлена _без_ знака прочерка:
  
-{{ida2asm-link_Image_4.png}}+; Segment type:​Externs
  
-Рисунок 5 трансляция ассемблерного листинга в режиме совместимости с MASM 5.1+; BOOL __stdcall WriteFile(HANDLE hFile, LPCVOID lpBuffer,
  
-Подавляющее большинство ошибок имеют тип "​missingoperatorinexpression"​ (в выражении отсутствует оператор) и чем скорее мы с ними разберемсятем будет лучше как для нас самихтак и для транслируемой программы.+; DWORD nBytesToWriteLPDWORD lpNumberOfBytesWritten,LPOVERLAPPED lpOverlapped);​
  
-Переходим к строке 141 и видим:+**extrn****WriteFile****:****dword**
  
-******moveax,​ large fs:0**+Листинг 4 объявления API-функций в ассемблерном листинге
  
-pusheax+А теперь открываем demo_3.obj в любом hex-редакторе (например,​ в FAR'е по <F3> или в HIEW'​е) и повторяем процедуру поиска еще раз:
  
-******movlarge fs:0, esp**+{{ida2asm-link_Image_0.png}}
  
-Листинг 8 здесь возникает ошибка типа "отсутствующий оператор"+Рисунок просмотр obj-файла ​в hex-редакторе
  
-Ну и зачем ассемблерному генератору было вставлять "​large"?​ Все равно MASM его не понимает ​и отродясь ​не понималНаходясь во интегрированном редакторе ​FAR'а, нажимаем <​Ctrl-F7> ​(replace) и заменяем все ​"​largefs" ​на просто "​fs"​ (см. рис. 6)+Строка "​WriteFile"​ встречается дважды (смрис. 1) — один раз со знаком прочерка, другой — ​без. Вот этот самый прочерк линкеру и не нравится. Откуда же он берется?! А оттуда! Курим листинг 4 и убеждаемся, насколько IDA Pro коварна и хитра. Тип API-функции (stdcall) задан только в комментарии! Транслятор же комментариев не читаети берет тип по умолчанию, которым в данном случае является Си (cdecl), предписывающий перед всеми символьными именами ставить знак ​прочерка, чтособственно говоря, и происходит.
  
-{{ida2asm-link_Image_5.png}}+Кстати говоря,​ комментарий неправильный. Потому что тип вызова никак не stdcall, согласно которому,​ транслятор должен превратить "​WriteFile"​ в "​_WriteFile@20",​ где 20 — размер аргументов в байтах,​ заданный в десятеричной нотации. Это вообще не сама функция,​ а двойное слово, в которое операционная система заносит эффективный адрес WriteFile при загрузке PE-файла в память. В библиотеке KERNEL32.LIB (входящей,​ в частности,​ в состав SDK) ему соответствует имя "​__imp__WriteFile@20"​. Именно такое "​титул"​ должен носить прототип API-функции,​ если мы хотим успешно слинковать obj-файл и именно это имя мы используем при вызове API-функции при программировании на "​голом"​ ассемблере (без включаемых файлов). Вот только IDA Pro во все эти подробности не вникает,​ перекладывая ​ их на наши плечи и хвост.
  
-Рисунок автоматическая ​замена всех "​largefs" ​на "​fs" ​в FAR'e+Если во всем ассемблерном листинге заменить "​WriteFile"​ на "​_imp__WriteFile@20",​ то линкер переварит его ​вполне нормально и даже не отрыгнет. Нет, это не опечатка. Именно "​_imp__WriteFile@20", ​а не "​__imp__WriteFile@20"​. Почему?​! Да потому, что второй символ прочерка транслятор добавит самостоятельно. Если же сразу указать два символа прочерка, то на выходе их образуется целых три, а это уже передоз.
  
-Теперь после трансляции остается совсем ​немного ошибок, на которые мы продолжим планомерно наступать:+Копируем "​demo_3.asm"​ в "​demo_3_test.asm",​ загружаем его в FAR по <F4>, давим <​CTRL-F7>​ (replace) ​и заменяем "​WriteFile"​ на "​_imp__WriteFile@20",​ ассемблируем как и раньше, после чего повторяем попытку ​линковки с явным указанием имени библиотеки KERNEL32.LIB:
  
-demo_3.asm(70) : error A2015: segment attributes cannot change : Alignment+$link /​SUBSYSTEM:​CONSOLE demo_3_test.obj **KERNEL32.LIB**
  
-demo_3.asm(8063: error A2189: invalid combination with segment alignment : 2048+Microsoft ​(RIncremental Linker Version 5.12.8181
  
-demo_3.asm(12004: error A2015: segment attributes cannot change : Alignment+Copyright ​(CMicrosoft Corp 1992-1998. All rights reserved.
  
-demo_3.asm(13742) ​: error A2005: symbol ​redefinition : cchMultiByte +demo_3_test.obj:​error ​LNK2001unresolved external ​symbol **_GetVersion**
- +
-demo_3.asm(14176) : error A2005: symbol redefinition : Filename +
- +
-demo_3.asm(14200) : error A2005: symbol redefinition : Locale +
- +
-demo_3.asm(14215) : error A2005: symbol redefinition : CodePage +
- +
-demo_3.asm(142) : error A2206: missing operator in expression +
- +
-demo_3.asm(2860) : error A2206: missing operator in expression +
- +
-demo_3.asm(2888) : error A2206: missing operator in expression +
- +
-demo_3.asm(2924) : error A2006: undefined symbol : loc_402480 +
- +
-demo_3.asm(3639) : error A2001: immediate operand not allowed +
- +
-demo_3.asm(4158) : error A2006: undefined symbol : loc_402D11 +
- +
-**demo_3.asm(1257) : error A2006: undefined symbol : $NORMAL_STATE$1535** +
- +
-**demo_3.asm(1258) : error A2006: undefined symbol : loc_4012AA** +
- +
-**demo_3.asm(1259) : error A2006: undefined symbol : loc_4012C5** +
- +
-**demo_3.asm(1260) : error A2006: undefined symbol : loc_401311** +
- +
-**demo_3.asm(1261) : error A2006: undefined symbol : loc_401348** +
- +
-**demo_3.asm(1262) : error A2006: undefined symbol : loc_401350** +
- +
-**demo_3.asm(1263) : error A2006: undefined symbol : loc_401385** +
- +
-**demo_3.asm(1264) : error A2006: undefined symbol : loc_401418** +
- +
-Листинг 9 перечень ошибок,​ выявленных ассемблером при очередном сеансе трансляции +
- +
-Беглый взгляд на листинг обнаруживает целый каскад ошибок типа "​undefinedsymbol"​ (неопределенный символ). Так, посмотрим,​ что же у нас не определено на этот раз. Переходим к строке 1257, за которой тянется целый хвост ошибок в строках 1258, 1259, 1260, 1261, 1262, 1263 и 1264. Это настоящее осиное гнездо! Обитель зла, которую мыщъх собирается разбить одним взмахом хвоста:​ +
- +
-1257:​off_401956 dd offset $NORMAL_STATE$1535 +
- +
-1258:dd offset loc_4012AA +
- +
-1259:dd offset loc_4012C5 +
- +
-1260:dd offset loc_401311 +
- +
-Листинг 10 очередная обитель зла на подступах к успешной трансляции +
- +
-Хм, выглядит вполне обычно и _все_ метки без исключения обнаруживаются простым контекстным поиском:​ +
- +
-$NORMAL_STATE$1535:​movecx,​ dword_406428+
  
  
  
-loc_4012AA:or[ebp+var_10],​ 0FFFFFFFFh+demo_3_test.obj:error LNK2001: unresolved external symbol _SetStdHandle
  
-+demo_3_test.obj:​error LNK2001: unresolved external symbol _CloseHandle
  
-loc_4012C5:movsxeax, bl+**demo_3_test.exe:fatal error LNK1120: ****__39 unresolved externals__**
  
-Листинг 11 "​потерянные метки" легко обнаруживаются ​контекстным поиском+Листинг результат линковки после замены ​WriteFile на _imp_WriteFile@20
  
-Почему же тогда ассемблерный транслятор их ни хрена не видит?! Все дело ​в том, что IDA Pro ​неверно ​определила границы функции, ​поместив ​обращения к меткам _за_ ​границы функции в которой они упоминаются!!! А метки ​вообще-то локальны. Вот потому-то транслятор их и не находит!+Оторвать ​мыщъх'​у хвост! Это работает! Количество ошибок уменьшилось ​на единицу и первое неразрешенное имя теперь не _WriteFile, а _GetVersion! Переименовав оставшиеся API-функции,​ мы добьемся нормальной линковки программыно… это же сколько труда предстоит! ​И в каждом новом ассемблерном файле, эту тупую работу ​придется повторять заново, вращая хвостом вплоть до полного его залегания. А ведь мы, мыщъх'ине хотим, чтобы ​наш хвост залегал,​ верно?
  
-$NORMAL_STATE$1535:+Настоящие мыщъх'​и (хакеры в смысле) идут другим путем — воспользовавшись директивами "​**externdef"​** и "​**equ"​** они создают для каждой API-функции свой алисас (alias), заставляющий транслятор трактовать функцию "​func"​ как "​__imp__func@XX"​. В частности,​ для WriteFile это будет выглядеть так:
  
-+**externdef** imp__WriteFile@20:​**PTR** pr5
  
-loc_4012AA:+WriteFile **equ** <​_imp__WriteFile@20>​
  
-+Листинг 6 создание алиаса для API-функции WriteFile
  
-loc_4012C5:+Эту работу необязательно выполнять вручную и за вечер-другой можно написать утилиту,​ захватывающую DLL и выдающую готовый набор алиасов на выходе. Другой вариант — воспользоваться макросредствами FAR'а или редактора TSE-Pro (бывший QEDIT), позволяющих делать все что угодно и даже больше.
  
-+Самое главное,​ что "​коллекцию"​ алиасов можно разместить в отдельном файле, подключаемом к ассемблерному листингу директивой "​**include**"​. Создав все необходимые включаемые файлы один-единственный раз, мы можем пользоваться ими сколько угодно,​ причем не только для ассемблирования дизассемблерных листингов,​ полученных IDA Pro, но и в своих собственных ассемблерных программах.
  
-loc_401311:+Параметр "​prN",​ идущий после "​PTR",​ показывает сколько аргументов принимает функция и численно равен их размеру (число после символа "​@"​) деленному на размер двойного слова, составляющему,​ как известно,​ 4 байта. То есть, в случае с WriteFile мы получаем20/4 = 5. Так же обратите внимание на символы прочерка. В первой строке "​imp__func@XX"​ пишется вообще без знаков прочерка,​ во второй — с одним прочерком. Любые другие варианты не работают. Так что не надо косячить!
  
-+{{ida2asm-link_Image_1.jpg}}
  
-**__outputendp ; ********конец****функции**+Рисунок 2 никакой хак ​не обходится без черной магии
  
-; ───────────────────────────────────────────────────────────────────────────+===== колдование макроса =====
  
-off_401956dd offset $NORMAL_STATE$1535+В нашем случае,​ создать включаемый файл для 40x API-функцией будет быстрее,​ чем писать и отлаживать полностью автоматизированную утилиту. С макросами на FAR'е вся работа не займет и пятнадцати минут. Главное — правильную стратегию иметь!!!
  
-dd offset loc_4012AA+Перенаправив вывод линкера в файл "​demo_3.err"​ (см. листинг 3),​ открываем его в редакторе по <F4>, подгоняем курсор к строке с первой ошибкой,​ затем по <​CTRL-TAB>​ возвращаемся назад в панели,​ открывая по <F4> файл KERNEL32.LIB из SDK и тут же нажимаем <​CTRL‑L>​ для запрета редактирования (чтобы случайно его не испортить). Вновь возвращаемся в панели по <​CTRL-TAB>​ и, нажав <​SHIFT-F4>,​ создаем новый файл "​demo_API.inc"​.
  
-dd offset loc_4012C5+На этом подготовительные работы можно считать законченными и самое время приступать к созданию макроса. При всей свой внешней простоте в макросах заключена огромная сила, но пользоваться ею могут только маги (черные),​ мыщъх'​и (серые,​ пещерные) и хомяки (всех пород). Я бы еще добавил к этому списку траву и грибы, да ведь только редакция,​ стремающая наркоконтроля,​ ни за что это не пропустит,​ хотя… трава — она и под забором растет. Я же не конкретизировал! Короче,​ ситуация напоминает бородатый (как мыщъх) анекдот:​ идет еврей по послевоенной Москве и причитает:​ сколько бед и все от одного человека. Его вяжут парни из ГБ и начинают пытать:​ скажите,​ а какого человека вы имели ввиду? Еврей — Гитлера,​ конечно! Гэбисты — Хм, ну тогда идите. Еврей — простите,​ а вы кого имели ввиду?
  
-dd offset loc_401311+{{ida2asm-link_Image_2.jpg}}
  
-Листинг 12 IDA Pro поместила обращения к меткам после конца функции, удалив их из границ видимости транслятора+Рисунок 3 мыщъх серый, небритый, ​лохматый, пещерный. совершенно запущенный ​тип
  
-Чтобы исправить ситуацию, ​необходимо переместить директиву "__output endp" ​_законец обращений к меткам. Так, чтобы они стали частью функции __output. После чего ассемблерный код будет выглядеть ​так:+Короче, как ни крути, а без травы не обойтись, потому что, совершить следующий ритуал можно только с похмелья или находясь в состоянии расширенного сознания. Но он работает! И это главное! Значит так, находясь ​в "demo_API.inc", нажимаем <​CTRL-.>,​ переводя FAR в режим ​записи макроса (при этом в левом верхнем углу злобно загорается красная буквица R, что означает Record). Погружаемся в состояние медитации и…
  
-off_401956dd offset $NORMAL_STATE$1535+  - вызываем меню "​Screen"​ по <​F12>,​ в котором окна перечислены в порядке их открытия;​ 
 +  - нажимаем <1> для перехода в "​demo_3.err",​ которое мы открыли первым;​ 
 +  - нажимаем <END> для перехода в конец строки;​ 
 +  - нажимаем <​CTRL-LEFT>​ для перемещения курсора в начало имени функции;​ 
 +  - нажимаем <​LEFT>​ для перехода,​ через символ прочерка;​ 
 +  - нажимаем <​SHIFT-END>​ для выделения имени API-функции;​ 
 +  - нажимаем <​CTRL-INS>​ для копирования его в буфер обмена;​ 
 +  - нажимаем <​HOME>,​ <​DOWN>​ для перехода к следующей строке;​ 
 +  - нажимаем <F12> для вызова меню "​Screen"​ и давим <2> для открытия KERNEL32.LIB;​ 
 +  - нажимаем <F7> (search) и вставляем имя функции по <​SHIF-INS>,​ затем <​ENTER>;​ 
 +  - нажимаем <​SHIFT-CTRL-RIGHT>​ для выделения имени функции со знаком "​@XX";​ 
 +  - копируем его в буфер обмена по <​CTRL-INS>;​ 
 +  - нажимаем <​HOME>,​ чтобы следующий поиск начинался сначала файла;​ 
 +  - нажимаем <F12> и по нажатию <3> переходим в demo_API.inc;​ 
 +  - пишем "​externdefimp__"​ и нажимаем <​SHIFT-INS>​ для вставки имени из буфера;​ 
 +  - дописываем к нему ":​PTRpr0"​ и нажимаем <​ENTER>​ для перехода к след. строке;​ 
 +  - нажимаем <​SHIFT-INS>​ еще раз, вставляя имя типа "​WriteFile@20";​ 
 +  - нажимаем "​пробел"​ и вставляем имя еще раз; 
 +  - нажимаем <​HOME>​ для перехода в начало строки;​ 
 +  - нажимаем <F7> и затем "​@",​ <​ENTER>​ для поиска символа "​@";​ 
 +  - нажимаем <​SHIFT-CTRL-LEFT>​ для выделения "​@NN";​ 
 +  - нажимаем <​SHIFT-DEL>​ для удаления "​@NN"​ в буфер обмена;​ 
 +  - пишем " equ <​_imp__"​ (с ведущим пробелом в начале);​ 
 +  - нажимаем <DEL> для удаления символа проблема под курсором;​ 
 +  - нажимаем <END> для перехода в конец строки;​ 
 +  - пишем ">";​ 
 +  - нажимаем <​ENTER>​ для перехода на следующую строку;​ 
 +Все! Создание макроса завершено! Нажимаем <​CTRL-.>​ и вешаем макрос на любую незанятую комбинацию горячих клавиш (например,​ на <​CTRL-~>​),​ после чего нам остается только уронить кирпич на <​CTRL-~>,​ созерцая как трудолюбивый макрос выполняет всю рутинную работу за нас. Ну… или почти всю. Количество аргументов в параметре "​pr0"​ необходимо вычислить самостоятельно,​ но это уже мелочи,​ почти не отнимающие времени.
  
-dd offset loc_4012AA+{{ida2asm-link_Image_3.png}}
  
-dd offset loc_4012C5+Рисунок 4 плагины калькуляторов к FAR'у
  
-__outputendp+Тем не менее, при желании можно сотворить полностью автоматизированный макрос. Для этого нам потребуется скачать с http://​plugring.farmanager.com/​index_e.html один из многих валяющихся там калькуляторов (см. рис. 4),​ после чего, дойдя до шага 23, слегка изменить свою тактическую стратегию,​ представленную ниже (чтобы не перебивать макрос заново,​ имеем смысл обзавестись редактором макросов,​ так же представляющим собой плагин):​
  
-Листинг 13 исправленный вариантпозволяющий транслятору обнаружить "​недостающие метки"+  - вызываем калькулятор, ​используя свойственный ему метод вызова;​ 
 +  - нажимаем <​SHIFT-INS>​ вставляя "​@NN"​ из буфера обмена; 
 +  - нажимаем <​HOME>​ для перехода ​в начало строки
 +  - нажимаем <DEL> для удаления символа "​@";​ 
 +  - нажимаем <END> для перехода в конец строки; 
 +  - пишем "/​4"​ и нажимаем <​ENTER>​ для расчета значения;​ 
 +  - копируем вычисленное ​значение ​в буфер обмена;​ 
 +  - *** продолжаем выполнение прежней макропоследовательности до шага 27 *** 
 +  - нажимаем <UP> для перехода на строку вверх;​ 
 +  - нажимаем <END> для перехода в конец строки (на "pr0"​);​ 
 +  - нажимаем <​BASKSPACE> ​для удаления "​0"​ и вставляет результат вычислений;​ 
 +  - нажимаем <​DOWN>,​ <END> для перехода в конец следующей строки
 +  - *** продолжаем выполнение прежней макропоследовательности с шага 27 ***
  
-После ассемблирования количество ошибок тает буквально на глазах и мы даже в порыве вдохновения едва удерживаемся от того, чтобы не закурить новый косяк: 
  
-demo_3.asm(70) : error A2015: segment attributes cannot change : Alignment+В результате у нас должен образоваться включаемый файл следующего вида ​(смотрите,​ сколько времени у нас заняло составление макроса и сколько бы отняла разработка программы на любом другом языке программирования!):
  
-demo_3.asm(8064) ​error A2189: invalid combination with segment alignment : 2048+externdefimp__WriteFile@20:PTRpr5
  
-demo_3.asm(12005) : error A2015: segment attributes cannot change : Alignment+WriteFileequ <​_imp__WriteFile@20>​
  
-demo_3.asm(13743) ​error A2005: symbol redefinition : cchMultiByte+externdefimp__GetVersion@0:PTR pr0
  
-demo_3.asm(14177) : error A2005: symbol redefinition : Filename+GetVersion equ <​_imp__GetVersion@0>​
  
-demo_3.asm(14201) ​error A2005: symbol redefinition : Locale+externdef imp__ExitProcess@4:PTR pr1
  
-demo_3.asm(14216) : error A2005: symbol redefinition : CodePage+ExitProcess equ <​_imp__ExitProcess@4>​
  
-**demo_3.asm(2861) : error A2206: missing operator in expression**+Листинг 7 фрагмент включаемого файла demo_API.inc
  
-**demo_3.asm(2889) : error A2206: missing operator in expression**+===== непонятки с ___imp_RtlUnwind =====
  
-**demo_3.asm(2925: error A2006: undefined symbol : loc_402480**+Пока мыщъх'​и оттягивались на конопляном поле в полный рос, находясь в блаженном кайфе, магический макрос споткнулся на функции "​___imp_RtlUnwind" ​(он попросту не нашел ее в KERNEL32.LIB), все пошло кувырком и мыщъх'​ей сразу попустило.
  
-demo_3.asm(3640) : error A2001: immediate operand not allowed+Что же это за противная функция такая?​! Кстати,​ в KERNEL32.LIB ее действительно нет. Так что макрос тут не причем. Смотрим в demo_3.asm (не забывая убрать один из символов прочерка,​ вставленный транслятором). Контекстный поиск тут же находит RtlUnwind, представляющую собой классический переходник (thunk) к одноименной функции из KERNEL32.DLL:
  
-**demo_3.asm(4159) : error A2006: undefined symbol : loc_402D11**+RtlUnwindproc near
  
-Листинг 14 список ошибок,​ обнаруженных ассемблером после очередного сеанса трансляции (все ближе, ближе долгожданный миг победы!)+jmpds:​__imp_RtlUnwind
  
-В глаза бросается пара уже известных нам ошибок типа "​undefinedsymbol",​ первую из которых исправить достаточно легко:+RtlUnwindendp
  
-__NLG_Notify1:​+Листинг 8 переходник (thunk) к API-функции RtlUnwind из KERNEL32.DLL
  
-pushebx+Только вот по не совсем понятной причине,​ IDA Pro не нашла этой функции в KERNEL32.DLL (не знала о ней? или не захотела искать?​!),​ но тот же HIEW отобразил thunk совершенно правильно!
  
-pushecx+{{ida2asm-link_Image_4.png}}
  
-movebxoffset unk_406364+Рисунок 5 HIEW правильно отображает имя API-функции RtlUnwindа IDA Pro – нет
  
-jmpshort **loc_402480**+Собственно,​ багофича заключается в том, что IDA Pro дает API-функции неправильное имя. Ну какой же это "​__imp_RtlUnwind"?​! Правильный вариант включает в себя _два_ символа прочерка между "​imp"​ и "​RtlUnwind"​. Естественно,​ наш магический (но слегка туповатый) макрос не ожидал такой внезапной подлости! Приходится брать бразды правления на себя и либо править ассемблерный листинг,​ добавляя еще один символ прочерка,​ либо алиасить функцию как есть. Последний вариант более предпочтителен,​ поскольку он не требует вмешательства в исходный код. Включаемый файл нужно писать,​ так, чтобы он работал,​ а не исходить из того, что правильно/​неправильно и не пытаться оправдаться "​это же не наш баг? почему мы должны его учитывать?​!"​. Положитесь на мой хвост парни! Мы должны! И правильный алисасинг выглядит так:
  
-; ███████████████ S U B R O U T I N E ███████████████████████████████████████+externdef imp__RtlUnwind@16:​PTR pr4
  
-__NLG_Notifyproc near+__imp_RtlUnwind equ < _imp__RtlUnwind@16>​
  
-pushebx+Листинг 9 алиасинг API-функции __imp_RtlUnwind
  
-pushecx+===== линковка =====
  
-movebxoffset unk_406364+Копируем файл demo_3.asm в demo_4.asmдобавляем в его начало директиву "​include .\demo_api.inc"​ (см. рис. Х),​ подключающую включаемый файл, и повторяем весь цикл трансляции вновь. Ассемблируем:​ "​ML.EXE /​coff /​I. /​c /​Cp /​Zp1 /​Zm demo_4.asm"​. Убеждаемся в отсутствии ошибок и линкуем:​ "link /​SUBSYSTEM:​CONSOLEdemo_3_test.objKERNEL32.LIB"​.
  
-movecx[ebp+8]+О чудо! Линкер совсем без матюгов и почти без перекуров создает demo_4.exeприближая нас к конечной цели еще на один шаг!
  
-**loc_402480**:​+{{ida2asm-link_Image_5.png}}
  
-mov[ebx+8], ecx+Рисунок 6 результат запуска demo_4.exe
  
-mov[ebx+4], eax+===== запуск файла или отложенное заключение =====
  
-mov[ebx+0Ch]ebp+Полученный таким трудом exe-файл при попытке его запуска выдает сообщение о критической ошибке и отваливает. Значитеще не все косяки исправлены и IDA Pro где-то конкретно напортачила с дизассемблированием. Чтобы узнать где — необходимо взять в лапы отладчик и проанализировать причины падения программы,​ устраняя ошибки одну за другой (а их там — целый легион!). Но этим мы займемся в следующий раз, а пока покурим и попутно подведем итоги.
  
-__NLG_Dispatch:+Мы проделали объемную работу и сделали больше делоразобрались с алиасами,​ научились готовить включаемые файлы, позволяющие транслировать дизассемблерные листинги без их доработки напильником,​ и — самое главное — испытали на себе действие черной магии макросов FAR'​а,​ а эти чувства не забываются!
  
-popecx+===== >>>​ врезка "не ассемблируется aam 16" =====
  
-popebx+После выхода первой статьи этого цикла, на хакерском форуме читатель с ником realstudent задал предметный вопрос из которого выяснилось,​ что во-первых,​ у него не ассемблируется инструкция "​aam 16"​ (но это мелочи,​ сейчас докурим и ее обломаем),​ и, во-вторых,​ "​секреты ассемблирования дизассемблерных листингов"​ (в девичестве) превратились в "​сношение с идой"​. Вот такая трава растет на широте Москвы!
  
-retn4+А само сообщение (и ответ на него) выглядели так:
  
-__NLG_Notifyendp+> есть древнее приложение - программер микрухи через LPT
  
-Листинг 15 оригинальный код, сгенерированный IDA Pro, который не хочет транслироваться+> (очень напрягает он своей работойно без сырцов,
  
-Достаточно "​завести"​ подпрограмму __NLG_Notify1 под "​retn 4"​ процедуры __NLG_Notyfy, ​но перед директивой __NLG_Notify endp, ​тогда она метка будет распознаваться как надо!+> и твоя статья пришлась очень в тему. ​а тема такая:
  
-__NLG_Notifyproc near+> решил восстановить его и дописать если возможно.
  
-pushebx+> пользовался IDA 5.x (лицензионная,​ ясное дело!) и
  
-pushecx+> MASM 9.0 (тоже лицензионный,​ с митино).
  
-movebx, offset unk_406364+> все ошибки убил кроме одной и в чем ее смысл
  
-movecx[ebp+8]+> никак не могу понять (в асме я нормально разбираюсь,
  
-**loc_402480**:​+> смотрел другие ​ исходникик на koders.com - все у людей также,
  
-mov[ebx+8], ecx+> был на microsoft.com - но так и не понял к чему эта ошибка здесь).
  
-mov[ebx+4], eax+> не ассемблируется строка "aam 16" ​
  
-mov[ebx+0Ch],​ ebp+> - error A2008: syntax error : integer (блинэтопоследняяошибка!)
  
-__NLG_Dispatch:​ +по мануалам от Intel'а команда поддерживает аргумент
- +
-popecx +
- +
-popebx +
- +
-retn4 +
- +
-**__NLG_Notify1:​** +
- +
-**pushebx** +
- +
-**pushecx** +
- +
-**movebx, offset unk_406364** +
- +
-**jmpshort loc_402480** +
- +
-__NLG_Notifyendp +
- +
-Листинг 16 исправленный вариант +
- +
-А вот со следующей ошибкой справиться уже сложение,​ поскольку функция strcpy совершает прыжок в середину функции strcat: +
- +
-**_strcpyproc near** +
- +
-arg_0= dword ptr 8 +
- +
-pushedi +
- +
-movedi, [esp+arg_0] +
- +
-jmp**loc_402D11** +
- +
-**_strcpyendp** +
- +
-**_strcatproc near** +
- +
-arg_0= dword ptr 4 +
- +
-arg_4= dword ptr 8 +
- +
-movecx, [esp+arg_0] +
- +
-… +
- +
-**loc_402D11:​** +
- +
-movecx, [esp+4+arg_4] +
- +
-testecx, 3 +
- +
-jzloc_402D36 +
- +
-… +
- +
-retn +
- +
-_strcatendp +
- +
-Листинг 17 IDA Pro сгенерировала неработоспособный листинг для парной функции strcpy/​strcat +
- +
-Никаким ухищрениями у нас не получится перетасовать код так, чтобы метка loc_402D11 оказалась в границах видимости, но… ведь как то же это было запрограммировано?! Обратившись к исходным текстам библиотеки LIBC.LIB (они ​поставляются вместе с компилятором) мы обнаружим волшебный ключик. Чтобы метка была видна отовсюду, после нее должен стоят не один знак ":",​ а целых два — "::"​. +
- +
-Самая трудная задача осталась позади и теперь нам предстоит разобраться с уже встречавшимися ошибками типа "​missingoperatorinexpression"​. На этот раз транслятору не понравились конструкции "​push large dword ptr fs:​0"​ и "​pop large dword ptr fs:​0"​. Убираем все лишнее,​ превращая их в "​push fs:​0"​ и "​pop fs:​0"​ и движемся дальше, ​где нас ждет ошибка "​immediateoperandnotallowed"​ (непосредственный операнд недозволен),​ затаившаяся в 3640 строке:​ "​cmp Locale,​0"​. Естественно,​ транслятор решил трактовать Locale как смещение,​ а не как содержимое ячейки,​ поэтому без явной расстановки квадратных скобок здесь не обойтись:​ "​cmp dword ptr ds:​[Locale],​0"​. +
- +
-Теперь на линии фронта остается лишь большой конгломерат ошибок типа "​symbolredefinition"​ (символ переопределен), против которых не пропрешь,​ ведь он действительно переопределен,​ вот например,​ взять тот же cchMultiByte:​ +
- +
-___crtLCMapStringA procnear; CODE XREF: _setSBUpLow+BEp +
- +
-; _setSBUpLow+E6p +
- +
-Locale= dwordptr ​ 8 +
- +
-lpMultiByteStr= dwordptr ​ 10h +
- +
-cchMultiByte= dwordptr ​ 14h +
- +
-… +
- +
-___crtLCMapStringA endp +
- +
-+
  
-cchMultiBytedd 1; DATA XREF_wctomb+31r+> (в смысле команда правильная),​ а вверху листинга у меня торчит:
  
-Листинг 18 дважды определенный символ Locale+>..686p
  
-Ничего не остается как "​расщеплять"​ переменные вручную,​ давая им различные имена. Главное — не перепутать переменные местами. Впрочем,​ перепутать будет довольно трудно,​ поскольку,​ одна копия переменной — локальная и адресуется через стек, а другая — глобальная и обращение с ней происходит через непосредственную адресацию.+> ;.mmx 
  
-Разобравшись с астральными переменными,​ нам остается только побороть три ошибки,​ связанные с выравниваниемНу, ошибку в строке 8064 мы ликвидируем путем удаления директивы "​align 800h"​ (800h в десятичном представлении как раз и будет 2048)Две остальные ошибки требуют переименования сегментов _text и _data во что-нибудь другоенапример,​ _text1 и _data1, только это переименование должно иди по всему тексту.+..model largeC
  
-Все! Теперь ассемблерный листинг, сгенерированный дизассемблером, ​и "слегка" ​исправленный напильником, транслируется ​без ошибок! Добавим к командой строке ​MASM'а ключ "/​Cp"​, чтобы он соблюдал регистр публичных имен и.+без измен, мужиктолько без измен! это она по спецификациям ​ассемблируется, ​но только разработчики ассемблера спецификации читают по диагонали, и у них на этот ​счет имеется свое, особое мнение,​ которое умом не понятьа в свете того, что Microsoft озаботилась разработкой собственного ​процессорасдается ​мне, что x86 коллектив разработчиков ​не осилил, что совсем не удивительно, если вернутся на десяток лет назад и вспомнить, что ​Microsoft ​не могла разобраться в разработанных ​ею же спецификациях на расширенную память ​и драйвера забивали косяки только так.
  
-===== заключение ​=====+но это была лирика. ​что же касается сути проблемы,​ то она обходится методом "ну и не ассемблируется... ну и хрен с ней..."​. вставляем директиву DB и записываем инструкцию непосредственно в машинных кодах.
  
-…и вот тут-то выясняется, что полученный obj наотрез отказывается линковаться, ​потому что линкер не может найти API-функции! Это не покажется удивительным,​ если вспоминать, что IDA Pro объявила их в "удобочитаемом"​ виде, который совсем не совпадет с тем, как они объявлены в библиотеках. Но линковка (и последующая доводка программы до ума) — это уже тема совсем другого разговора, а, может быть, и целой ​статьи.+в данном случае это выглядит так: "DB 0DBh, 10h", где DBh – ​опкод команды AAM, а 10h — ​непосредственный ​операнд. та же самая история наблюдается ​и с командой AAD (да и не только ей), опкод которой D5h и в машинной форме она вызывается так: "​DB D5h,​ XXh"​.