Skip to content

Лекция 7

Inspirate789 edited this page May 22, 2022 · 7 revisions

Макроопределения.

"Работают точно так же, как и в Си."

Макроопределение (макрос) - именованный участок программы, который ассемблируется каждый раз, когда его имя встречается в тексте программы.

Макросы обрабатываются препроцессором.

  • Определение:
имя MACRO параметры
...
ENDM
  • Пример:
load_reg MACRO register1, register2
push register1
pop register2
ENDM

Директива присваивания =.

Директива присваивания служит для создания целочисленной макропеременной или изменения её значения и имеет формат:

Макроимя = Макровыражение
  • Макровыражение (или Константное выражение) - выражение, вычисляемое препроцессором, которое может включать целочисленные константы, макроимена, вызовы макрофункций, знаки операций и круглые скобки, результатом вычисления которого является целое число
  • Операции: арифметические (+, -, *, /. MOD), логические, сдвигов, отношения (сравнения)

Директивы отождествления EQU, TEXTEQU.

Директива для представления текста и чисел:

Макроимя EQU нечисловой текст и не макроимя ЛИБО число
Макроимя EQU <Операнд>

TEXTEQU никак не обрабатывает выражение и просто присваивает его как текстовую строку левой части выражения (макроимени).

Макроимя TEXTEQU Операнд

Пример:

X EQU [EBP+8]
MOV ESI,X

Макрооперации.

Помимо очевидных арифметических операций, есть специфические операции

  • % - вычисление выражения перед представлением числа в символьной форме (позволяет подставлять куда-либо результаты вычислений в качестве текстового значения)
  • <> - подстановка текста без изменений (без конкатенаций или арифметических вычислений, т. е. не изменяя операнд и не интерпретируя его как выражение)
  • & - склейка текста (конкатенация слов)
  • ! - считать следующий символ текстом, а не знаком операции (можно ставить перед знаками <, >, = и т.д.)
  • ;; - исключение строки из макроса (комментарий в макросе). Если в макросах ставить ;, которые мы обычно используем для строковых комментариев, то эта строка попадёт в результат работы макроса!

Блоки повторения.

  • Повтор фиксированное число раз
