TarmoPikaro TarmoPikaro - 2 months ago 5
C++ Question

Executing function in remote process using code injection

I'm using advanced code injection code to start up .dll on remote process.
You can find how this works / code snipet for example from here:

https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/RemoteInit.cpp

I've noticed that with some applications this approach does not work - it crashes host application. Main problem seems to be special kind 3-rd party software like

ConEmuHk64.dll
which intercepts
kernel32.dll GetProcAddress
by providing it's own hook function - after that I'm getting function pointer like this:

*((FARPROC*) &info.pfuncGetProcAddress) = GetProcAddress(hKernel32, "GetProcAddress");


But instead I'm getting pointer to function located in ConEmuHk64.dll.

In my own process calling that function is acceptable, but when trying to do the same in remote process - it crashes, since
ConEmuHk64.dll
is not necessarily available there.

I've figure out mechanism how to auto-probe correct address of that function by manually walking in DOS/NE other header - here is code snippet:

//
// We use GetProcAddress as a base function, with exception to when GetProcAddress itself is hooked by 3-rd party
// software and pointer to function returned to us is incorrect - then we try to locate function manually by
// ourselfes.
//
FARPROC GetProcAddress2( HMODULE hDll, char* funcName )
{
FARPROC p = GetProcAddress( hDll, funcName );

if( !p )
return NULL;

IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER *) hDll;

if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return p;

IMAGE_NT_HEADERS* pNtHeaders = (IMAGE_NT_HEADERS *) (((char*) pDosHeader) + pDosHeader->e_lfanew);

if ( pNtHeaders->Signature != IMAGE_NT_SIGNATURE )
return p;

IMAGE_OPTIONAL_HEADER* pOptionalHeader = &pNtHeaders->OptionalHeader;

if( (char*) p >= (char*)hDll && (char*) p <= ((char*)hDll) + pOptionalHeader->SizeOfCode )
// Sounds like valid address.
return p;

// Does not sounds right, may be someone hooked given function ? (ConEmuHk64.dll or ConEmuHk.dll)
IMAGE_DATA_DIRECTORY* pDataDirectory = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExp = (IMAGE_EXPORT_DIRECTORY *) ((size_t) pDosHeader + pDataDirectory->VirtualAddress);

ULONG* addrofnames = (ULONG *) ((BYTE*) hDll + pExp->AddressOfNames);
ULONG* funcaddr = (ULONG*) ((BYTE*) hDll + pExp->AddressOfFunctions);

for ( DWORD i = 0; i < pExp->NumberOfNames; i++ )
{
char* funcname = (char*) ((BYTE*) hDll + addrofnames[i]);

if ( strcmp( funcname, funcName ) == 0 )
{
void* p2 = (void*) ((BYTE*) hDll + funcaddr[i]);
return (FARPROC) p2;
}
} //for

return p;
} //GetProcAddress2


This seems to be working for
GetProcAddress
- I can detect hooked function and override it's behavior. However - this approach is not generic. I have tried similar function calls for other methods, for example for
FreeLibrary/AddDllDirectory/RemoveDllDirectory
- and those function pointers pinpoints out of dll boundary -
GetProcAddress
returns address before DOS header.

I suspect that comparison by dll / code size range is not correct one:

if( (char*) p >= (char*)hDll && (char*) p <= ((char*)hDll) + pOptionalHeader->SizeOfCode )


But don't have a clue how formula can be improved.

Can you recommend me how to make this fix fully - so any 3-rd party software can intercept any function, and I can survive from it without crashes ?

Answer

Function pointer resolving is incorrect in case if "Exported function forward" is used (Can be googled by that term).

A proper function resolving can be written like this: (What you see above is some copy-pasted function from some forum).

//
//  We use GetProcAddress as a base function, with exception to when GetProcAddress itself is hooked by 3-rd party 
//  software and pointer to function returned to us is incorrect - then we try to locate function manually by
//  ourselfes.
//
FARPROC GetProcAddress2( HMODULE hDll, char* funcName )
{
    FARPROC p = GetProcAddress( hDll, funcName );

    if( !p )
        return NULL;

    IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER *) hDll;

    if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
        return p;

    IMAGE_NT_HEADERS* pNtHeaders = (IMAGE_NT_HEADERS *) (((char*) pDosHeader) + pDosHeader->e_lfanew);

    if ( pNtHeaders->Signature != IMAGE_NT_SIGNATURE )
        return p;

    IMAGE_OPTIONAL_HEADER* pOptionalHeader = &pNtHeaders->OptionalHeader;

    if( (char*) p >= (char*)hDll && (char*) p <= ((char*)hDll) + pOptionalHeader->SizeOfCode )
        // Sounds like valid address.
        return p;

    // Does not sounds right, may be someone hooked given function ? (ConEmuHk64.dll or ConEmuHk.dll)
    IMAGE_DATA_DIRECTORY* pDataDirectory = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    IMAGE_EXPORT_DIRECTORY* pExp = (IMAGE_EXPORT_DIRECTORY *) ((size_t) pDosHeader + pDataDirectory->VirtualAddress);

    ULONG* addrofnames = (ULONG *) ((BYTE*) hDll + pExp->AddressOfNames);
    ULONG* funcaddr = (ULONG*) ((BYTE*) hDll + pExp->AddressOfFunctions);

    for ( DWORD i = 0; i < pExp->NumberOfNames; i++ )
    {
        char* funcname = (char*) ((BYTE*) hDll + addrofnames[i]);

        if ( strcmp( funcname, funcName ) == 0 )
        {
            ULONG addressOfFunction = funcaddr[i];
            void* p2 = (void*) ((BYTE*) hDll + addressOfFunction);

            if( addressOfFunction >= pDataDirectory->VirtualAddress && addressOfFunction < pDataDirectory->VirtualAddress + pDataDirectory->Size )
            {
                // "Exported function forward" - address of function can be found in another module.
                // Actually for example AddDllDirectory is truly located in KernelBase.dll (alias api-ms-win-core-libraryloader-l1-1-0.dll ?)
                char* dll_func = (char*) p2;
                char* pdot = strchr(dll_func, '.');
                if( !pdot ) pdot = dll_func + strlen( dll_func );
                CStringA dllName(dll_func, (int)(pdot - dll_func));
                dllName += ".dll";

                HMODULE hDll2 = GetModuleHandleA(dllName);
                if( hDll2 == NULL )
                    return p;

                return GetProcAddress2( hDll2, pdot + 1 );
            }

            return (FARPROC) p2;
        }
    } //for

    return p;
} //GetProcAddress2

Besides this it's possible still to get .dll to be loaded at different address, but this does not happen with kernel32.dll or kernelbase.dll.

But if .dll rebasing comes as a problem - one approach to solve is to use EasyHook approach - can be located here:

https://github.com/EasyHook/EasyHook/blob/b8b2e37cfe1c269eea7042420bde305eb127c973/EasyHookDll/RemoteHook/thread.c

See function GetRemoteFuncAddress.