-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Improved 32bit detection #104
Conversation
This seems like a good idea. Is that the problems you see? I could check. |
@jaykrell The problem I see is that Detours incorrectly detects a 64bit process as 32bit since it merely looks at the PE headers (but without considering an optional IMAGE_COR20_HEADER header). Consequently, hooking would fail. |
I think we are saying the same thing, but I wasn't clear. Now, if you were to ignore anything "managed" (based on the presence of the data directory), you would get much closer. But I think you'd still be left with the problem, that a 32bit process might not be able to enumerate anything in a 64bit process -- everything might be above 4GB. It is really too bad there isn't ReadProcessMemory64, WriteProcessMemory64, VirtualAlloc64, VirtualProtect64, VirtualFree64, etc. The way you can simulate those, roundabout, is launch a native process, and RPC to it, and have it run those functions on your behalf. But I dislike any solution involving another process. There are sneaky ways to run native system services from a 32bit process, I've done it, but pretty unsupportable. I'm a little confused as to the two IsWow64Process calls you are making..just because I didn't read closely,, but yeah that is a reliable simple mechanism. Except er um, for all the ARM stuff these days...that can run 3 different architectures--the boolean turned out to be insufficient. An enum is needed.. |
Yeah, even if you were to ignore any managed code, it would still be tricky to identify the target process architecture as you point out. I agree that the approach using IsWow64Process seems a little tricky. I needed to make a little drawing to convince myself that it's accurate, too. :-) The idea is straightforward though: First, determine if a 64bit operating system is used.
Now, with this decision made (and stored in the
I think this is attractive since it does not involve any file header parsing (which involves architecture-specific sizes of addresses) at all. It relies only on just one function: |
Thanks for the description of the logic @frerich, would you mind encoding some of that into code comments as part of your change? |
27ee549
to
3498aeb
Compare
@bgianfo good point - I think it's evident that the logic is tricky enough to warrant a comment. Thanks for the suggestion! I now force-pushed a new state of the branch in which comments are included which explain the two-step process for determining the bitness of the target process. What do you think? |
Alas, the current CI builds appear to fail with
it appears this is independent of this PR though. Can anybody tell what's going on? |
I think you need to rebase your changes onto latest master. That build error was fixed with the change that introduced the CI workflow (#121). |
3498aeb
to
890f1b7
Compare
Thanks for the hint, @bgianfo -- that helped! |
If official people prefer to merge my RP#127, this will no longer be a problem |
@sonyps5201314 Your branch is really interesting, a lot of good stuff! I had a look at how you solved the issue of identifying 32bit processes and saw that you used the same approach: first, determine if the OS is 32bit or 64bit. Then, for 64bit operating systems, test if the target process is running under Wow64 or not. I'm happy to see that we both seem to have arrived at the same solution, independently. :-) One thing which caught my eye was that you used a fairly elaborate method to determining whether a 64bit OS is in use. I see that you defined this function: BOOL Is64BitOS()
{
BOOL bRet = FALSE;
VOID(WINAPI * _GetNativeSystemInfo)(OUT LPSYSTEM_INFO lpSystemInfo) = (void(__stdcall *)(LPSYSTEM_INFO))GetProcAddress(GetModuleHandle(TEXT("kernel>
if (!_GetNativeSystemInfo)
{
return bRet;
}
SYSTEM_INFO si;
_GetNativeSystemInfo(&si);
if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64 ||
si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA64)
{
bRet = TRUE;
}
return bRet;
}; I wonder -- what happens when calling this function on Windows 32bit running on a 64bit CPU. Does it actually return the architecture of the operating system, or is it rather the architecture of the CPU? I'm not totally sure whether it's possible to run 32bit Windows on a 64bit (Intel) CPU, but I believe it is. Having second thoughts, I researched a bit more and found that the .NET libraries have a function |
cd77187
to
4cd906e
Compare
Hmm, I tried to clean up the branch by rebasing on top of master (skipping the merge) and integrating the fix regarding |
4cd906e
to
e12e2f0
Compare
This patch improves the logic for detecting whether the process to be patched is a 32bit or a 64bit process. The old logic would first enumerate the modules in the process and see if 1. There is a 32bit executable module 2. There is a 64bit DLL module In case 1.) is true and 2.) is false, i.e. a 32bit executable but no 64bit DLL, the process was deemed to be a 32bit process. This seems plausible, but I encountered a case in which it is not true: I launched an IL-only .NET application (a Windows Forms GUI application) in Windows 10. Right after the CreateProcess call, there were just two modules in the process - A 32bit executable - A 32bit ntdll.dll library I.e. the .NET runtime was not loaded yet. Hence, because there *is* a 32bit executable but there is *not* a 64bit DLL, bIs32BitProcess was set to TRUE. However, when resuming the process and inspecting with Process Explorer, it appears that the process executed in 64bit mode! I suppose it would be possible to replicate the behaviour of the Windows loader and be a bit smarter about looking for 32bit executables: instead of just looking at the 'machine' flag, also look for a potential IMAGE_COR20_HEADER (which basically acts as the PE header for .NET executables) and see if that requires 32bit. However, I think there is an easier way to check if the process is 32bit or not. The new logic performs two steps: 1. Detect whether the operating system is 64bit. If the code is compiled as 64bit, then the OS is trivially 64bit. If the code does not have _WIN64 defined, i.e. it is 32bit, but it is running under WOW64, then the OS is 64bit, too. 2. Detect if the process to be patched is 32bit. If the OS is *not* 64bit, the process can't possibly be 64bit. So it must be 32bit. If the OS *is* 64bit, we can identify 32bit processes by calling IsWow64Process() again.
Due to the previous commit, the flags bHas32BitExe and bHas64BitDll are no longer used. Hence, let's remove the code which set them up.
e12e2f0
to
615fdf2
Compare
I now rebased this branch on top of the current master, avoiding the merge commit and any fixups for using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Thanks!
Great, thanks a lot for your feedback and for integrating the patch! |
I noticed that Detours failed to inject a DLL into an IL-only .NET application. I'm pretty sure this worked in the past (right now, this is only triggered on Windows 10 machines but not on Windows 7).
These two commits resolve the issue. Instead of sticking with the approach of enumerating the modules, I went for a simpler mechanism for detecting if the running process is 32bit or 64bit. This seems to work well. See the commit logs for all the gory details.
Any feedback would be much appreciated!