REPT число … ENDM
  • Подстановка фактических параметров по списку на место формального: IRP или FOR (в разных версиях MASM'а):
IRP form,<fact_1[,fact_2,...]> … ENDM    ;; перебираем значения, которые по очереди будут подставляться 
                                         ;; вместо формального имени (которое мы должны указать первым операндом)
                                         ;; и это формальное имя можно использовать в теле цикла 
                                         ;; кол-во итераций цикла = кол-во параметров в угловых скобках
  • Подстановка символов строки на место формального параметра: IRPC или FORC (в разных версиях MASM'а):
IRPC form,fact … ENDM                    ;; поочерёдно перебираем все символы (элементы) строки fact
  • Цикл с условием: WHILE
WHILE cond … ENDM                        ;; условие cond должно возвращать значение типа bool
                                         ;; пока cond истинно, выполняется подстановка собственного содержимого в текст программы

Директивы условного ассемблирования.

Похожи на условные операторы из языков высокого уровня.
Директивы:

  • IF:
IF c1
...
ELSEIF c2                                ;; может отсутствовать, как и в условных операторах языков высокого уровня
...
ELSE                                     ;; может отсутствовать, как и в условных операторах языков высокого уровня
...
ENDIF
  • IFB <par> - истинно, если параметр не определён
  • IFNB <par> - истинно, если параметр определён
  • IFIDN <s1>,<s2> (IF IDeNtical) - истинно, если строки совпадают
  • IFDIF <s1>,<s2> - истинно, если строки разные
  • IFDEF/IFNDEF <name> - истинно, если имя объявлено/не объявлен

Директивы управления листингом.

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

  • Листинг - файл, формируемый компилятором и содержащий текст ассемблерной программы, список определённых меток, перекрёстных ссылок и сегментов. Директивы управления листингом позволяют указывать заголовок/подзаголовок каждой страницы листинга и задавать её параметры (сколько в ней будет строк, какая у них будет ширина и т.д.).
    Директивы MASM'а:
  • TITLE, SUBTTL - заголовок, подзаголовок на каждой странице
  • PAGE - высота, ширина
  • NAME - имя программы
  • .LALL - включение полных макрорасширений, кроме ;;
  • .XALL - по умолчанию (почти полный листинг, опускается лишь небольшая часть данных)
  • .SALL - не выводить тексты макрорасширений
  • .NOLIST - прекратить вывод листинга

Комментарии.

Многострочные комментарии в макросах:

comment @
... многострочный текст ...
@
Я:  Всё, что вы сейчас показали, особенно условные конструкции (IF, 
    IFDEF, IFNDEF...), наводит на мысль о том, что теперь можно 
    с помощью этих макросов пародировать программирование на 
    высокоуровневых языках. Уже вроде бы "человеческие" условия 
    появились, которые выглядят, как в Си...
ДА: С языками высокого уровня это не соотносится с одной стороны, 
    а с другой стороны, условно, программирования на языках высокого 
    уровня мы всё равно не получим.
Я:  Но выглядеть оно будет уже как-то ближе, так ведь?
ДА: Ну я немножко не соглашусь потому что подпрограмму мы всё равно, 
    с одной стороны, можем вызывать именно как подпрограмму, а 
    макроподстановка просто приведёт к (не смог разобрать слово) 
    функции. Отчасти, конечно, да, макросы сильно упрощают жизнь, 
    если много писать на ассемблере...
Я:  Ну, упрощают жизнь в плане помогают приблизить 
    синтаксис [ассемблера] к сишному?
ДА: Если большие программы на ассемблере придётся писать, то очень 
    удобно использовать макросы для каких-то типовых или повторяющихся 
    блоков, таких как инициализация подпрограммы, завершение 
    подпрограммы, вызов подпрограммы и т. д. [... привёл пример 
    макроса вызова подпрограммы ... . Также можно вынести в макрос 
    вычисление смещений переменных в стеке, это будет выглядеть 
    понятнее и аккуратнее].
Я:  Короче, можно сильно упростить себе жизнь, да...
ДА: Если большое что-то на ассемблере писать, то да.
Я:  А что-то большое на ассемблере пишется вообще?)
ДА: Ну раньше писалось, а сейчас ... для компьютеров - как-то 
    сомневаюсь, а для контроллеров отдельно пишется.
Я:  Это да... (мне ещё в курсаче по кг с микроконтроллерами 
    возиться... поставь звёздочку, чтобы жалко)
ДА: Сейчас это [ассемблер] скорее такой язык, который ушёл во что-то 
    более простое, типа контроллеров, да и там уже на Си пишут по-моему. 
    Так что я не думаю, что его где-то можно найти в современных 
    больших проектах.

Виды трансляторов ассемблера.

  • MASM
  • TASM
  • NASM
  • FASM
  • YASM
  • as
  • ...

AT&T-синтаксис.

Мы весь семестр работаем с транслятором MASM, и пока мы это делали под 16-разрядные процессоры (под DOS через эмулятор). DOS был "эпохой ассемблера", и даже под него было ещё несколько компиляторов помимо MASM. Один из них, tasm, - компилятор от фирмы Borland. Этой фирмы уже давно нет, а MASM существует до сих пор. Нотации этих компиляторов, в целом, похожи, т.е. одинаковый порядок операндов команд (вначале приёмник, затем источник), названия команд, похожий синтаксис описания сегментов и т.д. если же брать стандартный компилятор для UNIX-подобных систем, as, то синтаксис там уже другой, он сильно отличается от синтаксиса Intel, которым мы пользовались. Этот синтаксис называется AT&T.

Синтаксис стандартного ассемблера для UNIX - as
Основные отличия от Intel-синтаксиса:

  1. Имена регистров предваряются префиксом %.
  2. Обратный порядок операндов: вначале источник, затем приёмник.
  3. Размер операнда задается суффиксом, замыкающим инструкцию (синтаксис Intel сам "угадывал" размеры операндов, а в AT&T надо явно писать movb, movl, movq и т.д.).
  4. Числовые константы записываются в Си-соглашении (шестнадцатеричные числа, например, будут записываться не с суффиксом h, а с префиксом 0x; аналогично двоичные записываются через 0b, а восьмеричные - через 0o).
  5. Для получения смещения метки используется префикс $ (вместо OFFSET). ...

Далее пошёл обзор очень хорошей статьи об отличиях синтаксисов от человека под ником "Крис Касперски".

"Крис Касперски" в доисторические времена был популярным экспертом в области отечественного низкоуровневого программирования и реверс-инжиниринга. Так что на его материалы стоит обращать внимание.

Создание оконных приложений на ассемблере под x86.

Идея сейчас не очень актуальна, т.к. касается 32-разрядных систем. В те времена, когда ещё не было .NET и других современных оболочек, люди вполне успешно писали оконные приложения на ассемблере, которые позволяли полностью использовать функционал ОС, причём такие приложения получались минимального размера [в них есть только то, что нужно, и ничего лишнего - предположение автора].
Для того, чтобы написать приложение под современную ОС, уже недостаточно написать несколько строчек и повызывать какие-нибудь прерывания, нужно пользоваться библиотеками и системными вызовами.

Системный вызов — обращение прикладной программы к ядру операционной системы для выполнения какой-либо операции.

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

Далее мы перешли на сайт MASM32, посмотрели мифы о MASM

(он до сих пор поддерживается, на нём можно писать под Windows 10 и 11, он низкоуровневый, позволяет получать компактные программы и т.д.)

На том же сайте его можно скачать и поставить.

Также у MASM32 есть интересная IDE и примеры программ.

Скачать с официального сайта у меня не получилось, поэтому качал здесь.

Пример оконного приложения под x86:

; �������������������������������������������������������������������������

      .486                      ; create 32 bit code
      .model flat, stdcall      ; 32 bit memory model
      option casemap :none      ; case sensitive

      include \masm32\include\dialogs.inc
      include simple.inc

      dlgproc PROTO :DWORD,:DWORD,:DWORD,:DWORD

    .code

; �������������������������������������������������������������������������

start:

      mov hInstance, FUNC(GetModuleHandle,NULL)

      call main

      invoke ExitProcess,eax

; �������������������������������������������������������������������������

main proc

    Dialog "Simple Dialog","MS Sans Serif",10, \            ; caption,font,pointsize
            WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \     ; style
            2, \                                            ; control count
            50,50,150,80, \                                 ; x y co-ordinates
            1024                                            ; memory buffer size

    DlgButton "&OK",WS_TABSTOP,48,40,50,15,IDCANCEL
    DlgStatic "Simple Dialog Written In MASM32",SS_CENTER,2,20,140,9,100

    CallModalDialog hInstance,0,dlgproc,NULL

    ret

main endp

; �������������������������������������������������������������������������

dlgproc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD

    .if uMsg == WM_INITDIALOG
      invoke SendMessage,hWin,WM_SETICON,1,FUNC(LoadIcon,NULL,IDI_ASTERISK)

    .elseif uMsg == WM_COMMAND
      .if wParam == IDCANCEL
        jmp quit_dialog
      .endif

    .elseif uMsg == WM_CLOSE
      quit_dialog:
      invoke EndDialog,hWin,0

    .endif

    xor eax, eax
    ret

dlgproc endp

; �������������������������������������������������������������������������

end start
Я:  Ну вот вы сейчас показали GUI на ассемблере, да?
ДА: Да.
Я:  Есть ли такие случаи, когда мы должны отказаться от написания 
    GUI на Си, на питоне или на чём-нибудь ещё, и решить, что здесь 
    нам нужен ассемблер?)
