Closed randydu closed 3 years ago
Hi this should be supported, but it seems there may be a bug. The issue is that the call is very early in the prologue:
00007FFA69484260 | 48:83EC 28 | sub rsp,28 | kernel32::BindIoCompletionCallback
00007FFA69484264 | 48:FF15 BDEB0500 | call qword ptr ds:[<&RtlSetIoCompletionCallback>] |
This call is rewritten by polyhook to point to a jmp target that is capable of redirecting the call from the far away trampoline back to the original location the call would go to. This re-write process can be complex, i'm not sure what may be wrong here.
0000018BB4ACB430 | 48:83EC 28 | sub rsp,28 |
0000018BB4ACB434 | 48:FF15 06000000 | call qword ptr ds:[18BB4ACB441] |
0000018BB4ACB43B | FF25 46000000 | jmp qword ptr ds:[18BB4ACB487] |
0000018BB4ACB441 | FF25 38000000 | jmp qword ptr ds:[18BB4ACB47F] <--- point call here
Could you debug and determine which instruction faults?
Here is my detailed debug info:
00007FF86AC04260 | 48:83EC 28 | sub rsp,28 |
00007FF86AC04264 | 48:FF15 BDEB0500 | call qword ptr ds:[<&RtlSetIoCompletionCallback>] |
00007FF86AC0426B | 0F1F4400 00 | nop dword ptr ds:[rax+rax],eax |
00007FF86AC04270 | 85C0 | test eax,eax |
00007FF86AC04272 | 0F88 24230100 | js kernel32.7FF86AC1659C |
00007FF86AC04278 | B8 01000000 | mov eax,1 |
00007FF86AC0427D | 48:83C4 28 | add rsp,28 |
00007FF86AC04281 | C3 | ret |
00007FF86AC04282 | CC | int3
00007FF86AC04260 | FF25 85FFFFFF | jmp qword ptr ds:[<&int __cdecl MyBindIoCompletionCallback_Impl(void * __ptr64,void (__cdecl*)(unsigned long,unsigned long,struct _OVERLAPPED * __ptr64),unsigned long)>] |
00007FF86AC04266 | 66:90 | nop |
00007FF86AC04268 | 66:90 | nop |
00007FF86AC0426A | 90 | nop |
00007FF86AC0426B | 0F1F4400 00 | nop dword ptr ds:[rax+rax],eax |
00007FF86AC04270 | 85C0 | test eax,eax |
00007FF86AC04272 | 0F88 24230100 | js kernel32.7FF86AC1659C |
00007FF86AC04278 | B8 01000000 | mov eax,1 |
00007FF86AC0427D | 48:83C4 28 | add rsp,28 |
00007FF86AC04281 | C3 | ret |
0000028816C5B430 | 48:83EC 28 | sub rsp,28 |
0000028816C5B434 | 48:FF15 06000000 | call qword ptr ds:[28816C5B441] |
0000028816C5B43B | FF25 46000000 | jmp qword ptr ds:[28816C5B487] |
0000028816C5B441 | FF25 38000000 | jmp qword ptr ds:[28816C5B47F] |
//////////
0000028816C5B47F | 282E | sub byte ptr ds:[rsi],ch | rsi:"ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ�
0000028816C5B481 | C6 | ??? |
0000028816C5B482 | 6A F8 | push FFFFFFFFFFFFFFF8 |
0000028816C5B484 | 7F 00 | jg 28816C5B486 |
0000028816C5B486 | 006B 42 | add byte ptr ds:[rbx+42],ch |
0000028816C5B489 | C06A F8 7F | shr byte ptr ds:[rdx-8],7F | rdx-8:std::basic_string<char,std::char_traits<char>,std::allocator<char> >::insert+68
0000028816C5B48D | 0000 | add byte ptr ds:[rax],al |
0000028816C5B48F | FD | std |
0000028816C5B490 | FD | std |
0000028816C5B491 | FD | std |
IAT of kernel32.dll:
00007FF86AC62E28 30 2B D2 6B F8 7F 00 00 60 58 C9 6B F8 7F 00 00 0+Òkø...`XÉkø...
00007FF86AC62E38 70 BE C1 6B F8 7F 00 00 D0 D8 C8 6B F8 7F 00 00 p¾Ákø...ÐØÈkø...
00007FF86AC62E48 70 D1 CA 6B F8 7F 00 00 80 1E C9 6B F8 7F 00 00 pÑÊkø.....Ékø...
00007FF86AC62E58 10 D7 CA 6B F8 7F 00 00 20 06 C7 6B F8 7F 00 00 .×Êkø... .Çkø...
00007FF86AC62E68 40 E3 CA 6B F8 7F 00 00 90 74 CE 6B F8 7F 00 00 @ãÊkø....tÎkø...
00007FF86AC62E78 60 76 CE 6B F8 7F 00 00 E0 71 CE 6B F8 7F 00 00 `vÎkø...àqÎkø...
``` |
ntdll::RtlSetIoCompletionCallback:
00007FF86BD22B30 | 48:8BC4 | mov rax,rsp |
00007FF86BD22B33 | 48:8958 08 | mov qword ptr ds:[rax+8],rbx |
00007FF86BD22B37 | 48:8970 10 | mov qword ptr ds:[rax+10],rsi
00007FF86BD22B3B | 57 | push rdi
00007FF86BD22B3C | 48:83EC 30 | sub rsp,30 |
00007FF86BD22B40 | 48:8BF2 | mov rsi,rdx
00007FF86BD22B43 | 48:8BF9 | mov rdi,rcx
00007FF86BD22B46 | 48:8360 20 00 | and qword ptr ds:[rax+20],0
My question is, the call d_p [xxxx]:
0000028816C5B434 | 48:FF15 06000000 | call qword ptr ds:[28816C5B441]
will set %eip := [28816C5B441] which is { FF, 25, 38,00,00,00,CD,CD } or as address 0xCDCD0000003825FF,
it is an invalid code address and will crash immediately.
Also the code:
0000028816C5B441 | FF25 38000000 | jmp qword ptr ds:[28816C5B47F]
Even it can be executed properly, cannot lead us to the correct target, it needs two levels of dereferences:
target = * ( * ( 28816C5B47F ) )
[28816C5B47F] ==> 00007FF86AC62E28
[00007FF86AC62E28] ==> 00007FF86BD22B30 (our target ntdll::RtlSetIoCompletionCallback)
I'll hopefully find some time to look into this before too long. Thank you for the detailed report I will try to replicate. As far as the jump, the FF25 jump is a bit special as it's indirect. The address it points to is a data address that it dereferences, then it jumps there. It is odd there's two levels of indirection there though, I'd expect a single de-reference. Share the c++ code you are using for this particular hook?
I've fixed this issue in my forked repository.
Now after the hooking, the trampoline code looks like:
00007FFA69484260 | 48:83EC 28 | sub rsp,28 | kernel32::BindIoCompletionCallback
00007FFA69484264 | 48:FF15 BDEB0500 | call qword ptr ds:[<&RtlSetIoCompletionCallback>] |
00007FFA6948426B | 0F1F4400 00 | nop dword ptr ds:[rax+rax],eax |
00007FFA69484270 | 85C0 | test eax,eax |
00007FFA69484272 | 0F88 24230100 | js kernel32.7FFA6949659C |
Trampoline code:
0000022A1C7BEDC0 48 83 EC 28 sub rsp,28h
0000022A1C7BEDC4 48 FF 15 45 00 00 00 call qword ptr [22A1C7BEE10h]
0000022A1C7BEDCB FF 25 47 00 00 00 jmp qword ptr [22A1C7BEE18h]
0000022A1C7BEDD1 C0 0F 88 ror byte ptr [rdi],88h
0000022A1C7BEDD4 24 23 and al,23h
//////// dest holders ////////
0000022A1C7BEE10 30 2B EA 40 FC 7F 00 00 ; => &RtlSetIoCompletionCallback
0000022A1C7BEE18 6B 42 22 3F FC 7F 00 00 ; => & test eax, eax
The CALL instruction only needs a dest-holder to specify its destination.
It is odd there's two levels of indirection there though, I'd expect a single de-reference
Yes, there is a bug here that does not set the indirect flag properly: https://github.com/stevemk14ebr/PolyHook_2_0/blob/146c97eb5694201701b3dbcbb10d9c988835a485/sources/CapstoneDisassembler.cpp#L96
should be:
(hasGroup(capInst, x86_insn_group::X86_GRP_CALL) && inst.size() >= 3 && inst.getBytes().at(1) == 0xff && inst.getBytes().at(2) == 0x15)
the CALL is FF15, not FF25, as a result the PLH::Instruction::getDestionation() does not dereference the correct calling target.
Glad you've fixed your issues! You will want to revert your change of 0x25 -> 0x15, the 0x25 case handles an indirect jump. What you want is to introduce a second check for the additional 0x15 indirect call case which you are absolutely correct I do not handle correctly (or like, at all). Thank you for looking into this!
This is resolved
I am testing PLH::detour::hook() to hook kernel32::BindIoCompletionCallback() api on Win10.
On 32bit code, everything works fine, however, on Win64, the api BindIoCompletionCallback() can be hooked successfully ( PLH::x64Detour::hook() returns true), but the app crashes immediately when calling the original /detoured api.
Before Hooking
After Hooking
The original code:
When running the hooked api, the memory access violation exception occurs.
From the debugger, the code hits invalid region:
There is nothing special in my_hooked_api test, just calling the original code.
The same test code works fine on Win32, and the 32bit kernel32::BindIoCompletionCallback code looks normal and easy to be detoured.
I am not sure if this api's code pattern is supported by the current detour-hook algorithm?