[Bug 56968] New: Easyhook remote hooking does not work, breaking some game modding frameworks
https://bugs.winehq.org/show_bug.cgi?id=56968 Bug ID: 56968 Summary: Easyhook remote hooking does not work, breaking some game modding frameworks Product: Wine Version: unspecified Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: kernel32 Assignee: wine-bugs(a)winehq.org Reporter: katharine.chui(a)gmail.com Distribution: --- There exist game modding frameworks that uses EasyHook, for example, https://andrasteframework.github.io/content/1.0.0/index.html EasyHook remote hooking uses ReadProcessMemory to fetch module handle and function addresses before using CreateRemoteThread to perform remote hooking with the fetched function addresses On wine, this breaks at https://github.com/EasyHook/EasyHook/blob/16f641c8e2197b01095f548c94dcbe696a... When trying to fetch export directory from remote process' kernel32.dll's PE header, ReadProcessMemory would succeed, eliminating the fallback codepath outright, but the ExportDirectory buffer would then get filled with 0s. With an export directory data structure filled with 0s, EasyHook would not be able to do much with CreateRemoteThread as functions fetched at https://github.com/EasyHook/EasyHook/blob/16f641c8e2197b01095f548c94dcbe696a... are all unavailable. Patching the routine with a loop to loop until the function addresses can be fetched, it seems that it's not (just) a timing issue either because the loop just seems to go on forever. Interestingly, through patching EasyHook itself and force the fallback code path at https://github.com/EasyHook/EasyHook/blob/16f641c8e2197b01095f548c94dcbe696a... which grabs export directory from PE NT headers, it can actually fetch an export directory, then eventually fetch the addresses of LoadLibraryW, FreeLibrary, GetProcAddress, ExitThread and GetLastError, but not VirtualFree and VirtualProtect With the EasyHook patches and dotnet48 installed, it is currently enough to keep EasyHook going as it is now able to continue it's code injection and init routine with LoadLibraryW. Would it be possible for remote processes to fetch export directory from remote PE headers? Would it be possible to fetch address to VirtualFree and VirtualProtect? Thanks! -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 Fabian Maurer <dark.shadow4(a)web.de> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |dark.shadow4(a)web.de --- Comment #1 from Fabian Maurer <dark.shadow4(a)web.de> --- Created attachment 76797 --> https://bugs.winehq.org/attachment.cgi?id=76797 Sample to reproduce the issue Attaching a sample program made from EasyHook code -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 Fabian Maurer <dark.shadow4(a)web.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|UNCONFIRMED |NEW Keywords| |download, testcase Ever confirmed|0 |1 --- Comment #2 from Fabian Maurer <dark.shadow4(a)web.de> --- Confirming -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #3 from Fabian Maurer <dark.shadow4(a)web.de> --- Created attachment 76798 --> https://bugs.winehq.org/attachment.cgi?id=76798 Hack to work around the issue Attaching a dirty hack that should make it work. No idea why it would work that way, but apparently it does. Not sure about 64bit though... -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #4 from Dmitry Timoshkov <dmitry(a)baikal.ru> --- (In reply to Fabian Maurer from comment #3)
Created attachment 76798 [details] Hack to work around the issue
Attaching a dirty hack that should make it work.
+ void *ptr = &sec[i].VirtualAddress; Looks like a typo, '&' should not be needed. Does your hack work if it's only done at the end of perform_relocations()? Probably when relocating a PE image the loader is also supposed to fix up VirtualAddress field in every section in the sections table. Also NtProtectVirtualMemory() probably should be called only for the section table, not for every random section in the PE binary. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #5 from Fabian Maurer <dark.shadow4(a)web.de> ---
+ void *ptr = &sec[i].VirtualAddress; Looks like a typo, '&' should not be needed.
No, we need the address of where the VirtualAddress is stored, not where it points.
Does your hack work if it's only done at the end of perform_relocations()? Probably when relocating a PE image the loader is also supposed to fix up VirtualAddress field in every section in the sections table.
I suppose so, since we have "get_rva( module, sec[i].VirtualAddress );" before, otherwise that would need to be changed. But I only really tested the case without relocations.
Also NtProtectVirtualMemory() probably should be called only for the section table, not for every random section in the PE binary.
Probably, it's just a dirty hack. Not sure how it should exactly work, especially since on 64bit you can't put a pointer in that VirtualAddress DWORD. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #6 from Dmitry Timoshkov <dmitry(a)baikal.ru> --- (In reply to Fabian Maurer from comment #5)
+ void *ptr = &sec[i].VirtualAddress; Looks like a typo, '&' should not be needed.
No, we need the address of where the VirtualAddress is stored, not where it points.
Why do we need it and where? If you mean the NtProtectVirtualMemory() call, it's already done by passing '&ptr'. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #7 from Dmitry Timoshkov <dmitry(a)baikal.ru> --- (In reply to Fabian Maurer from comment #5)
Does your hack work if it's only done at the end of perform_relocations()? Probably when relocating a PE image the loader is also supposed to fix up VirtualAddress field in every section in the sections table.
I suppose so, since we have "get_rva( module, sec[i].VirtualAddress );" before, otherwise that would need to be changed. But I only really tested the case without relocations.
In the case without relocations sec.VirtualAddress already contains correct address. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #8 from Fabian Maurer <dark.shadow4(a)web.de> ---
Why do we need it and where? If you mean the NtProtectVirtualMemory() call, it's already done by passing '&ptr'.
Because I modify the "VirtualAddress" value here:
sec[i].VirtualAddress = (UINT_PTR)get_rva( module, sec[i].VirtualAddress ); but that is readonly, so I need to make it read-write.
So we now have
void *ptr = &sec[i].VirtualAddress; which is the pointer to the address I want to modify. And since NtProtectVirtualMemory needs a pointer to a pointer I pass "&ptr".
In the case without relocations sec.VirtualAddress already contains correct address.
Sure, there is no relocations, but that programs expect the sec.VirtualAddress to not be an offset but an absolute value. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #9 from Alexandre Julliard <julliard(a)winehq.org> --- (In reply to Fabian Maurer from comment #8)
Sure, there is no relocations, but that programs expect the sec.VirtualAddress to not be an offset but an absolute value.
sec.VirtualAddress is an RVA, not a pointer. That program is broken. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #10 from Fabian Maurer <dark.shadow4(a)web.de> ---
sec.VirtualAddress is an RVA, not a pointer. That program is broken.
I thought maybe there is weird behavior in windows, but I retested against windows and the program only seems to get away with it because the windows dlls don't have a ".edata" section. Not sure if we want to mimic that windows behavior to make that broken program happy. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 Dario <dario86(a)tutamail.com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |dario86(a)tutamail.com --- Comment #11 from Dario <dario86(a)tutamail.com> --- I think this might be related to issue https://bugs.winehq.org/show_bug.cgi?id=57395 although the attached patch does not work for that issue. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #12 from Fabian Maurer <dark.shadow4(a)web.de> --- (In reply to Dario from comment #11)
I think this might be related to issue https://bugs.winehq.org/show_bug.cgi?id=57395 although the attached patch does not work for that issue.
Why do you think they are related? -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #13 from Dario <dario86(a)tutamail.com> --- «Resident Evil - Seamless HD Project» and «Resident Evil 2 - Seamless HD Project» do work while «Resident Evil 3 - Seamless HD Project» does not. Injecting DLLs of all three games only use a few methods from the standard library and Libwebp. The one that does not work though, additionally uses functions from KERNEL32.dll in its injecting DLL, namely «FlushInstructionCache», «FreeLibrary», «GetCurrentProcess», «LoadLibraryA» and «VirtualProtect». All of the three look similar in their implementation, but in the one that does not work the methods for loading high definition textures are never executed. -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
https://bugs.winehq.org/show_bug.cgi?id=56968 --- Comment #14 from Dario <dario86(a)tutamail.com> --- (In reply to Dario from comment #11)
I think this might be related to issue https://bugs.winehq.org/show_bug.cgi?id=57395 although the attached patch does not work for that issue.
Likely not related to the issue I mentioned in my previous comment, but I have a workaround for that issue: https://bugs.winehq.org/show_bug.cgi?id=53775#c12 -- Do not reply to this email, post in Bugzilla using the above URL to reply. You are receiving this mail because: You are watching all bug changes.
participants (1)
-
WineHQ Bugzilla