线程进程隐藏
三环TEB/PEB
x64下,每个线程都有一个TEB结构来存储线程的一些属性结构,gs:[0x30]寄存器在ring3指向TEB结构,
在0x60的偏移,有一个_PEB结构,PEB就是进程用来记录自己信息的一个结构
在PEB +0x18的位置有一个_PEB_LDR_DATA结构
就会发现有三个链表
链表是这个结构,可以通过这三个链表,遍历到所有模块
因此我们就可以在三环断链来隐藏模块
我们知道如果要枚举模块一般都是使 用 CreateToolhelp32Snapshot 拍摄快照,然后找到模块列表之后进行遍历,其实api也是通过找 _PEB_LDR_DATA 这个结构来获取程序有哪些模块,那么我们如果想隐藏某个dll,就可以通过修改这几 个双向链表的方法来进行隐藏
三环断链代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| #include <iostream> #include <Windows.h>
extern "C" SIZE_T GetPPEB_LDR_DATA();
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;
void HideModule(HMODULE hModule) { PPEB_LDR_DATA ldr; PLDR_DATA_TABLE_ENTRY ldte; ldr = (PPEB_LDR_DATA)GetPPEB_LDR_DATA(); PLIST_ENTRY Head, Cur;
Head = &(ldr->InLoadOrderModuleList); Cur = Head->Flink; do { ldte = CONTAINING_RECORD(Cur, LDR_DATA_TABLE_ENTRY,InLoadOrderModuleList); if (ldte->BaseAddress == hModule) { ldte->InLoadOrderModuleList.Blink->Flink = ldte -> InLoadOrderModuleList.Flink; ldte->InLoadOrderModuleList.Flink->Blink = ldte -> InLoadOrderModuleList.Blink; } Cur = Cur->Flink; } while (Head != Cur);
Head = &(ldr->InMemoryOrderModuleList); Cur = Head->Flink; do { ldte = CONTAINING_RECORD(Cur, LDR_DATA_TABLE_ENTRY,InMemoryOrderModuleList); if (ldte->BaseAddress == hModule) { ldte->InMemoryOrderModuleList.Blink->Flink = ldte -> InMemoryOrderModuleList.Flink; ldte->InMemoryOrderModuleList.Flink->Blink = ldte -> InMemoryOrderModuleList.Blink; } Cur = Cur->Flink; } while (Head != Cur);
Head = &(ldr->InInitializationOrderModuleList); Cur = Head->Flink; do { ldte = CONTAINING_RECORD(Cur, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); if (ldte->BaseAddress == hModule) { ldte->InInitializationOrderModuleList.Blink->Flink = ldte -> InInitializationOrderModuleList.Flink; ldte->InInitializationOrderModuleList.Flink->Blink = ldte -> InInitializationOrderModuleList.Blink; } Cur = Cur->Flink; } while (Head != Cur);
}
int main(int argc, CHAR* argv[]) { printf("点任意按键开始断链"); getchar(); HideModule(GetModuleHandleA(argv[1])); printf("断链成功\n"); getchar(); return 0; }
|
汇编:
1 2 3 4 5 6 7 8 9 10 11 12 13
| .code
GetPPEB_LDR_DATA PROC
; 读取 PEB 地址,gs:[0x60] 是指向 PEB 的偏移量 mov rax, qword ptr gs:[60h] ; 从 PEB 结构的 0x18 偏移处获取 LDR 数据 mov rax, qword ptr [rax + 18h]
ret GetPPEB_LDR_DATA ENDP
end
|
三环断链效果
断链前
断链后
零环断链隐藏过程
在操作系统层面上,进程本质上就是一个结构体,当操作系统想要创建一个进程时,就分配一块内存,填入一个结构体,并为结构体中的每一项填充一些具体值,而这个结构体,就是 EPROCESS
在EPROCESS结构体里面,有一个活动进程链表,一般去遍历进程都是通过这个表,当然还有别的方式
链表总有一个头,全局变量 PsActiveProcessHead (八个字节)指向全局链表头。这个链表跟进程隐藏有关,只要我们把想要隐藏进程对应的 EPROCESS 的链断掉,就可以达到在0环进程隐藏的目的
图解就长这样
前四个字节指向的是下一个 EPROCESS 结构,但指向的并不是 EPROCESS 的首地址,而是每一个进程的 _EPROCESS + 0x188 (上面的图是32位的,这里以64位为准)的位置
在+0x2e0的位置处还有文件名
通过 EPROCESS 找到我们要隐藏的进程的 ActiveProcessLinks ,将双向链表的值修改,就可以将我们想要隐藏的这个进程的 ActiveProcessLinks 从双向链表中抹去的效果,这里的话如果在windbg里面直接使用 ed 修改的话是比较方便的,但是如果要使用代码来进行修改的话就需要首先定位到 EPROCESS
首先通过msr寄存器获取到Gs寄存器的基地址,指向的就是_KPCR这个结构体
0xc0000101H就是GS寄存器的基地址(dg gs这个指令已经在x64不适用了)
在_KPCR这个结构体里面,有一个 _KPRCB结构体,跟进去
在这个结构体可以找到 _KTHREAD结构体
在 _KTHREAD结构体里面,我们能找到Process成员,这个就指向了EPROCESS结构体
零环断链代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #pragma once extern "C" { #include <ntddk.h> NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path); }
#include "header.h" NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path); VOID DriverUnload(PDRIVER_OBJECT driver); extern "C" PEPROCESS GetEprocess(); NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { reg_path; PEPROCESS pEprocess, pCurProcess; PCHAR ImageFileName; KdBreakPoint(); pEprocess = GetEprocess(); pCurProcess = pEprocess; do { ImageFileName = (PCHAR)pCurProcess + 0x2e0; if (strcmp(ImageFileName, "notepad.exe") == 0) { PLIST_ENTRY preNode, curNode, nextNode; curNode = (PLIST_ENTRY)((SIZE_T)pCurProcess + 0x188); nextNode = curNode->Flink; preNode = curNode->Blink; preNode->Flink = curNode->Flink; nextNode->Blink = curNode->Blink; DbgPrint("断链成功!\n"); } pCurProcess = (PEPROCESS)(*(PSIZE_T)((SIZE_T)pCurProcess + 0x188) - 0x188); } while (pEprocess != pCurProcess); driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; } VOID DriverUnload(PDRIVER_OBJECT driver) { driver; DbgPrint("驱动卸载成功\n"); }
|
汇编:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| .code
GetEprocess proc mov rcx, 0C0000101h ; MSR 编号 0xC0000101(IA32_GS_BASE) rdmsr ; 读取 MSR 到 edx:eax 中 shl rdx, 32 ; 将 edx 左移 32 位放入 rdx or rax, rdx ; 将 eax 和 rdx 组合成 64 位结果,此时Rax存的就是_KPCR的基地址 mov rax,[rax+188h] ;获取到_KPRCB结构体 ;获取到_KTHREAD结构体 mov rax,[rax+210h] ;获取到_KPROCESS结构体,也就是EPROCESS结构体,这俩一样的 ret GetEprocess endp
end
|
可以看到,已经找不到notepad.exe了