От http://lastfrag.com/hotpatching-and-inline-hooking-explained/,
Q1) Код переходит из верхней памяти в малую память или наоборот?
Q2) Что более важно, во время вычисления смещения замены, почему вы должны минусить преамбулу функции? Это потому, что смещение начинается с конца инструкции, а не с начала?
DWORD ReplacementAddressOffset = ReplacementAddress - OriginalAddress - 5;
Полный код:
void HookAPI(wchar_t *Module, char *API, DWORD Function)
{
HMODULE hModule = LoadLibrary(Module);
DWORD OriginalAddress = (DWORD)GetProcAddress(hModule, API);
DWORD ReplacementAddress = (DWORD)Function;
DWORD ReplacementAddressOffset = ReplacementAddress - OriginalAddress - 5;
LPBYTE pOriginalAddress = (LPBYTE)OriginalAddress;
LPBYTE pReplacementAddressOffset = (LPBYTE)(&ReplacementAddressOffset);
DWORD OldProtect = 0;
DWORD NewProtect = PAGE_EXECUTE_READWRITE;
VirtualProtect((PVOID)OriginalAddress, 5, NewProtect, &OldProtect);
for (int i = 0; i < 5; i++)
Store[i] = pOriginalAddress[i];
pOriginalAddress[0] = (BYTE)0xE9;
for (int i = 0; i < 4; i++)
pOriginalAddress[i + 1] = pReplacementAddressOffset[i];
VirtualProtect((PVOID)OriginalAddress, 5, OldProtect, &NewProtect);
FlushInstructionCache(GetCurrentProcess(), NULL, NULL);
FreeLibrary(hModule);
}
Q3) В этом коде заменяется относительный адрес инструкции jmp; relAddrSet — указатель на исходный пункт назначения; это указатель на новый пункт назначения. Я не понимаю вычисление адреса, почему вы должны добавить исходный пункт назначения к функции ForHook + opcodeOffset?
DWORD *relAddrSet = (DWORD *)(currentOpcode + 1);
DWORD_PTR to = (*relAddrSet) + ((DWORD_PTR)functionForHook + opcodeOffset);
*relAddrSet = (DWORD)(to - ((DWORD_PTR)originalFunction + opcodeOffset));
Да, код работает «вперед», если я правильно понимаю этот вопрос. Одна инструкция выполняется за другой, если она не ветвится.
Инструкция, которая выполняет относительный переход (JMP, CALL), выполняет переход относительно начала следующий инструкция. Вот почему вы должны вычесть длину инструкции (здесь: 5) из разницы.
Я не могу ответить на ваш третий вопрос. Пожалуйста, дайте некоторый контекст и то, что код должен делать.
Да, относительный адрес — это смещение после инструкций, поэтому вы должны вычесть 5.
Но, на мой взгляд, вы должны просто забыть идею относительного скачка и попробовать абсолютный скачок.
Зачем ? Потому что это намного проще и совместимо с x86-64 (относительные переходы ограничены +/ -2GB).
Абсолютный скачок (х64):
48 b8 ef cd ab 89 67 45 23 01 mov rax, 0x0123456789abcdef
ff e0 jmp rax
И для x86:
b8 67 45 23 01 mov eax, 0x01234567
ff e0 jmp eax
Вот модифицированный код (загрузчик теперь 7 байтов вместо 5):
void HookAPI(wchar_t *Module, char *API, DWORD Function)
{
HMODULE hModule = LoadLibrary(Module);
DWORD OriginalAddress = (DWORD)GetProcAddress(hModule, API);
DWORD OldProtect = 0;
DWORD NewProtect = PAGE_EXECUTE_READWRITE;
VirtualProtect((PVOID)OriginalAddress, 7, NewProtect, &OldProtect);
memcpy(Store, OriginalAddress, 7);
memcpy(OriginalAddress, "\xb8\x00\x00\x00\x00\xff\xe0", 7);
memcpy(OriginalAddress+1, &ReplacementAddress, sizeof(void*));
VirtualProtect((PVOID)OriginalAddress, 7, OldProtect, &NewProtect);
FlushInstructionCache(GetCurrentProcess(), NULL, NULL);
FreeLibrary(hModule);
}
Код такой же для x64, но вы должны добавить 2 NOPS (90
) в начале или в конце по порядку соответствуют размеру следующих инструкций, поэтому загрузчик "\x48\xb8<8-bytes addr>\xff\xe0\x90\x90"
(14 байт)
Q1) Программа запускается от нижнего к верхнему адресу (то есть счетчик программы увеличивается на размер каждой инструкции, за исключением случаев прыжков, вызовов или повтора). Но я, вероятно, упускаю суть вопроса.
Q2) Да, на x86 переходы выполняются после увеличения счетчика программы на размер инструкции перехода (5 байт); когда ЦП добавляет смещение перехода к счетчику программ для вычисления целевого адреса, счетчик программ уже был увеличен на 5.
Q3) Этот код довольно странный, но он может работать. Я полагаю, что * relAddrset изначально содержит смещение перехода к originalFunction (т.е. * relAddSet == originalFunction -lativeOffset). Если это правда, окончательный результат состоит в том, что * relayddrSet содержит смещение перехода к functionFoHook. Действительно, последняя инструкция становится:
* RelAddrSet = (originalFunction-relativeOffset) + functionForHook-originalFunction
== functionForHook -lativeOffset