Инструкции ассемблера и регистры процессора
-
В теме рассматриваются простые команды ассемблера, принцип работы и основные регистры процессора
Как написано в статье про типы данных, для компьютера всё состоит из едениц и нулей в выделенных областях памяти, исполняемый код не исключение, он состоит из байтов и может называться байткодом.
Компьютер выполняет вычисления указанные в байтовом коде и любая запущенная программа состоит из него. Написанная на любом языке программа в итоге конвертируется в байткод перед выполнением.Человеко-читаемое отображение байткода - ассемблер. Отладчик может прочитать байткод и показать его в виде ассемблерных инструкций, понятных человеку, а так же может давать возможность изменять этот код прямо в работающей программе, что бы корректировать указания даваемые компьютеру.
Cheat Engine обладает этими функциями и прежде чем они будут разобраны, изучите основные команды, регистры и структуру автоассемблера используемого Cheat Engine.Регистры:
Регистры процессора представляют собой временное хранилище данных для быстрой обработки и вычислений, вычисления большинства команд могут производиться только с регистрами, то есть выражение должно включать в себя регистр. Регистры состоят из частей, например регистр eax вмещает в себя 4 байта, 32 бита и состоит из нескольких частей:Как показано на этой картинке, регистр
eax
состоит из первой части, где 16 бит не обозначены отдельным подрегистром, и регистраAX
, размером 16 бит, как вы помните (2 байта), который в свою очередь состоит из двух частей по 8 бит (1 байт)AH
иAl
.
Данная информация нужна для понимания если вам нужно работать с разноразмерными значениями, но в основном вам будут встречаться значения размером 4 и 8 байт,rax
это 64 битная версия регистраeax
которая вмещает 8 байт и бывает в 64 битных приложениях.Основные регистры следующие, с указанием размера в байтах:
8 4 2 1 1 RAX EAX AX AH AL RBX EBX BX BH BL RCX ECX CX CH CL RDX EDX DX DH DL RSI ESI SI RDI EDI DI RBP EBP BP RSP ESP SP
В дальнейшем использовать для временного хранения данных вам следует все кроме ebp и esp, так как эти регистры могут использоваться для взаимодействия со стеком, о котором будет рассказано позже.
Помните что использовать 8 байтовые регистры можно только в приложениях 64 бит. Регистры имеют осмысленные названия, например ESP - Extended Stack Pointer (Расширенный указатель на стек). Расширенный он, грубо описывая, потому что архитектура эволюционировала постепенно, было 16 бит, существовал только SP, разработали 32 бит, добавили ещё 2 байта вперёд и так далее.
Подробнее про эти регистры можно почитать на английском тут.Команды ассемблера:
Команды ассемблера используются для операций со значениями расположенными в регистрах и памяти приложения.
Основные команды с синтаксисом Cheat Engine autoassembler которые пригодятся для понимания следующих статей, с примерами:mov
- переместить (скопировать) данныеmov eax, A //Поменстить в eax число 10 (A - шестнадцатиричная система) mov eax, #10 //Поместить в eax число 10 (#10 - десятичная система) mov eax, [адрес] //Переместить значение по адресу 4 байта в регистр eax mov [адрес], eax //Переместить значение из регистра eax 4 байта по адресу mov ecx, eax //Переместить значение из регистра eax в регистр ecx mov [адрес], al ///Переместить значение из регистра al 1 байт по адресу mov [адрес], [адрес] //Не правильно и работать НЕ БУДЕТ, все двойные операции только с регистрами.
add
- добавитьmov eax, 5 mov ecx, 4 add eax, ecx //Добавить ecx к eax, после этого eax = 9 add [адрес], eax //Добавить к значению по адресу eax 4 байта
sub
- вычестьmov eax, 5 mov ecx, 4 sub eax, ecx //Вычесть из eax ecx, после этого eax = 1 sub [адрес], eax //Вычесть из значения по адресу eax 4 байта
inc
- увеличить на 1
dec
- уменьшить на 1inc [адрес] //Увеличить значение по адресу на 1 dec eax //Уменьшить eax на 1
mul
- умножить, использует регистры и операнд. Регистр eax умножается на операнд и результат помещается в eax, остаток помещается в edxmov eax, 5 mov ecx, 4 mul ecx //Результат, eax = 20, edx = 0
div
- разделить, использует регистры и операнд. Значение из двух частей в eax:edx (записывайте edx 0 если хотите делить значение в рамках размерности 4 байт) делится на операнд и результат помещается в eax, остаток помещается в edxmov eax, 8 mov edx, 0 mov ecx, 4 div ecx //Результат eax = 2, edx = 0 mov eax, #10 mov edx, 0 mov [адрес], 5 div [адрес] //Результат eax = 2, edx = 0
xor
- побитовое исключающее ИЛИ, но для начала достаточно знать что это команда часто используется для обнуления регистров компиляторами, сравнивая их самих с собой, так как она обычно занимает меньше байткода чем напримерmov eax, 0
mov eax, 5 xor eax, eax //eax = 0 mov rax, 8 xor rax, rax //rax = 0
lea
- получить адрес значения, вместо него самого, может использоваться во многих ситуациях и вычислениях с регистрами, некоторые будут рассмотрены в следующих статьях.//Допустим адрес = A040D8, значение по адресу 5 mov eax, [адрес] //eax = 5 lea eax, [адрес] //eax = A040D8
nop
- ничего не делающая инструкция, занимает место 1 байт, или столько, сколько указано, эта инструкция может быть использована для замены на неё других инструкций, что бы отключить их выполнение, в следующих статьях есть примеры.nop //Ничего не произошло, занимает 1 байт nop 5 //Ничего не произошло, занимает 5 байт
Справочник со всеми командами на английском
Структура выполнения кода:
Байткод выполняется последовательно, ассемблерные инструкции тоже. Поток проходит по порядку с инструкции на инструкцию и выполняет их. Его положение может быть изменено с помощью некоторых команд, специальный регистрEIP
отображает положение потока, текущий адрес на котором он находится:Скриншот из отладчика Cheat Engine. В этом примере можно заметить, слева адрес инструкции, дальше байты инструкции (байткод), а справа интерпретация в виде ассемблерного кода который можно прочитать.
Инструкцияjmp
позволяет перемещать поток на определённое количество байт вперёд, или назад. Cheat Engine сам посчитает байты, вам нужно только указать адрес:В этом примере инструкция
add eax, ecx
выполняться не будет, вместо этого сразу будет выполнятьсяsub eax, ecx
.Другой пример:
В этом примере поток останется в бесконечном цикле и программа зависнет. Для того что бы безопасно управлять положением потока используются условные переходы, рассмотренные в следующей статье.