div.main {margin-left: 20pt; margin-right: 20pt}
Написание самомодифицирующегося кода под Windows 95
Краткий обзор
Самомодифицирующийся код - программный прием, при котором приложение
создает или изменяет часть своего программного кода во время выполнения.
Windows 95 имеет большую степень защиты кода программы, чем DOS. Обычно
приложения получают доступ к сегменту данных и стеку, но они не имеют
права изменять собственный сегмент кода. Однако это не значит, что сделать
это невозможно - достаточно "попросить" Windows, вызвав функцию
VirtualProtect(). В качестве параметра в функцию передаются адрес первого
байта блока памяти, к которому мы хотим получить доступ, количество байтов
в блоке, а также флаг, определяющий, какой тип доступа нам нужен (т.е.
чтение, запись, выполнение и т.д.). Также в функцию передается адрес переменной,
в которой сохраняется предыдущее состояние защиты указанного блока, которое
мы обязаны восстановить, когда проделаем необходимые манипуляции.
Следующий фрагмент Wn32-кода показывает эту технику "в деле".
Ассемблерный оператор после метки myloop - "mov dword ptr
a, 0x12345678". Предшествующие операторы изменяют его на "mov
dword prt a, 0x87654321". Попробуйте поместить приведенный код в
функцию CC++ и по шагам пройти ее в отладчике:
LPVOID address;
// Получаем адрес слова, которое нужно изменить
_asm mov dword ptr address, offset [myloop+3]
// Получаем разрешение модифицировать участок кода
result = VirtualProtect(address, 4, PAGE_WRITECOPY, &oldprotect);
// Изменяем ассемблерный код. Это эквивалентно *(LPVOID) address = 0x87654321
_asm mov ebx, dword ptr address
_asm mov dword ptr [ebx], 0x87654321
// Все сделано
result = VirtualProtect(address, 4, PAGE_EXECUTE, &oldprotect);
mylopp:
_asm mov dword ptr a, 0x12345678
Создаем новый фрагмент кода
Аналогично можно создать новый фрагмент кода программы. В качестве примера
рассмотрим создание функции, в которую передаются два целых слова и возвращается
их сумма. Определим тип указателя на функцию и переменную типа указателя
на функцию:
typedef LONG (* FunctionType) (LONG, LONG);
FunctionType ComputeSum;
Итак, ComputeSum - переменная типа FunctionType, определяющего указатель
на функцию следующего вида:
LONG Function(LONG, LONG);
Во-первых, нужно опередлить опкоды. которые будут составлять нашу функцию.
Выясням, что для реализации операции сложения двух чисел нужно 11 байт.
Эти 11 байт нужно выделить с помощью оператора new:
ComputeSum = (FunctionType) new BYTE[11];
Теперь можно заполнить выделенную область опкодами:
((LPBYTE) ComputeSum)[0] = 0x55; // push ebp
((LPBYTE) ComputeSum)[1] = 0x8B; // mov ebp, esp
((LPBYTE) ComputeSum)[2] = 0xEC;
((LPBYTE) ComputeSum)[3] = 0x8B; // mov eax, [bp+8]
((LPBYTE) ComputeSum)[4] = 0x45;
((LPBYTE) ComputeSum)[5] = 0x08;
((LPBYTE) ComputeSum)[6] = 0x03; // add eax, [bp+12]
((LPBYTE) ComputeSum)[7] = 0x45;
((LPBYTE) ComputeSum)[8] = 0x0C;
((LPBYTE) ComputeSum)[9] = 0x5D; // pop ebp
((LPBYTE) ComputeSum)[10] = 0xC3; // ret eax
Наконец, можно вызвать функцию VirtualProtect(). Все наши команды на
месте, нам нужно просто разрешить выполнить их. Следующая команда сделает
это:
VirtualProtect(ComputeSum, 11, PAGE_EXECUTE, &oldprotect);
Как и раньше, переменная oldprotect должна быть типа DWORD. Итак, сейчас
у нас есть указатель на обычную функцию, которую можно вызвать, например,
так:
sum = ComputeSum(1, 2);
sum = ComputeSum(val1, val2);
Единственная деталь, о которой не нужно забывать - освободить память,
выделенную для нашей функции оператором new. Перед этим не помешает восстановить
статус защиты выделенного блока памяти:
VirtualProtect(ComputeSum, 11, oldprotect, &oldprotect);
delete (LPBYTE) ComputeSum;
И в заключение небольшое замечание: если вы хотите, чтобы ваша "мутирующая"
программа выполнялась правильно и под Windows NT, после модификаций сегмента
кода нужно вызывать функцию FlushInstructionCache(). Для Windows 95 это
необязательное требование, т.к. эта операционная система поддерживает
только один процессор, однако для совместимости со старшими версиями Windows
вы должны использовать FlushInstructionCahce();
Удачного программирования!
|