*Закадровый смех*
ДА: в 2022 году, думаю, нет таких примеров.
*Закадровый смех*
ДА: Это скорее исследовательский такой вариант, чтобы понять ...
Я:  ... Что на ассемблере это тоже можно писать?
ДА: Да, собственно, следующая ваша лабораторная работа [11] этому 
    посвящена. Надо будет по примерам немного сориентироваться и такую 
    простенькую оконную программу написать. Это, по крайней мере, 
    выполнимая задача.
Я:  Потрясающе, класс.
ДА: Ну и с другой стороны понятно на основе примеров, во что компиляторы 
    языка Си или других языков высокого уровня преобразует свой код, когда 
    делает исполняемый файл оконного приложения. Языки высокого уровня 
    подобным образом используют системные вызовы. Ну и это такой способ 
    познакомиться. Если мы весь семестр писали под DOS, используя 21 
    прерывание, а тут мы можем руками пощупать системные вызовы Windows, 
    как раз предназначенные для формирования окон, работы с графическими 
    элементами. Ну в терминологии Win32 API окном является всё: не только 
    окно нашей программы, но и отдельные элементы интерфейса (кнопки, поля 
    ввода и т.д.). Это тоже окна, которые привязываются к родительским окнам.

Дизассемблирование. Реверс-инжиниринг.

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

