RESOLVED: Terminating the host application crashes 64-bit target but not 32-bit?

May 1, 2013 at 5:00 AM
Using 64-bit Windows 7, my first time trying EasyHook 2.7 Beta is quite successful. However, just closing the host application crashes 64-bit target (hooked) applications but not 32-bit.

For example, try hooking notepad.exe.

For 64-bit notepad, attaching it to vs2010 debugger shows an "Access violation" (i.e. SegFault). The disassembly is like this:
; ole32.dll
000007FEFDE402B0  movdqa      xmmword ptr [rsp+20h],xmm0 
However, hooking the 32-bit ("C:\Windows\SysWOW64\notepad.exe") doesn't crash.

Is there a valid explanation, or had anyone also have this problem before?
May 1, 2013 at 8:30 AM
There is a known issue that is logged in the issue tracker. I have tracked it down to the uninstall of the hook in 64-bit applications.

I currently have a very busy schedule and will not be able to work on a solution for a little while.

Cheers,
Justin
May 1, 2013 at 9:40 AM
I'm not sure what you mean by "the uninstall of the hook", but it seems that the target didn't crash when I call LocalHook.Dispose() (which I think should uninstall the hook). Instead, it crashed after the Run method returned.

In short, if I put an infinite loop in the catch block (catching the exception from remoting), the application doesn't crash. Whenever I return from the Run method, it crashes. Is it that I can simply use an infinite loop as a workaround?

I understand that working on open source projects isn't any easy job. I really appreciate the work of all of you!
May 1, 2013 at 11:39 AM
Thanks for looking into this.

There was a change made for a memory leak in 2.7, this is the only code I can think of that has changed that would execute after the Run, but I'm working off memory here and will have to look into it.

Cheers,
Justin
May 28, 2013 at 12:14 PM
Just to post another stack and some disassembly:

One of the possible crashes after unloading the hook to notepad.exe x64 (Windows 7):

Call Stack:
>   advapi32.dll!__tailMerge_CRYPTSP_dll()  + 0x18 bytes    
    clr.dll!Fls_Callback()  + 0x30 bytes    
    clr.dll!string "d:\\iso_whid\\amd64fre\\base\\ntos\\r"...()  + 0x4bdac5 bytes   
    clr.dll!CExecutionEngine::ThreadDetaching()  + 0x17 bytes   
    clr.dll!EEDllMain()  + 0x8d bytes   
    clr.dll!DllMain()  + 0x59e5d bytes  
    clr.dll!_DllMainCRTStartup()  + 0xb0 bytes  
    ntdll.dll!LdrShutdownThread()  - 0x28 bytes 
    ntdll.dll!RtlExitUserThread()  + 0x38 bytes 
    000000000218fe8f()  
    0000000000001000()  
Near the instruction of the first frame on stack:
__imp_load_CryptImportKey:
000007FEFF19DC6E  nop  
000007FEFF19DC6F  nop  
__tailMerge_CRYPTSP_dll:
000007FEFF19DC70  mov         qword ptr [rsp+8],rcx  
000007FEFF19DC75  mov         qword ptr [rsp+10h],rdx  
000007FEFF19DC7A  mov         qword ptr [rsp+18h],r8  
000007FEFF19DC7F  mov         qword ptr [rsp+20h],r9  
000007FEFF19DC84  sub         rsp,68h  
000007FEFF19DC88  movdqa      xmmword ptr [rsp+20h],xmm0  ; crashing instruction
000007FEFF19DC8E  movdqa      xmmword ptr [rsp+30h],xmm1  
000007FEFF19DC94  movdqa      xmmword ptr [rsp+40h],xmm2  
000007FEFF19DC9A  movdqa      xmmword ptr [rsp+50h],xmm3  
; other ignored
And near the instruction of the second frame on stack:
Fls_Callback:
000007FEEA6C62F0  test        rcx,rcx  
000007FEEA6C62F3  je          Fls_Callback+50h (7FEEA6C6340h)  
000007FEEA6C62F5  mov         qword ptr [rsp+8],rbx  
000007FEEA6C62FA  mov         qword ptr [rsp+10h],rsi  
000007FEEA6C62FF  push        rdi  
000007FEEA6C6300  sub         rsp,20h  
000007FEEA6C6304  mov         rbx,rcx  
000007FEEA6C6307  lea         rdi,[rcx+8]  
000007FEEA6C630B  mov         esi,2  
000007FEEA6C6310  mov         rcx,qword ptr [rdi]  
000007FEEA6C6313  test        rcx,rcx  
000007FEEA6C6316  je          Fls_Callback+30h (7FEEA6C6320h)  
000007FEEA6C6318  xor         edx,edx  
000007FEEA6C631A  call        qword ptr [__imp_CryptReleaseContext (7FEEACAAEE8h)]  ; call to this function causes the crash
000007FEEA6C6320  add         rdi,8  
May 28, 2013 at 4:05 PM
More info:

The crash almost always come from a certain movdqa xmmword ptr [rsp+20h],xmm0 instruction which has a label __tailMerge_SOME_DLL 18-bytes before it (according to the debug symbols from Microsoft Symbol Servers). Later in the disassembly calls a __delayLoadHelper2 function in the same module, which seems to be related to the DLL delay-loading mechanism.

Still not sure what actually cause the crash, but the value of rsp+20h isn't zero, and it's value is aligned to a 16-byte boundary.
May 30, 2013 at 12:33 PM
alvinhochun wrote:
Still not sure what actually cause the crash, but the value of rsp+20h isn't zero, and it's value is aligned to a 16-byte boundary.
I am sorry, but I actually got it wrong. rsp+20h is not aligned to a 16-byte boundary, it is actually 8 bytes off.

