实现三环PEB模块断链隐藏
前言:作为三环PEB断链的学习笔记和实现
参考文章:http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_x86.html
什么是三环PEB模块断链
进程会在4GB的虚拟内存空间中加载导入表对应的所有的DLL,而一个进程创建对应的内存中的PEB进程环境块和TEB线程环境块在内核模式建立后映射到用户空间的。
因此,多个进程的PEB地址可能是同一个指向,该TEB结构体包含进程中运行线程的各种信息,进程中的每个线程都对应着一个TEB结构体。
知识点:不同OS中TEB结构体的形态略微有点不同
实现三环PEB模块断链隐藏思路
要实现模块隐藏,首先需要得到进程的TEB结构体,这个结构体在fs寄存器中保存着。而TEB结构体偏移0x30处可以得到PEB结构体。从PEB结构体0x0c的地方保存的就是PEB_LDR_DATA结构体的指针。这个结构体中保存了三个LIST_ENTRY的双向链表,从这三个链表中的任何一个都连着下一个LDR_DATA_TABLE_ENTRY结构中的LIST_ENTRY结构,这个结构保存了模块的信息,每个模块都有一个。具体的过程如下图
因为这三个链表中保存的都是相关的加载信息,也就是 PEB_LDR_DATA->A.DLL信息(_LDR_DATA_TABLE_ENTRY)->B.DLL信息(_LDR_DATA_TABLE_ENTRY)->C.DLL信息(_LDR_DATA_TABLE_ENTRY)
那么想要实现断链的话B.dll断链的话,其实只需要PEB_LDR_DATA->A.DLL信息(_LDR_DATA_TABLE_ENTRY)->C.DLL信息(_LDR_DATA_TABLE_ENTRY),就是要断链的节点的前节点的尾指针指向要断链节点的后节点的前指针即可。
windbg中进行观察该结构体
首先还是需要列出所有的进程!process 0 0
,获取PROCESS 86e51020,因为相关环境块在内核模式建立后映射到用户空间的,所以直接在内核中看即可,我是这么理解的,老师还没系统的讲到。
接着拿到想要的进程地址0x86e51020,再执行.process 0x86e51020
通过fs相关寄存器来进行打印
PEB_LDR_DATA* pPebLdr = NULL; LDR_MODULE* pLdrModule = NULL; int main(int argc, char* argv[]) { // KERNEL32.dll __asm { push eax; push ebx; push ecx; mov eax, fs:[0x30]; // eax = peb mov ecx, dword ptr ds:[eax+0xC]; // ecx = Ldr's ptr mov pPebLdr, ecx; // pPebLdr = ecx mov ebx, dword ptr ds:[ecx+0xC]; mov pLdrModule, ebx; pop ecx; pop ebx; pop eax; } printf("%x\n", pPebLdr); return 0; }
dt _PEB_LDR_DATA 0x241ea0
接着就可以用_LDR_DATA_TABLE_ENTRY来遍历该三个链表了,dt _LDR_DATA_TABLE_ENTRY 0x241ee0
实现三环PEB模块断链隐藏
由于PEB_LDR_DATA和LDR_MODULE结构体是未导出的,所以需要我们在程序中自己定义。
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 InInitializeationOrderModuleList; //代表按初始化顺序构成的模块链表 PVOID DllBase; //该模块的基地址 PVOID EntryPoint; //该模块的入口 ULONG SizeOfImage; //该模块的影像大小 UNICODE_STRING FullDllName; //模块的完整路径 UNICODE_STRING BaseDllName; //模块名 ULONG Flags; SHORT LoadCount; SHORT TlsIndex; HANDLE SectionHandle; ULONG CheckSum; ULONG TimeDataStamp; }LDR_MODULE, *PLDR_MODULE;
测试代码如下:
#include<stdio.h> #include<stdlib.h> #include<string.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 InInitializeationOrderModuleList; //代表按初始化顺序构成的模块链表 PVOID DllBase; //该模块的基地址 PVOID EntryPoint; //该模块的入口 ULONG SizeOfImage; //该模块的影像大小 UNICODE_STRING FullDllName; //模块的完整路径 UNICODE_STRING BaseDllName; //模块名 ULONG Flags; SHORT LoadCount; SHORT TlsIndex; HANDLE SectionHandle; ULONG CheckSum; ULONG TimeDataStamp; }LDR_MODULE, *PLDR_MODULE; PEB_LDR_DATA* g_pPebLdr = NULL; LDR_MODULE* g_pLdrModule = NULL; LIST_ENTRY* g_pInLoadOrderModule; LIST_ENTRY* g_pInMemoryOrderModule; LIST_ENTRY* g_pInInitializeationOrderModule; void ring3BrokenChains(LIST_ENTRY* pEntry, DWORD dwModule) { /* typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY; */ LIST_ENTRY* pHeadEntry = pEntry; do{ pEntry = pEntry->Blink; g_pLdrModule = (LDR_MODULE*)pEntry; printf("current pEntry -> %x, current module: %x\n", pEntry, (DWORD)g_pLdrModule->DllBase); if(dwModule == (DWORD)g_pLdrModule->DllBase) { printf("\tfind module -> %x\n", dwModule); g_pLdrModule->InLoadOrderModuleList.Blink->Flink = g_pLdrModule->InLoadOrderModuleList.Flink; g_pLdrModule->InLoadOrderModuleList.Flink->Blink = g_pLdrModule->InLoadOrderModuleList.Blink; g_pLdrModule->InInitializeationOrderModuleList.Blink->Flink = g_pLdrModule->InInitializeationOrderModuleList.Flink; g_pLdrModule->InInitializeationOrderModuleList.Flink->Blink = g_pLdrModule->InInitializeationOrderModuleList.Blink; g_pLdrModule->InMemoryOrderModuleList.Blink->Flink = g_pLdrModule->InMemoryOrderModuleList.Flink; g_pLdrModule->InMemoryOrderModuleList.Flink->Blink = g_pLdrModule->InMemoryOrderModuleList.Blink; break; } }while (pHeadEntry != pEntry); } int main(int argc, char* argv[]) { __asm { push eax; push ebx; push ecx; mov eax, fs:[0x30]; // eax = peb mov ecx, dword ptr ds:[eax+0xC]; // ecx = Ldr's ptr mov g_pPebLdr, ecx; // pPebLdr = ecx ///////////////////////////// mov ebx, ecx; add ebx, 0xC; mov g_pInLoadOrderModule, ebx; mov ebx, ecx; add ebx, 0x14; mov g_pInMemoryOrderModule, ebx; mov ebx, ecx; add ebx, 0x1C; mov g_pInInitializeationOrderModule, ebx; pop ecx; pop ebx; pop eax; } printf("g_LDR: 0x%x\n", g_pPebLdr); printf("g_pInLoadOrderModule: 0x%x-0x%x\n", g_pInLoadOrderModule->Flink, g_pInLoadOrderModule->Blink); printf("g_pInMemoryOrderModule: 0x%x-0x%x\n", g_pInMemoryOrderModule->Flink, g_pInMemoryOrderModule->Blink); printf("g_pInInitializeationOrderModule: 0x%x-0x%x\n", g_pInInitializeationOrderModule->Flink, g_pInInitializeationOrderModule->Blink); ///////////////// DWORD dwKernelModule = (DWORD)GetModuleHandle("kernel32.dll"); ring3BrokenChains(g_pInLoadOrderModule, dwKernelModule); return 0; }
断链前:
断链后:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY