Exetools  

Go Back   Exetools > General > General Discussion

Notices

Reply
 
Thread Tools Display Modes
  #1  
Old 01-18-2015, 16:12
ioannis ioannis is offline
Friend
 
Join Date: Jan 2015
Posts: 28
Rept. Given: 6
Rept. Rcvd 9 Times in 5 Posts
Thanks Given: 6
Thanks Rcvd at 16 Times in 10 Posts
ioannis Reputation: 9
Question 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?
Reply With Quote
  #2  
Old 01-18-2015, 16:28
DMichael's Avatar
DMichael DMichael is offline
Family
 
Join Date: Apr 2012
Location: Israel
Posts: 199
Rept. Given: 139
Rept. Rcvd 281 Times in 72 Posts
Thanks Given: 13
Thanks Rcvd at 31 Times in 25 Posts
DMichael Reputation: 200-299 DMichael Reputation: 200-299 DMichael Reputation: 200-299
at entrypoint?about memory you can just hook some kernel functions for memory allocation and follow it
Reply With Quote
  #3  
Old 01-18-2015, 16:59
ioannis ioannis is offline
Friend
 
Join Date: Jan 2015
Posts: 28
Rept. Given: 6
Rept. Rcvd 9 Times in 5 Posts
Thanks Given: 6
Thanks Rcvd at 16 Times in 10 Posts
ioannis Reputation: 9
Quote:
Originally Posted by DMichael View Post
at entrypoint?about memory you can just hook some kernel functions for memory allocation and follow it
If I hook at RtlImageNtHeaderEx, I can get the EntryPoint
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...

[email protected]:
0x0FD91154 jmp _DllMainCRTStartup (0FD92B00h)
...
...
[email protected]:
0x0FD91276 jmp CoGetMalloc (0FD91518h)
0x0FD9127B int 3
0x0FD9127C int 3

Can i use the space after [email protected] 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 ?
Reply With Quote
  #4  
Old 01-19-2015, 01:14
Archer's Avatar
Archer Archer is offline
retired
 
Join Date: Aug 2005
Posts: 232
Rept. Given: 1
Rept. Rcvd 46 Times in 19 Posts
Thanks Given: 3
Thanks Rcvd at 382 Times in 53 Posts
Archer Reputation: 46
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?!
Reply With Quote
  #5  
Old 01-19-2015, 01:29
Carbon Carbon is offline
VIP
 
Join Date: Sep 2013
Posts: 113
Rept. Given: 7
Rept. Rcvd 189 Times in 48 Posts
Thanks Given: 0
Thanks Rcvd at 59 Times in 18 Posts
Carbon Reputation: 100-199 Carbon Reputation: 100-199
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
Reply With Quote
  #6  
Old 01-20-2015, 03:01
deroko's Avatar
deroko deroko is offline
cr4zyserb
 
Join Date: Nov 2005
Posts: 217
Rept. Given: 13
Rept. Rcvd 30 Times in 14 Posts
Thanks Given: 7
Thanks Rcvd at 32 Times in 15 Posts
deroko Reputation: 30
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
Reply With Quote
The Following 2 Users Gave Reputation+1 to deroko For This Useful Post:
b30wulf (01-22-2015), sh3dow (01-23-2015)
  #7  
Old 01-22-2015, 05:43
ioannis ioannis is offline
Friend
 
Join Date: Jan 2015
Posts: 28
Rept. Given: 6
Rept. Rcvd 9 Times in 5 Posts
Thanks Given: 6
Thanks Rcvd at 16 Times in 10 Posts
ioannis Reputation: 9
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:
[email protected]:
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
I've also tried Carbon's solution but for some reason the following simplified code is failing on me at "SymFromName".
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);
}
Reply With Quote
  #8  
Old 01-23-2015, 02:42
Carbon Carbon is offline
VIP
 
Join Date: Sep 2013
Posts: 113
Rept. Given: 7
Rept. Rcvd 189 Times in 48 Posts
Thanks Given: 0
Thanks Rcvd at 59 Times in 18 Posts
Carbon Reputation: 100-199 Carbon Reputation: 100-199
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
Reply With Quote
  #9  
Old 01-24-2015, 22:50
SLV SLV is offline
Friend
 
Join Date: May 2005
Posts: 62
Rept. Given: 3
Rept. Rcvd 4 Times in 3 Posts
Thanks Given: 5
Thanks Rcvd at 2 Times in 2 Posts
SLV Reputation: 4
Hook LoadLibraryEx -> check if dll is your -> fix flags to DONT_RESOLVE_DLL_REFERENCES -> call original -> set hooks -> call DllMain.
Reply With Quote
  #10  
Old 01-25-2015, 04:41
Carbon Carbon is offline
VIP
 
Join Date: Sep 2013
Posts: 113
Rept. Given: 7
Rept. Rcvd 189 Times in 48 Posts
Thanks Given: 0
Thanks Rcvd at 59 Times in 18 Posts
Carbon Reputation: 100-199 Carbon Reputation: 100-199
Quote:
Originally Posted by SLV View Post
Hook LoadLibraryEx -> check if dll is your -> fix flags to DONT_RESOLVE_DLL_REFERENCES -> call original -> set hooks -> call DllMain.
This is a bad solution... quote from msdn:

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
Reply With Quote
  #11  
Old 01-28-2015, 06:58
SLV SLV is offline
Friend
 
Join Date: May 2005
Posts: 62
Rept. Given: 3
Rept. Rcvd 4 Times in 3 Posts
Thanks Given: 5
Thanks Rcvd at 2 Times in 2 Posts
SLV Reputation: 4
Ok, set hooks -> fill dll's imports -> call DllMain
There is no legal way to do this without durty hacks.
Reply With Quote
  #12  
Old 05-09-2015, 22:44
ioannis ioannis is offline
Friend
 
Join Date: Jan 2015
Posts: 28
Rept. Given: 6
Rept. Rcvd 9 Times in 5 Posts
Thanks Given: 6
Thanks Rcvd at 16 Times in 10 Posts
ioannis Reputation: 9
Smile

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);
}
Reply With Quote
  #13  
Old 07-29-2015, 01:09
ioannis ioannis is offline
Friend
 
Join Date: Jan 2015
Posts: 28
Rept. Given: 6
Rept. Rcvd 9 Times in 5 Posts
Thanks Given: 6
Thanks Rcvd at 16 Times in 10 Posts
ioannis Reputation: 9
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.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


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


All times are GMT +8. The time now is 02:56.


Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX
( 1998 - 2022 )