So here's the problem: The stack is not aligned to a 16-byte boundary somewhere!

I used WinDbg and hopefully I get a correct screenshot... (I don't actually know how to use WinDbg.)

The stack is mis-aligned from the very beginning

The stack is mis-aligned from the very beginning! I am actually surprised that the hooked process didn't crash earlier.

Since I am not familiar with the internals of EasyHook, I have no idea what could have caused it or how to fix it, but the problem is likely that somewhere in EasyHook's unmanaged code (could even be assembly) that made the stack frame mis-aligned.

A page that I've referred to (regarding x64 calling convection on Windows) http://blogs.msdn.com/b/oldnewthing/archive/2004/01/14/58579.aspx

Hopefully my diagnosis is correct...
May 30, 2013 at 4:06 PM
Hey, I think I found a way to make it at least not crash.
--- a/DriverShared/ASM/HookSpecific_x64.asm Thu May 30 22:55:31 2013
+++ b/DriverShared/ASM/HookSpecific_x64.asm Thu May 30 22:55:45 2013
@@ -379,7 +379,7 @@
    mov rcx, qword ptr [r14 + 32]
    
    lea rax, qword ptr [rsp + 8]
-   sub rsp, 40
+   sub rsp, 48
    jmp rax
    
 ; outro signature, to automatically determine code size
Pretty much fixed all the stack mis-alignments... right?


Since the following instruction is a jump, it doesn't push the address to the stack. That's why the stack pointer needs to be subtracted by a 16-multiply to make the stack align to a 16-bit boundary, correct?

Not sure whether this patch is really correct since I'm a noob on assembly and I don't really understand the x64 calling convection. At least this works for me (no crash when unhooking).

Can someone please review this?
Marked as answer by spazzarama on 12/29/2013 at 2:49 PM
May 31, 2013 at 7:52 AM
Edited May 31, 2013 at 7:52 AM
Thanks for tracking this down Alvin, I'll take a look at this for you ASAP and let you know if it fixes the problem here also.

My ASM skills aren't the best either - I'll have to remove some cobwebs from my brain and review the calling conventions for 64-bit myself.
May 31, 2013 at 8:21 AM
Making that change stops the crash here. I will need to review this some more and work out what was wrong. I'm also experiencing other issues (running on Windows 8 now) so might be a bit before I get it in there.
May 31, 2013 at 12:44 PM
Edited May 31, 2013 at 12:48 PM
This is what I did. I first get WinDbg to debug the target application (I used notepad.exe since it only has one thread), then hook and unhook it to break at the exception. Then I view the call stack and get the above screenshot.

After that, I restart debugging, set WinDbg to break on thread creation, then hook it, then step to where the EasyHook assembly is, and right before calling the entry point I set a breakpoint to the next instruction and let the target continue (go until all threads are running), then unhook and we are at the breakpoint, then step until the call stack window start stacking mis-aligned stack addresses. This is how I figured out the source of error, or at least I think so.

Hope this helps :)


I would also like to help EasyHook on Windows 8, but sadly I don't have a Windows 8 installation. Will do if I really get one.
Jun 2, 2013 at 12:37 AM
Thanks Alvin, that will narrow down the time I need to spend.
Oct 26, 2013 at 3:06 PM
Hi,
Is there any update for this issue in Windows 8? I see the EasyHook is also crashed with Win 8 64-bit apps.

Thanks.
Dec 26, 2013 at 3:54 AM
Hi alvinhochun,

Looks like there is a similar issue under Windows 8 when the hook callback is triggered. Have you had a chance to get Windows 8 and try it out yet?

I'll keep looking into it.
Dec 26, 2013 at 5:57 AM
Looks like the error is occurring within the Trampoline for 64-bit under Windows 8.
Feb 8, 2014 at 3:45 PM
Edited Feb 8, 2014 at 3:46 PM
Any news about this issue spazzarama? I have serious problem with it.

Also what is the best way to prevent it before you release a new version? Is it safe for me to keep the loop active and prevent code from returning?
Feb 10, 2014 at 8:28 AM
Edited Feb 10, 2014 at 8:29 AM
DarkSoroush - the source has been updated, so after getting latest and providing you are using Visual Studio 2012 and install the MSBuildTasks (https://github.com/loresoft/msbuildtasks) you can build it yourself by running "build.bat" directly.

Just run .\easyhook\branches\easyhook-2.7\build.bat

You could keep a loop running or something but that would obviously not be ideal.
Feb 11, 2014 at 9:16 AM
Hello,
I was not able to run the build scripts of the new changes with VS 2010, because the PlatformToolset was set to v110 in UnmanagedHook.vcxproj and EasyHookDll.vcxproj files. I changed them to v100 and then it worked.
Feb 11, 2014 at 9:59 AM
Edited Feb 11, 2014 at 9:59 AM
Test-1
It (rev. 73766) still crashes the x64 target (on Win 7) during unload.

Test-2
It has no effect on Win8. managed hooks are not being called.
Feb 13, 2014 at 10:11 AM
I just wanted to add that this crash is not limited to 64bit apps. It may happen in 32bit apps as well but rarely. (Or it was what I experienced)

Any way, used latest source code and problem was solved. Thank you spazzarama.

PS: I have an other question which is not related actually; which files I need to include in my project? I am not sure if I need to include EasyHookSvc.exe. My project is "Any CPU" and I want to be able to inject code on both 32 and 64 bit apps on both 32 and 64 bit OSs. Do I need to include all of 3 dlls and 3 exe files?