简介:本资源为Windows安全研究领域经典的WinlogonHack核心DLL源码包,面向逆向工程初学者、系统安全研究人员及底层编程学习者,聚焦Windows登录机制剖析与Gina层Hook技术实践。压缩包共11个文件,含2个核心CPP实现文件(Hookmsgina.cpp、StdAfx.cpp)、2个头文件(resource.h、StdAfx.h)支撑模块化结构,另有DSP/DSP工程配置、DEF导出定义、RC资源脚本及NCB/OPT等VC6开发环境辅助文件,完整保留原始编译调试上下文,包体仅11KB,轻量但信息密度高。已有141人下载学习,适合通过源码级分析理解msgina.dll劫持原理、登录凭据捕获逻辑及XP时代Gina接口扩展机制。读者可直接编译调试、对照博文逐函数跟踪Hook注入流程,并结合ReadMe.txt掌握内存替换时机与关键API拦截点,是深入Windows认证子系统与实战Hook技术的优质教学样本。
1. WinlogonHack 不是“黑盒后门”,而是 Windows 登录层 Hook 的教科书级拆解样本
去年在复现一个某高校实验室的模拟项目X时,我本想快速验证 GINA 替换机制的兼容性边界,结果卡在 winlogon.exe 加载 msgina.dll 的符号解析阶段整整三天——调试器进不去、断点全失效、日志无输出。直到翻出这份名为WinlogonHack核心dll源码的资源,才真正看清:它根本不是网上流传的“免杀后门模板”,而是一份完整暴露了 Windows XP/2003 登录子系统底层交互契约的逆向工程笔记。它用最朴素的 C++ + Win32 API 实现了对 msgina.dll 的 inline hook、函数指针劫持与结构体偏移重定位,所有关键逻辑都写在 .cpp 文件里,连WlxDisplaySASNotice这种冷门回调的参数校验都加了注释。如果你正面临登录界面定制、域控策略绕过分析、或想搞懂 Windows 身份认证链最前端的控制权争夺逻辑,这份源码就是你该亲手编译、单步跟进去的第一块“登陆跳板”。它不教你写木马,但教会你怎么让系统在用户敲下 Ctrl+Alt+Del 的瞬间,先听你的话。
2. 从加载时机到函数劫持:WinlogonHack 如何精准卡在登录流程咽喉处
2.1 Winlogon.exe 的加载生命周期:为什么必须在 Session 0 中注入?
Winlogon 是 Windows 登录会话的守门人,运行在 Session 0(系统会话),且以 LocalSystem 权限启动。它在系统初始化后期加载msgina.dll,并通过硬编码的导出函数名(如WlxInitialize、WlxDisplaySASNotice)调用其接口。WinlogonHack 的核心 DLL 并非通过 CreateRemoteThread 注入,而是利用注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GinaDLL键值强制替换 GINA 模块路径——这是 Windows 原生支持的合法扩展机制。但注意:该机制仅在 Classic 模式(非 Fast User Switching)下生效,且从 Windows Vista 起已被 Credential Providers 取代。因此,这份源码的适用场景明确限定在 XP/2003 测试环境或遗留系统加固分析中。源码中DllMain的DLL_PROCESS_ATTACH分支会立即调用SetThreadDesktop(NULL)确保线程绑定到 winlogon 的 WinSta0\Default 桌面,否则 GUI 函数(如MessageBox)将因桌面句柄无效而静默失败。
2.2 msgina.dll 导出函数劫持的三重锚点
WinlogonHack 并未使用 IAT/EAT Hook,而是采用更底层的 inline hook 技术,针对三个关键函数实施跳转覆盖:
WlxDisplaySASNotice:用户按下 Ctrl+Alt+Del 后首个被调用的函数,用于显示安全注意提示(SAS)。源码在此处插入自定义 UI,并通过GetAsyncKeyState(VK_LCONTROL)等组合键检测实现“隐藏触发入口”。WlxLoggedOutSAS:用户未登录状态下响应 SAS 的主逻辑。源码在此处 patch 了g_WlxFuncsTable函数指针表,将WlxDisplaySASNotice地址替换为自定义函数地址,完成控制流接管。WlxActivateUserShell:登录成功后启动用户 shell(如 explorer.exe)前的最后钩子。源码在此处注入进程监控逻辑,可拦截 shell 启动参数或延迟执行。
提示:所有 inline hook 均使用
VirtualProtectEx修改代码段内存属性为PAGE_EXECUTE_READWRITE,并严格遵循 x86 指令边界(如避免截断mov eax, [ebx+0x10]这类多字节指令)。源码中HookFunction函数内嵌了反调试检查(IsDebuggerPresent()返回真时跳过 hook),这是为避免在分析环境中被误判为恶意行为。
2.3 结构体偏移的硬编码陷阱:为什么WLX_DISPATCH_VERSION_1_0必须手动计算?
msgina.dll 的函数表WLX_DISPATCH_VERSION_1_0并非导出符号,而是由 winlogon 在运行时通过GetProcAddress获取其地址后,再按固定偏移读取函数指针。WinlogonHack 源码中g_WlxFuncsTable的初始化代码如下:
// 偏移计算依据:WLX_DISPATCH_VERSION_1_0 结构体定义(MSDN 文档 + IDA 反编译交叉验证) // 成员顺序:pWlxUseCtrlAltDel, pWlxSetContextPointer, ..., pWlxActivateUserShell // 每个指针占 4 字节(x86),pWlxDisplaySASNotice 位于第 3 个位置(索引 2) DWORD* pDispatchTable = (DWORD*)GetProcAddress(hMsgina, "WlxInitialize"); if (pDispatchTable) { g_pOriginalDisplaySAS = (PFN_WLXDISPLAYSASNOTICE)(pDispatchTable[2]); // 索引 2 = WlxDisplaySASNotice DWORD dwOldProtect; VirtualProtect((LPVOID)pDispatchTable, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); pDispatchTable[2] = (DWORD)MyWlxDisplaySASNotice; // 覆盖函数指针 VirtualProtect((LPVOID)pDispatchTable, 4, dwOldProtect, &dwOldProtect); }这段代码的关键在于:WlxInitialize的返回值即为WLX_DISPATCH_VERSION_1_0*指针,而该结构体在不同 Windows 版本中成员数量可能变化(如 1.0 vs 1.1),因此硬编码索引[2]仅对目标版本有效。源码注释明确标注了测试环境为 Windows XP SP3,若在 2003 上运行需重新确认结构体布局。
3. 编译与调试避坑:那些让你在 winlogon 里“失联”的致命细节
3.1 现象:DLL 加载后 winlogon.exe 立即崩溃(0xC0000005)
原因:源码中MyWlxDisplaySASNotice函数未正确处理hWlx参数传递。winlogon 调用此函数时传入的是其内部维护的上下文句柄,而原始 msgina.dll 会将其作为WLX_DISPATCH_VERSION_1_0*解析。若 hook 函数直接将该句柄转发给原函数,但原函数期望的是经过WlxInitialize初始化后的有效结构体,则会导致访问非法内存。
解决:在MyWlxDisplaySASNotice开头添加句柄有效性校验:
if (!hWlx || IsBadReadPtr(hWlx, sizeof(WLX_DISPATCH_VERSION_1_0))) { return FALSE; // 防御性退出,避免崩溃 } // 正确转发:确保 hWlx 是 WlxInitialize 返回的有效指针 return g_pOriginalDisplaySAS(hWlx, pWlxWindowManager, pWlxSasInfo);3.2 现象:自定义 UI 窗口无法显示,CreateWindowEx返回 NULL
原因:winlogon 运行在 Session 0,其桌面对象(WinSta0\Default)与用户会话桌面隔离。若未显式指定hWndParent为 winlogon 的主窗口句柄(可通过FindWindow("Shell_TrayWnd", NULL)获取失败,因其不存在于 Session 0),或未调用SetThreadDesktop(GetThreadDesktop(GetCurrentThreadId())),则窗口创建会因桌面句柄无效而失败。
解决:在MyWlxDisplaySASNotice中强制切换线程桌面:
HDESK hDesk = OpenDesktop("Default", 0, FALSE, DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS); if (hDesk) { SetThreadDesktop(hDesk); CloseDesktop(hDesk); } // 此后调用 CreateWindowEx 才能成功3.3 现象:Hook 成功但WlxActivateUserShell中无法获取用户 token
原因:WlxActivateUserShell被调用时,用户会话尚未完全初始化,OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)可能返回ERROR_NO_TOKEN。源码中直接调用DuplicateTokenEx会因缺少有效 token 而失败。
解决:改用WTSQueryUserToken获取当前登录会话 token:
DWORD sessionId; if (ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) { HANDLE hUserToken; if (WTSQueryUserToken(sessionId, &hUserToken)) { // 使用 hUserToken 创建进程 CreateProcessAsUser(hUserToken, ...); CloseHandle(hUserToken); } }3.4 现象:编译后 DLL 在目标机上加载失败(错误 0x7E)
原因:源码项目配置为 Unicode 字符集,但msgina.dll本身是 ANSI 编译。当DllMain中调用LoadLibrary("user32.dll")后,若后续GetProcAddress获取MessageBoxA失败,转而调用MessageBoxW,则因字符集不匹配导致函数解析失败。
解决:统一使用 ANSI 版本 API,在stdafx.h中定义:
#define UNICODE #define _UNICODE // 改为: #undef UNICODE #undef _UNICODE #pragma comment(lib, "user32.lib") // 所有 MessageBox 调用显式使用 MessageBoxA4. 安全分析视角:如何用这份源码反向验证 GINA 类攻击的检测盲区
4.1 内存特征提取:从 inline hook 到 YARA 规则生成
WinlogonHack 的 inline hook 在WlxDisplaySASNotice函数头部写入JMP指令(0xE9+ 4 字节相对偏移),这是典型的代码段 patch 行为。我们可基于此生成 YARA 规则检测异常 GINA 模块:
rule GINA_inline_hook_jmp { meta: description = "Detects JMP instruction at start of WlxDisplaySASNotice-like function" author = "analyst" strings: $jmp_prefix = { E9 [4] } // JMP rel32 $func_name = "WlxDisplaySASNotice" wide ascii condition: $jmp_prefix at 0 and $func_name }但需注意:该规则易误报,因合法 GINA 也可能包含 JMP。更可靠的方式是结合内存扫描——在 winlogon 进程中搜索msgina.dll模块基址 +WlxDisplaySASNoticeRVA 处是否被修改。源码中HookFunction的 patch 地址可作为基准偏移(如0x1234),实际检测时用ReadProcessMemory读取该地址 5 字节,比对是否为0xE9开头。
4.2 进程行为建模:winlogon 异常子进程的判定逻辑
WinlogonHack 在WlxActivateUserShell中可能启动cmd.exe或notepad.exe等进程。正常情况下,winlogon 启动的子进程应为explorer.exe或logonui.exe。我们可构建如下判定树:
- 若 winlogon 创建的子进程
ImageFileName不在白名单(explorer.exe,logonui.exe,lsass.exe)中; - 且该进程的父进程
CreateTime与 winlogon 的CreateTime时间差 < 5 秒; - 且该进程的
TokenElevationType为TokenElevationTypeFull(表明拥有高权限);
则标记为高风险。源码中CreateProcessAsUser的调用位置(MyWlxActivateUserShell函数末尾)即为此类行为的典型植入点。
4.3 日志取证线索:注册表与文件系统联动分析
WinlogonHack 的持久化依赖注册表键GinaDLL,但攻击者常配合文件替换(如将msgina.dll备份为msgina.bak,再放自己的 DLL)。因此,EDR 应关联以下事件:
- 注册表
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GinaDLL值被修改; - 同一时间点,
%SystemRoot%\System32\msgina.dll文件大小/签名/哈希发生变化; - winlogon.exe 进程加载了非微软签名的 DLL(通过
GetModuleInformation获取模块签名状态)。
源码中DllMain的DLL_PROCESS_ATTACH分支会记录日志到%SystemRoot%\Temp\winlogonhack.log,这恰好成为溯源关键——若发现该日志文件被创建,即可反向定位注册表修改时间。
5. 从源码到实战:一份可落地的 GINA Hook 安全加固检查清单
5.1 环境准备:三步建立可复现的测试沙箱
- 系统选择:使用 Windows XP SP3 虚拟机(VMware Workstation 16 + VMware Tools),禁用自动更新与防火墙,确保
msgina.dll未被 KB 补丁修改(验证方法:sigcheck -i %SystemRoot%\System32\msgina.dll,应显示Verified: Signed且Publisher: Microsoft Corporation)。 - 调试配置:安装 WinDbg 10,设置符号路径为
SRV*c:\symbols*https://msdl.microsoft.com/download/symbols,在虚拟机中启用内核调试(bcdedit /debug on+bcdedit /dbgsettings serial debugport:1 baudrate:115200),主机用串口连接调试。 - 源码编译:用 Visual Studio 2010(兼容 XP 运行时)打开项目,配置为
Release|Win32,在Project Properties > Configuration Properties > General中设置Platform Toolset为v100,Character Set为Not Set(ANSI),输出目录设为%SystemRoot%\System32\winlogonhack.dll。
5.2 动态验证:五步确认 Hook 是否生效
| 步骤 | 操作 | 预期结果 | 关键命令/工具 |
|---|---|---|---|
| 1 | 修改注册表GinaDLL值为C:\Windows\System32\winlogonhack.dll | winlogon 重启后加载新 DLL | reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v GinaDLL /t REG_SZ /d "C:\Windows\System32\winlogonhack.dll" /f |
| 2 | 触发 Ctrl+Alt+Del,观察是否弹出自定义 SAS 窗口 | 显示 “WinlogonHack Active” 标题栏 | 目视确认 |
| 3 | 在 WinDbg 中附加 winlogon.exe,执行lm m msgina* | 列出winlogonhack.dll模块,而非msgina.dll | lm m winlogonhack* |
| 4 | 在WlxDisplaySASNotice入口下断点:bp winlogonhack!MyWlxDisplaySASNotice | 断点命中,r查看寄存器eax应为有效上下文地址 | bp+g |
| 5 | 检查内存 patch:db msgina!WlxDisplaySASNotice L5 | 首字节为E9(JMP),非原始55(PUSH EBP) | db命令 |
5.3 加固建议:三道防线阻断 GINA 类攻击
- 注册表防护:通过组策略
Computer Configuration > Windows Settings > Security Settings > Registry,将HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon键设置为Administrators: Full Control; SYSTEM: Full Control; Everyone: Read,禁止普通用户修改GinaDLL。 - 文件完整性监控:使用 Sysmon v13.9+,配置
Event ID 11(FileCreate)监控%SystemRoot%\System32\msgina.dll及其备份文件(*.bak,*.old),并启用Hash字段记录文件哈希。 - 进程行为审计:部署 ETW(Event Tracing for Windows)跟踪
winlogon.exe的ProcessCreate事件,过滤ParentProcessId == winlogon PID且ImageName NOT IN ("explorer.exe", "logonui.exe")的进程,实时告警。
从那以后我每次做登录层安全评估,都会先用这份源码编译一个最小化 DLL,在测试机上跑通全流程,再对照它的内存 patch 方式、注册表操作、进程创建逻辑,去反推真实环境中可能存在的同类攻击痕迹。它逼着我把 Windows 登录链的每个环节都亲手拧开看过——不是为了复刻,而是为了认出那些藏在合法接口背后的异样脉搏。希望帮到你。
本文还有配套的精品资源,点击获取