该方法主要是对PEB结构体中的LDR链表进行操作,分别把三种排序的LDR链进行断链操作以达到模块隐藏的效果,TEB和PEB的结构随着系统的不同会有所变化,本文以Windows 10中的32位程序为例子。该例子仅为简单的断链,通过测试目前会影响CreateToolhelp32Snapshot + Module32First获取模块信息。

image-20210608195249580

0x00 PEB&TEB介绍

PEB和TEB是Windows进程管理的两个重要数据结构,在PEB和TEB结构中存在许多有用的成员,本文主要着重于PEB里的LDR双向链表,链表的内容为各模块的相关信息。

FS:[0x00] == TEB
FS:[0x30] == TEB.ProcessEnvironmentBlock == address of PEB
// 使用内联汇编的方式获取PEB位于内存的虚拟地址。
mov eax, dword ptr fs:[30h]

TEB32

image-20210608194106387

PEB32

image-20210608194233783

PEB_LDR_DATA

image-20210608194452597

0x01 隐藏思路

在LDR表中有三种链表排序顺序,分别是模块加载顺序、模块内存顺序、模块初始化顺序。image-20210608201141096

这三个链表结构是PEB_LDR_DATA和LDR_DATA_TABLE_ENTRY共用的

img

LDR_DATA_TABLE_ENTRY

image-20210608200825359

所以需要分别遍历这三种排序方式,将目标模块的LDR_DATA_TABLE_ENTRY从链上断开。

image-20210608201401472

0x02 代码实现

模块遍历代码

#include <stdio.h>
#include <windows.h>
#include <Tlhelp32.h>
int main(int argc, char* argv[])
{
    HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hProcessSnapshot)
    {
        return 0;
    }
    DWORD dwPid = 0;
    PROCESSENTRY32 pi;
    pi.dwSize = sizeof(PROCESSENTRY32);
    BOOL Ret = Process32First(hProcessSnapshot, &pi);
    while (Ret)
    {
        if (strcmp("LDR_Break_Link.exe", pi.szExeFile) == 0)
        {
            dwPid = pi.th32ProcessID;
            break;
        }
        Ret = Process32Next(hProcessSnapshot, &pi);
    }
    CloseHandle(hProcessSnapshot);


    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);//上面获取了进程PID,下面使用即可.
    if (INVALID_HANDLE_VALUE == hSnapshot)
    {
        return 0;
    }
    MODULEENTRY32 mi;
    mi.dwSize = sizeof(MODULEENTRY32); 
    BOOL  bRet = Module32First(hSnapshot, &mi);
    while (bRet)
    {
        printf("%s\n", mi.szModule);

        bRet = Module32Next(hSnapshot, &mi);
    }
    CloseHandle(hSnapshot);
    return 0;
}

断链代码

// LDR断链 隐藏模块
// Coding By Jev0n
#include <stdio.h>
#include <windows.h>


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

typedef struct _PEB_LDR_DATA {
    ULONG                   Length;
    BOOLEAN                 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;
    LPVOID              BaseAddress;
    LPVOID              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;


int main(int argc, const char* argv[])
{

    if (argc != 2)
    {
        printf("[+] Using Example: LDR_Break_Link.exe ntdll.dll \n");
        return -1;
    }


    // PEB LDR
    DWORD pPEB = 0;
    PPEB_LDR_DATA pLDR = NULL;
    _asm 
    {
        mov eax ,fs:[0x30]
        mov pPEB ,eax
        mov eax ,[eax + 0x0C]
        mov pLDR ,eax
    }

    // Break_Link
    DWORD status = 0;
    char* pDllName = argv[1];
    HMODULE hMod = GetModuleHandle(pDllName);
    PLIST_ENTRY pMiddle, pNext;
    PLDR_DATA_TABLE_ENTRY pLDTE;
    pMiddle = &(pLDR->InLoadOrderModuleList);
    pNext = pMiddle->Flink;
    

    do
    {
        pLDTE = CONTAINING_RECORD(pNext, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
        

        if (hMod == pLDTE->BaseAddress)
        {
            status = 1;

            // InLoadOrderModuleList
            pLDTE->InLoadOrderModuleList.Blink->Flink = pLDTE->InLoadOrderModuleList.Flink;
            pLDTE->InLoadOrderModuleList.Flink->Blink = pLDTE->InLoadOrderModuleList.Blink;

            // InInitializationOrderModuleList
            pLDTE->InInitializationOrderModuleList.Blink->Flink = pLDTE->InInitializationOrderModuleList.Flink;
            pLDTE->InInitializationOrderModuleList.Flink->Blink = pLDTE->InInitializationOrderModuleList.Blink;

            // InMemoryOrderModuleList
            pLDTE->InMemoryOrderModuleList.Blink->Flink = pLDTE->InMemoryOrderModuleList.Flink;
            pLDTE->InMemoryOrderModuleList.Flink->Blink = pLDTE->InMemoryOrderModuleList.Blink;

            break;
        }
        pNext = pNext->Flink;
    } while (pMiddle != pNext);

    if (status)
    {
        printf("Success !\n");
    }
    else 
    {
        printf("Error !\n");
    }

    getchar();

    return 0;
}

0x03 参考链接

VERGILIUS

基于断链的DLL隐藏

关于LDR的疑问与探索

Windows编程之模块遍历