标题: Win10主控台登录认证流程 创建: 2022-01-14 10:31 更新: 链接: https://scz.617.cn/windows/202201141031.txt -------------------------------------------------------------------------- 目录: ☆ 背景介绍 ☆ Win10交互式登录大框架 ☆ winlogon!Wlui* ☆ logoncontroller!Wluir* ☆ SspiSrv!SspirLogonUser ☆ sspicli!SspipLogonUser ☆ sspicli!LsaLogonUser ☆ usermgrcli!UMgrLogonUser ☆ logoncontroller!WluirRequestCredentials ☆ credprovs!KerbInteractiveUnlockLogonPack ☆ sechost!CredProtectW ☆ sechost!CredUnprotectW ☆ winlogon的状态与信号 ☆ 后记 ☆ 参考资源 -------------------------------------------------------------------------- ☆ 背景介绍 搜某些关键字时命中[1],作者在看雪论坛分享过Win7主控台登录认证流程中的部分 环节。正好当时有深究Win10 RPC调试的动机,这篇也涉及一些,实验环境唾手可得, 就在Win10上实践一番。 我对登录认证流程没有研究过,那个可能是黑产党的刚需。本文不涉及黑产党视角, 就是简单看看[1]之作者所述内容在Win10上的变化,以调试视角呈现内容。阅读本文 前,应该先看[1]。 本文在Win10企业版2016 LTSB 1607(OS Build 14393.4704)上测试。 ☆ Win10交互式登录大框架 Win10主控台登录动态创建进程树 winlogon.exe LogonUI.exe 假设在主控台登录成功,则上述进程树被销毁;此时若锁屏,会创建新的进程树。每 种交互式登录都有这样的进程树被创建,RDP登录时也会创建这样的进程树。 下面这个框架对应几件事 a) 出现密码输入界面 b) 输入密码点击确定后的RPC调用 c) 报告登录验证结果 -------------------------------------------------------------------------- winlogon!StateMachineWorkerCallback ntdll!LdrpDispatchUserCallTarget winlogon!WLGeneric_Request_Logon_Credz_Execute winlogon!RequestCredentials winlogon!WluiRequestCredentials // 要求用户输入密码,出现新的密码输入界面 logoncontroller!WluirRequestCredentials // 从winlogon进入LogonUI winlogon!SignalManagerSetSignal // 错误密码触发 ntdll!LdrpDispatchUserCallTarget winlogon!WLGeneric_Logon_ReportFailedResult_Execute winlogon!ReportResult winlogon!WluiReportResult // 报告登录验证结果 logoncontroller!WluirReportResult winlogon!WluiDisplayRequestCredentialsError // 提示"密码不正确" logoncontroller!WluirDisplayRequestCredentialsError winlogon!WlStateMachineSetSignal winlogon!SignalManagerSetSignal // 提示"密码不正确",点击确定,触发 winlogon!WLGeneric_Authenticating_Execute winlogon!WlDisplayStatus winlogon!WluiDisplayStatus logoncontroller!WluirDisplayStatus winlogon!AuthenticateUser // 输入密码,提交,触发 usermgrcli!UMgrLogonUser usermgrcli!DoRpcCall< > usermgrcli!::operator() RPCRT4!NdrClientCall3 RPCRT4!NdrpClientCall3 usermgr!svcUMLogonUser // 从winlogon进入UserManager服务 usermgr!UserMgrCli::UMLogonUser sspicli!LsaLogonUser // Win10在UserManager服务中调用此函数 sspicli!SspipLogonUser RPCRT4!NdrClientCall3 SspiSrv!SspirLogonUser // 从UserManager服务进入lsass winlogon!SignalManagerSetSignal // 错误密码触发 -------------------------------------------------------------------------- Win10在winlogon与lsass之间多了一个UserManager服务 winlogon->UserManager服务->lsass 过去可能由winlogon直接发起RPC请求sspicli!SspipLogonUser,Win10有变化,由 UserManager服务发起RPC请求。 ☆ winlogon!Wlui* winlogon!WluiRequestCredentials负责展现新的密码输入界面,函数名中的"ui"暗 示了这是用户界面相关的函数。winlogn中这类函数还有 > x /1 winlogon!Wlui*[a-z] winlogon!WluiiReleaseLessPrivilegedToken winlogon!WluiiStartupImpl winlogon!WluiiWaitForServer winlogon!WluiiShutdownImpl winlogon!WluiDisplayStatus winlogon!WluiDisplayRequestCredentialsError winlogon!WluiDisplayTSDisconnectOptionsList winlogon!WluiPromptForCredentials winlogon!WluiDisplayLocked winlogon!WluiAbort winlogon!WluiSecureDelayLocked winlogon!WluiNotifyIsReadyForDesktopSwitch winlogon!WluiFinishOperation winlogon!WluiDisplayMessage winlogon!WluiGetLockScreenIdleTimeout winlogon!WluiDelayLocked winlogon!WluiWaitForLockScreenDismiss winlogon!WluiGetShutdownResolverInfo winlogon!WluiShutdown winlogon!WluiAbortUIThread winlogon!WluiDisplayTSDisconnectUI winlogon!WluiDisplayTSReconnectUI winlogon!WluipCopyToUnicodeString winlogon!WluiReportResult winlogon!WluiiDestroySharedEvents winlogon!WluiInformLogonUI winlogon!WluiDisplayTSDisconnectOptionsMessage winlogon!WluiDisplaySecurityOptions winlogon!WluiDisplayWelcome winlogon!WluiSecureDisplayLocked winlogon!WluiNotifyUserIsLoggedOn winlogon!WluiReleaseUI winlogon!WluiRequestCredentials winlogon!WluiDisplayTSReconnectOptions winlogon!WluiReleaseContext winlogon!WluiClearUIState 直接"x /1 winlogon!Wlui*"会显示很多内部函数,比如 winlogon!WluiDisplayStatus$filt$0 参看windbg帮助中的"String Wildcard Syntax"小节。"x /1 winlogon!Wlui*[a-z]" 大小写不敏感。 ☆ logoncontroller!Wluir* 从框架图中看到winlogon!WluiRequestCredentials通过RPC调用LogonUI进程中的 logoncontroller!WluirRequestCredentials,函数名多了一个字母"r",表示RPC。 这一对函数分别对应RPC Client、RPC Server。类似的还有 winlogon!WluiReportResult logoncontroller!WluirReportResult winlogon!WluiDisplayRequestCredentialsError logoncontroller!WluirDisplayRequestCredentialsError winlogon!WluiDisplayStatus logoncontroller!WluirDisplayStatus 调试LogonUI.exe,查看 > .shell -ci "x /1 logoncontroller!Wluir*" findstr Wluir logoncontroller!WluirDisplayLocked logoncontroller!WluirSecureDisplayLocked logoncontroller!WluirDisplayTSReconnectUI logoncontroller!WluirSignalShutdown logoncontroller!WluirDelayLocked logoncontroller!WluirInformLogonUI logoncontroller!WluirDisplayStatus logoncontroller!WluirDisplayMessage logoncontroller!WluirRequestCredentials logoncontroller!WluirPromptForCredentials logoncontroller!WluirNotifyUserIsLoggedOn logoncontroller!WluirDisplaySecurityOptions logoncontroller!WluirGetShutdownResolverInfo logoncontroller!WluirSecureDelayLocked logoncontroller!WluirWaitForLockScreenDismiss logoncontroller!WluirNotifyIsReadyForDesktopSwitch logoncontroller!WluirAbort logoncontroller!WluirReleaseContext logoncontroller!WluirDisplayRequestCredentialsError logoncontroller!WluirDisplayTSDisconnectOptions logoncontroller!WluirDisplayWelcome logoncontroller!WluirClearUIState logoncontroller!WluirReportResult logoncontroller!WluirDisplayTSDisconnectUI logoncontroller!WluirFinishOperation 由于x命令后面的模板大小写不敏感,直接"x /1 logoncontroller!Wluir*"会命中这 种 logoncontroller!WluiRequestReasonToLogonReason 不是我们想要的,所以用了".shell -ci"技巧。上述25个函数都有相应的winlogon版 本。 更精准的办法是,用IDA的findrpc插件看LogonController.dll的 "f3f09ffd-fbcf-4291-944d-70ad6e0e73bb"接口,该接口提供的远程过程正是上述25 个函数。关于findrpc,参看 《利用findrpc寻找RPC接口信息》 https://scz.617.cn/python/202111061555.txt ☆ SspiSrv!SspirLogonUser 主控台登录时流程会过lsass进程空间的SspiSrv!SspirLogonUser。对之设断,断下 来时查看IID/ProcNum/RPC Client PID bp SspiSrv!SspirLogonUser r $t0=poi(@rsp+0xc8+0xe0);dt ntdll!_GUID poi(@$t0+0x28)+4;? dwo(@$t0+0x1c) r $t1=poi(@$t0+0x68);? qwo(@$t1+8) 在SspiSrv!SspirLogonUser入口处获取RPC Client PID的Hacking方案不可用于产品。 其他IID/ProcNum目标函数入口处未必如此,需要逆向确认,不可直接套用。假设获 取信息如下 IID 4f32adc8-6052-4a04-8701-293ccf2096f0 ProcNum SspiSrv!SspirLogonUser (12) RPC Client PID 872 $ tasklist /svc /fi "pid eq 872" Image Name PID Services ========================= ======== ============================================ svchost.exe 872 Appinfo, AppMgmt, BITS, CertPropSvc, DsmSvc, gpsvc, IKEEXT, iphlpsvc, lfsvc, ProfSvc, RasMan, Schedule, seclogon, SENS, SessionEnv, Themes, UserManager, UsoSvc, Winmgmt, WpnService RPC客户端是svchost(872),不是winlogon.exe,具体说,是UserManager服务。 ☆ sspicli!SspipLogonUser 调试svchost(872),调试UserManager服务 bp RPCRT4!NdrpClientCall3 "r $t0=poi(poi(@rdx));.if(@$t0){.if(qwo(@$t0+4)==0x4a0460524f32adc8 and qwo(@$t0+0xc)==0xf09620cf3c290187 and @r8==0n12){kpn}.else{gc}}.else{gc}" dt -io ntdll!_GUID @$t0+4;? @r8 IID/ProcNum匹配时断下 # Call Site 00 RPCRT4!NdrpClientCall3 01 RPCRT4!NdrClientCall3+0xf2 02 sspicli!SspipLogonUser+0x394 03 sspicli!LsaLogonUser+0x83 04 usermgr!UserMgrCli::UMLogonUser+0x2c6 05 usermgr!svcUMLogonUser+0xd7 06 RPCRT4!Invoke+0x73 07 RPCRT4!Ndr64StubWorker+0xbb1 08 RPCRT4!NdrServerCallAll+0x3c 09 RPCRT4!DispatchToStubInCNoAvrf+0x24 0a RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1bd 0b RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0x15e 0c RPCRT4!LRPC_SCALL::DispatchRequest+0x177 0d RPCRT4!LRPC_SCALL::HandleRequest+0x2bc 0e RPCRT4!LRPC_ADDRESS::HandleRequest+0x36c 0f RPCRT4!LRPC_ADDRESS::ProcessIO+0x91b 10 RPCRT4!LrpcIoComplete+0xaa 11 ntdll!TppAlpcpExecuteCallback+0x25e 12 ntdll!TppWorkerThread+0x8d9 13 KERNEL32!BaseThreadInitThunk+0x14 14 ntdll!RtlUserThreadStart+0x21 UserManager服务调用sspicli!SspipLogonUser,发起RPC,调用lsass的 SspiSrv!SspirLogonUser。 ☆ sspicli!LsaLogonUser sspicli!SspipLogonUser的父函数是sspicli!LsaLogonUser,有个同名函数文档化过, 参[2] -------------------------------------------------------------------------- NTSTATUS LsaLogonUser ( [in] HANDLE LsaHandle, // rcx /* * dt UxTheme!LSA_STRING @rdx * * "Winlogon" */ [in] PLSA_STRING OriginName, // rdx /* * dt UxTheme!_SECURITY_LOGON_TYPE * * Interactive(2) */ [in] SECURITY_LOGON_TYPE LogonType, // r8 [in] ULONG AuthenticationPackage, // r9 /* * A pointer to an input buffer that contains authentication information, * such as user name and password. The format and content of this buffer * are determined by the authentication package. * * db poi(@rsp+0x28) l dwo(@rsp+0x30) */ [in] PVOID AuthenticationInformation, // poi(@rsp+0x28) [in] ULONG AuthenticationInformationLength, // dwo(@rsp+0x30) [in] PTOKEN_GROUPS LocalGroups, // poi(@rsp+0x38) [in] PTOKEN_SOURCE SourceContext, // poi(@rsp+0x40) [out] PVOID *ProfileBuffer, [out] PULONG ProfileBufferLength, [out] PLUID LogonId, [out] PHANDLE Token, [out] PQUOTA_LIMITS Quotas, [out] PNTSTATUS SubStatus ) -------------------------------------------------------------------------- 我以为AuthenticationInformation指向下述结构 > dt UxTheme!_MSV1_0_INTERACTIVE_LOGON +0x000 MessageType : _MSV1_0_LOGON_SUBMIT_TYPE +0x008 LogonDomainName : _UNICODE_STRING +0x018 UserName : _UNICODE_STRING +0x028 Password : _UNICODE_STRING bp sspicli!LsaLogonUser "db poi(@rsp+0x28) l dwo(@rsp+0x30)" user:scz pass:xxx 断在sspicli!LsaLogonUser时查看AuthenticationInformation 00000205`a41b4c40 01 80 00 00 06 00 00 00-ba 00 00 00 00 00 00 00 ................ 00000205`a41b4c50 40 42 0a 9c 05 02 00 00-02 00 00 00 ff 7f 00 00 @B.............. 00000205`a41b4c60 18 00 18 00 ff 7f 00 00-40 00 00 00 00 00 00 00 ........@....... 00000205`a41b4c70 06 00 06 00 00 00 00 00-58 00 00 00 00 00 00 00 ........X....... 00000205`a41b4c80 5c 00 5c 00 00 00 00 00-5e 00 00 00 00 00 00 00 \.\.....^....... 00000205`a41b4c90 00 00 00 00 00 00 00 00-44 00 45 00 53 00 4b 00 ........D.E.S.K. 00000205`a41b4ca0 54 00 4f 00 50 00 2d 00-54 00 45 00 53 00 54 00 T.O.P.-.T.E.S.T. 00000205`a41b4cb0 73 00 63 00 7a 00 40 00-40 00 44 00 07 00 08 00 s.c.z.@.@.D..... 00000205`a41b4cc0 0c 00 0a 00 0d 00 59 00-41 00 41 00 41 00 41 00 ......Y.A.A.A.A. 00000205`a41b4cd0 41 00 6e 00 50 00 41 00-41 00 41 00 41 00 41 00 A.n.P.A.A.A.A.A. 00000205`a41b4ce0 41 00 41 00 41 00 41 00-53 00 48 00 58 00 7a 00 A.A.A.A.S.H.X.z. 00000205`a41b4cf0 64 00 63 00 5a 00 65 00-5a 00 51 00 71 00 47 00 d.c.Z.e.Z.Q.q.G. 00000205`a41b4d00 6f 00 76 00 37 00 47 00-57 00 61 00 54 00 6d 00 o.v.7.G.W.a.T.m. 00000205`a41b4d10 5a 00 Z. 可能与winlogon通过RPC到UserManager服务有关,AuthenticationInformation不再 直接对应MSV1_0_INTERACTIVE_LOGON结构。后来跟踪到usermgr!UserMgrCli::WrapWlAuthInfo 中,AuthenticationInformation指向"struct UserMgrCli::_WL_AUTH_INFO"。手工 解码如下 -------------------------------------------------------------------------- 01 80 00 00 // +0x0 在usermgr!UserMgrCli::WrapWlAuthInfo+0x53处被设置 06 00 00 00 // +0x4 /* * buf/len均来自UserMgrCli::UMLogonUser的形参a7,其类型是"struct _AUTH_INFO_RPC *" */ ba 00 00 00 // +0x8 len=dwo()=0xba 00 00 00 00 // +0xc 40 42 0a 9c 05 02 00 00 // +0x10 buf=0x2059c0a4240 // db 0x2059c0a4240 l 0xba // 跟后面的数据区相同 -------------------------------------------------------------------------- 02 00 00 00 // +0x0 MessageType=dwo()=0x2 ff 7f 00 00 // +0x4 填充对齐 -------------------------------------------------------------------------- 18 00 // +0x8 LogonDomainNameLen=wo()=0x18 18 00 // +0xa ff 7f 00 00 // +0xc 填充对齐 40 00 00 00 00 00 00 00 // +0x10 LogonDomainNameOff=0x40 -------------------------------------------------------------------------- 06 00 // +0x18 UserNameLen=wo()=0x6 06 00 // +0x1a 00 00 00 00 // +0x1c 填充对齐 58 00 00 00 00 00 00 00 // +0x20 UserNameOff=0x58 -------------------------------------------------------------------------- 5c 00 // +0x28 PasswordLen=0x5c 5c 00 // +0x2a 00 00 00 00 // +0x2c 填充对齐 5e 00 00 00 00 00 00 00 // +0x30 PasswordOff=0x5e -------------------------------------------------------------------------- 00 00 00 00 00 00 00 00 // +0x38 填充对齐 -------------------------------------------------------------------------- 44 00 45 00 53 00 4b 00 // +0x40 LogonDomainName="DESKTOP-TEST" 54 00 4f 00 50 00 2d 00 54 00 45 00 53 00 54 00 -------------------------------------------------------------------------- 73 00 63 00 7a 00 // +0x58 UserName="scz" -------------------------------------------------------------------------- 40 00 40 00 44 00 07 00 // +0x5e Password=(混淆过) 08 00 0c 00 0a 00 0d 00 59 00 41 00 41 00 41 00 // +0x6e "YAAAAAnPAAAAAAAAASHXzdcZeZQqGov7GWaTmZ" 41 00 41 00 6e 00 50 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 53 00 48 00 58 00 7a 00 64 00 63 00 5a 00 65 00 5a 00 51 00 71 00 47 00 6f 00 76 00 37 00 47 00 57 00 61 00 54 00 6d 00 5a 00 -------------------------------------------------------------------------- ☆ usermgrcli!UMgrLogonUser 看大框架图,winlogon会调用usermgrcli!UMgrLogonUser -------------------------------------------------------------------------- usermgrcli!UMgrLogonUser ( a1, // rcx a2, // rdx AuthenticationPackage, // r8 /* * db @r9 l dwo(@rsp+0x28) */ AuthenticationInformation, // r9 AuthenticationInformationLength, // dwo(@rsp+0x28) ... ) -------------------------------------------------------------------------- bp usermgrcli!UMgrLogonUser "db @r9 l dwo(@rsp+0x28)" # Call Site 00 usermgrcli!UMgrLogonUser 01 winlogon!AuthenticateUser+0x556 02 winlogon!WLGeneric_Authenticating_Execute+0x45e 03 winlogon!StateMachineWorkerCallback+0xc8 04 ntdll!TppWorkpExecuteCallback+0x35e 05 ntdll!TppWorkerThread+0x474 06 KERNEL32!BaseThreadInitThunk+0x14 07 ntdll!RtlUserThreadStart+0x21 000001e3`b4d70000 02 00 00 00 ff 7f 00 00-18 00 18 00 ff 7f 00 00 ................ 000001e3`b4d70010 40 00 00 00 00 00 00 00-06 00 06 00 00 00 00 00 @............... 000001e3`b4d70020 58 00 00 00 00 00 00 00-5c 00 5c 00 00 00 00 00 X.......\.\..... 000001e3`b4d70030 5e 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ^............... 000001e3`b4d70040 44 00 45 00 53 00 4b 00-54 00 4f 00 50 00 2d 00 D.E.S.K.T.O.P.-. 000001e3`b4d70050 54 00 45 00 53 00 54 00-73 00 63 00 7a 00 40 00 T.E.S.T.s.c.z.@. 000001e3`b4d70060 40 00 44 00 07 00 08 00-0c 00 0a 00 0d 00 59 00 @.D...........Y. 000001e3`b4d70070 41 00 41 00 41 00 41 00-41 00 6e 00 50 00 41 00 A.A.A.A.A.n.P.A. 000001e3`b4d70080 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 000001e3`b4d70090 53 00 48 00 58 00 7a 00-64 00 63 00 5a 00 65 00 S.H.X.z.d.c.Z.e. 000001e3`b4d700a0 5a 00 51 00 71 00 47 00-6f 00 76 00 37 00 47 00 Z.Q.q.G.o.v.7.G. 000001e3`b4d700b0 57 00 61 00 54 00 6d 00-5a 00 W.a.T.m.Z. 对混淆过的Password设置数据断点,逐步溯源至 # Call Site 00 RPCRT4!Ndr64ComplexStructUnmarshall+0x503 01 RPCRT4!Ndr64pClientUnMarshal+0x28f 02 RPCRT4!NdrpClientCall3+0x1010 03 RPCRT4!NdrClientCall3+0xf2 04 winlogon!WluiRequestCredentials+0xa4 05 winlogon!RequestCredentials+0x9f 06 winlogon!WLGeneric_Request_Logon_Credz_Execute+0x190 07 winlogon!StateMachineWorkerCallback+0x7f 08 ntdll!TppWorkpExecuteCallback+0x35e 09 ntdll!TppWorkerThread+0x474 0a KERNEL32!BaseThreadInitThunk+0x14 0b ntdll!RtlUserThreadStart+0x21 长度为0xba的数据来自winlogon!WluiRequestCredentials,调用栈表明正在返回RPC 结果,其服务端是logoncontroller!WluirRequestCredentials。这意味着对明文口 令"xxx"的混淆工作很可能发生在LogonUI.exe进程空间。 ☆ logoncontroller!WluirRequestCredentials -------------------------------------------------------------------------- winlogon!WluiRequestCredentials ( a1, a2, a3, /* * out型形参,返回时会设置poi(@r9+0x10)、dwo(@r9+8) * * 对应logoncontroller!WluirRequestCredentials */ sth, // r9 a5 ) -------------------------------------------------------------------------- logoncontroller!WluirRequestCredentials ( a1, a2, a3, a4, /* * 对应winlogon!WluiRequestCredentials的sth */ sth, // poi(@rsp+0x28) a6 ) -------------------------------------------------------------------------- sth就是长度为0xba的数据。对sth中混淆过的Password设置数据断点,异常艰难地逐 步溯源,中途几欲放弃,这个时候特别怀念VMware 7之前的Record/Replay功能。后 来溯源至credprovs!KerbInteractiveUnlockLogonPack。 ☆ credprovs!KerbInteractiveUnlockLogonPack -------------------------------------------------------------------------- credprovs!KerbInteractiveUnlockLogonPack ( /* * dt -r uxtheme!_KERB_INTERACTIVE_UNLOCK_LOGON @rcx */ a1, ... ) -------------------------------------------------------------------------- 该函数第一形参指向下述结构 > dt -r uxtheme!_KERB_INTERACTIVE_UNLOCK_LOGON @rcx +0x000 Logon : _KERB_INTERACTIVE_LOGON +0x000 MessageType : 2 ( KerbInteractiveLogon ) +0x008 LogonDomainName : _UNICODE_STRING "DESKTOP-TEST" +0x000 Length : 0x18 +0x002 MaximumLength : 0x1a +0x008 Buffer : 0x000001cf`0b0d5b60 "DESKTOP-TEST" +0x018 UserName : _UNICODE_STRING "scz" +0x000 Length : 6 +0x002 MaximumLength : 8 +0x008 Buffer : 0x000001cf`0679b020 "scz" +0x028 Password : _UNICODE_STRING "@@D???" +0x000 Length : 0x5c +0x002 MaximumLength : 0x5e +0x008 Buffer : 0x000001cf`0a86f980 "@@D???" +0x038 LogonId : _LUID +0x000 LowPart : 0 +0x004 HighPart : 0n0 如下断点可以查看混淆过的Password bp credprovs!KerbInteractiveUnlockLogonPack "r $t0=@rcx+0x28;db poi(@$t0+8) l wo(@$t0)" ☆ sechost!CredProtectW 从credprovs!KerbInteractiveUnlockLogonPack开始,对混淆过的Password用数据断 点溯源,这个时候就比较容易了。后来溯源至 # Call Site 00 ntdll!memcpy+0xb9 01 sechost!CredProtectW+0xa6 02 credprovs!CPasswordCredential::_EncryptAndMarshal+0xfe 03 credprovs!CPasswordCredential::_KerbInteractiveUnlockLogonFill+0xf7 04 credprovs!CPasswordCredential::_GetSerializationUnlockLogon+0x7f 05 credprovs!CPasswordCredential::GetSerialization+0x1f4 06 credprovhost!CGetSerializationJob::Do+0x224 07 credprovhost!CJobQueue::DoJobInternal+0xec 08 credprovhost!CCredentialProviderThread::_vThreadProc+0x355 09 credprovhost!CCredentialProviderThread::_sThreadProc+0xc7 0a KERNEL32!BaseThreadInitThunk+0x14 0b ntdll!RtlUserThreadStart+0x21 sechost!CredProtectW是文档化的导出函数,参[3],大致流程如下 -------------------------------------------------------------------------- BOOL CredProtectW ( [in] BOOL fAsSelf, /* * 明文口令 */ [in] LPWSTR pszCredentials, // rdx /* * 明文口令长度,是字符长度,不是字节长度 */ [in] DWORD cchCredentials, // r8 /* * 返回混淆后的Password */ [out] LPWSTR pszProtectedCredentials, // r9 /* * 返回混淆后的Password长度,是字符长度,不是字节长度 */ [in, out] DWORD *pcchMaxChars, // poi(@rsp+0x28) [out] CRED_PROTECTION_TYPE *ProtectionType ) -------------------------------------------------------------------------- sechost!CredProtectW sechost!CredpEncryptAndMarshalBinaryBlobEx // db @r8 l @r9 sechost!CredpEncodeSecretEx // 对明文口令做混淆,混淆结果是16字节字节流 // db @rdx l @r8 CRYPTBASE!SystemFunction040 // db @rcx l @rdx // 该函数就地混淆(加密) ntdll!NtDeviceIoControlFile // db poi(@rsp+0x38) l dwo(@rsp+0x40) // 访问"\Device\KsecDD" // ? qwo(CRYPTBASE!g_hKsecDD) sechost!CredMarshalCredentialW // 对混淆结果序列化 -------------------------------------------------------------------------- 在LogonUI进程空间拦截sechost!CredProtectW,查看交互式登录的明文口令 bp sechost!CredProtectW "db @rdx l @r8*2" 有两次命中,第一次先计算结果长度,第二次才生成结果,非常熟悉的Win32 API套 路。此外,下列断点都可以查看交互式登录的明文口令 bp sechost!CredpEncryptAndMarshalBinaryBlobEx "db @r8 l @r9" bp sechost!CredpEncodeSecretEx "db @rdx l @r8" bp CRYPTBASE!SystemFunction040 "db @rcx l @rdx" bp ntdll!NtDeviceIoControlFile "db poi(@rsp+0x38) l dwo(@rsp+0x40)" ☆ sechost!CredUnprotectW sechost!CredUnprotectW是逆函数,也是文档化的导出函数,参[3] -------------------------------------------------------------------------- BOOL CredUnprotectW ( [in] BOOL fAsSelf, /* * 混淆过的Password */ [in] LPWSTR pszProtectedCredentials, // rdx /* * 混淆过的Password长度 */ [in] DWORD cchProtectedCredentials, // r8 /* * 返回明文口令 */ [out] LPWSTR pszCredentials, // r9 /* * 返回明文口令长度 */ [in, out] DWORD *pcchMaxChars // poi(@rsp+0x28) ) -------------------------------------------------------------------------- 调试lsass进程 bp sechost!CredUnprotectW "db @rdx l @r8*2" 00000251`8519b110 40 00 40 00 44 00 07 00-08 00 0c 00 0a 00 0d 00 @.@.D........... 00000251`8519b120 59 00 41 00 41 00 41 00-41 00 41 00 6e 00 50 00 Y.A.A.A.A.A.n.P. 00000251`8519b130 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 00000251`8519b140 41 00 53 00 48 00 58 00-7a 00 64 00 63 00 5a 00 A.S.H.X.z.d.c.Z. 00000251`8519b150 65 00 5a 00 51 00 71 00-47 00 6f 00 76 00 37 00 e.Z.Q.q.G.o.v.7. 00000251`8519b160 47 00 57 00 61 00 54 00-6d 00 5a 00 00 00 G.W.a.T.m.Z... # Call Site 00 sechost!CredUnprotectW 01 lsasrv!LsapDecodeSecret+0xe9 02 lsasrv!LsapConvertLogonAuthInfo+0x253 03 lsasrv!SspiExLogonUser+0x610 04 SspiSrv!SspirLogonUser+0x247 05 RPCRT4!Invoke+0x73 06 RPCRT4!Ndr64StubWorker+0xbb1 07 RPCRT4!NdrServerCallAll+0x3c 08 RPCRT4!DispatchToStubInCNoAvrf+0x24 09 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1bd 0a RPCRT4!RPC_INTERFACE::DispatchToStub+0xcb 0b RPCRT4!LRPC_SCALL::DispatchRequest+0x34c 0c RPCRT4!LRPC_SCALL::HandleRequest+0x2bc 0d RPCRT4!LRPC_ADDRESS::HandleRequest+0x36c 0e RPCRT4!LRPC_ADDRESS::ProcessIO+0x91b 0f RPCRT4!LrpcIoComplete+0xaa 10 ntdll!TppAlpcpExecuteCallback+0x25e 11 ntdll!TppWorkerThread+0x8d9 12 KERNEL32!BaseThreadInitThunk+0x14 13 ntdll!RtlUserThreadStart+0x21 看到前面介绍过的SspiSrv!SspirLogonUser,它会间接调用sechost!CredUnprotectW 获取明文口令。 sechost!CredUnprotectW大致流程如下 -------------------------------------------------------------------------- sechost!CredUnprotectW sechost!CredpUnmarshalandDecodeStringEx // db @r8 // du @r8 sechost!CredUnmarshalCredentialW // 反序列化 // db @rcx // du @rcx sechost!CredpUnmarshalandDecodeStringEx+0xbc // 查看明文口令 // p;db poi(@rbp+0x60) l dwo(@rbp+38) sechost!CredpDecodeSecretEx // 反混淆,r8此时等于0x10 // 查看密文口令 // db @rdx l @r8 sechost!CredpDecodeSecretEx+0x132 CRYPTBASE!SystemFunction041 // db @rcx l @rdx // 该函数就地反混淆(解密) CRYPTBASE!SystemFunction041+0x55 ntdll!NtDeviceIoControlFile // db poi(@rsp+0x38) l dwo(@rsp+0x40) // 访问"\Device\KsecDD" // ? qwo(CRYPTBASE!g_hKsecDD) -------------------------------------------------------------------------- 在lsass进程空间查看交互式登录的明文口令 bp sechost!CredpUnmarshalandDecodeStringEx+0xc1 "db poi(@rbp+0x60) l dwo(@rbp+0x38)" bp sechost!CredUnprotectW+0x6a "db poi(@rsp+0x48) l dwo(@rsp+0x40)" ☆ winlogon的状态与信号 因为[1]提到了winlogon的状态与信号,这里也说一下Win10的情况。Win10没有Win7 的StateMachineSetSignal,只有SignalManagerSetSignal winlogon!WlStateMachineSetSignal winlogon!SignalManagerSetSignal Win10的winlogon共有100个状态,41个信号 > dps winlogon!g_rpWLGeneric_States l 0n100 winlogon!g_xWLGeneric_Start_State winlogon!g_xWLGeneric_NotifyCreateSession_State winlogon!g_xWLGeneric_Welcome_State .... winlogon!g_xWLGeneric_PseudoLogging_Off3_Unlock_State winlogon!g_xWLGeneric_NotifyTerminateSession_State > dqs winlogon!g_rpWLGeneric_Signals l 0n41 winlogon!g_xAction_Succeeded_Signal winlogon!g_xAction_Failed_Signal winlogon!g_xLogoff_NtUserCompleted_Signal ... winlogon!g_xWLGeneric_ChangePassword_Signal winlogon!g_xWLGeneric_ChangeIsAlreadyDone_Signal ☆ 后记 本文只研究了[1]在Win10中的变化,没有进一步拓展。有兴趣继续深入研究这个方向 的朋友,可以参照框架图设断、动态调试。 ☆ 参考资源 [1] Windows7口令认证流程调试 - boywhp [2013-02-14] https://bbs.pediy.com/thread-162632-1.htm [2] LsaLogonUser function (ntsecapi.h) https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsalogonuser SECURITY_LOGON_TYPE enumeration (ntsecapi.h) https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/ne-ntsecapi-security_logon_type MSV1_0_INTERACTIVE_LOGON structure (ntsecapi.h) https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-msv1_0_interactive_logon [3] CredProtectW function (wincred.h) https://docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credprotectw CredUnprotectW function (wincred.h) https://docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credunprotectw CRED_PROTECTION_TYPE enumeration (wincred.h) https://docs.microsoft.com/en-us/windows/win32/api/wincred/ne-wincred-cred_protection_type