Mimikatz:msv功能模块浅析
字数 2443 2025-08-06 18:07:44
Mimikatz MSV功能模块深度分析
0x01 环境准备与调试
编译环境配置
- 开发环境: VS2022社区版
- 常见编译问题及解决方案:
- 类型强制转换错误: 从"PVOID"到"DWORD"的指针截断
- 解决方案: 在报错处修改为指针类型强制转换成DWORD
- PRINTER_NOTIFY_CATEGORY_ALL宏重定义
- 解决方案: 将重复定义变量修改为原始定义的值
- 缺少Debug配置
- 解决方案: 手动添加Debug配置
- 类型强制转换错误: 从"PVOID"到"DWORD"的指针截断
远程调试配置
- 以管理员身份运行msvsmon.exe
- 选择"无身份验证"模式最合适
0x02 Mimikatz命令执行机制
程序入口
- 入口函数:
wmain()(位于同名c文件中) - 命令处理循环:
for (i = MIMIKATZ_AUTO_COMMAND_START; (i < argc) && (status != STATUS_PROCESS_IS_TERMINATING) && (status != STATUS_THREAD_IS_TERMINATING); i++) { kprintf(L"\n" MIMIKATZ L"(" MIMIKATZ_AUTO_COMMAND_STRING L") # %s\n", argv[i]); status = mimikatz_dispatchCommand(argv[i]); }
命令分发机制
- 命令传入
mimikatz_dispatchCommand()函数 - 根据命令前缀决定执行方式:
!+: 使用mimidrv.sys驱动执行- 默认: 通过
mimikatz_doLocal()进入常用命令场景
模块与命令匹配
mimikatz_modules数组包含所有可用模块:const KUHL_M * mimikatz_modules[] = { &kuhl_m_standard, &kuhl_m_crypto, &kuhl_m_sekurlsa, &kuhl_m_kerberos, &kuhl_m_ngc, &kuhl_m_privilege, &kuhl_m_process, &kuhl_m_service, &kuhl_m_lsadump, &kuhl_m_ts, &kuhl_m_event, &kuhl_m_misc, &kuhl_m_token, &kuhl_m_vault, &kuhl_m_minesweeper, #if defined(NET_MODULE) &kuhl_m_net, #endif &kuhl_m_dpapi,&kuhl_m_busylight,&kuhl_m_sysenv, &kuhl_m_sid, &kuhl_m_iis, &kuhl_m_rpc, &kuhl_m_sr98, &kuhl_m_rdm, &kuhl_m_acr, };- 匹配流程:
- 将module参数与各模块的
shortName比较 - 将command参数与模块中的命令比较
- 执行匹配的命令函数
- 将module参数与各模块的
0x03 MSV模块深入分析
MSV模块概述
- 功能: 枚举LM & NTLM凭证
- 核心原理: 通过分析lsasrv.dll中的
LogonSessionListLock()函数定位LogonSessionList和LogonSessionListCount全局变量
关键数据结构
typedef struct _KUHL_M_SEKURLSA_PACKAGE {
const wchar_t * Name;
PKUHL_M_SEKURLSA_ENUM_LOGONDATA CredsForLUIDFunc;
BOOL isValid;
const wchar_t * ModuleName;
KUHL_M_SEKURLSA_LIB Module;
} KUHL_M_SEKURLSA_PACKAGE, *PKUHL_M_SEKURLSA_PACKAGE;
MSV模块初始化
KUHL_M_SEKURLSA_PACKAGE kuhl_m_sekurlsa_msv_package = {
L"msv",
kuhl_m_sekurlsa_enum_logon_callback_msv,
TRUE,
L"lsasrv.dll",
{{{NULL, NULL}, 0, 0, NULL}, FALSE, FALSE}
};
执行流程
- 入口函数:
kuhl_m_sekurlsa_msv() - 调用链:
kuhl_m_sekurlsa_getLogonData()kuhl_m_sekurlsa_enum()kuhl_m_sekurlsa_acquireLSA()
关键步骤分析
1. 获取LSASS进程信息
- 通过
kull_m_process_getProcessIdForName()获取lsass.exe的PID - 调用链:
kull_m_process_getProcessInformation()kull_m_process_NtQuerySystemInformation()NtQuerySystemInformation()(未公开系统API)
2. 打开LSASS进程
- 使用
OpenProcess(processRights, FALSE, pid)获取进程句柄 - 初始化内存结构:
KUHL_M_SEKURLSA_CONTEXT cLsass = {NULL, {0, 0, 0}};
3. 获取系统版本信息
- 存储Windows版本信息到
cLsass.osContext - 示例值(Windows 1803):
MIMIKATZ_NT_BUILD_NUMBER=17134MIMIKATZ_NT_MINOR_VERSION=0MIMIKATZ_NT_MAJOR_VERSION=10
4. 获取模块信息
- 通过
kull_m_process_peb()获取PEB(进程环境块) - 遍历
PEB.Ldr.InMemoryOrderModuleList查找lsasrv.dll模块 - 回调函数
kuhl_m_sekurlsa_findlibs()存储模块信息
5. 定位关键变量
- 使用
kuhl_m_sekurlsa_utils_search_generic搜索特征码 - 通过偏移量定位:
LogonSessionListLogonSessionListCount
- 示例偏移(Windows 1803):
LogonSessionListCount相对LogonSessionListLock: -4LogonSessionList相对LogonSessionListLock: 23
6. 获取加密密钥
- 调用
kuhl_m_sekurlsa_nt6_acquireKeys() - 使用
kull_m_patch_getGenericFromBuild()根据系统版本获取特征码 - 搜索并获取初始化向量和密钥
7. 枚举会话信息
- 根据系统版本确定结构体偏移量
- 从
LogonSessionList获取:- UserName
- LogonDomain
- LogonServer
- 回调函数
kuhl_m_sekurlsa_enum_callback_logondata打印信息
8. 获取凭证信息
- 调用链:
kuhl_m_sekurlsa_enum_logon_callback_msv()kuhl_m_sekurlsa_msv_enum_cred()kuhl_m_sekurlsa_msv_enum_cred_callback_std()kuhl_m_sekurlsa_genericCredsOutput()
9. 提取NTLM哈希
- 结合特征码和偏移量定位内存中的NTLM哈希
- 使用
kull_m_string_wprintf_hex()将哈希值转换为字符串输出
0x04 技术总结
核心原理
Mimikatz通过以下步骤获取NTLM凭证:
- 定位lsass.exe进程中的lsasrv.dll模块
- 通过特征码搜索定位
LogonSessionList和LogonSessionListCount全局变量 - 解析
LogonSessionList结构体 - 根据系统特定的偏移量读取用户凭证信息
防御规避
- 直接从lsass.exe进程内存读取敏感数据
- 会被大多数杀软/EDR检测为可疑行为
- 需要收集和维护不同Windows版本的特征码和偏移量
关键点
- 版本适配: 需要为不同Windows版本维护特征码和偏移量
- 内存分析: 通过分析lsass.exe进程内存获取凭证
- 加密处理: 需要获取系统密钥来解密受保护的内存区域
- 结构解析: 深入理解Windows登录会话数据结构