Введение в реверсинг с нуля используя IDA PRO. Часть 6

AnGel

Администратор
Команда форума
27 Авг 2015
3,266
1,871
113
Давайте продолжим с арифметическими и логическими инструкциями.

ADD A, B

Инструкция складывает значение B и A, и помещает результат в A.

A может быть регистром или содержимым ячейки памяти, B может быть регистром, константой или содержимым ячейки памяти (A и B не должны быть в памяти в одно и то же время, в одной и той же инструкции)

Рассмотрим несколько примеров инструкции ADD ища текст ADD через VEVIEWER.

71dc6141edc82285a7ae999e10187974._.png

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

c53ba0a4fa1d197249a1921f196d2883._.png

В этом примере, если ECX равно 10000, то константа 4 складывается с ним и результат 10004 сохраняется в тот же ECX.

400785056dc1303a924f9ca25c0e05c0._.png

В этом случае, инструкция добавит 0xFFFFFFFF к значению ECX, которое получено на предыдущем шаге, т.е. содержимое адреса будет указывать сейчас на ECX + 30, если этот адрес имеет разрешение на запись, инструкция сложит значения и будет сохранять результат там.

Если содержимое ECX, для примера, равно 0x10000, то по адресу 0x10030 содержимое этого адреса равно значению 1 и когда мы прибавляем к этому значению 0xFFFFFFFF, что то же самое, что и -1, следовательно, результат был бы равен нулю и будет сохраняться по адресу 0x10030.

В нашем CRACKME.EXE есть несколько примеров суммы двух регистров.

84529a042fa9e64d75353f0e5188da8b._.png

В этом случае, оба регистра будут сложены и сохранены в EDI.

Конечно, мы можем также складывать 16-битные и 8-битные регистры.

ADD AL, 8
ADD AX, 8
ADD BX, AX
ADD BYTE PTR DS: [EAX], 7


Инструкция будет складывать байт содержимого на которое указывает EAX, и значение 7, и будет сохранять результат в то же самое место.

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

SUB A, B

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

45b3fa33252b7ed1840bd4c776389163._.png

INC A и DEC A

Инструкция увеличивает или уменьшает регистр, или содержимое ячейки памяти на 1, на самом деле это частный случай сложения и вычитания.

dfc164f30a58cfe9c58c04def725be3e._.png

Оба используются для примера, чтобы увеличить или уменьшить счётчик на 1.

IMUL

Это инструкция умножения со знаком и существует две её формы.

IMUL A, B
IMUL A, B, C


Первая инструкция выполняет целочисленное умножение A на B, и результат сохраняется в A, а вторая инструкция выполняет умножение B на C, и результат сохраняется в A.

В обоих случаях A может быть только регистром, B может быть регистром или содержимым ячейки памяти и C может быть только константой.

IMUL EAX, [ECX]
IMUL ESI, EDI, 25


Давайте рассмотрим пример в VEVIEWER

f5dc0f5815b11538a826877e10910bcb._.png

Мы видим, что есть только примеры для первой инструкции, в обоих случаях будет производиться целочисленное умножение обоих членов и будет сохраняться результат в первый член - ECX.

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

IMUL EAX, EDI, 25

Здесь инструкция умножает EDI на 25 и сохраняет результат в EAX, это очень легко.

IDIV A

В этом случае A определяет только ДЕЛИТЕЛЬ операции, делимое же как и частное не определяются, потому что они всегда используют одинаковые регистры.

2e2b6a9dfeabcf417eb51b96bb135a0e._.png

Эта операция создаёт большое 64-битное число, старшая часть которого находится в EDX, а младшая часть в EAX. Инструкция делит значение на A и сохраняет результат в EAX, а остаток значение в EDX.

bd50e330050cceadb74efefe3dbdb32d._.png

Если EAX для примера равно 5, EDX равно нулю и ECX равно 2 инструкция выполнит целочисленное деление, результат деления 5 на 2 будет равен 2 и он будет сохранён в EAX, а остаток сохранён в EDX.

