Заклинание кода: Вав

Дата публикации 20 дек 2002

Заклинание кода: Вав — Архив WASM.RU

Есть разные пути к Силе. И каждый по-разному себе её представляет. Кто-то представляет Силу как хитроумную шкатулку с невидимыми духами внутри, а кто-то день за днём пишет строки бессмысленного кода, двигающего колесо Сансары, и видит Силу в том, чтобы это колесо продолжалось вертеться. В погоне за Силой некоторые постигают дао программирования, другие продают душу Баалу, третьи призывают на помощь могучих демонов. Заклинание кода не является путём к Силе, оно является ключом ко многим путям. И пусть по ним вас ведёт безупречность.

В прошлой главе я рассказал, как читать Книгу Двойных Слов. Теперь вы можете колдовать почти любой 32-битный код, хотя кое-что и осталось недосказанным. Наверняка многие из вас хотели бы узнать, как можно на практике применить заклинание кода. Давайте рассмотрим простую самомодифирующуюся программу.

Предположим, в нашей программе есть следующий код:

Код (Text):
  1.  
  2.         mov esi,50
  3.         mov edi,20
  4.         imul esi,5
  5.         add esi,edi
  6.         mov eax,esi

Очевидно, этот код вычисляет следующее значение в eax: esi*5+edi=50*5+20=270. А теперь мы хотим, чтобы наша программа модифицировала сама себя так, чтобы приведенный код превратился в:

Код (Text):
  1.  
  2.         mov ecx,50
  3.         mov edx,20
  4.         imul edx,5
  5.         sub edx,ecx
  6.         mov eax,edx

Это можно записать как edx*5-ecx=20*5-50=50. "Но возможно ли, чтобы программа без нашего участия сама себя изменила?", - может спросить иной читатель. Да, возможно. Разумеется, если мы правильно её запрограммируем и будем следовать канонам алхимии, а именно - смешивать всё в правильных пропорциях и давать свои зелья на пробу другим, прежде чем пить их самим.

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

Код (Text):
  1.  
  2.         db 0BEh ; mov ecx, 50
  3.         dd 50
  4.         db 0BFh ; mov edx, 20
  5.         dd 20

Чтобы выяснить заклинание для третьей инструкции, следует обратиться к Книге Двойных Слов. Из всех опкодов нам подходит следующий:

Код (Text):
  1.  
  2. 6B /r ib         IMUL r32,r/m32,imm8

Согласно описанию, в поле Reg задается регистр назначения, в R/M - регистр или адрес ячейки памяти с умножаемым значением, а за опкодом следует байт с множителем. Таким образом, у нас получается следующее:

Код (Text):
  1.  
  2.         db 6Bh,11110110b,8

С операцией сложения регистров мы также уже имели дело:

Код (Text):
  1.  
  2.         db 3,11110111b

Как вы должны помнить из предыдущей главы, "MOV регистр,регистр" можно закодировать двумя способами. Как выяснилось, FASM заколдовал нужную нам инструкцию сложения так:

Код (Text):
  1.  
  2. 01 /r    ADD r/m32,r32     Add r32 to r/m32

А мы в предыдущих главах использовали следующую инструкцию:

Код (Text):
  1.  
  2. 03 /r    ADD r32,r/m32     Add r/m32 to r32

Не страшно. Это просто результат того, что набор инструкций избыточный и к одной и той же цели ведут разные пути. Главное - аккуратно определить заклинание:

Код (Text):
  1.  
  2.         db 1,11111110b ; 03(Опкод), 11(Mod)-111(Reg=EDI)-110(R/M=ESI)

Теперь перейдем к самомодифицированию программы. Вот рабочий текст:

Код (Text):
  1.  
  2. format PE console
  3. entry start
  4.  
  5. include '..\..\include\kernel.inc'
  6. include '..\..\include\user.inc'
  7. include '..\..\include\macro\stdcall.inc'
  8. include '..\..\include\macro\import.inc'
  9. include '..\..\include\macro\ccall.inc'
  10.  
  11. section '.data' data readable writeable
  12.  
  13. _d      db '%d', 0
  14. var     dd 50
  15.  
  16. section '.code' code executable readable writeable
  17.  
  18. start:
  19.  
  20.                 mov     [.mov_esi_imm],byte 0B9h
  21.                 mov     [.mov_edi_imm],byte 0BAh
  22.                 mov     [.imul_esi_imm+1],byte 11010010b
  23.                 mov     [.add_esi_edi],byte 02Bh
  24.                 mov     [.add_esi_edi+1],byte 11010001b
  25.                 mov     [.mov_eax_esi+1],byte 11010000b
  26.  
  27.                 ; Модифицируемый код
  28. .mov_esi_imm:   mov     esi,50
  29. .mov_edi_imm:   mov     edi,20
  30. .imul_esi_imm:  imul    esi,5
  31. .add_esi_edi:   add     esi,edi
  32. .mov_eax_esi:   mov     eax,esi
  33.  
  34.                 ; Проверка модификации
  35.                 push    edx
  36.                 push    _d
  37.                 call    [printf]
  38.                 add     esp, 8
  39.  
  40.                 invoke  ExitProcess,0
  41.  
  42. section '.idata' import data readable writeable
  43.  
  44.                 library kernel32,'kernel32.dll',\
  45.                         msvcrt,'msvcrt.dll'
  46. kernel32:       import  ExitProcess,'ExitProcess'
  47. msvcrt:         import  printf,'printf'

Строчка "section '.code' code executable readable writeable" указывает, что в сегмент кода можно писать. Мы модифицируем наш код очень простым методом: поверх имеющихся кодов записываем новые. После этого управление получает уже модифицированный код.

Как видите, самомодифицируемый код - это не так сложно: у вас есть код, и он сам себя модифицирует. © Aquila / WASM.RU


0 968
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532