Есть разные пути к Силе. И каждый по-разному себе её представляет. Кто-то представляет Силу как хитроумную шкатулку с невидимыми духами внутри, а кто-то день за днём пишет строки бессмысленного кода, двигающего колесо Сансары, и видит Силу в том, чтобы это колесо продолжалось вертеться. В погоне за Силой некоторые постигают дао программирования, другие продают душу Баалу, третьи призывают на помощь могучих демонов. Заклинание кода не является путём к Силе, оно является ключом ко многим путям. И пусть по ним вас ведёт безупречность.
В прошлой главе я рассказал, как читать Книгу Двойных Слов. Теперь вы можете колдовать почти любой 32-битный код, хотя кое-что и осталось недосказанным. Наверняка многие из вас хотели бы узнать, как можно на практике применить заклинание кода. Давайте рассмотрим простую самомодифирующуюся программу.
Предположим, в нашей программе есть следующий код:
mov esi,50
mov edi,20
imul esi,5
add esi,edi
mov eax,esi
Очевидно, этот код вычисляет следующее значение в eax: esi*5+edi=50*5+20=270. А теперь мы хотим, чтобы наша программа модифицировала сама себя так, чтобы приведенный код превратился в:
mov ecx,50
mov edx,20
imul edx,5
sub edx,ecx
mov eax,edx
Это можно записать как edx*5-ecx=20*5-50=50. "Но возможно ли, чтобы программа без нашего участия сама себя изменила?", - может спросить иной читатель. Да, возможно. Разумеется, если мы правильно её запрограммируем и будем следовать канонам алхимии, а именно - смешивать всё в правильных пропорциях и давать свои зелья на пробу другим, прежде чем пить их самим.
Сначала давайте выясним, как будут выглядеть заколдованные инструкции. Первые две не представляют для нас особого труда, ведь мы уже работали с ними в предыдущих главах. Их заклинания выглядят так:
db 0BEh ; mov ecx, 50
dd 50
db 0BFh ; mov edx, 20
dd 20
Чтобы выяснить заклинание для третьей инструкции, следует обратиться к Книге Двойных Слов. Из всех опкодов нам подходит следующий:
6B /r ib IMUL r32,r/m32,imm8
Согласно описанию, в поле Reg задается регистр назначения, в R/M - регистр или адрес ячейки памяти с умножаемым значением, а за опкодом следует байт с множителем. Таким образом, у нас получается следующее:
db 6Bh,11110110b,8
С операцией сложения регистров мы также уже имели дело:
db 3,11110111b
Как вы должны помнить из предыдущей главы, "MOV регистр,регистр" можно закодировать двумя способами. Как выяснилось, FASM заколдовал нужную нам инструкцию сложения так:
01 /r ADD r/m32,r32 Add r32 to r/m32
А мы в предыдущих главах использовали следующую инструкцию:
03 /r ADD r32,r/m32 Add r/m32 to r32
Не страшно. Это просто результат того, что набор инструкций избыточный и к одной и той же цели ведут разные пути. Главное - аккуратно определить заклинание:
db 1,11111110b ; 03(Опкод), 11(Mod)-111(Reg=EDI)-110(R/M=ESI)
Теперь перейдем к самомодифицированию программы. Вот рабочий текст:
format PE console
entry start
include '..\..\include\kernel.inc'
include '..\..\include\user.inc'
include '..\..\include\macro\stdcall.inc'
include '..\..\include\macro\import.inc'
include '..\..\include\macro\ccall.inc'
section '.data' data readable writeable
_d db '%d', 0
var dd 50
section '.code' code executable readable writeable
start:
mov [.mov_esi_imm],byte 0B9h
mov [.mov_edi_imm],byte 0BAh
mov [.imul_esi_imm+1],byte 11010010b
mov [.add_esi_edi],byte 02Bh
mov [.add_esi_edi+1],byte 11010001b
mov [.mov_eax_esi+1],byte 11010000b
; Модифицируемый код
.mov_esi_imm: mov esi,50
.mov_edi_imm: mov edi,20
.imul_esi_imm: imul esi,5
.add_esi_edi: add esi,edi
.mov_eax_esi: mov eax,esi
; Проверка модификации
push edx
push _d
call [printf]
add esp, 8
invoke ExitProcess,0
section '.idata' import data readable writeable
library kernel32,'kernel32.dll',\
msvcrt,'msvcrt.dll'
kernel32: import ExitProcess,'ExitProcess'
msvcrt: import printf,'printf'
Строчка "section '.code' code executable readable writeable" указывает, что в сегмент кода можно писать. Мы модифицируем наш код очень простым методом: поверх имеющихся кодов записываем новые. После этого управление получает уже модифицированный код.
Как видите, самомодифицируемый код - это не так сложно: у вас есть код, и он сам себя модифицирует.
[C] Aquila / WASM.RU