![image](https://habrastorage.org/storage2/b3d/9b7/a72/b3d9b7a727f981af9e22aa61dc095a15.jpg)
Речь пойдёт о том, как можно спрятать «лишние» ассемблерные команды в обычном коде. Данный метод полезен для усложнения дизассемблирования кода, особенно, если генерацию «скрытых» команд автоматизировать.
Инструментарий: отладчик OllyDbg.
Странный странный код
Взглянем на следующий код, в котором скрыто намного больше команд, чем видно на первый згляд:
MOV EAX,1EBC031
MOV EBX,90DB3190
CMP EAX,EBX
JNE SHORT 0000009E
NOP
(возможно, будет трудно вставить данный фрагмент, кода с помощью ассемблера в OllyDbg, поэтому советую воспользоваться встроенным HEX-редактором: “B8 31 C0 EB 01 BB 90 31 DB 90 39 D8 75 F3 90” )
Как вы думаете, данный код будет выполняться бесконечно, так как EAX и EBX не равны, а команда JNE будет делать переход в район первой команды пока эти два регистра не будут одинаковы?
Давайте поставим break-point на последний оператор NOP, запустим и посмотрим результат.
![](https://habrastorage.org/storage2/f1f/d95/3af/f1fd953af567d7fdbe191188f9277f7c.jpg)
Ставим break-point (адрес подсвечен красным, позиция выполнения — чёрным), нажимаем кнопку запуска.
![](https://habrastorage.org/storage2/4e7/ad3/8c7/4e7ad38c78bf9768c9f0698a99cdf7ae.jpg)
После запуска процесс выполнения остановился в нужной точке.
Как ни странно, но программа не зависла и значение регистра EAX и EBX равны нулю. Но как же это могло произойти?
Посмотрим на код повнимательнее. Первая команда помещает в регистр EAX значение 1EBC031, вторая команда помещает в EBX значение 90DB3190. CMP сравнивает два регистра. JNE делает переход, если значения регистров не совпадают. А вот тут самое интересное — переход делается не в начало первой команды, а на второй байт первой команды. Давайте будем трассировать код по одной команде.
Ставим на первую позицию.
![](https://habrastorage.org/storage2/544/545/aa8/544545aa8ed3cd828da79ddfbf277aa2.jpg)
Выполняются команды, как обычно.
![](https://habrastorage.org/storage2/140/4ba/43b/1404ba43ba66fa5a5508d24fcc28634c.jpg)
… и так доходим до перехода.
![](https://habrastorage.org/storage2/010/273/6e0/0102736e071734dbbb066084a0cdbac5.jpg)
Выполнился короткий переход, и что же мы видим? Две спрятанные команды — XOR EAX,EAX выполняет обнуление регистра EAX.
![](https://habrastorage.org/storage2/58c/ab7/245/58cab7245f9784df4aa00eaf6f248fe2.jpg)
И короткий переход, который передаёт управление внутрь другого MOV-а.
![](https://habrastorage.org/storage2/e7b/19c/25a/e7b19c25af5d0cd242af22202f2216aa.jpg)
![](https://habrastorage.org/storage2/49e/af7/fc2/49eaf7fc2a3f80e9f26ade20c6d15241.jpg)
Ещё место осталось :)
![](https://habrastorage.org/storage2/29b/c8b/b44/29bc8bb4453df6efe41182df7e2eb33c.jpg)
Обнуляем второй регистр
![](https://habrastorage.org/storage2/22c/082/ce7/22c082ce7a251a7d6b6a63dbf0f650df.jpg)
Доходим до проверки. В это время регистры EAX и EBX равны нулю.
![](https://habrastorage.org/storage2/7b2/47e/15f/7b247e15f4ea5030f0f495145a6c2e4c.jpg)
Успешно проходит проверка.
![](https://habrastorage.org/storage2/128/6e7/6ed/1286e76ed7f9d21cfde6f43dac2bb9a9.jpg)
![](https://habrastorage.org/storage2/c63/a2e/dce/c63a2edcef930645935db8b5cfc79083.jpg)
Дело в том, что в регистр EAX записано не простое число, а машинный код команды. Из-за таких «накладок» код практически не возможно представить на языке ассемблера. Ассемблерные команды имеют разный размер. Команда «MOV EAX,1EBC031» занимает 5 байт, когда как команда XOR EAX,EAX — 2 байта. С помощью команд бинарного сдвига и, используя полу-регистры AL и AH, можно такими «прыжками» набрать обработку и ввод целого регистра EAX.
Хочу отметить, что при вставке машинных команд, как параметр MOV, нужно менять последовательность байтов, так например команда 31CO(XOR EAX,EAX), помещённая в команду MOV будет выглядеть как «MOV EAX,C031».
Данный метод применяется во многих системах защиты программного обеспечения, затрудняя поиски перехода. Так же, значения, занесённые в регистры можно применять для простого шифрования методом XOR. Думаю, можно создать программу, автоматизирующую процесс перекомпилирования куска ассемблерного кода на такие небольшие фрагменты, что точно затруднит процесс обратной инженерии.
Кроме 32-х разрядных регистров, на 64-х битных системах есть 64-х разрядные регистры, это: RAX,RBX,RCX,RDX. В них можно поместить в 2 раза больше команд.
Вставка ассемблерного кода в приложение
Можно воспользоваться стандартными средствами среды разработки высокого уровня: для C++ — этот код будет выглядеть так:
int someint=123;
__asm {
MOV EAX, someint
INC EAX
// ...
MOV someint,EAX
}
для Delphi:
Var someint:Integer; somelong:Longint;
begin
asm
mov ax,i
mov ebx,somelong
// ...
end;
end.
Можно воспользоваться и OllyDbg, предварительно отведя место для кода.
При этом не стоит забывать, что программы, в которые вы вставляете кусок ассемблерного кода, сами используют некоторые регистры, так что не забывайте сохранять значения регистров до их изменения (PUSH) и восстанавливать их по завершению (POP).