This is the fourth part of the Bit Twiddling series. For your convenience you can find other parts in the table of contents in Par 1 — Modifying Android application on a binary level
Today we’re going to disable CTRL+ALT+HOME shortcut in mstsc. We want to disable the shortcut so it doesn’t “unfocus” the RDP session but still works inside the connection. I needed that for easier work in nested RDPs and I couldn’t find any decent RDP client for Windows that wouldn’t have this shortcut. The only one I found was Thincast but it lacked some other features as well.
Let’s begin.
Table of Contents
Finding the entry point
The hardest part is as always finding “the place”. I took API Monitor which is like strace for Windows. I started the mstsc.exe, connected to some machine, pressed CTRL+ALT+HOME and observed what happened. After some digging here and there I found that when I press CTRL+ALT+HOME, the WM_USER+19
message is sent inside the application:
This is clearly a hint what’s going on. We can see that the key combination is captured by mstscax.dll
which is the ActiveX part of the RDP. We also found the call stack and the address 0x000007ffec2d82fe2
.
Analyzing the code
Now we can use WinDBG or whatever other debugger to figure out what’s going on. I attached to the running mstsc.exe, added a breakpoint with bu 0x00007ffec2d82fe2
and then pressed CTLR+ALT+HOME. As expected, the breakpoint was hit and I could observe the call stack:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
0:003> kb # RetAddr : Args to Child : Call Site 00 00007ffe`c2d27655 : 00000000`00000000 00000000`00000000 00000000`00000024 00000000`00008000 : mstscax!CTSCoreEventSource::InternalFireAsyncNotification+0xca 01 00007ffe`c2d262a5 : 00000000`00000100 00000000`00000000 00000000`00000024 00000135`bc82ea28 : mstscax!CTSInput::IHPostMessageToMainWindow+0x1c5 02 00007ffe`c2d261c8 : 00000000`00000001 00000000`01470001 00000000`003f0b28 00000000`00000113 : mstscax!CTSInput::IHInputCaptureWndProc+0x85 03 00007fff`5a04ef75 : 00000000`00000001 00000033`4faff2d0 00000000`003f0b28 00000000`80000022 : mstscax!CTSInput::IHStaticInputCaptureWndProc+0x58 04 00007fff`5a04e69d : 00000000`00000000 00007ffe`c2d26170 00000033`4f5d3800 00007fff`3f646aae : USER32!UserCallWinProcCheckWow+0x515 05 00007fff`3f64ab32 : 00007ffe`c2d26170 00000000`00000000 00000000`ffffffff 00007fff`3f646a35 : USER32!DispatchMessageWorker+0x49d 06 00007fff`3f643997 : 00000000`00000000 00000000`00000000 00000135`bc830b00 00007fff`00000000 : apimonitor_drv_x64+0xab32 07 00000135`bc8fb89f : 00000135`bc25da6a 00000000`00000001 00000000`00000001 00000000`00000001 : apimonitor_drv_x64+0x3997 08 00000135`bc25da6a : 00000000`00000001 00000000`00000001 00000000`00000001 00000135`bd33e0e0 : 0x00000135`bc8fb89f 09 00000000`00000001 : 00000000`00000001 00000000`00000001 00000135`bd33e0e0 00007ffe`c2cd761c : 0x00000135`bc25da6a 0a 00000000`00000001 : 00000000`00000001 00000135`bd33e0e0 00007ffe`c2cd761c 00000033`4faff480 : 0x1 0b 00000000`00000001 : 00000135`bd33e0e0 00007ffe`c2cd761c 00000033`4faff480 00000135`bba40000 : 0x1 0c 00000135`bd33e0e0 : 00007ffe`c2cd761c 00000033`4faff480 00000135`bba40000 00000000`00000083 : 0x1 0d 00007ffe`c2cd761c : 00000033`4faff480 00000135`bba40000 00000000`00000083 00000000`7ffef000 : 0x00000135`bd33e0e0 0e 00007ffe`c2cd7444 : 00000000`00000000 4e478f48`d4f63727 00000000`000051c0 0000aa52`0fad086d : mstscax!PAL_System_CondWait+0x1cc 0f 00007ffe`c2cf0f75 : 00000000`00000400 00000000`00000000 00000033`4faff8c0 00000135`bd35a720 : mstscax!CTSThreadInternal::ThreadSignalWait+0x34 10 00007ffe`c2cf1f8d : 00000000`00000000 00000000`00000000 00000135`bd35a720 00000000`00000400 : mstscax!CTSThread::internalMsgPump+0x6d 11 00007ffe`c2d8693c : 00000000`00000000 00007ffe`c2cee22d 00000135`bd344640 00007ffe`c2fdf3f0 : mstscax!CTSThread::internalThreadMsgLoop+0x14d 12 00007ffe`c3128550 : 00007ffe`c3475808 00000033`4faff8c0 00000000`00000000 00000135`ba29b8c0 : mstscax!CTSThread::ThreadMsgLoop+0x1c 13 00007ffe`c2fdeca8 : 00000135`bd35a720 00000135`ba29b8c0 00000135`bd35a720 00000135`ba29b748 : mstscax!CSND::SND_Main+0x148 14 00007ffe`c2fe73c2 : 00000135`bd35d100 00000135`bd35a720 00000033`4f67e3f0 00000000`00000000 : mstscax!CTSThread::TSStaticThreadEntry+0x258 15 00007fff`5a1f7344 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : mstscax!PAL_System_Win32_ThreadProcWrapper+0x32 16 00007fff`5b7a26b1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14 17 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21 |
Perfect! We can see the method names and they are really useful. We can immediately see that some IHStaticInputCaptureWndProc
method captures the regular message from the OS, then it calls the window procedure IHInputCaptureWndProc
, and then the method posts the message to the main mstsc.exe window in IHPostMessageToMainWindow
.
This shows us the way. We can also ask the WinDBG to get the call stack with addresses to find out that mstscax!CTSInput::IHInputCaptureWndProc+0x85
is mstscax+0x562a5
. We can now dump the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
0:004> u mstscax+0x562a5-0x85 mstscax+0x562a5 mstscax+0x56220: 00007ffe`c2d26220 4053 push rbx 00007ffe`c2d26222 55 push rbp 00007ffe`c2d26223 56 push rsi 00007ffe`c2d26224 57 push rdi 00007ffe`c2d26225 4154 push r12 00007ffe`c2d26227 4155 push r13 00007ffe`c2d26229 4156 push r14 00007ffe`c2d2622b 4157 push r15 00007ffe`c2d2622d 4881ece8000000 sub rsp,0E8h 00007ffe`c2d26234 488b05cdc37500 mov rax,qword ptr [mstscax!DllUnregisterServer+0x6f1308 (00007ffe`c3482608)] 00007ffe`c2d2623b 4833c4 xor rax,rsp 00007ffe`c2d2623e 48898424d0000000 mov qword ptr [rsp+0D0h],rax 00007ffe`c2d26246 33ed xor ebp,ebp 00007ffe`c2d26248 0f57c0 xorps xmm0,xmm0 00007ffe`c2d2624b 448bf5 mov r14d,ebp 00007ffe`c2d2624e 896c245c mov dword ptr [rsp+5Ch],ebp 00007ffe`c2d26252 f30f7f442470 movdqu xmmword ptr [rsp+70h],xmm0 00007ffe`c2d26258 498bf1 mov rsi,r9 00007ffe`c2d2625b 418bd8 mov ebx,r8d 00007ffe`c2d2625e 4c8be2 mov r12,rdx 00007ffe`c2d26261 488bf9 mov rdi,rcx 00007ffe`c2d26264 488b059df57400 mov rax,qword ptr [mstscax!DllUnregisterServer+0x6e4508 (00007ffe`c3475808)] 00007ffe`c2d2626b 4c8d2d96f57400 lea r13,[mstscax!DllUnregisterServer+0x6e4508 (00007ffe`c3475808)] 00007ffe`c2d26272 4c8bbc2450010000 mov r15,qword ptr [rsp+150h] 00007ffe`c2d2627a 493bc5 cmp rax,r13 00007ffe`c2d2627d 740a je mstscax+0x56289 (00007ffe`c2d26289) 00007ffe`c2d2627f f6401c01 test byte ptr [rax+1Ch],1 00007ffe`c2d26283 0f85c6010000 jne mstscax+0x5644f (00007ffe`c2d2644f) 00007ffe`c2d26289 39af88030000 cmp dword ptr [rdi+388h],ebp 00007ffe`c2d2628f 0f85f1020000 jne mstscax+0x56586 (00007ffe`c2d26586) 00007ffe`c2d26295 4d8bcf mov r9,r15 00007ffe`c2d26298 4c8bc6 mov r8,rsi 00007ffe`c2d2629b 8bd3 mov edx,ebx 00007ffe`c2d2629d 488bcf mov rcx,rdi 00007ffe`c2d262a0 e8eb110000 call mstscax+0x57490 (00007ffe`c2d27490) 00007ffe`c2d262a5 85c0 test eax,eax |
Great. We can see that the line 00007ffe`c2d262a0
calls some internal method and then tests if the output of the call is equal to 0 (this is the meaning of test eax,eax
). We can now comment out this line and see what happens (mind the empty line):
1 2 3 4 5 6 7 |
a 00007ffe`c2d262a0 xor eax,eax nop nop nop g |
We come back to mstsc.exe and we can see that CTRL+ALT+HOME doesn’t unfocus the window anymore! We can also check that the combination is handled properly inside the connection so it still unfocuses nested RDPs.
Automation
Automating this is pretty straightforward with cdb. We could modify the dll in place, but that would affect all the RDP connections we ever make. If we want to disable CTRL+ALT+HOME for some of them only, then this is what we can do:
First, create the file mstsc.txt
with the following code:
1 2 3 4 5 6 7 |
a mstscax+0x562a0 xor eax, eax nop nop nop qd |
Next, create batch file no_home.bat
1 |
cdb.exe -p %1 -cf mstsc.txt |
Finally, run it like this:
1 |
no_home.bat PROCESS_ID_OF_MSTSC_YOU_WANT_TO_HACK |
Changing shortcut to something else
How about we change shortcut instead of getting rid of it entirely? Let’s disassemble the method a bit more:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
0:001> u mstscax+0x57655-0x1c5 mstscax+0x57655 mstscax!CTSInput::IHPostMessageToMainWindow: 00007ffe`c2d27490 48895c2408 mov qword ptr [rsp+8],rbx 00007ffe`c2d27495 48896c2410 mov qword ptr [rsp+10h],rbp 00007ffe`c2d2749a 4889742418 mov qword ptr [rsp+18h],rsi 00007ffe`c2d2749f 57 push rdi 00007ffe`c2d274a0 4156 push r14 00007ffe`c2d274a2 4157 push r15 00007ffe`c2d274a4 4883ec30 sub rsp,30h 00007ffe`c2d274a8 33db xor ebx,ebx 00007ffe`c2d274aa 4d8bf1 mov r14,r9 00007ffe`c2d274ad 498be8 mov rbp,r8 00007ffe`c2d274b0 8bf2 mov esi,edx 00007ffe`c2d274b2 488bf9 mov rdi,rcx 00007ffe`c2d274b5 399958030000 cmp dword ptr [rcx+358h],ebx 00007ffe`c2d274bb 0f8582000000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0xb3 (00007ffe`c2d27543) 00007ffe`c2d274c1 81fa00010000 cmp edx,100h 00007ffe`c2d274c7 0f8489000000 je mstscax!CTSInput::IHPostMessageToMainWindow+0xc6 (00007ffe`c2d27556) 00007ffe`c2d274cd b901000000 mov ecx,1 00007ffe`c2d274d2 8d82fcfeffff lea eax,[rdx-104h] 00007ffe`c2d274d8 3bc1 cmp eax,ecx 00007ffe`c2d274da 0f47cb cmova ecx,ebx 00007ffe`c2d274dd 399f24030000 cmp dword ptr [rdi+324h],ebx 00007ffe`c2d274e3 0f84c3040000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x51c (00007ffe`c2d279ac) 00007ffe`c2d274e9 448bc3 mov r8d,ebx 00007ffe`c2d274ec 81fe04010000 cmp esi,104h 00007ffe`c2d274f2 0f84c7040000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x52f (00007ffe`c2d279bf) 00007ffe`c2d274f8 8bd3 mov edx,ebx 00007ffe`c2d274fa 85c9 test ecx,ecx 00007ffe`c2d274fc 0f85d1040000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0x543 (00007ffe`c2d279d3) 00007ffe`c2d27502 8bc3 mov eax,ebx 00007ffe`c2d27504 85d2 test edx,edx 00007ffe`c2d27506 0f8558010000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0x1d4 (00007ffe`c2d27664) 00007ffe`c2d2750c 85c0 test eax,eax 00007ffe`c2d2750e 0f8550010000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0x1d4 (00007ffe`c2d27664) 00007ffe`c2d27514 8bc3 mov eax,ebx 00007ffe`c2d27516 4585c0 test r8d,r8d 00007ffe`c2d27519 0f85c8040000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0x557 (00007ffe`c2d279e7) 00007ffe`c2d2751f 85c0 test eax,eax 00007ffe`c2d27521 0f85c0040000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0x557 (00007ffe`c2d279e7) 00007ffe`c2d27527 488b6c2458 mov rbp,qword ptr [rsp+58h] 00007ffe`c2d2752c 8bc3 mov eax,ebx 00007ffe`c2d2752e 488b5c2450 mov rbx,qword ptr [rsp+50h] 00007ffe`c2d27533 488b742460 mov rsi,qword ptr [rsp+60h] 00007ffe`c2d27538 4883c430 add rsp,30h 00007ffe`c2d2753c 415f pop r15 00007ffe`c2d2753e 415e pop r14 00007ffe`c2d27540 5f pop rdi 00007ffe`c2d27541 c3 ret 00007ffe`c2d27542 cc int 3 00007ffe`c2d27543 81fe13010000 cmp esi,113h 00007ffe`c2d27549 0f851f010000 jne mstscax!CTSInput::IHPostMessageToMainWindow+0x1de (00007ffe`c2d2766e) 00007ffe`c2d2754f bb01000000 mov ebx,1 00007ffe`c2d27554 ebd1 jmp mstscax!CTSInput::IHPostMessageToMainWindow+0x97 (00007ffe`c2d27527) 00007ffe`c2d27556 8b8128030000 mov eax,dword ptr [rcx+328h] 00007ffe`c2d2755c be00800000 mov esi,8000h 00007ffe`c2d27561 483be8 cmp rbp,rax 00007ffe`c2d27564 0f8470010000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x24a (00007ffe`c2d276da) 00007ffe`c2d2756a 8b8744030000 mov eax,dword ptr [rdi+344h] 00007ffe`c2d27570 483be8 cmp rbp,rax 00007ffe`c2d27573 0f84dc010000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x2c5 (00007ffe`c2d27755) 00007ffe`c2d27579 8b8748030000 mov eax,dword ptr [rdi+348h] 00007ffe`c2d2757f 483be8 cmp rbp,rax 00007ffe`c2d27582 0f8440020000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x338 (00007ffe`c2d277c8) 00007ffe`c2d27588 8b874c030000 mov eax,dword ptr [rdi+34Ch] 00007ffe`c2d2758e 483be8 cmp rbp,rax 00007ffe`c2d27591 0f84a4020000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x3ab (00007ffe`c2d2783b) 00007ffe`c2d27597 8b8750030000 mov eax,dword ptr [rdi+350h] 00007ffe`c2d2759d 483be8 cmp rbp,rax 00007ffe`c2d275a0 0f8408030000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x41e (00007ffe`c2d278ae) 00007ffe`c2d275a6 4883fd24 cmp rbp,24h 00007ffe`c2d275aa 0f8471030000 je mstscax!CTSInput::IHPostMessageToMainWindow+0x491 (00007ffe`c2d27921) 00007ffe`c2d275b0 4883fd2d cmp rbp,2Dh 00007ffe`c2d275b4 0f856dffffff jne mstscax!CTSInput::IHPostMessageToMainWindow+0x97 (00007ffe`c2d27527) 00007ffe`c2d275ba 8d4de5 lea ecx,[rbp-1Bh] 00007ffe`c2d275bd 48ff15acec5b00 call qword ptr [mstscax!_imp_GetKeyState (00007ffe`c32e6270)] 00007ffe`c2d275c4 0f1f440000 nop dword ptr [rax+rax] 00007ffe`c2d275c9 6685c6 test si,ax 00007ffe`c2d275cc 0f8455ffffff je mstscax!CTSInput::IHPostMessageToMainWindow+0x97 (00007ffe`c2d27527) 00007ffe`c2d275d2 8d4de4 lea ecx,[rbp-1Ch] 00007ffe`c2d275d5 48ff1594ec5b00 call qword ptr [mstscax!_imp_GetKeyState (00007ffe`c32e6270)] 00007ffe`c2d275dc 0f1f440000 nop dword ptr [rax+rax] 00007ffe`c2d275e1 6685c6 test si,ax 00007ffe`c2d275e4 0f843dffffff je mstscax!CTSInput::IHPostMessageToMainWindow+0x97 (00007ffe`c2d27527) 00007ffe`c2d275ea e8c581fcff call mstscax!CClientUtilsWin32::UT_IsRunningInAppContainer (00007ffe`c2cef7b4) 00007ffe`c2d275ef 85c0 test eax,eax 00007ffe`c2d275f1 0f8430ffffff je mstscax!CTSInput::IHPostMessageToMainWindow+0x97 (00007ffe`c2d27527) 00007ffe`c2d275f7 488b050ae27400 mov rax,qword ptr [mstscax!WPP_GLOBAL_Control (00007ffe`c3475808)] 00007ffe`c2d275fe 4c8d3d03e27400 lea r15,[mstscax!WPP_GLOBAL_Control (00007ffe`c3475808)] 00007ffe`c2d27605 493bc7 cmp rax,r15 00007ffe`c2d27608 7430 je mstscax!CTSInput::IHPostMessageToMainWindow+0x1aa (00007ffe`c2d2763a) 00007ffe`c2d2760a f6401c01 test byte ptr [rax+1Ch],1 00007ffe`c2d2760e 742a je mstscax!CTSInput::IHPostMessageToMainWindow+0x1aa (00007ffe`c2d2763a) 00007ffe`c2d27610 80781904 cmp byte ptr [rax+19h],4 00007ffe`c2d27614 7224 jb mstscax!CTSInput::IHPostMessageToMainWindow+0x1aa (00007ffe`c2d2763a) 00007ffe`c2d27616 e82de60000 call mstscax!RdpWppGetCurrentThreadActivityIdPrefix (00007ffe`c2d35c48) 00007ffe`c2d2761b 488b0de6e17400 mov rcx,qword ptr [mstscax!WPP_GLOBAL_Control (00007ffe`c3475808)] 00007ffe`c2d27622 4c8d0557ca5c00 lea r8,[mstscax!WPP_f5f71bb7bac236b27f26969128cc1e12_Traceguids (00007ffe`c32f4080)] 00007ffe`c2d27629 448bc8 mov r9d,eax 00007ffe`c2d2762c bafd000000 mov edx,0FDh 00007ffe`c2d27631 488b4910 mov rcx,qword ptr [rcx+10h] 00007ffe`c2d27635 e85ecdfcff call mstscax!WPP_SF_D (00007ffe`c2cf4398) 00007ffe`c2d2763a 488b8f60050000 mov rcx,qword ptr [rdi+560h] 00007ffe`c2d27641 4533c0 xor r8d,r8d 00007ffe`c2d27644 488b01 mov rax,qword ptr [rcx] 00007ffe`c2d27647 418d5010 lea edx,[r8+10h] 00007ffe`c2d2764b 488b4048 mov rax,qword ptr [rax+48h] 00007ffe`c2d2764f ff1523f75b00 call qword ptr [mstscax!_guard_dispatch_icall_fptr (00007ffe`c32e6d78)] 00007ffe`c2d27655 c7871404000001000000 mov dword ptr [rdi+414h],1 |
The most interesting line is:
1 |
00007ffe`c2d275a6 4883fd24 cmp rbp,24h |
0x24
is the code key of HOME. We can replace it with something else, like insert which is 2D
:
1 |
e mstscax+0x575A9 0x2D |
And there you go. You can now press CTRL+ALT+HOME to unfocus nested RDP and CTRL+ALT+INSERT to unfocus the outer one. This gives you two levels of unfocusing.