昨天遇到一件怪事,在进行远程线程注入的时候从csrss进程中竟然弹出了vc的ASSERT对话框。根据对话框提示信息找到了断言的位置在mfc的auxdata.cpp的第95行代码:void AUX_DATA::UpdateSysMetrics(){ // System metrics cxIcon = GetSystemMetrics(SM_CXICON); cyIcon = GetSystemMetrics(SM_CYICON);
// System metrics which depend on subsystem version afxData.cxVScroll = GetSystemMetrics(SM_CXVSCROLL) + CX_BORDER; afxData.cyHScroll = GetSystemMetrics(SM_CYHSCROLL) + CY_BORDER;
// Device metrics for screen HDC hDCScreen = GetDC(NULL); ASSERT(hDCScreen != NULL); <------------------就是这个断言 cxPixelsPerInch = GetDeviceCaps(hDCScreen, LOGPIXELSX); cyPixelsPerInch = GetDeviceCaps(hDCScreen, LOGPIXELSY); ReleaseDC(NULL, hDCScreen);}具体的环境就是win xp进行多用户登陆:首先登陆一个windows帐号user1, session id = 0, 对csrss进程成功进行了远程线程注入。接着在保持user1登陆的情况下切换用户登陆
到user2,进去之后同样的对新的session(session id = 1)的csrss进程进行远程线程注入,没什么问题。但是切回到user1后发现界面上有一个断言对话框,显示了上面代码位置
的断言失败。查看这个对话框所在的进程确实是session 0的csrss进程。因为是一个稳定的重现的问题,使用windbg远程附加到session 0的csrss进程,查看弹出对话框的线程调
用栈如下: ChildEBP RetAddr 0071f8d8 77d193f5 ntdll!KiFastSystemCallRet (FPO: [0,0,0]) 0071f910 77d2688a USER32!NtUserWaitMessage+0xc 0071f938 77d3b7c5 USER32!InternalDialogBox+0xd0 (FPO: [6,1,4]) 0071fbf8 77d3b12b USER32!SoftModalMessageBox+0x938 (FPO: [1,165,4]) 0071fd48 77d65fdf USER32!MessageBoxWorker+0x2ba (FPO: [1,78,4]) 0071fda0 764f9b1b USER32!MessageBoxTimeoutW+0x7a (FPO: [6,19,0]) 0071fe7c 764f9d5b winsrv!HardErrorHandler+0x2e8 (FPO: [0,44,4]) 0071fe9c 764fb0f1 winsrv!ProcessHardErrorRequest+0x9b (FPO: [1,3,4]) 0071febc 764fb173 winsrv!UserHardErrorEx+0x234 (FPO: [3,2,4]) 0071fed0 75aa47a0 winsrv!UserHardError+0x12 (FPO: [2,0,0]) 0071fff4 00000000 CSRSRV!CsrApiRequestThread+0x18a (FPO: [Non-Fpo])这里很奇怪,断言的调用链里面应该会出现__crtMessageBoxA,除非是符号不对,但是调用栈里面所有的返回地址都有符号。难道是调用栈不完整,于是结合汇编代码对调用栈进行核实---没问题。一个偶然的原因在ReactOS上面看了一下CsrApiRequestThread函数的源代码才意识到这个对话框是通过LPC
端口投递过来的消息显示。winsrv!UserHardError是最后一个出错处理回调例程。用户态调试查不出这个LPC是哪里投递过来的,转而使用内核态调试。根据从运程线程注入的结果,对session 1的csrss进程进行的远程线程一直没有收到注入成功的回复,很自然想到看一下session 1的csrss进程的情况。
kd> !process 81a81468 PROCESS 81a81468 SessionId: 1 Cid: 0238 Peb: 7ffd5000 ParentCid: 0240 DirBase: 084003e0 ObjectTable: e178ca80 HandleCount: 128. Image: csrss.exe VadRoot 81711920 Vads 100 Clone 0 Private 702. Modified 2674. Locked 0. DeviceMap e10000b8 Token e1474138 ElapsedTime 00:01:01.984 UserTime 00:00:01.500 KernelTime 00:00:01.921 QuotaPoolUsage[PagedPool] 123956 QuotaPoolUsage[NonPagedPool] 4160 Working Set Sizes (now,min,max) (125, 50, 345) (500KB, 200KB, 1380KB) PeakWorkingSetSize 2818 VirtualSize 59 Mb PeakVirtualSize 76 Mb PageFaultCount 3524 MemoryPriority BACKGROUND BasePriority 13 CommitCharge 874
/////略去一些无关线程信息
THREAD 8170eda8 Cid 0238.096c Teb: 7ffd3000 Win32Thread: e24c1ba0 WAIT: (WrLpcReply) UserMode Non-Alertable 8170ef9c Semaphore Limit 0x1 Waiting for reply to LPC MessageId 0000209e: Current LPC port e1353840 Not impersonating DeviceMap e10000b8 Owning Process 81a81468 Image: csrss.exe Wait Start TickCount 13207 Ticks: 2483 (0:00:00:38.796) Context Switch Count 17 LargeStack UserTime 00:00:00.140 KernelTime 00:00:00.015 Win32 Start Address 0x7ff90000 Start Address KERNEL32!BaseThreadStartThunk (0x7c810856) Stack Init f72f1000 Current f72f0a94 Base f72f1000 Limit f72ee000 Call 0 Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 0 ChildEBP RetAddr f72f0aac 8050117a nt!KiSwapContext+0x2e (FPO: [Uses EBP] [0,0,4]) f72f0ab8 804fa9be nt!KiSwapThread+0x46 (FPO: [0,0,0]) f72f0ae0 805989d5 nt!KeWaitForSingleObject+0x1c2 (FPO: [Non-Fpo]) f72f0b18 80598b21 nt!LpcpRequestWaitReplyPort+0x43d (FPO: [Non-Fpo]) f72f0b30 8060a467 nt!LpcRequestWaitReplyPortEx+0x21 (FPO: [Non-Fpo]) f72f0cd4 8060a8c5 nt!ExpRaiseHardError+0x1bd (FPO: [Non-Fpo]) f72f0d44 8053d808 nt!NtRaiseHardError+0x16b (FPO: [Non-Fpo]) f72f0d44 7c92eb94 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f72f0d64) 0350b348 7c92e273 ntdll!KiFastSystemCallRet (FPO: [0,0,0]) 0350b34c 77d65d43 ntdll!NtRaiseHardError+0xc (FPO: [6,0,0]) 0350b3a8 77d48ffe USER32!ServiceMessageBox+0x145 (FPO: [Non-Fpo]) 0350b504 77d65fdf USER32!MessageBoxWorker+0x13e (FPO: [Non-Fpo]) 0350b55c 77d66084 USER32!MessageBoxTimeoutW+0x7a (FPO: [Non-Fpo]) 0350b590 77d50598 USER32!MessageBoxTimeoutA+0x9c (FPO: [Non-Fpo]) 0350b5b0 77d50550 USER32!MessageBoxExA+0x1b (FPO: [Non-Fpo]) 0350b5cc 1020c2aa USER32!MessageBoxA+0x45 (FPO: [Non-Fpo]) 0350b604 1020cd3e MSVCR71D!__crtMessageBoxA+0x16a (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\crtmbox.c @ 119] 0350c75c 1020ca2a MSVCR71D!CrtMessageWindow+0x2be (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\dbgrpt.c @ 617] 0350f7d8 03666b3f MSVCR71D!_CrtDbgReport+0x41a (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\dbgrpt.c @ 516] 0350f818 0361bad2 MFC71UD!AfxAssertFailedLine+0x2f (FPO: [Non-Fpo]) (CONV: stdcall) [f:\vs70builds\3077\vc\mfcatl\ship\atlmfc\src\mfc\afxasert.cpp @
28] 0350f830 0368cc9e MFC71UD!AUX_DATA::UpdateSysMetrics+0x62 (FPO: [Non-Fpo]) (CONV: thiscall) [f:\vs70builds\3077
\vc\mfcatl\ship\atlmfc\src\mfc\auxdata.cpp @ 95] 0350f840 0368cd5d MFC71UD!AUX_DATA::AUX_DATA+0x2e (FPO: [Non-Fpo]) (CONV: thiscall) [f:\vs70builds\3077\vc\mfcatl\ship\atlmfc\src\mfc\auxdata.cpp @
35] 0350f848 10201d48 MFC71UD!$E5+0xd (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\mfcatl\ship\atlmfc\src\mfc\auxdata.cpp @ 22] 0350f850 035da943 MSVCR71D!_initterm+0x18 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\crt0dat.c @ 600] 0350f860 035daa5a MFC71UD!_CRT_INIT+0xa3 (FPO: [Non-Fpo]) (CONV: stdcall) [f:\vs70builds\3077\vc\crtbld\crt\src\crtdll.c @ 184] 0350f8ac 7c9211a7 MFC71UD!_DllMainCRTStartup+0x9a (FPO: [Non-Fpo]) (CONV: stdcall) [f:\vs70builds\3077\vc\crtbld\crt\src\crtdll.c @ 266] 0350f8cc 7c93cbab ntdll!LdrpCallInitRoutine+0x14 0350f9d4 7c936178 ntdll!LdrpRunInitializeRoutines+0x344 (FPO: [Non-Fpo]) 0350fc80 7c9362da ntdll!LdrpLoadDll+0x3e5 (FPO: [Non-Fpo]) 0350ff28 7c801bb9 ntdll!LdrLoadDll+0x230 (FPO: [Non-Fpo]) 0350ff90 7c80ace4 KERNEL32!LoadLibraryExW+0x18e (FPO: [Non-Fpo]) 0350ffa4 7ff90010 KERNEL32!LoadLibraryW+0x11 (FPO: [Non-Fpo])WARNING: Frame IP not in any known module. Following frames may be wrong. 0350ffb4 7c80b50b 0x7ff90010 0350ffec 00000000 KERNEL32!BaseThreadStart+0x37 (FPO: [Non-Fpo])
看到这一个线程的信息之后,原因终于找到了,是在对session 1的csrss进行注入之后,加载dll,初始化mfc动态链接库的时候导致断言失败,对话框通过LPC的方式重定向到
session 0的csrss进程去显示了。到这里还是有很多疑问,主要是两个问题:1.为什么auxdata.cpp第94行HDC hDCScreen = GetDC(NULL);获取屏幕DC的时候会得到NULL句柄。2.为什么在对session 0的csrss进程注入的时候没有出现这个问题。
写了如下的测试dll:#include "stdafx.h"#include <crtdbg.h>
#define ASSERT(expr, msg) \ do { if (!(expr) && \ (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, msg))) \ _CrtDbgBreak(); } while (0)
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ if (DLL_PROCESS_ATTACH == ul_reason_for_call){ _asm int 3 ;这里的断点,是为了跟踪调试GetDC ASSERT(NULL != GetDC(0), NULL); } return TRUE;}
同时写一个简单的注入程序将上面的dll注入到csrss进程中。内核调试在int 3断点中断,nop掉int 3指令。单步跟踪GetDC(注意跟踪的时候对)同时结合windows nt的源代码,发
现之所以返回NULL,是因为csrss进程没有关联桌面,获取不到屏幕DC(具体参考nt源代码)。同时发现session 0确实也拿不到屏幕DC,继续跟踪断言消息框显示流程。发现__crtMessageBoxA函数(VC有源代码)在uType参数加上了MB_SERVICE_NOTIFICATION标志,导致
MessageBoxWorker函数(win2k源代码)最终调用ServiceMessageBox,而不是SoftModalMessageBox。
kd> !process 8150b5c0 PROCESS 8150b5c0 SessionId: 1 Cid: 0dc0 Peb: 7ffdd000 ParentCid: 023c DirBase: 02b403e0 ObjectTable: e11a0e48 HandleCount: 136. Image: csrss.exe VadRoot 816ca8d0 Vads 86 Clone 0 Private 199. Modified 2530. Locked 0. DeviceMap e1000110 Token e1157930 ElapsedTime 01:41:39.406 UserTime 00:00:02.906 KernelTime 00:00:04.250 QuotaPoolUsage[PagedPool] 127108 QuotaPoolUsage[NonPagedPool] 3600 Working Set Sizes (now,min,max) (96, 50, 345) (384KB, 200KB, 1380KB) PeakWorkingSetSize 2097 VirtualSize 59 Mb PeakVirtualSize 64 Mb PageFaultCount 4342 MemoryPriority BACKGROUND BasePriority 13 CommitCharge 304
//略去无关线程
THREAD 8141c278 Cid 0dc0.01b0 Teb: 7ffd3000 Win32Thread: e1dd6938 WAIT: (WrLpcReply) UserMode Non-Alertable 8141c46c Semaphore Limit 0x1 Waiting for reply to LPC MessageId 00003e93: Current LPC port e13cb988 Not impersonating DeviceMap e1000110 Owning Process 0 Image: <Unknown> Attached Process 8150b5c0 Image: csrss.exe Wait Start TickCount 23986 Ticks: 4438 (0:00:01:09.343) Context Switch Count 17 LargeStack UserTime 00:00:00.093 KernelTime 00:00:00.031 Win32 Start Address 0x02b60000 Start Address KERNEL32!BaseThreadStartThunk (0x7c810669) Stack Init f72f6000 Current f72f5a94 Base f72f6000 Limit f72f3000 Call 0 Priority 13 BasePriority 13 PriorityDecrement 0 DecrementCount 0 Kernel stack not resident. ChildEBP RetAddr f72f5aac 80501366 nt!KiSwapContext+0x2e (FPO: [Uses EBP] [0,0,4]) f72f5ab8 804fabd0 nt!KiSwapThread+0x46 (FPO: [0,0,0]) f72f5ae0 80598d01 nt!KeWaitForSingleObject+0x1c2 (FPO: [5,5,4]) f72f5b18 80598e4d nt!LpcpRequestWaitReplyPort+0x43d (FPO: [4,4,0]) f72f5b30 8060a799 nt!LpcRequestWaitReplyPortEx+0x21 (FPO: [3,0,0]) f72f5cd4 8060abf7 nt!ExpRaiseHardError+0x1bd (FPO: [Non-Fpo]) f72f5d44 8053da48 nt!NtRaiseHardError+0x16b (FPO: [Non-Fpo]) f72f5d44 7c92eb94 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ f72f5d64) 02c6b2d8 7c92e273 ntdll!KiFastSystemCallRet (FPO: [0,0,0]) 02c6b2dc 77d65d43 ntdll!NtRaiseHardError+0xc (FPO: [6,0,0]) 02c6b338 77d48ffe USER32!ServiceMessageBox+0x145 (FPO: [4,13,0]) 02c6b494 77d65fdf USER32!MessageBoxWorker+0x13e (FPO: [1,78,4]) 02c6b4ec 77d66084 USER32!MessageBoxTimeoutW+0x7a (FPO: [6,19,0]) 02c6b520 77d50598 USER32!MessageBoxTimeoutA+0x9c (FPO: [6,2,4]) 02c6b540 77d50550 USER32!MessageBoxExA+0x1b (FPO: [5,0,0]) 02c6b55c 1001811a USER32!MessageBoxA+0x45 (FPO: [4,0,0]) 02c6b594 10012c5e ApiHook!__crtMessageBoxA+0x16a (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\crtmbox.c @ 119] 02c6c6ec 1001283a ApiHook!CrtMessageWindow+0x2be (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\dbgrpt.c @ 617] 02c6f768 10011fa3 ApiHook!_CrtDbgReport+0x41a (FPO: [Non-Fpo]) (CONV: cdecl) [f:\vs70builds\3077\vc\crtbld\crt\src\dbgrpt.c @ 516] 02c6f850 1001320b ApiHook!DllMain+0x53 (FPO: [Non-Fpo]) (CONV: stdcall) [e:\lzz\vc proj\apihook\apihook.cpp @ 20] 02c6f898 7c9211a7 ApiHook!_DllMainCRTStartup+0xbb (FPO: [Non-Fpo]) (CONV: stdcall) [f:\vs70builds\3077\vc\crtbld\crt\src\dllcrt0.c @ 297] 02c6f8b8 7c93cbab ntdll!LdrpCallInitRoutine+0x14 02c6f9c0 7c936178 ntdll!LdrpRunInitializeRoutines+0x344 (FPO: [Non-Fpo]) 02c6fc6c 7c9362da ntdll!LdrpLoadDll+0x3e5 (FPO: [Non-Fpo]) 02c6ff14 7c801bb9 ntdll!LdrLoadDll+0x230 (FPO: [Non-Fpo]) 02c6ff7c 7c801d6e KERNEL32!LoadLibraryExW+0x18e (FPO: [Non-Fpo]) 02c6ff90 7c801da4 KERNEL32!LoadLibraryExA+0x1f (FPO: [3,0,0]) 02c6ffac 02b6000a KERNEL32!LoadLibraryA+0x94 (FPO: [1,0,0])WARNING: Frame IP not in any known module. Following frames may be wrong. 02c6ffec 00000000 0x2b6000a内核函数ExpRaiseHardError根据当前进程的ExceptionPort发送这个消息框调用。这时候刚好csrss的ExceptionPort为NULL,系统使用默认的ExpDefaultErrorPort,但csrss
(session 0)是默认的ExpDefaultErrorPortProcess, 操作系统对这个进程的消息不进行LPC投递,直接返回. 这就是为什么第一个csrss被注入没有弹出断言消息框的原因。这之后
的第二个csrss进程的ExceptionPort也是NULL,LPC被发送到了ExpDefaultErrorPort,第一个csrss对该消息进行了处理显示了断言消息框。
进一步发现系统中只有system,smss,csrss进程没有ExceptionPort,其它的进程都有ExceptionPort。这个Port的名字就是"ApiPort",每一个session都有一个这样的Port,csrss
进程负责监听,而没有ExceptionPort的进程则通过ExpDefaultErrorPort发送,默认由session 0的csrss进程处理。