Ассемблер: прячем несколько команд в команде

в 8:08, , рубрики: защита от копирования, ненормальное программирование, Песочница, метки: , ,

image
Речь пойдёт о том, как можно спрятать «лишние» ассемблерные команды в обычном коде. Данный метод полезен для усложнения дизассемблирования кода, особенно, если генерацию «скрытых» команд автоматизировать.
Инструментарий: отладчик 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, запустим и посмотрим результат.
Ассемблер: прячем несколько команд в команде
Ставим break-point (адрес подсвечен красным, позиция выполнения — чёрным), нажимаем кнопку запуска.
Ассемблер: прячем несколько команд в команде
После запуска процесс выполнения остановился в нужной точке.

Как ни странно, но программа не зависла и значение регистра EAX и EBX равны нулю. Но как же это могло произойти?
Посмотрим на код повнимательнее. Первая команда помещает в регистр EAX значение 1EBC031, вторая команда помещает в EBX значение 90DB3190. CMP сравнивает два регистра. JNE делает переход, если значения регистров не совпадают. А вот тут самое интересное — переход делается не в начало первой команды, а на второй байт первой команды. Давайте будем трассировать код по одной команде.

Ставим на первую позицию.
Ассемблер: прячем несколько команд в команде
Выполняются команды, как обычно.
Ассемблер: прячем несколько команд в команде
… и так доходим до перехода.
Ассемблер: прячем несколько команд в команде
Выполнился короткий переход, и что же мы видим? Две спрятанные команды — XOR EAX,EAX выполняет обнуление регистра EAX.
Ассемблер: прячем несколько команд в команде
И короткий переход, который передаёт управление внутрь другого MOV-а.
Ассемблер: прячем несколько команд в команде

Ассемблер: прячем несколько команд в команде
Ещё место осталось :)
Ассемблер: прячем несколько команд в команде
Обнуляем второй регистр
Ассемблер: прячем несколько команд в команде
Доходим до проверки. В это время регистры EAX и EBX равны нулю.
Ассемблер: прячем несколько команд в команде
Успешно проходит проверка.
Ассемблер: прячем несколько команд в команде

Ассемблер: прячем несколько команд в команде

Дело в том, что в регистр 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).

Автор: godAlex


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js