How to get a 64-bit exitCode using GetExitCodeThread or some other Windows Api

78 Views Asked by At

I'm trying to unload a dll after inject it to a remote process,I use CreateRemoteThread to invoke "LoadLibraryA" in that remote process, which means the thread function is "LoadLibraryA". Now, I'm using the "GetExitCodeThread" to get the exitcode of the thread.

code snippets:

    // create remote thread to invoke LoadLibraryA
    HANDLE tThread = CreateRemoteThread(remoteProcess, NULL, 0, LoadLibraryAAddr, remoteAddress, 0, NULL);
    if (!tThread) {
        cout << "failed to create remote thread" << endl;
        return 1;
    }

    // wait for remote thread stop
    HMODULE exitCode;
    WaitForSingleObject(tThread, INFINITE);
    cout << "successfully load library" << endl;
    
    GetExitCodeThread(tThread, (LPDWORD)&exitCode);

    // clean up, call FreeLibrary to unload dll
    LPTHREAD_START_ROUTINE freeLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
    if (NULL == freeLibraryAddr) {
        cout << "failed to get freeLibrary address" << endl;
        return 1;
    }

declaration of "GetExitCodeThread":

BOOL GetExitCodeThread(
  [in]  HANDLE  hThread,
  [out] LPDWORD lpExitCode
);

document for GetExitCodeThread from ms

According to the description of the document, the lpExitCode shoud take the return value of "LoadLibraryA", other words, the base address of dll loaded by "LoadLibraryA". And then I create another remote process to invoke "FreeLibrary" to unload this dll, but failed.

After debugging, I found the problem: lpExitCode just hold the low 32-bit value of which that "LoadLibraryA" returned.

runtime value of exitCode

the high 32-bit is 0xcccccccc, because the variable "exitCode" is in stack, which was initialized with 0xcc. This result is easy to explain: the type of "lpExitCode" is LPDWORD, which is a pointer to a 32-bit value, so it can just influence the low 32-bit of "exitCode"

The code from the beginning would run well in x86 environment, cause the address in x86 environment is 32-bit long, which can be pass correctly. However, I run it in x64 environment and the "exitCode" just get low 32-bit of the dll base address, which then passed to FreeLibrary and making the invoke failed. As a result, the dll still lay in the memory of that remote process, which doesn't match my expectation.

So is there any way to get a 64-bit retuen value using GetExitCodeThread or some other windows api, hope someone can reply.

1

There are 1 best solutions below

0
w3nl1ng On
HMODULE getTargetModuleBase(HANDLE processHandle, string targetModuleName) {
    // get all moduels in the process
    HMODULE moduleHandleList[1024];
    DWORD cbNeeded;
    BOOL ret = EnumProcessModules(processHandle, moduleHandleList, sizeof(moduleHandleList), &cbNeeded);
    if (!ret) {
        cout << "failed to enum process modules" << endl;
        return NULL;
    }
    if (cbNeeded > sizeof(moduleHandleList)) {
        cout << "to many modules" << endl;
        return NULL;
    }
    DWORD processCount = cbNeeded / sizeof(HMODULE);
    // traverse to find target module
    char moduleName[1024];
    for (DWORD i = 0; i < processCount; i++) {
        GetModuleBaseNameA(processHandle, moduleHandleList[i], moduleName, 1024);
        if (!strncmp(targetModuleName.c_str(), moduleName, targetModuleName.size())) {
            return moduleHandleList[i];
        }
    }
    return NULL;
}

Use EnumProcessModules and GetModuleBaseNameA to traverse and compare the name of loaded modules, you can find the target module and return it's base address, then pass the base address to FreeLibrary to unload the module.

The above code is just a simple example, without error handling: moduleHandleList may out of range when it holds the result, so take care of it.

You can check the return values of EnumProcessModules and GetModuleBaseNameA to determine whether to call those API functions again with a larger array.