Отладчики (AFDPRO и другие) косвенно являются дизассемблерами.

Дизассемблирование - процесс получения текста программы на ассемблере из программы в машинных кодах.

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

"Ни в коем случае мы здесь не намекаем на какое-то незаконное 
использование, потому что реверс-инжиниринг часто связан с 
полузаконным или незаконным применением. Но надо помнить, что 
в России есть уголовный кодекс, в других странах тоже есть 
законодательство (*Закадровый смех*). ... Законодательство 
пишут юристы. Если почитать УК, то у технического специалиста 
появится много вопросов от том, что же (здесь) имеется в виду. 
Что понимать под копированием информации, что понимать под 
получением доступа к информации. То, что мы по сети скачали, 
но на диск не сохранили, а просо на экран вывели, - это 
копирование информации или нет? Для технического человека это 
разумный вопрос ..., а юристы таким вопросом обычно не задаются, 
ну либо есть какие-то отдельные разъяснения помимо закона. 
Поэтому, конечно, от маломальских нарушений закона лучше 
держаться подальше, во избежание любых возможных проблем, которые 
в 2022 году получить себе достаточно легко. Если в 90-е этим 
никто не занимался, то в современном мире эта сфера достаточно 
легко контролируется, ..., и любые нарушения, по крайней мере 
в части доступа к каким-либо объектам, выявляются."

Далее мы посмотрели на удобный и современный инструмент для отладки и реверс-инжиниринга (по крайней мере, для исследования готовых Windows-приложений):

x64DBG (open-sourse debugger для 32- и 64-разрядных приложений, но только под Windows) - работает и как дизассемблер, и как отладчик.

ДА: Чем исполняемый файл, собранный в режиме debug mode, 
    отличается от исполняемого файла, предназначенного 
    для дистрибуции программы?
ДА: Отладочный исполняемый файл больше, так как содержит 
    отладочные символы (отладочную информацию) - отдельная 
    область данных. Там сохраняются для отладки имена 
    подпрограмм, переменных и т.д..
Я:  Вы по-моему раньше говорили, что там вставляются 
    пустые места, куда отладчик может вставлять свои 
    функции или точки останова...
ДА: Ну может быть, да. Что тут вставляется, я не скажу, 
    но главное то, что вставляется информация об именах, 
    которые были в исходном тексте программы, для удобства 
    последующей отладки (чтобы соотносить наши имена и области 
    данных программы).

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

Под реверс-инжинирингом подразумевается исследование популярных программ с целью кражи/взлома.

Например, найти в программе алгоритм генерации ключа, который нужно ввести, чтобы она заработала. Или можно заблокировать часть программы, проверяющую лицензию (например, поставить jump).

Методы, которыми пользуются программисты, чтобы помешать реверс-инжинирингу:

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

Как это сделать?
- Составлять блоки программы таким образом, чтобы она 
  выглядела нелогично.
- Не применять общепринятые паттерны проектирования.
- Вставлять мусорные блоки кода, которые вроде бы 
  что-то делают, а на самом деле никогда не вызываются.
- Программа, будучи уже загруженной в оперативную 
  память, может модифицироваться при запуске (заменять 
  адреса по сложным правилам, вычисляя их на лету...)
Clone this wiki locally