线程进程隐藏

三环TEB/PEB

x64下,每个线程都有一个TEB结构来存储线程的一些属性结构,gs:[0x30]寄存器在ring3指向TEB结构,

在0x60的偏移,有一个_PEB结构,PEB就是进程用来记录自己信息的一个结构

1728047848025

在PEB +0x18的位置有一个_PEB_LDR_DATA结构

1728047907802

就会发现有三个链表

1728047978847

链表是这个结构,可以通过这三个链表,遍历到所有模块

1728048066495

因此我们就可以在三环断链来隐藏模块

我们知道如果要枚举模块一般都是使 用 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;
// LDR表项,存储了模块信息
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

三环断链效果

断链前

1728105946072

断链后

1728105919059

零环断链隐藏过程

在操作系统层面上,进程本质上就是一个结构体,当操作系统想要创建一个进程时,就分配一块内存,填入一个结构体,并为结构体中的每一项填充一些具体值,而这个结构体,就是 EPROCESS

在EPROCESS结构体里面,有一个活动进程链表,一般去遍历进程都是通过这个表,当然还有别的方式

1728107245920

链表总有一个头,全局变量 PsActiveProcessHead (八个字节)指向全局链表头。这个链表跟进程隐藏有关,只要我们把想要隐藏进程对应的 EPROCESS 的链断掉,就可以达到在0环进程隐藏的目的

1728107511662

1728107566072

图解就长这样

1728107580065

前四个字节指向的是下一个 EPROCESS 结构,但指向的并不是 EPROCESS 的首地址,而是每一个进程的 _EPROCESS + 0x188 (上面的图是32位的,这里以64位为准)的位置

在+0x2e0的位置处还有文件名

1728107709559

通过 EPROCESS 找到我们要隐藏的进程的 ActiveProcessLinks ,将双向链表的值修改,就可以将我们想要隐藏的这个进程的 ActiveProcessLinks 从双向链表中抹去的效果,这里的话如果在windbg里面直接使用 ed 修改的话是比较方便的,但是如果要使用代码来进行修改的话就需要首先定位到 EPROCESS

首先通过msr寄存器获取到Gs寄存器的基地址,指向的就是_KPCR这个结构体

1728108988299

0xc0000101H就是GS寄存器的基地址(dg gs这个指令已经在x64不适用了)

1728109012265

在_KPCR这个结构体里面,有一个 _KPRCB结构体,跟进去

1728109120757

在这个结构体可以找到 _KTHREAD结构体

1728109180442

在 _KTHREAD结构体里面,我们能找到Process成员,这个就指向了EPROCESS结构体

1728109546368

零环断链代码实现:

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

1728112765350

可以看到,已经找不到notepad.exe了

1728113426273