#1
|
|||
|
|||
How can I hook DllMain ?
I'm playing around with VLD which is inline patching LdrLoadDll in order to hook to dynamic library functions that allocate memory, in order to detect memory leaks.
At this point the dll has not been loaded yet so I dont have a module handle to setup library hooks. Also setting up hooks after the original LdrLoadDll is called means that DllMain has already been called, which might allocate some memory that is not recorded by the leak detector. My question is, where is the best place to setup the hooks just before the call to DllMain, in order to be able to record memory allocations within DllMain? |
#2
|
||||
|
||||
at entrypoint?about memory you can just hook some kernel functions for memory allocation and follow it
|
#3
|
|||
|
|||
Quote:
0x0FD91154 e9 a7 19 00 00 which is a near relative jump to _DllMainCRTStartup If i understand correctly i need a long jump (absolute address), which is a 2 byte op code, to enter the hook function in my module. So there is no space to add the additional op code... __DllMainCRTStartup@12: 0x0FD91154 jmp _DllMainCRTStartup (0FD92B00h) ... ... _CoGetMalloc@8: 0x0FD91276 jmp CoGetMalloc (0FD91518h) 0x0FD9127B int 3 0x0FD9127C int 3 Can i use the space after _CoGetMalloc@8 to make a near jump instruction there, and then a long jump to my module ? Also is there any guarantee that there will always be space there to include an additional jump instruction ? |
#4
|
||||
|
||||
Load your own DLL. At EP of your DLL get the return address from stack. C code can use MSVC intrinsics for this. It'll be address in the system DLL from which all DLL EPs are called. Hook it. Profit?!
|
#5
|
|||
|
|||
I think the function is called LdrpCallInitRoutine. Just hook it. You can get the address from NTDLL debug symbols.
Code:
BOOLEAN NTAPI LdrpCallInitRoutine ( IN PDLL_INIT_ROUTINE EntryPoint, IN PVOID BaseAddress, IN ULONG Reason, IN PVOID Context )
__________________
My blog: https://ntquery.wordpress.com |
#6
|
||||
|
||||
You can do it also by hooking NtMapViewOfSection and getting name of mapped section, if it matches wanted dll, look in pe header of mapped dll for entrypoint and hook it That's the simplest way.
Somebody in the past also asked how to know when dlls are loaded, and I will also point to same code : http://deroko.phearless.org/itracer.zip <--- look for hook of NtMapViewOfSection. There is detailed code how to find dll name too As you may see from previous answers, there are many ways to do it
__________________
http://accessroot.com |
#7
|
|||
|
|||
I'll have to try every solution more extensively to find the one that requires the least amount of assembly knowledge, before I mark best answer.
I have already tried Archer's suggestion that gives me a pointer inside LdrpCallInitRoutine function at the red line below, so now I need to figure out how to change the function to call and return from my function pointer. Code:
_LdrpCallInitRoutine@16: 7785998C 55 push ebp 7785998D 8B EC mov ebp,esp 7785998F 56 push esi 77859990 57 push edi 77859991 53 push ebx 77859992 8B F4 mov esi,esp 77859994 FF 75 14 push dword ptr [ebp+14h] 77859997 FF 75 10 push dword ptr [ebp+10h] 7785999A FF 75 0C push dword ptr [ebp+0Ch] 7785999D FF 55 08 call dword ptr [ebp+8] 778599A0 8B E6 mov esp,esi 778599A2 5B pop ebx 778599A3 5F pop edi 778599A4 5E pop esi 778599A5 5D pop ebp 778599A6 C2 10 00 ret 10h 778599A9 90 nop 778599AA 90 nop 778599AB 90 nop 778599AC 90 nop 778599AD 90 nop Code:
if (SymInitializeW(g_currentProcess, symbolpath, FALSE)) { DWORD64 dwBaseAddress = SymLoadModuleExW(g_currentProcess, NULL, L"ntdll.dll", NULL, (DWORD64)ntdll, NULL, NULL, NULL); IMAGEHLP_MODULE64 moduleinfo = { sizeof(IMAGEHLP_MODULE64) }; BOOL bInfo = SymGetModuleInfo64(g_currentProcess, dwBaseAddress, &moduleinfo); TCHAR szSymbolName[MAX_SYM_NAME] = TEXT("LdrpCallInitRoutine"); ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)] = { 0 }; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; BOOL symfound = SymFromName(g_currentProcess, szSymbolName, pSymbol); } |
#8
|
|||
|
|||
Look here for a simple PDB-GetProcAddress
https://bitbucket.org/NtQuery/pdb-getprocaddress/src/eebe9737d6de34261f6bec5b7b57ae973978c9e2/PDBReader/Source.cpp?at=master
__________________
My blog: https://ntquery.wordpress.com |
#9
|
|||
|
|||
Hook LoadLibraryEx -> check if dll is your -> fix flags to DONT_RESOLVE_DLL_REFERENCES -> call original -> set hooks -> call DllMain.
|
#10
|
|||
|
|||
Quote:
If this value is used, and the executable module is a DLL, the system does not call DllMain for process and thread initialization and termination. Also, the system does not load additional executable modules that are referenced by the specified module. https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179%28v=vs.85%29.aspx
__________________
My blog: https://ntquery.wordpress.com |
#11
|
|||
|
|||
Ok, set hooks -> fill dll's imports -> call DllMain
There is no legal way to do this without durty hacks. |
#12
|
|||
|
|||
I have developed a working solution I wanted to run by your briliant minds for comments, feedback or any other considerations i might have missed.
I used Archer recommendation to get the ReturnAddress and work my way from there by creating a code cave. This solution should work for all versions of Windows XP+ x86 and x64. Code:
typedef BOOLEAN(NTAPI *PDLL_INIT_ROUTINE)(IN PVOID DllHandle, IN ULONG Reason, IN PCONTEXT Context OPTIONAL); BOOLEAN WINAPI LdrpCallInitRoutine(IN PVOID BaseAddress, IN ULONG Reason, IN PVOID Context, IN PDLL_INIT_ROUTINE EntryPoint) { #ifdef _DEBUG TCHAR szName[MAX_PATH] = { 0 }; GetModuleFileName((HMODULE)BaseAddress, szName, _countof(szName)); #endif return EntryPoint(BaseAddress, Reason, (PCONTEXT)Context); } PBYTE NtDllFindDetourAddress(const PBYTE pAddress, SIZE_T dwSize) { MEMORY_BASIC_INFORMATION meminfo = { 0 }; if (VirtualQuery(pAddress, &meminfo, sizeof(meminfo))) { // Find spare bytes at the end of the memory region that are unused // so we can jump to this address and set up the detour. PBYTE end = (PBYTE)meminfo.BaseAddress + meminfo.RegionSize; PBYTE begin = end; while (((SIZE_T)(end - begin) < dwSize) && (begin != pAddress)) { if (*(--begin) != 0x00) end = begin; } if (begin != pAddress) return begin; } return NULL; } PBYTE NtDllFindParamAddress(const PBYTE pAddress) { PBYTE ptr = pAddress; // Test previous 32 bytes to find the begining address we need to patch // for 32bit find => push [ebp][14h] => parameters are pushed to stack // for 64bit find => mov r8,... => parameters are moved to registers r8, rdx, rcx while (pAddress - --ptr < 0x20) { #ifdef _WIN64 if (((ptr[0] & 0x4D) == ptr[0]) && (ptr[1] == 0x8B) && ((ptr[2] & 0xC7) == ptr[2])) { #else if ((ptr[0] == 0xFF) && (ptr[1] == 0x75) && (ptr[2] == 0x14)) { #endif return ptr; } } return NULL; } PBYTE NtDllFindCallAddress(const PBYTE pAddress) { PBYTE ptr = pAddress; // Test previous 32 bytes to find the begining address we need to patch // for 32bit find => call [ebp][08h] // for 64bit find => call <register> while (pAddress - --ptr < 0x20) { #ifdef _WIN64 if ((ptr[0] == 0xFF) && ((ptr[1] & 0xD7) == ptr[1])) { if ((*(ptr - 1) & 0x41) == *(ptr - 1)) { --ptr; } #else if ((ptr[0] == 0xFF) && (ptr[1] == 0x55) && (ptr[2] == 0x08)) { #endif return ptr; } } return NULL; } typedef struct _NTDLL_LDR_PATCH { PBYTE pPatchAddress; SIZE_T nPatchSize; BYTE pBackup[0x20]; PBYTE pDetourAddress; SIZE_T nDetourSize; BOOL bState; } NTDLL_LDR_PATCH, *PNTDLL_LDR_PATCH; NTDLL_LDR_PATCH patch; BOOL NtDllPatch(const PBYTE pReturnAddress, NTDLL_LDR_PATCH &NtDllPatch) { if (NtDllPatch.bState == FALSE) { #ifdef _WIN64 BYTE ptr[] = { '?', 0x87, '?' }; // xchg r.., r9 BYTE mov[] = { 0x48, 0xB8, '?', '?', '?', '?', '?', '?', '?', '?' }; // mov rax, 0x0000000000000000 BYTE call[] = { 0xFF, 0xD0, '?', 0x87, '?' }; // call rax // xchg r.., r9 #else BYTE ptr[] = { 0xFF, 0x75, 0x08 }; // push [ebp][08h] BYTE mov[] = { 0x90, 0xB8, '?', '?', '?', '?' }; // mov eax, 0x00000000 BYTE call[] = { 0xFF, 0xD0 }; // call eax #endif BYTE jmp[] = { 0xE9, '?', '?', '?', '?' }; // jmp 0x00000000 NtDllPatch.pPatchAddress = NtDllFindParamAddress(pReturnAddress); PBYTE pCallAddress = NtDllFindCallAddress(pReturnAddress); NtDllPatch.nPatchSize = pReturnAddress - NtDllPatch.pPatchAddress; SIZE_T nParamSize = pCallAddress - NtDllPatch.pPatchAddress; NtDllPatch.nDetourSize = _countof(ptr) + nParamSize + _countof(mov) + _countof(jmp); NtDllPatch.pDetourAddress = NtDllFindDetourAddress(pReturnAddress, NtDllPatch.nDetourSize); if (NtDllPatch.pPatchAddress && NtDllPatch.pDetourAddress && ((_countof(jmp) + _countof(call)) <= NtDllPatch.nPatchSize)) { memcpy(NtDllPatch.pBackup, NtDllPatch.pPatchAddress, NtDllPatch.nPatchSize); DWORD dwProtect = 0; if (VirtualProtect(NtDllPatch.pDetourAddress, NtDllPatch.nDetourSize, PAGE_EXECUTE_READWRITE, &dwProtect)) { memset(NtDllPatch.pDetourAddress, 0x90, NtDllPatch.nDetourSize); #ifdef _WIN64 // Copy original param instructions memcpy(&NtDllPatch.pDetourAddress[0], NtDllPatch.pPatchAddress, nParamSize); // Exchange the register that holds the EntryPoint with r9 BYTE reg = ((pCallAddress[0] & 0x41) == 0x41 ? 0x08 : 0x00) + (pCallAddress[pReturnAddress - pCallAddress - 1] & 0x07); ptr[0] = 0x4C + ((reg & 0x08) ? 0x01 : 0x00); //ptr[0] = 0x49 + ((reg & 0x08) ? 0x04 : 0x00); ptr[2] = 0xC8 + (reg & 0x07); //ptr[2] = 0xC1 + (((reg & 0x07) / 2) * 0x10) + ((reg & 0x07) % 2 ? 0x08 : 0x00); memcpy(&NtDllPatch.pDetourAddress[nParamSize], &ptr, _countof(ptr)); #else // Push EntryPoint as last parameter memcpy(&NtDllPatch.pDetourAddress[0], &ptr, _countof(ptr)); // Copy original param instructions memcpy(&NtDllPatch.pDetourAddress[_countof(ptr)], NtDllPatch.pPatchAddress, nParamSize); #endif // Move LdrpCallInitRoutine to eax/rax *(PSIZE_T)(&mov[2]) = (SIZE_T)LdrpCallInitRoutine; memcpy(&NtDllPatch.pDetourAddress[_countof(ptr) + nParamSize], &mov, _countof(mov)); // Jump to original function *(DWORD*)(&jmp[1]) = (DWORD)(pReturnAddress - _countof(call) - (NtDllPatch.pDetourAddress + NtDllPatch.nDetourSize)); memcpy(&NtDllPatch.pDetourAddress[_countof(ptr) + nParamSize + _countof(mov)], &jmp, _countof(jmp)); VirtualProtect(NtDllPatch.pDetourAddress, NtDllPatch.nDetourSize, dwProtect, &dwProtect); if (VirtualProtect(NtDllPatch.pPatchAddress, NtDllPatch.nPatchSize, PAGE_EXECUTE_READWRITE, &dwProtect)) { memset(NtDllPatch.pPatchAddress, 0x90, NtDllPatch.nPatchSize); // Jump to detour address *(DWORD*)(&jmp[1]) = (DWORD)(NtDllPatch.pDetourAddress - (pReturnAddress - _countof(call))); memcpy(pReturnAddress - _countof(call) - _countof(jmp), &jmp, _countof(jmp)); #ifdef _WIN64 // Exchange r9 with the register that originally held the EntryPoint memcpy(&call[2], &ptr, _countof(ptr)); #endif // Call LdrpCallInitRoutine from eax/rax memcpy(pReturnAddress - _countof(call), &call, _countof(call)); VirtualProtect(NtDllPatch.pPatchAddress, NtDllPatch.nPatchSize, dwProtect, &dwProtect); NtDllPatch.bState = TRUE; } } } } return NtDllPatch.bState; } BOOL NtDllRestore(NTDLL_LDR_PATCH &NtDllPatch) { // Restore patched bytes BOOL bResult = FALSE; if (NtDllPatch.bState && NtDllPatch.nPatchSize && &NtDllPatch.pBackup[0]) { DWORD dwProtect = 0; if (VirtualProtect(NtDllPatch.pPatchAddress, NtDllPatch.nPatchSize, PAGE_EXECUTE_READWRITE, &dwProtect)) { memcpy(NtDllPatch.pPatchAddress, NtDllPatch.pBackup, NtDllPatch.nPatchSize); VirtualProtect(NtDllPatch.pPatchAddress, NtDllPatch.nPatchSize, dwProtect, &dwProtect); if (VirtualProtect(NtDllPatch.pDetourAddress, NtDllPatch.nDetourSize, PAGE_EXECUTE_READWRITE, &dwProtect)) { memset(NtDllPatch.pDetourAddress, 0x00, NtDllPatch.nDetourSize); VirtualProtect(NtDllPatch.pDetourAddress, NtDllPatch.nDetourSize, dwProtect, &dwProtect); bResult = TRUE; } } } return bResult; } #define _DECL_DLLMAIN // for _CRT_INIT #include <process.h> // for _CRT_INIT #pragma comment(linker, "/entry:DllEntryPoint") __declspec(noinline) BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { // Patch/Restore ntdll address that calls the dll entry point if (fdwReason == DLL_PROCESS_ATTACH) { NtDllPatch((PBYTE)_ReturnAddress(), patch); } if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH) if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved)) return(FALSE); if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH) if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved)) return(FALSE); if (fdwReason == DLL_PROCESS_DETACH) { NtDllRestore(patch); } return(TRUE); } |
#13
|
|||
|
|||
Guys, no love from you?
None of you gurus can review the code sample above to give me some comments/pointers ? I would appreciate any comments greatly. |
Thread Tools | |
Display Modes | |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Windows Hook | user1 | Source Code | 0 | 04-24-2021 05:23 |
SST Hook -> Bluescreen!? | Cobi | General Discussion | 12 | 05-04-2005 09:37 |
SYSENTER hook | niom | General Discussion | 13 | 08-12-2004 02:50 |