Skip to content

标题: 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

00000205a41b4c40 01 80 00 00 06 00 00 00-ba 00 00 00 00 00 00 00 ................ 00000205a41b4c50 40 42 0a 9c 05 02 00 00-02 00 00 00 ff 7f 00 00 @B.............. 00000205a41b4c60 18 00 18 00 ff 7f 00 00-40 00 00 00 00 00 00 00 ........@....... 00000205a41b4c70 06 00 06 00 00 00 00 00-58 00 00 00 00 00 00 00 ........X....... 00000205a41b4c80 5c 00 5c 00 00 00 00 00-5e 00 00 00 00 00 00 00 \.\.....^....... 00000205a41b4c90 00 00 00 00 00 00 00 00-44 00 45 00 53 00 4b 00 ........D.E.S.K. 00000205a41b4ca0 54 00 4f 00 50 00 2d 00-54 00 45 00 53 00 54 00 T.O.P.-.T.E.S.T. 00000205a41b4cb0 73 00 63 00 7a 00 40 00-40 00 44 00 07 00 08 00 s.c.z.@[email protected]..... 00000205a41b4cc0 0c 00 0a 00 0d 00 59 00-41 00 41 00 41 00 41 00 ......Y.A.A.A.A. 00000205a41b4cd0 41 00 6e 00 50 00 41 00-41 00 41 00 41 00 41 00 A.n.P.A.A.A.A.A. 00000205a41b4ce0 41 00 41 00 41 00 41 00-53 00 48 00 58 00 7a 00 A.A.A.A.S.H.X.z. 00000205a41b4cf0 64 00 63 00 5a 00 65 00-5a 00 51 00 71 00 47 00 d.c.Z.e.Z.Q.q.G. 00000205a41b4d00 6f 00 76 00 37 00 47 00-57 00 61 00 54 00 6d 00 o.v.7.G.W.a.T.m. 00000205a41b4d10 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

000001e3b4d70000 02 00 00 00 ff 7f 00 00-18 00 18 00 ff 7f 00 00 ................ 000001e3b4d70010 40 00 00 00 00 00 00 00-06 00 06 00 00 00 00 00 @............... 000001e3b4d70020 58 00 00 00 00 00 00 00-5c 00 5c 00 00 00 00 00 X.......\.\..... 000001e3b4d70030 5e 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ^............... 000001e3b4d70040 44 00 45 00 53 00 4b 00-54 00 4f 00 50 00 2d 00 D.E.S.K.T.O.P.-. 000001e3b4d70050 54 00 45 00 53 00 54 00-73 00 63 00 7a 00 40 00 T.E.S.T.s.c.z.@. 000001e3b4d70060 40 00 44 00 07 00 08 00-0c 00 0a 00 0d 00 59 00 @.D...........Y. 000001e3b4d70070 41 00 41 00 41 00 41 00-41 00 6e 00 50 00 41 00 A.A.A.A.A.n.P.A. 000001e3b4d70080 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 000001e3b4d70090 53 00 48 00 58 00 7a 00-64 00 63 00 5a 00 65 00 S.H.X.z.d.c.Z.e. 000001e3b4d700a0 5a 00 51 00 71 00 47 00-6f 00 76 00 37 00 47 00 Z.Q.q.G.o.v.7.G. 000001e3b4d700b0 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 : 0x000001cf0b0d5b60 "DESKTOP-TEST" +0x018 UserName : _UNICODE_STRING "scz" +0x000 Length : 6 +0x002 MaximumLength : 8 +0x008 Buffer : 0x000001cf0679b020 "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"

000002518519b110 40 00 40 00 44 00 07 00-08 00 0c 00 0a 00 0d 00 @[email protected]........... 000002518519b120 59 00 41 00 41 00 41 00-41 00 41 00 6e 00 50 00 Y.A.A.A.A.A.n.P. 000002518519b130 41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00 A.A.A.A.A.A.A.A. 000002518519b140 41 00 53 00 48 00 58 00-7a 00 64 00 63 00 5a 00 A.S.H.X.z.d.c.Z. 000002518519b150 65 00 5a 00 51 00 71 00-47 00 6f 00 76 00 37 00 e.Z.Q.q.G.o.v.7. 000002518519b160 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