Я хочу научиться делать крючки, поэтому я попробовал простую программу, чтобы проверить меня. Крюк работает отлично, но я также хотел вызвать исходную функцию после вызова hooked. Пытался делать по-разному, перемещая стек, восстанавливая исходные байты, а затем вызывая исходную функцию в конце зацепившейся функции, но она не работала.
Моя программа просто ждет любой ключ и печатает текст.
Мой крючок (DLL):
#include <windows.h>
#include <stdio.h>
void WriteMem(DWORD dwAddr, BYTE *dwNew, int Size);
void MyPrintf(char *text)
{
printf("\n Original message: %s\n", buff);
}
void WriteMem(DWORD dwAddr, BYTE *dwNew, int Size)
{
DWORD OldProt;
VirtualProtect((void*)dwAddr, Size, PAGE_EXECUTE_READWRITE, &OldProt);
memset((void*)(dwAddr), 0x90, Size);
memcpy((void*)(dwAddr), (void*)(dwNew), Size);
VirtualProtect((void*)(dwAddr), Size, OldProt, &OldProt);
}
void SetJMP(INT32 dwOld, LPVOID dwNew, INT32 Size)
{
BYTE dwNewBytes[5] = {0xE9, 0x00, 0x00, 0x00, 0x00};
DWORD calc = ((DWORD)dwNew - dwOld - 5);
memcpy(&dwNewBytes[1], &calc, 4);
WriteMem(dwOld, dwNewBytes, Size);
}
int SetIntercepet()
{ // 0x40102A printf address
SetJMP(0x40102A, MyPrintf, 7);
return 0;
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
SetIntercepet();
break;
}
return TRUE;
}
Моя тестовая программа (C):
#include <stdio.h>
#include <windows.h>
int main()
{
while (1)
{
system("pause");
printf("ORIGINAL\n");
}
}
часть программы тестирования декомпилирована:
00401000 /$ 55 PUSH EBP
00401001 |. 8BEC MOV EBP,ESP
00401003 |> B8 01000000 /MOV EAX,1
00401008 |. 85C0 |TEST EAX,EAX
0040100A |. 74 1C |JE SHORT test.00401028
0040100C |. 68 00E04000 |PUSH test.0040E000 ; ASCII "pause"
00401011 |. E8 D9000000 |CALL test.004010EF
00401016 |. 83C4 04 |ADD ESP,4
00401019 |. 68 08E04000 |PUSH test.0040E008 ; ASCII "ORIGINAL"
0040101E |. E8 07000000 |CALL test.0040102A
00401023 |. 83C4 04 |ADD ESP,4
00401026 |.^EB DB \JMP SHORT test.00401003
00401028 |> 5D POP EBP
00401029 \. C3 RETN
0040102A /$ 6A 0C PUSH 0C
0040102C |. 68 50D44000 PUSH test.0040D450
00401031 |. E8 52140000 CALL test.00402488
00401036 |. 33C0 XOR EAX,EAX
00401038 |. 33F6 XOR ESI,ESI
0040103A |. 3975 08 CMP DWORD PTR SS:[EBP+8],ESI
Поскольку вы перезаписываете фактическую функцию printf, вам придется скопировать инструкции там, а затем выполнить соответствующую "исправление", чтобы заставить ее работать в новом месте, а также вернуться к "после вашего исправления". Это будет либо подразумевать точное знание исходного кода (другими словами, push 0c, push test.0040d450
), либо понимание достаточного количества машинного кода для разделения инструкций на их границах.
Другим, гораздо более простым способом было бы заменить исходное словосочетание новым кодом. Итак, вместо исправления кода в 0x40102a вы исправляете свой код в 40101E, сохраняя 40102a от исходной точки вызова, и как только вы сделали то, что вам нужно сделать, вы перезвоните в 40102a.
Что-то вроде этого сделало бы это:
void* origPrintf;
void MyPrintf(char *text)
{
void (*orig)(char *text) = reinterpret_cast<void (*)(char *text)>(origPrintf);
printf("\n Original message: %s\n", buff);
orig(text);
}
void WriteMem(DWORD dwAddr, BYTE *dwNew, int Size, void &*oldCall)
{
DWORD OldProt;
int offset;
VirtualProtect((void*)dwAddr, Size, PAGE_EXECUTE_READWRITE, &OldProt);
memcpy(offset, (void*)(dwAddr + 1), sizeof(offset));
oldCall = (void*)dwAddr + 5 + offset; // 5 byte call instruction assumed.
memset((void*)(dwAddr), 0x90, Size);
memcpy((void*)(dwAddr), (void*)(dwNew), Size);
VirtualProtect((void*)(dwAddr), Size, OldProt, &OldProt);
}
void SetJMP(INT32 dwOld, LPVOID dwNew, INT32 Size, void&*oldCall)
{
BYTE dwNewBytes[5] = {0xE9, 0x00, 0x00, 0x00, 0x00};
DWORD calc = ((DWORD)dwNew - dwOld - 5);
memcpy(&dwNewBytes[1], &calc, 4);
WriteMem(dwOld, dwNewBytes, Size, oldCall);
}
int SetIntercepet()
{ // 0x40102A printf address
SetJMP(0x40102A, MyPrintf, 7);
return 0;
}
[Я не могу проверить код, так как я уверен, что на моей 64-разрядной машине Linux довольно разные адреса, но он должен дать разумный принцип]
void&*
и SetJMP(4 parameters)
называемым SetJMP(3 arguments)
.