stevemk14ebr / PolyHook_2_0

C++20, x86/x64 Hooking Libary v2.0
MIT License
1.61k stars 227 forks source link

Win64 api kernel32::BindIoCompletionCallback() hooking error #96

Closed randydu closed 3 years ago

randydu commented 3 years ago

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

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                                     |
00007FFA69484278 | B8 01000000              | mov eax,1                                                    |
00007FFA6948427D | 48:83C4 28               | add rsp,28                                                   |
00007FFA69484281 | C3                       | ret      

After Hooking

00007FFA69484260 | FF25 85FFFFFF            | jmp qword ptr ds:[&my_hooked_api]                
00007FFA69484266 | 66:90                    | nop                                                                                                                                                                                                    |
00007FFA69484268 | 66:90                    | nop                                                                                                                                                                                                    |
00007FFA6948426A | 90                       | nop                                                                                                                                                                                                    |
00007FFA6948426B | 0F1F4400 00              | nop dword ptr ds:[rax+rax],eax                                                                                                                                                                         |
00007FFA69484270 | 85C0                     | test eax,eax                                                                                                                                                                                           |
00007FFA69484272 | 0F88 24230100            | js kernel32.7FFA6949659C                                                                                                                                                                               |
00007FFA69484278 | B8 01000000              | mov eax,1                                                                                                                                                                                              |
00007FFA6948427D | 48:83C4 28               | add rsp,28                                                                                                                                                                                             |
00007FFA69484281 | C3                       | ret    

The original code:

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]                                                                                                                                                                         |
0000018BB4ACB447 | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB449 | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB44B | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB44D | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB44F | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB451 | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB453 | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB455 | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB457 | CD CD                    | int CD                                                                                                                                                                                                 |
0000018BB4ACB459 | CD CD                    | int CD                                                                                                                                                                                                 |

When running the hooked api, the memory access violation exception occurs.

SIGSEGV - Segmentation violation signal

From the debugger, the code hits invalid region:

qword ptr ds:[0000018BB4ACB441]=CDCD0000003825FF

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?

stevemk14ebr commented 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?

randydu commented 3 years ago

Here is my detailed debug info:

Before Hooking

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     

After Hooking

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)
stevemk14ebr commented 3 years ago

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?

randydu commented 3 years ago

I've fixed this issue in my forked repository.

Now after the hooking, the trampoline code looks like:

Before hooking

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                                     |

After hooking

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.

stevemk14ebr commented 3 years ago

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!

stevemk14ebr commented 3 years ago

This is resolved