То же самое произойдёт, если A - это содержимое ячейки памяти, EDX:EAX будет разделено на это значение и результат сохранится в EAX, а остаток в EDX.

ЛОГИЧЕСКИЕ ОПЕРАЦИИ

AND
, OR или XOR

AND A, B


Инструкция выполняет логическую операцию И между двумя значениями и будет сохранять результат в A, то же самое происходит с инструкциями ИЛИ или XOR, каждая инструкция имеет свою таблицу истинности, которая применяется к каждому члену и результат будет сохранён в A.

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

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

XOR EAX, EAX для примера при любом значение EAX поместит в результате ноль, так как таблица истинности для XOR такая.

bb3c2f5b349646b7037b41c2561997d1._.png

В этому случае результат - это последняя колонка и мы видим, что если XORИМ число против себя , это может случиться только, когда в двочином виде оба биты равны нулю или оба бита равны единице, так как это то же самое число, A и B равны и побитовый результат всегда даёт ноль в обоих случаях.

f05229af97bc6087c3add62fcbaa508c._.png
c0dd8f023e624eca5b96bdb79f7edbb3._.png
Записывая значения как бинарные данные в PYTHON строке и используя символ ^, который является операцией XOR в PYTHON, я вижу, что применяя операцию XOR два одинаковых числа всегда дают 0.

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

51b7959a01e999bbad48e0caeceab295._.png

Другое простое использование для примера.

AND EAX, 0F

0F
это 1111 в двоичной системе счисления.
31032e553154128a68b5a89400066134._.png
1297de0db415604cb9d20181d2a17c67._.png
Мы видим, что второй член равен 1, результат не изменится, он останется равным как был первоначально, в то время как все другие биты станут равны нулю.

Таким образом, я легко поместил нули во все биты числа и оставил последние 4 бита нетронутыми.

1762ac9eb71a15e4f3b63672d2eb63de._.png

Там мы видим, что AND в PYTHON записывается символом & и результатом является 0B0111, а это последние четыре первоначальные биты.

В случае OR в PYTHON он записывается как вертикальная черта.

0ee50471941998869d06316f131a2b51._.png

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

NOT A

Инструкция инвертирует все биты A и сохраняется затем в A.

Инструкция NOT не существует в PYTHON, но это очень просто, если Вы имеете число 0101 и Вы применяете к нему операцию NOT.

0de0ab69d2fdb32926e40705c711e293._.png

Результатом будет инверсия каждого бита.

9287cd8d6e446944a9388632b4453d86._.png

Мы видим, что все нули изменились в один и наоборот.

NEG A

NEG A
превращает A в -A.

Это не то же, что операция ~ в PYTHON, потому что тут ещё вычитается единица.

7c63d04fdd96ac248b52133b8b3a6017._.png

Чтобы получить NEG в Python мы должны добавить к результату 1.

4ab80670f975ab176eec88126da713f6._.png

SHL, SHR
SHL A, B
SHR A, B

A
может быть регистром или ячейкой в памяти, а B константой или 8-бит. регистром.

Эти инструкции сдвигают байты влево(SHL) и вправо (SHR), байты которые исчезают с одной стороны, заменяются нулями с другой стороны, давайте рассмотрим пример.

Предположим, что у нас есть -1, для примера.

fb2b0fdf2212f43a2812257585ebc494._.png

И если я сделал SHL 2, я получу.

174213a5e8d96d4fb071b985abe7f732._.png

При перемещении битов влево два бита исчезли в левой части, и теперь заполнены двумя нулями биты в правой части.

014c1a163f58e483ea3bad4cf7bee9f7._.png
То же самое произойдёт, если мы используем SHR, биты сдвинутся к правой стороне. Те биты которые исчезнут с правой стороны, будут заменены на 0 с левой стороны.

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

5faa4a0836566f83d9e6454447686805._.png

До встрече в 7 главе.
 
  • Лайк
Reactions: _k0NsuL