Windows 8 and conhost.exe

While debugging a console application on Windows 8, I noticed the console application is trying to create a process in the very beginning:

windbg.exe -xe ld:ntdll.dll -c "bm ntdll!*CreateProcess*; g; k" cmd.exe

 CommandLine: cmd.exe
ModLoad: 000007ff`01d60000 000007ff`01f1e000   ntdll.dll
ntdll!RtlUserThreadStart:
000007ff`01d7c3d0 4883ec48        sub     rsp,48h
Processing initial command 'bm ntdll!*CreateProcess*; g; k'
0:000> bm ntdll!*CreateProcess*; g; k
  1: 000007ff`01d90f60 @!"ntdll!RtlCreateProcessParametersEx"
  2: 000007ff`01d63070 @!"ntdll!NtCreateProcessEx"
breakpoint 2 redefined
  2: 000007ff`01d63070 @!"ntdll!ZwCreateProcessEx"
  3: 000007ff`01e1bf74 @!"ntdll!RtlCreateProcessReflection"
  4: 000007ff`01da8bb4 @!"ntdll!RtlpCreateProcessRegistryInfo"
  5: 000007ff`01e1ceac @!"ntdll!RtlCreateProcessParameters"
  6: 000007ff`01d63651 @!"ntdll!ZwCreateProcess"
breakpoint 6 redefined
  6: 000007ff`01d63651 @!"ntdll!NtCreateProcess"
Breakpoint 1 hit
Child-SP          RetAddr           Call Site
000000bf`8268e558 000007fe`feea02a4 ntdll!RtlCreateProcessParametersEx
000000bf`8268e560 000007fe`feea00be KERNELBASE!ConsoleLaunchServerProcess+0x60
000000bf`8268e5f0 000007fe`fee95d40 KERNELBASE!ConsoleAllocate+0xf6
000000bf`8268e8c0 000007fe`fee7f6db KERNELBASE!ConsoleInitialize+0x1d1
000000bf`8268e950 000007fe`fee7230d KERNELBASE!KernelBaseBaseDllInitialize+0x4dd
000000bf`8268ec20 000007ff`01d6b9be KERNELBASE!KernelBaseDllInitialize+0xd
000000bf`8268ec50 000007ff`01d8b3fc ntdll!LdrpCallInitRoutine+0x3e
000000bf`8268eca0 000007ff`01d8a88b ntdll!LdrpInitializeNode+0x192
000000bf`8268eda0 000007ff`01d8e74e ntdll!LdrpInitializeGraph+0x6f
000000bf`8268ede0 000007ff`01d8c322 ntdll!LdrpInitializeGraph+0x8d
000000bf`8268ee20 000007ff`01d8cc02 ntdll!LdrpPrepareModuleForExecution+0x1a5
000000bf`8268ee70 000007ff`01d8337b ntdll!LdrpLoadDll+0x344
000000bf`8268f0a0 000007ff`01d9264f ntdll!LdrLoadDll+0xa7
000000bf`8268f120 000007ff`01d91826 ntdll!LdrpInitializeProcess+0x1664
000000bf`8268f420 000007ff`01d7c1ae ntdll!_LdrpInitialize+0x1565e
000000bf`8268f490 00000000`00000000 ntdll!LdrInitializeThunk+0xe
 
0:000> dc rbx
000000bf`8268e660  00000000 00000000 00000000 00000000  ................
000000bf`8268e670  003f005c 005c003f 003a0043 0057005c  \.?.?.\.C.:.\.W.
000000bf`8268e680  004e0049 004f0044 00530057 0073005c  I.N.D.O.W.S.\.s.
000000bf`8268e690  00730079 00650074 0033006d 005c0032  y.s.t.e.m.3.2.\.
000000bf`8268e6a0  006f0063 0068006e 0073006f 002e0074  c.o.n.h.o.s.t...
000000bf`8268e6b0  00780065 00200065 00780030 00660066  e.x.e. .0.x.f.f.
000000bf`8268e6c0  00660066 00660066 00660066 00000000  f.f.f.f.f.f.....
000000bf`8268e6d0  8268e960 000000bf 00000008 00000000  `.h.............

