Горячее исправление функции

#c #winapi #assembly #reverse-engineering

#c #winapi #сборка #обратный инжиниринг

Вопрос:

Я пытаюсь выполнить горячее исправление EXE-файла в памяти, исходный код доступен, но я делаю это в учебных целях. (поэтому, пожалуйста, никаких комментариев, предлагающих мне изменить исходный код или использовать обходные пути или любые другие библиотеки)

Ниже приведены функции, с которыми у меня возникают проблемы.

 vm_t* VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret )
{
    MessageBox(NULL, L"Oh snap! We hooked VM_Create!", L"Success!", MB_OK);
    return NULL;
}

void Hook_VM_Create(void)
{

    DWORD dwBackup;
    VirtualProtect((void*)0x00477C3E, 7, PAGE_EXECUTE_READWRITE, amp;dwBackup);

    //Patch the original VM_Create to jump to our detoured one.
    BYTE *jmp = (BYTE*)malloc(5);
    uint32_t offset = 0x00477C3E - (uint32_t)amp;VM_Create; //find the offset of the original function from our own
    memset((void*)jmp, 0xE9, 1);
    memcpy((void*)(jmp 1), amp;offset, sizeof(offset));
    memcpy((void*)0x00477C3E, jmp, 5);

    free(jmp);
}
  

У меня есть функция VM_Create, которую я хочу вызвать вместо исходной функции. Я еще не написал trampoline, поэтому он вылетает (как и ожидалось). Однако не появляется окно сообщения о том, что я изменил исходное создание виртуальной машины на свое собственное. Я полагаю, что таким образом я перезаписываю исходные инструкции.

Ответ №1:

Я вижу несколько проблем.

Я предполагаю, что это 0x00477C3E адрес исходной VM_Create функции. Вам действительно не следует жестко кодировать это. Используйте amp;VM_Create вместо этого. Конечно, это будет означать, что вам нужно использовать другое имя для вашей функции замены.

Смещение вычисляется неправильно. У вас неверный знак. Более того, смещение применяется к указателю инструкции в конце инструкции, а не в начале. Итак, вам нужно сдвинуть ее на 5 (размер инструкции). Смещение также должно быть целым числом со знаком.

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

 int32_t offset = (int32_t)amp;New_VM_Create - ((int32_t)amp;VM_Create 5);
  

Спасибо Хансу Пассанту за исправление моей собственной глупой ошибки sign в оригинальной версии!

Если вы работаете на 64-разрядной машине, вам нужно выполнить свою арифметику в 64 битах и, как только вы вычислили смещение, сократить его до 32-разрядного смещения.

Другой нюанс заключается в том, что вы должны восстановить доступ к памяти только для чтения после записи новой JMP инструкции и вызова FlushInstructionCache .

Комментарии:

1. Хорошо, я обновил это мое смещение, чтобы теперь оно было правильным. Я не думал о добавлении 5, теперь это имеет смысл. Но я все еще не получаю окно, в котором говорится, что мы подключились к моему vm_create. Я знаю, что способ записи байтов в память УРОДЛИВ, есть ли более аккуратный способ сделать это? Вы видите какие-либо проблемы с тем, как я записываю в память?

2. Вы не забыли повернуть знак в смещении. В вашей терминологии код должен быть: int32_t offset = (int32_t)amp;VM_Create 5 - (int32_t)0x00477C3E;

3. Способ записи в память прекрасен. Вы должны проверить возвращаемое значение VirtualProtect , но если это не сработает, вы получите ошибку seg. Вы должны передать 5, а не 7 в VirtualProtect . Действительно, единственное, что здесь может пойти не так, это вычисление смещения.

4. Знак неправильный. Смещение = amp;newFunc — (patchAddr 5). -5.

5. Хорошо, теперь все работает идеально. Теперь мне просто нужно записать мою функцию trampoline с помощью байтов, которые я переписал. И теперь я прекрасно понимаю арифметику перехода, команда jmp переходит относительно того места, где она заканчивается, а не где начинается функция (я знал это, я не знаю, почему я думал иначе), поэтому, чтобы вычислить смещение, мы добавляем 5 к исходному vm_create , поскольку объем данных, которые мы записали для jmp относительный адрес, составлял 5 байт. Большое вам спасибо. И я действительно сбросил память до PAGE_EXECUTE_READ.