Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem in 64-bit environment #22

Closed
bot-1450 opened this issue Oct 11, 2023 · 6 comments · Fixed by #23
Closed

Problem in 64-bit environment #22

bot-1450 opened this issue Oct 11, 2023 · 6 comments · Fixed by #23
Assignees
Labels

Comments

@bot-1450
Copy link
Contributor

In a 64-bit environment (Win64), DWORD is a 32-bit unsigned integer type, while HMODULE, which is essentially a pointer, is 64 bits long.

HMODULE LoadLibraryW(
  [in] LPCWSTR lpLibFileName
);

DWORD WINAPI ThreadProc(
  _In_ LPVOID lpParameter
);

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

if(!ExitCode)
throw std::runtime_error("Call to LoadLibraryW in remote process failed.");

For instance, when a Dll is loaded with low double-word of base address being zero, false runtime error is thrown.

@nefarius
Copy link
Owner

nefarius commented Oct 11, 2023

Not sure I'm following, the api docs of GetExitCodeThread say nothing about different behaviour on different architectures?

@bot-1450
Copy link
Contributor Author

The GetExitCodeThread function does behave consistently across different architectures. It retrieves the termination status of the specified thread, whether you're on a 32-bit or 64-bit system.

What does change between 32-bit and 64-bit systems is the size of certain types, such as pointers and thus HMODULE, which is defined as a pointer.

  • If a thread function returns an HMODULE in a 32-bit program, you can use GetExitCodeThread to retrieve it without issue because both HMODULE and DWORD are 32 bits.
  • However, in a 64-bit program, HMODULE is 64 bits while DWORD remains at 32 bits. So if that thread function tries to return an HMODULE and you call GetExitCodeThread, the HMODULE value will be truncated to fit into the 32-bit DWORD.

The difference here is not in how GetExitCodeThread itself works, but in how the HMODULE value fits (or doesn't fit) into a DWORD.

This is why, in a 64-bit environment, you typically can't use a thread's exit code to pass back an HMODULE value without losing information. Instead, you might write the HMODULE to a known location in memory, and have the creating process read from that location to obtain the full HMODULE.

However, I am afraid that shellcode need be written in this manner.

@nefarius
Copy link
Owner

Ah, because the HMODULE of LoadLibraryW gets cast into a 32bit DWORD behind the scenes, now I get it.

I doubt this error has ever popped up before so IDK how big of a deal it is...

@fredemmott
Copy link
Contributor

For instance, when a Dll is loaded with low double-word of base address being zero, false runtime error is thrown.

as x64 and Arm64 (in windows) are little-endian, it's non-zero high bits that mess this up:

#include <iostream>
#include <Windows.h>

int main() {
        HMODULE handle {reinterpret_cast<HMODULE>(1)};
        HMODULE handle2 {reinterpret_cast<HMODULE>(1 << 32)};
        DWORD value { };
        DWORD value2 { };
        value = *reinterpret_cast<DWORD*>(&handle);
        std::cout << sizeof(handle) << std::endl << value << std::endl << value2 << std::endl;
        return 0;
}
8
1
0

@fredemmott
Copy link
Contributor

fredemmott commented Oct 11, 2023

Ah, sorry, you're right, I need a coffee and to wake up >_< non-zero high bits are what's needed for an incorrect complete HMODULE, but zero low bits are what's needed for a bad bool conversion in this logic.

@fredemmott
Copy link
Contributor

Doesn't fundamentally change the issue, but just for some context, there are some constraints on these values - they are literally pointers to the DLL's address space, not an arbitrary 64-bit value:

https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain

A handle to the DLL module. The value is the base address of the DLL. The HINSTANCE of a DLL is the same as the HMODULE of the DLL, so hinstDLL can be used in calls to functions that require a module handle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants