Вам, наверное, известно, что мы уже больше чем 1.5 года занимаемся проблемами самоизменения кода. Так вот, только сейчас мы уже реально близки к желаемому результату. На данном этапе мы закончили работу над движком, который позволяет разбирать код по командам, изменять его и проч.

Сам движок состоит из 2х файлов:

  1. alma.inc - дизасм Alma v3.0
  2. belma.inc - набор необходимых процедур.

Все нижеописанные процедуры используют одну и туже структуру переменных, ссылка на которую должна быть в регистре EBP:

  BelmaGlobalVars	struc
   MAproc	dd ?	; Адрес процедуры выделения памяти
   MFproc	dd ?	; Адрес процедуры освобождения памяти
   CStart	dd ?	; Начало кода
   CLen	dd ?	; Длина кода
   Head	dd ?	; Голова списка
   ComNum	dd ?	; Количество команд в списке
   TempVar dd ?	; Переменная, использующаяся в различных целях
   TempVar2 dd ?	; Переменная, использующаяся в различных целях
  BelmaGlobalVars	ends
Т.к. практически все процедуры работают с памятью, вы должны указать адреса необходимых функций. Предполагается, что формат вызова этих функций такой же как у АПИшных GlobalAlloc и GlobalFree.

Вот схема действий движка:

  1. Дизассемблируем код и составляем список инструкций

    На этом этапе работает наш дизасм инструкций 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] = Адрес последнего элемента, вставленного подсписка
    

    Т.е. вы опять передаёте кусок кода, которым вы бы хотели заменить некую команду, Belma_Replace сама его дизасемблирует и вклеит в нужное место :) + она ещё и сама удалит ненужную замещённую команду.

    К сожалению, мы ещё не написали удобных процедур для перестановки команд местами, но это дело недалёкого будущего...

    Кстати, саму процедуру мутации напишите сами :)), конечно же используя вышепредставленные две процедуры.

  3. Ассемблирование списка

    Тут всё просто - переводим список в готовый работаспособный код. Для этого вам необходимо выделить достаточное количество памяти под будущий код и вызвать процедуру 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