想第一时间看到我们的大图推送吗?赶紧把我们设为星标吧!这样您就不会错过任何精彩内容啦!感谢您的支持!🌟
免责声明
文章所涉及内容,仅供安全研究教学使用,由于传播、利用本文所提供的信息而造成的任何直接或间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。
前言
首先我们介绍一下PEB断链,相信各位读者大大对PEB这个结构体可以说相当熟悉了,这里我们简单描述一下,做一个介绍
在开始使用TEB/PEB 获取进程的模块信息之前,我想有必要解释一下这两个名词:PEB 指的是进程环境块(Process Environment Block),用于存储进程状态信息和进程所需的各种数据。每个进程都有一个对应的 PEB 结构体。TEB 指的是线程环境块(Thread Environment Block),用于存储线程状态信息和线程所需的各种数据。每个线程同样都有一个对应的 TEB 结构体。
PEB 中包含了进程的代码、数据段指针、进程的环境变量、进程启动参数信息以及加载的模块信息等。在 x86-32 体系下,FS 段寄存器偏移 0x30 处存放了索引,索引查找的指针指向当前进程的 PEB 结构体,在 x86-64 下该指针位于 FS 段寄存器偏移 0x60 处。其他进程可以通过访问自己的 PEB 结构体来获取自己的状态和信息。
TEB 中包含了线程的堆栈指针、TLS(线程本地存储)指针、异常处理链表指针、用户模式分页表指针等信息。在 x86-32体系下,FS 段寄存器偏移 0x18 处通常为指向 TEB 结构体的指针,在 x86-64 下该指针位于 FS 段寄存器偏移 0x30 处。其他线程可以通过访问自己的 TEB 结构体来获取自己的状态和信息。
typedef struct _PEB_LDR_DATA32 { ULONG Length; // +0x00 BOOLEAN Initialized; // +0x04 PVOID SsHandle; // +0x08 LIST_ENTRY InLoadOrderModuleList; // +0x0c LIST_ENTRY InMemoryOrderModuleList; // +0x14 LIST_ENTRY InInitializationOrderModuleList;// +0x1c } PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24 typedef struct _PEB32 { UCHAR InheritedAddressSpace; //0x0 UCHAR ReadImageFileExecOptions; //0x1 UCHAR BeingDebugged; //0x2 union { UCHAR BitField; //0x3 struct { UCHAR ImageUsesLargePages : 1; //0x3 UCHAR IsProtectedProcess : 1; //0x3 UCHAR IsImageDynamicallyRelocated : 1; //0x3 UCHAR SkipPatchingUser32Forwarders : 1; //0x3 UCHAR IsPackagedProcess : 1; //0x3 UCHAR IsAppContainer : 1; //0x3 UCHAR IsProtectedProcessLight : 1; //0x3 UCHAR IsLongPathAwareProcess : 1; //0x3 }; }; PVOID Mutant; //0x4 PVOID ImageBaseAddress; //0x8 PEB_LDR_DATA32* Ldr; //0xc RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x10 PVOID SubSystemData; //0x14 PVOID ProcessHeap; //0x18 RTL_CRITICAL_SECTION* FastPebLock; //0x1c SLIST_HEADER* volatile AtlThunkSListPtr; //0x20 PVOID IFEOKey; //0x24 } PEB32, * PPEB32; typedef struct _STRING64 { USHORT Length; //0x0 USHORT MaximumLength; //0x2 ULONGLONG Buffer; //0x8 }STRING64, * LPSTRING64; typedef struct _PEB_LDR_DATA64 { ULONG Length; //0x0 UCHAR Initialized; //0x4 PVOID SsHandle; //0x8 LIST_ENTRY InLoadOrderModuleList; //0x10 LIST_ENTRY InMemoryOrderModuleList; //0x20 LIST_ENTRY InInitializationOrderModuleList; //0x30 PVOID EntryInProgress; //0x40 UCHAR ShutdownInProgress; //0x48 PVOID ShutdownThreadId; //0x50 }PEB_LDR_DATA64, *PPEB_LDR_DATA64; typedef struct _PEB64 { UCHAR InheritedAddressSpace; //0x0 UCHAR ReadImageFileExecOptions; //0x1 UCHAR BeingDebugged; //0x2 union { UCHAR BitField; //0x3 struct { UCHAR ImageUsesLargePages : 1; //0x3 UCHAR IsProtectedProcess : 1; //0x3 UCHAR IsImageDynamicallyRelocated : 1; //0x3 UCHAR SkipPatchingUser32Forwarders : 1; //0x3 UCHAR IsPackagedProcess : 1; //0x3 UCHAR IsAppContainer : 1; //0x3 UCHAR IsProtectedProcessLight : 1; //0x3 UCHAR IsLongPathAwareProcess : 1; //0x3 }; }; UCHAR Padding0[4]; //0x4 ULONGLONG Mutant; //0x8 ULONGLONG ImageBaseAddress; //0x10 PEB_LDR_DATA64* Ldr; //0x18 ULONGLONG ProcessParameters; //0x20 ULONGLONG SubSystemData; //0x28 ULONGLONG ProcessHeap; //0x30 ULONGLONG FastPebLock; //0x38 ULONGLONG AtlThunkSListPtr; //0x40 ULONGLONG IFEOKey; //0x48 }PEB64, *PPEB64; |
实现的效果:
这里我们可以看到已经隐藏成功。
这是效果图
R0层隐藏进程
注:基于R0层进行隐藏进程,需要使用驱动,但是有个问题就是加载驱动需要带有有效签名,如果没有签名windows是不会将驱动加载起来的。
获取进程模块的步骤为:获取 _KPCR -> _KPRCB -> KTHREAD -> _KAPC_STATE -> _KPROCESS -> _EPROCESS
(1)KPCR 的获取方法为:fs:[0]
(2)KPCR 结构体中偏移0x120处有一个成员 _KPRCB 结构体,该结构体中偏移0x4处(即 fs:[0x124])有一个 CurrentThread 指针,指向 _KTHREAD 结构体
(3)_KTHREAD 结构体偏移0x34处有一个 ApcState 结构体,该结构体偏移0x10处有一个 Process 指针,指向 _KPROCESS
(4)_EPROCESS 结构体的第一个成员是 Pcb,是一个 _KPROCESS 结构体
(5)(3)中的 Process 指针指向的就是 _EPROCESS 中的一个成员 _KPROCESS 结构体,也是 _EPROCESS 结构体的首地址。这一点和PEB断链中,
ldr->Flink 与 _LDR_DATA_TABLE_ENTRY 首地址相等是一样的
(6)_EPROCESS 结构体中偏移0x88有一个 _LIST_ENTRY 结构体,该结构体保存了当前进程在进程链表中的位置信息
(7)节点指针指向的是 _LIST_ENTRY 结构体,该结构体存储了模块在链表中的位置信息
(8)通过 _EPROCESS 结构体中偏移0x174的 ImageFileName 确定所属进程, 并通过该结构体中的 _LIST_ENTRY结构体删除该节点在链表中的位置信息(双向循环链表删除节点),即可达到0环下的进程隐藏。
PPID Spoofing
PPID spoofing是一种攻击技术,用于欺骗系统和安全工具,使其认为恶意进程的父进程ID(PPID)是合法的。通过伪装父进程ID,攻击者可以隐藏恶意进程的真实来源,绕过安全监控和检测。这种技术可能被用于实施恶意活动,如隐藏恶意进程、绕过安全防护、进行横向移动等。在安全领域中,需要采取相应的防御措施来防范PPID spoofing攻击。
PPID Spoofing 原理概述
PPID Spoofing 是通过在 STARTUPINFOEXW 结构体中的 PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 成员中,使用 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 来告诉最终调用调用的 CreateProcess 函数,将即将创建的进程,归入到指定的父进程之下。
PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 成员是通过 InitializeProcThreadAttributeList分配内存,并由 UpdateProcThreadAttribute 函数设置其属性(设置成 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS),来达到偷换父进程的目的。
PPID Spoofing 原理概述
PPID Spoofing 是通过在 STARTUPINFOEXW 结构体中的 PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 成员中,使用 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 来告诉最终调用调用的 CreateProcess 函数,将即将创建的进程,归入到指定的父进程之下。
PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList 成员是通过 InitializeProcThreadAttributeList分配内存,并由 UpdateProcThreadAttribute 函数设置其属性(设置成 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS),来达到偷换父进程的目的。
InitializeProcThreadAttributeList 函数
初始化用于创建进程和线程的指定属性列表。
BOOL InitializeProcThreadAttributeList( [out, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, [in] DWORD dwAttributeCount, DWORD dwFlags, [in, out] PSIZE_T lpSize ); [out, optional] lpAttributeList |
属性列表。此参数可以为 NULL,以确定支持指定数量的属性所需的缓冲区大小。
[in] dwAttributeCount
要添加到列表的属性计数。//这里我们只需要一个就行了,那就是PROC_THREAD_ATTRIBUTE_PARENT_PROCESS属性,用于创建指定的父进程。
dwFlags
此参数是保留的,必须为零。
[in, out] lpSize
如果 lpAttributeList 不为 NULL,则此参数指定输入时 lpAttributeList 缓冲区的大小(以字节为单位)。输出时,此参数接收初始化的属性列表的大小(以字节为单位)。
如果 lpAttributeList 为 NULL,则此参数接收所需的缓冲区大小(以字节为单位)。
UpdateProcThreadAttribute函数
BOOL UpdateProcThreadAttribute( [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, [in] DWORD dwFlags, [in] DWORD_PTR Attribute, [in] PVOID lpValue, [in] SIZE_T cbSize, [out, optional] PVOID lpPreviousValue, [in, optional] PSIZE_T lpReturnSize ); |
这里我们主要注意第三个参数,给定一个属性,这里有很多我们只使用其中的一个。
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS:
lpValue
参数是指向要使用的进程的句柄的指针,而不是作为所创建进程的父进程的调用进程。要使用的进程必须具有
PROCESS_CREATE_PROCESS
访问权限。
从指定进程继承的属性包括句柄、设备映射、处理器相关性、优先级、配额、进程令牌和作业对象。(请注意,某些属性(如调试端口)将来自创建进程,而不是此 handle 指定的进程。)
那么思路明确,我们先声明结构体,然后对结构体第二个值进行大小以及属性的初始化。
接下来上代码:
下面是使用的方法和效果。
那么这里我就实验成功了,多研究研究就行。
原文始发于微信公众号(泾弦安全):免杀断链
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论