Вероятно, многие считают, что самомодифицирующийся код (динамическая модификация кода программы) нужен только вирусам и троянам для того, чтобы затруднить распознавание антивирусными программами. Однако это далеко не так, и, как показывает практика, вынуждает нас этим заниматься сама великая и могучая корпорация — Microsoft.
Почему? — спросите Вы. Да очень просто. Каждая новая операционная система делает доступным для разработчиков ряд новых технологий. И это здорово! Проблема в том, что, несмотря на свое величие, даже Microsoft не в состоянии тщательно тестировать все свои продукты. Таким образом, часть того, что работало раньше, в некий момент перестает работать. Есть, правда, и случаи, когда это делалось Microsoft’ом намеренно (например, была заблокирована возможность запускать неподписанные драйвера под x64). Но это — тема для отдельного разговора.
Что же делать программным пакетам, если в новой версии операционной системы или Service Pack все перестало работать? Бывают случаи, что остаётся только одно — модифицировать код компонентов Microsoft на лету!
Для драйверов ядра с этим есть определенные проблемы. Я имею в виду Patch-Guard под 64-битными системами. Я собираюсь написать про него отдельно в следующих статьях. Ну, а что касается приложений пользовательского режима — возможность такая пока есть и, надеюсь, будет доступна и в дальнейшем.
Я говорю о паре Win32 функций, жизненно необходимых для таких операций:
BOOL WriteProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten); BOOL VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
При помощи первой из них мы, собственно говоря, можем изменить данные по нужному нам адресу. Вторая нам необходима для того, чтобы разрешить запись по нужному нам адресу, поскольку секции кода в большинстве случаев имеют доступ только на чтение и исполнение.
Во всем, что я уже успел перечислить, немаловажно и то, что, во-первых, изменение c использованием такого метода кода, например, Kernel32.dll, происходит только для текущего процесса и не будет влиять на работу других программ, а во-вторых, то, что все известные мне антивирусы относятся к этому весьма лояльно!
Если дела обстоят таким образом, что же нас останавливает от написания самомодифицирующегося кода? Да разве что вопрос — зачем? :). В статье про асинхронный DCOM я приведу конкретный пример, когда это необходимо. Сейчас же давайте попробуем освоить метод до конца.
Реализация метода
Итак, предположим, что мы хотим написать программу, которая будет подправлять свой же код в момент исполнения. Например, мы знаем, что некая функция ведет себя неправильно и выполняет сложение переменных, а должна их вычитать:
int BuggyFunction(volatile int a) { return a + 0x12345678; // А должно быть "return a - 0x12345678;" }
Таким образом, нам нужно:
1. Найти адрес инструкции сравнения переменных a и b (add) 2. Заменить ее на sub
Программа, которую я предлагаю вашему вниманию, и выполняет эти действия. Это простое консольное приложение для Windows. Входная точка программы — функция main — вычисляет адрес функции BuggyFunction, находит смещение инструкции add и выполняет модификацию кода:
void main() { // Вызываем оригинальную функцию и печатаем результат printf("Before modification: %08h\n", BuggyFunction(0x123)); const int ms = 256; // Функция BuggyFunction не может быть длиннее LPBYTE pCode = (LPBYTE)GetFunctionAddress(&BuggyFunction); for (int i = 0; i < ms; i++, pCode++) { if (*(DWORD *)pCode == 0x12345678) break; } if (i >= ms) { printf("Хмм. Инструкция не найдена\n"); return; } ... // Открываем процесс // Разрешаем запись в секцию кода используя VirtualProtect(); DWORD cb; BYTE bSub = 0x50; WriteProcessMemory(hProcess, pCode, &bSub, sizeof(bSub), &cb); ... // Восстанавливаем доступ // Освобождаем ресурсы // Вызываем модифицированную функцию и печатаем результат printf("After modification: %i\n", BuggyFunction(0x123)); }
Предлагаю скачать код проекта по ссылке внизу страницы и убедиться, что, как я и утверждаю, программа при исполнении напечатает
Before modification: 1234579Bh After modification: EDCBAAABh
Что и требовалось доказать. Спасибо за внимание.