├── README.md ├── hook_v1.cpp ├── injection ├── hookDLL.cpp └── injector.cpp └── hook_v2.cpp /README.md: -------------------------------------------------------------------------------- 1 | # basic-hooking 2 | 3 | Refrenced here - https://jayo78.github.io/WinAPI-Hooking-basics/ 4 | 5 | - hook_v1: 5 byte MessageBoxA hook with no trampoline 6 | - hook_v2: 5 byte MessageBoxA hook with a trampoline 7 | - injection: hook_v2 but now injected into a remote process using basic DLL injection 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /hook_v1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** Simple MessageBoxA hook using the classic 5 byte relative jump technique without a trampoline. 3 | ** Instead of bypassing the hook in the proxy function when passing execution to MessageBoxA, we 4 | ** will simply re-write the original bytes, unhooking the function. 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #pragma comment(lib,"user32.lib") 11 | 12 | char saved_buffer[5]; // buffer to save the original bytes 13 | FARPROC hooked_address= NULL; 14 | 15 | // The proxy function we will jump to after the hook has been installed 16 | int __stdcall proxy_function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) 17 | { 18 | std::cout << "Hello from MessageBox!\n"; 19 | std::cout << "Text: " << (LPCSTR)lpText << "\nCaption: " << (LPCSTR)lpCaption << std::endl; 20 | 21 | // unhook the function (re-write the saved buffer) to prevent infinite recursion 22 | WriteProcessMemory(GetCurrentProcess(), (LPVOID)hooked_address, saved_buffer, 5, NULL); 23 | 24 | // return to the original function, which is now unhooked, and modify the intended parameters 25 | return MessageBoxA(NULL, "yeet", "yeet", uType); 26 | } 27 | 28 | void install_hook() 29 | { 30 | HINSTANCE hinstLib; 31 | VOID *proxy_address; 32 | DWORD *relative_offset; 33 | DWORD src; 34 | DWORD dst; 35 | CHAR patch[5]= {0}; 36 | 37 | // 1. get memory address of the MessageBoxA function from user32.dll 38 | hinstLib= LoadLibraryA(TEXT("user32.dll")); 39 | hooked_address= GetProcAddress(hinstLib, "MessageBoxA"); 40 | 41 | // 2. save the first 5 bytes into saved_buffer 42 | ReadProcessMemory(GetCurrentProcess(), hooked_address, saved_buffer, 5, NULL); 43 | 44 | // 3. overwrite the first 5 bytes with a jump to proxy_function 45 | proxy_address= &proxy_function; 46 | src= (DWORD)hooked_address + 5; // will jump from the next instruction (after our 5 byte jmp instruction) 47 | dst= (DWORD)proxy_address; 48 | relative_offset= (DWORD *)(dst-src); 49 | 50 | memcpy(patch, "\xE9", 1); 51 | memcpy(patch + 1, &relative_offset, 4); 52 | 53 | WriteProcessMemory(GetCurrentProcess(), (LPVOID)hooked_address, patch, 5, NULL); 54 | } 55 | 56 | int main() 57 | { 58 | // call without hook 59 | MessageBoxA(NULL, "hello", "hello", MB_OK); 60 | 61 | install_hook(); 62 | 63 | // call with hook (arguments will be altered through the proxy function) 64 | MessageBoxA(NULL, "hello", "hello", MB_OK); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /injection/hookDLL.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #pragma comment(lib,"user32.lib") 5 | 6 | typedef int (WINAPI *defTrampolineFunc)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); 7 | LPVOID trampoline_address; 8 | 9 | // The proxy function we will jump to after the hook has been installed 10 | int __stdcall proxy_function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) 11 | { 12 | std::cout << "----------intercepted call to MessageBoxA----------\n"; 13 | std::cout << "Text: " << (LPCSTR)lpText << "\nCaption: " << (LPCSTR)lpCaption << "\n"; 14 | 15 | // pass to the trampoline with altered arguments which will then return to MessageBoxA 16 | defTrampolineFunc trampoline= (defTrampolineFunc)trampoline_address; 17 | return trampoline(hWnd, "hooked", "hooked", uType); 18 | } 19 | 20 | void install_hook() 21 | { 22 | HINSTANCE hinstLib; 23 | VOID *proxy_address; 24 | DWORD *relative_offset; 25 | DWORD *hook_address; 26 | DWORD src; 27 | DWORD dst; 28 | BYTE patch[5]= {0}; 29 | BYTE saved_buffer[5]; // buffer to save the original bytes 30 | FARPROC function_address= NULL; 31 | 32 | // 1. get memory address of the MessageBoxA function from user32.dll 33 | hinstLib= LoadLibraryA(_T("user32.dll")); 34 | function_address= GetProcAddress(hinstLib, _T("MessageBoxA")); 35 | 36 | // 2. save the first 5 bytes into saved_buffer 37 | ReadProcessMemory(GetCurrentProcess(), function_address, saved_buffer, 5, NULL); 38 | 39 | // 3. overwrite the first 5 bytes with a jump to proxy_function 40 | proxy_address= &proxy_function; 41 | src= (DWORD)function_address + 5; 42 | dst= (DWORD)proxy_address; 43 | relative_offset= (DWORD *)(dst-src); 44 | 45 | memcpy(patch, "\xE9", 1); 46 | memcpy(patch + 1, &relative_offset, 4); 47 | 48 | WriteProcessMemory(GetCurrentProcess(), (LPVOID)function_address, patch, 5, NULL); 49 | 50 | // 4. Build the trampoline 51 | trampoline_address= VirtualAlloc(NULL, 11, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 52 | hook_address= (DWORD *)((DWORD)function_address + 5); 53 | memcpy((BYTE *)trampoline_address, &saved_buffer, 5); 54 | memcpy((BYTE *)trampoline_address + 5, "\x68", 1); 55 | memcpy((BYTE *)trampoline_address + 6, &hook_address, 4); 56 | memcpy((BYTE *)trampoline_address + 10, "\xC3", 1); 57 | } 58 | 59 | BOOL WINAPI DllMain (HINSTANCE const instance, DWORD const reason, LPVOID const reserved) 60 | { 61 | switch (reason) 62 | { 63 | case DLL_PROCESS_ATTACH: 64 | MessageBoxA(NULL, "process attached", "Monitor", MB_OK); 65 | MessageBoxA(NULL, "installing hook", "Monitor", MB_OK); 66 | install_hook(); 67 | break; 68 | 69 | case DLL_THREAD_ATTACH: 70 | case DLL_PROCESS_DETACH: 71 | case DLL_THREAD_DETACH: 72 | break; 73 | } 74 | 75 | return TRUE; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /hook_v2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** Simple MessageBoxA hook using the classic 5 byte relative jump technique with a trampoline. 3 | ** The trampoline will be able to bypass the installed hook by executing the saved instructions 4 | ** and then calling MessageBoxA + 5 bytes 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #pragma comment(lib,"user32.lib") 11 | 12 | typedef int (WINAPI *defTrampolineFunc)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); 13 | LPVOID trampoline_address; 14 | 15 | // The proxy function we will jump to after the hook has been installed 16 | int __stdcall proxy_function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) 17 | { 18 | std::cout << "----------intercepted call to MessageBoxA----------\n"; 19 | std::cout << "Text: " << (LPCSTR)lpText << "\nCaption: " << (LPCSTR)lpCaption << "\n"; 20 | 21 | // pass to the trampoline with altered arguments which will then return to MessageBoxA 22 | defTrampolineFunc trampoline= (defTrampolineFunc)trampoline_address; 23 | return trampoline(hWnd, "yeet", "yeet", uType); 24 | } 25 | 26 | void install_hook() 27 | { 28 | HINSTANCE hinstLib; 29 | VOID *proxy_address; 30 | DWORD *relative_offset; 31 | DWORD *hook_address; 32 | DWORD src; 33 | DWORD dst; 34 | CHAR patch[5]= {0}; 35 | char saved_buffer[5]; // buffer to save the original bytes 36 | FARPROC function_address= NULL; 37 | 38 | // 1. get memory address of the MessageBoxA function from user32.dll 39 | hinstLib= LoadLibraryA(TEXT("user32.dll")); 40 | function_address= GetProcAddress(hinstLib, "MessageBoxA"); 41 | 42 | // 2. save the first 5 bytes into saved_buffer 43 | ReadProcessMemory(GetCurrentProcess(), function_address, saved_buffer, 5, NULL); 44 | 45 | // 3. overwrite the first 5 bytes with a jump to proxy_function 46 | proxy_address= &proxy_function; 47 | src= (DWORD)function_address + 5; 48 | dst= (DWORD)proxy_address; 49 | relative_offset= (DWORD *)(dst-src); 50 | 51 | memcpy(patch, "\xE9", 1); 52 | memcpy(patch + 1, &relative_offset, 4); 53 | 54 | WriteProcessMemory(GetCurrentProcess(), (LPVOID)function_address, patch, 5, NULL); 55 | 56 | // 4. Build the trampoline 57 | trampoline_address= VirtualAlloc(NULL, 11, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 58 | hook_address= (DWORD *)((DWORD)function_address + 5); 59 | memcpy((BYTE *)trampoline_address, &saved_buffer, 5); 60 | memcpy((BYTE *)trampoline_address + 5, "\x68", 1); 61 | memcpy((BYTE *)trampoline_address + 6, &hook_address, 4); 62 | memcpy((BYTE *)trampoline_address + 10, "\xC3", 1); 63 | } 64 | 65 | int main() 66 | { 67 | // call without hook 68 | MessageBoxA(NULL, "hello", "hello", MB_OK); 69 | 70 | install_hook(); 71 | 72 | // call with hook (arguments will be altered through the proxy function) 73 | MessageBoxA(NULL, "hello", "hello", MB_OK); 74 | MessageBoxA(NULL, "um hello?", "helllooo", MB_OK); 75 | MessageBoxA(NULL, "hmmm this", "isn't working", MB_OK); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /injection/injector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | void inject_DLL(TCHAR *dllPath, HANDLE process) 7 | { 8 | /* 9 | ** Variable declarations 10 | */ 11 | LPVOID lpBaseAddress; 12 | HANDLE hRemoteThread; 13 | HMODULE kernel32; 14 | FARPROC loadlibrary; 15 | SIZE_T pathLen; 16 | 17 | /* 18 | ** Initialize variables 19 | */ 20 | lpBaseAddress= NULL; 21 | hRemoteThread= NULL; 22 | loadlibrary= NULL; 23 | kernel32= NULL; 24 | pathLen= _tcslen(dllPath) * sizeof(TCHAR); 25 | 26 | kernel32= GetModuleHandle(_T("kernel32.dll")); 27 | loadlibrary= GetProcAddress(kernel32, _T("LoadLibraryA")); 28 | 29 | /* 30 | ** Allocate memory and write the dll path that will be injected 31 | */ 32 | lpBaseAddress= VirtualAllocEx(process, NULL, pathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 33 | if (lpBaseAddress == NULL) 34 | std::cout << "VirtualAllocEx failed: " << GetLastError() << "\n"; 35 | 36 | if (!WriteProcessMemory(process, lpBaseAddress, dllPath, pathLen, NULL)) 37 | std::cout << "WriteProcessMemory failed: " << GetLastError() << "\n"; 38 | 39 | /* 40 | ** Create a thread that will load the dll path using LoadLibrary as a start up routine 41 | */ 42 | hRemoteThread= CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)(VOID *)loadlibrary, lpBaseAddress, NULL, 0); 43 | if (hRemoteThread == NULL) 44 | std::cout << "CreateRemoteThread failed: " << GetLastError() << "\n"; 45 | 46 | /* 47 | ** Clean up 48 | */ 49 | WaitForSingleObject(hRemoteThread, INFINITE); 50 | CloseHandle(hRemoteThread); 51 | } 52 | 53 | 54 | int main(int argc, TCHAR *argv[]) 55 | { 56 | /* 57 | ** Variable declarations 58 | */ 59 | STARTUPINFO si; 60 | PROCESS_INFORMATION pi; 61 | TCHAR *targetExe; 62 | TCHAR *dllName; 63 | TCHAR dllPath[MAX_PATH]; 64 | SIZE_T pathLen; 65 | 66 | /* 67 | ** Initialize variables 68 | */ 69 | if (argc < 3) 70 | { 71 | std::cout << "Not enough arguments\n" << "Usage: injector.exe \n"; 72 | return 1; 73 | } 74 | 75 | targetExe= _T(argv[1]); 76 | dllName= _T(argv[2]); 77 | GetFullPathName(dllName, MAX_PATH, dllPath, NULL); 78 | 79 | ZeroMemory( &si, sizeof(si)); 80 | ZeroMemory( &pi, sizeof(pi)); 81 | si.cb = sizeof(si); 82 | 83 | /* 84 | ** Create the process as suspended - main thread created but no DLLs loaded 85 | */ 86 | if(!CreateProcess(NULL, targetExe, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) 87 | { 88 | std::cout << "CreateProcess failed: " << GetLastError() << "\n"; 89 | return 1; 90 | } 91 | 92 | /* 93 | ** inject the process that we created 94 | */ 95 | inject_DLL(dllPath, pi.hProcess); 96 | 97 | /* 98 | ** Resume the suspended process now with our DLL injected 99 | */ 100 | ResumeThread(pi.hThread); 101 | WaitForSingleObject( pi.hProcess, INFINITE ); 102 | CloseHandle( pi.hProcess ); 103 | CloseHandle( pi.hThread ); 104 | 105 | return 0; 106 | } --------------------------------------------------------------------------------