Динамическая модификация кода — самомодифицирующиеся программы
Опубликовано в Assembler / Reverse engineering Eduard Mishkurov 8 января, 2012![]()
Вероятно, многие считают, что самомодифицирующийся код (динамическая модификация кода программы) нужен только вирусам и троянам для того, чтобы затруднить распознавание антивирусными программами. Однако это далеко не так, и, как показывает практика, вынуждает нас этим заниматься сама великая и могучая корпорация — 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
Что и требовалось доказать. Спасибо за внимание.
Добавить комментарий Отменить ответ
Для отправки комментария вам необходимо авторизоваться.
Нет комментариев