`Вам, наверное, известно, что мы уже больше чем 1.5 года занимаемся проблемами самоизменения кода. Так вот, только сейчас мы уже реально близки к желаемому результату. На данном этапе мы закончили работу над движком, который позволяет разбирать код по командам, изменять его и проч. Сам движок состоит из 2х файлов: alma.inc - дизасм Alma v3.0 belma.inc - набор необходимых процедур. Все нижеописанные процедуры используют одну и туже структуру переменных, ссылка на которую должна быть в регистре EBP:
      BelmaGlobalVars	struc
       MAproc	dd ?	; Адрес процедуры выделения памяти
       MFproc	dd ?	; Адрес процедуры освобождения памяти
       CStart	dd ?	; Начало кода
       CLen	dd ?	; Длина кода
       Head	dd ?	; Голова списка
       ComNum	dd ?	; Количество команд в списке
       TempVar dd ?	; Переменная, использующаяся в различных целях
       TempVar2 dd ?	; Переменная, использующаяся в различных целях
      BelmaGlobalVars	ends
1. Т.к. практически все процедуры работают с памятью, вы должны указать адреса необходимых функций. Предполагается, что формат вызова этих функций такой же как у АПИшных GlobalAlloc и GlobalFree. Вот схема действий движка: Дизассемблируем код и составляем список инструкций На этом этапе работает наш дизасм инструкций Alma v3.0. Он позволяет не только получить длину команды, но и полностью разобрать её структуру, а так же понять что данная команда использует и изменяет (т.е. регистры, память, флаги). Полное описание структуры отдизассембленной команды (ComandInfo) смотрите в alma.inc Процедура Belma_Dizasm, которая лежит в belma.inc, позволяет перевести код в список. Структура элемента этого списка:
       Item    struc
       INext   dd  ?         ; Next
       IFlags  db  ?         ; Flags
                             ; 1-Jmp
                             ; 2-Labels
                             ; else-nothing = 0
       ICmdAdr dd  ?         ; Исходный адрес команды
       ITo     dd  ?         ; Указатель на метку (если JMP)
       ICI     ComandInfo ?  ; Cтруктуры отдизассембленной команды
       Item    ends
   Вход этой процедуры:
       [EBP.CStart] = начало кода (DWord)
       [EBP.CLen] = длина кода (DWord)
   Выход:
       [EBP.Head] = указатель на начало списка, или 0 при ошибке
       [EBP.ComNum] = количество команд в списке (DWord)
2. Т.о. если всё правильно, то после вызова данной процедуры мы получим полностью разобранный код, с которым можно будет немного поработать... Кстати, небольшая оговорочка - КОД ДОЛЖЕН БЫТЬ ЛИНЕЙНЫМ :) Т.е. не должно быть относительных команд перехода за пределы нашего кода и не должно быть данных среди нашего кода! Гм, обычные правила пермутации :) Мутация Здесь делается что угодно с нашим списком... Замена команд на синонимы, добавление мусора и т.д. Для этих целей в движок заложены следующие процедуры: Belma_Insert - Процедура, для вставки куска кода в заданное место списка. Вход:
       [EBP.CStart] = Адрес памяти где лежит код, который надо вставлять
       [EBP.CLen] = Длина кода
       [EBP.Head] = Голова списка
       [EBP.TempVar] = Адрес элемента списка, после которого вставлять
   Выход:
       CF = 1 при ошибке
   Т.е. вы передаёте кусок кода, который вы бы хотели вставить после определённого элемента списка, Belma_Insert сама его дизасемблирует и вклеит в нужное место. Belma_Replace - Процедура, для замены одного элемента списка куском кода в заданном месте. Вход:
       [EBP.CStart] = Адрес памяти где лежит код, которым надо заменять
       [EBP.CLen] = Длина кода
       [EBP.Head] = Голова списка
       [EBP.TempVar] = Адрес элемента списка, после которого замещать
   Выход:
       CF = 1 при ошибке
       [EBP.TempVar] = Адрес последнего элемента, вставленного подсписка
3. Т.е. вы опять передаёте кусок кода, которым вы бы хотели заменить некую команду, Belma_Replace сама его дизасемблирует и вклеит в нужное место :) + она ещё и сама удалит ненужную замещённую команду. К сожалению, мы ещё не написали удобных процедур для перестановки команд местами, но это дело недалёкого будущего... Кстати, саму процедуру мутации напишите сами :)), конечно же используя вышепредставленные две процедуры. Ассемблирование списка Тут всё просто - переводим список в готовый работаспособный код. Для этого вам необходимо выделить достаточное количество памяти под будущий код и вызвать процедуру Belma_Asm:
       mov    [EBP.CStart], offset на память
       call   Belma_Asm
   Для того, чтобы правильно определить нужное количество памяти, можно воспользоваться переменной [EBP.ComNum] - она показывает количество команд в данный момент в списке. Например так:
       mov     ECX, [EBP.ComNum] ; Возьмём количество команд
       shl     ECX, 4            ; и умножим на 16
4. Освобождение памяти из-под списка Это, пожалуй, самый важный шаг в нашей работе :) Ведь мы не имеем права захламлять память ничего не подозревающих бедных юзеров... Так вот, для освобождения памяти из-под списка есть процедура Belma_FreeList: Выход:
       [EBP.Head] = Голова списка
Вот и всё! Ещё хочется отметить, что в belma.inc имеются процедуры GetCommandLen для определения размера одной разобранной команды и Belma_AllocOneElement для удобного выделения памяти под 1 элемент списка. Описания их вызовов смотрите в belma.inc Напоминаем, что для работы ВСЕХ процедур этого проекта просто необходимо заполнить поля MAproc и MFproc. И не портить регистр EBP, конечно же! :) Данная версия движка ещё не закончина полностью и работа по его усовершенствованию ведётся постоянно. Наш дизасм понимает большинство команд x86 процесоров (полный список понимаемых команд см. alma.inc) за исключением, пожалуй, всяких извратных команд MMXа, навороченых команд последних AMDшек и т.п. Но для нашей цели вполне достаточно того что есть... 20.08.02`