This means conhost.exe process on Windows 8 will be created by the console application itself, instead of the CSRSS. And conhost.exe would always have the native bitness (on Windows 8 64bit version, only 64bit version of conhost.exe is available).

Now debug into conhost.exe using .childdbg, it's pretty clear that conhost.exe is in charge of drawing the console window, handling user inputs and communicate with the console application:

 0  Id: 124c.d34 Suspend: 1 Teb: 000007f6`3311b000 Unfrozen
Child-SP          RetAddr           Call Site
00000094`85aefb38 000007f6`33b91146 ntdll!NtWaitForSingleObject+0xa
00000094`85aefb40 000007ff`00c9167e conhost!ConsoleIoThread+0xda
00000094`85aefd80 000007ff`01d7c3f1 KERNEL32!BaseThreadInitThunk+0x1a
00000094`85aefdb0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
 #  1  Id: 124c.1428 Suspend: 1 Teb: 000007f6`3311e000 Unfrozen
Child-SP          RetAddr           Call Site
00000094`85b6fd28 000007ff`0140171e conhost!ConsoleWindowProc
00000094`85b6fd30 000007ff`014014d7 USER32!UserCallWinProcCheckWow+0x13a
00000094`85b6fdf0 000007f6`33b92fcc USER32!DispatchMessageWorker+0x1a7
00000094`85b6fe70 000007ff`00c9167e conhost!ConsoleInputThread+0xd2
00000094`85b6fed0 000007ff`01d7c3f1 KERNEL32!BaseThreadInitThunk+0x1a
00000094`85b6ff00 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

And cmd.exe itself doesn't draw the console window at all:

 0  Id: 1aec.5ec Suspend: 1 Teb: 000007f7`b280d000 Unfrozen
Child-SP          RetAddr           Call Site
00000008`50fff5b8 000007fe`fee8f17c ntdll!NtDeviceIoControlFile+0xa
00000008`50fff5c0 000007fe`fef0bb29 KERNELBASE!ConsoleCallServerGeneric+0x118
00000008`50fff710 000007fe`fef0b986 KERNELBASE!ReadConsoleInternal+0x131
00000008`50fff850 000007f7`b3621025 KERNELBASE!ReadConsoleW+0x1a
00000008`50fff890 000007f7`b362bd3e cmd!ReadBufFromConsole+0x111
00000008`50fff960 000007f7`b3604aae cmd!_chkstk+0x3820
00000008`50fffae0 000007f7`b36042e4 cmd!Lex+0x4be
00000008`50fffb50 000007f7`b362d560 cmd!Parser+0x128
00000008`50fffba0 000007f7`b361b721 cmd!_chkstk+0x5032
00000008`50fffc00 000007ff`00c9167e cmd!mystrchr+0x27d
00000008`50fffc40 000007ff`01d7c3f1 KERNEL32!BaseThreadInitThunk+0x1a
00000008`50fffc70 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

The interesting thing is that if we use Spy++, it would report that the console window is associated with the main thread of cmd.exe process! I believe this is a hack in the underlying implementation of GetWindowThreadProcessId for backward compatibility. Also, Spy++ cannot be used to inspect conhost.exe message loop.

Due to the side effects of IFEO Debugger, console application would fail to start if IFEO Debugger is enabled for conhost.exe.

The following call stack showed conhost.exe just created a normal window from Console Input Thread:

 USER32!CreateWindowExW
conhost!CreateWindowsWindow+0x105
conhost!InitWindowsSubsystem+0x69
conhost!ConsoleInputThread+0x22
KERNEL32!BaseThreadInitThunk+0x1a
ntdll!RtlUserThreadStart+0x1d
 0:001> du @rdx
000007f6`33b9d460  "ConsoleWindowClass"
 0:001> du @r8
00000025`f7dc37c0  "C:\WINDOWS\SYSTEM32\cmd.exe"

When cmd.exe exits, the Console I/O Thread would be notified:

 ntdll!NtTerminateProcess+0xa
ntdll!RtlExitUserProcess+0xb6
conhost!ConsoleIoThread+0xac4
KERNEL32!BaseThreadInitThunk+0x1a
ntdll!RtlUserThreadStart+0x1d

(to be continued...)