以下实验均在 windows xp系统下完成。
1、3环断链(PEB断链)
作用:3环断链通常用于在用户层隐藏dll
获取dll模块的步骤为:获取 TEB -> PEB -> ldr -> dll模块
TEB结构体:3环下的线程环境块结构体
kd> dt _TEB
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB // PPEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
... ...
PEB结构体:3环下的进程环境块结构体
kd> dt _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA // ldr
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
... ...
_PEB_LDR_DATA结构体:ldr的结构体
kd> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY // 模块加载顺序链表
+0x014 InMemoryOrderModuleList : _LIST_ENTRY // 模块在内存中的顺序链表
+0x01c InInitializationOrderModuleList : _LIST_ENTRY // 模块初始化的顺序链表
+0x024 EntryInProgress : Ptr32 Void
_LIST_ENTRY结构体:上述三个链表的结构体
kd> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY // 指向前驱节点的指针
+0x004 Blink : Ptr32 _LIST_ENTRY // 指向后继节点的指针
_LDR_DATA_TABLE_ENTRY结构体:存储dll模块的信息
kd> dt _LDR_DATA_TABLE_ENTRY
ntdll!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY // _LIST_ENTRY结构体中Flink或Blink指向的位置
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void // dll基址
+0x01c EntryPoint : Ptr32 Void
+0x020 SizeOfImage : Uint4B
+0x024 FullDllName : _UNICODE_STRING
+0x02c BaseDllName : _UNICODE_STRING // dll名称
+0x034 Flags : Uint4B
+0x038 LoadCount : Uint2B
+0x03a TlsIndex : Uint2B
+0x03c HashLinks : _LIST_ENTRY
+0x03c SectionPointer : Ptr32 Void
+0x040 CheckSum : Uint4B
+0x044 TimeDateStamp : Uint4B
+0x044 LoadedImports : Ptr32 Void
+0x048 EntryPointActivationContext : Ptr32 Void
+0x04c PatchInformation : Ptr32 Void
PEB断链源码:
#include<stdio.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(HMODULE hModule)
{
LIST_ENTRY* pHead = g_pInLoadOrderModule;
LIST_ENTRY* pCur = pHead;
do {
pCur = pCur->Blink;
g_pLdrModule = (PLDR_MODULE)pCur; // 这里为什么可以直接将pCur转为PLDR_MODULE,见下面代码解释
// CONTAINING_RECORD这个宏返回成员变量所在结构体的基址,ldte == g_pLdrModule
// PLDR_MODULE ldte = CONTAINING_RECORD(pCur, _LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
if (hModule == g_pLdrModule->DllBase)
{
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 (pHead != pCur);
}
int main(int argc, char* argv[])
{
__asm
{
mov eax, fs: [0x30] ; // PPEB
mov ecx, [eax + 0xC]; // ldr
mov g_pPebLdr, ecx;
mov ebx, ecx;
add ebx, 0xC;
mov g_pInLoadOrderModule, ebx; // 第1个链表
mov ebx, ecx;
add ebx, 0x14;
mov g_pInMemoryOrderModule, ebx; // 第2个链表
mov ebx, ecx;
add ebx, 0x1C;
mov g_pInInitializeationOrderModule, ebx; // 第3个链表
}
printf("点任意按键开始断链");
getchar();
ring3BrokenChains(GetModuleHandleA("kernel32.dll"));
printf("断链成功n");
getchar();
return 0;
}
关键代码解释:
pCur = pCur->Blink;
g_pLdrModule = (PLDR_MODULE)pCur; // 这里为什么可以直接将pCur转为PLDR_MODULE,见下面代码解释
这是因为 pCur 指向 _LIST_ENTRY 结构体,指向的地址刚好是_LDR_DATA_TABLE_ENTRY的首地址,因此二者在内存上刚好是对齐的。
// CONTAINING_RECORD这个宏返回成员变量所在结构体的基址,ldte == g_pLdrModule
PLDR_MODULE ldte = CONTAINING_RECORD(pCur, _LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
2、0环断链(EPROCESS断链)
作用:0环断链通常用于在内核层隐藏进程
获取进程模块的步骤为:获取 _KPCR -> _KPRCB -> KTHREAD -> _KAPC_STATE -> _KPROCESS -> _EPROCESS
KPRC(Kernel Processor Control Region, 内核处理器控制区域)结构体:
kd> dt _kpcr
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 DebugActive : UChar
+0x051 Number : UChar
+0x052 Spare0 : UChar
+0x053 SecondLevelCacheAssociativity : UChar
+0x054 VdmAlert : Uint4B
+0x058 KernelReserved : [14] Uint4B
+0x090 SecondLevelCacheSize : Uint4B
+0x094 HalReserved : [16] Uint4B
+0x0d4 InterruptMode : Uint4B
+0x0d8 Spare1 : UChar
+0x0dc KernelReserved2 : [17] Uint4B
+0x120 PrcbData : _KPRCB // _KPRCB 结构体
_KPRCB 结构体:
kd> dt _KPRCB
ntdll!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD // fs:[0x124]
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
... ...
_KTHREAD 结构体:
kd> dt _KTHREAD
ntdll!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY
+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x020 Teb : Ptr32 Void
+0x024 TlsArray : Ptr32 Void
+0x028 KernelStack : Ptr32 Void
+0x02c DebugActive : UChar
+0x02d State : UChar
+0x02e Alerted : [2] UChar
+0x030 Iopl : UChar
+0x031 NpxState : UChar
+0x032 Saturation : Char
+0x033 Priority : Char
+0x034 ApcState : _KAPC_STATE // _KAPC_STATE 结构体
+0x04c ContextSwitches : Uint4B
... ...
_KAPC_STATE 结构体:
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS // _KTHREAD + 0x44 = _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
kd> dt _eprocess
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY // 保存进程的双线循环链表结构体,与PEB中的ldr 结构体一样
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE_X86
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar // 进程名称
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
+0x1a0 ActiveThreads : Uint4B
+0x1a4 GrantedAccess : Uint4B
+0x1a8 DefaultHardErrorProcessing : Uint4B
+0x1ac LastThreadExitStatus : Int4B
+0x1b0 Peb : Ptr32 _PEB // 3环的PEB地址
+0x1b4 PrefetchTrace : _EX_FAST_REF
... ...
EPROCESS断链源码:
#include <ntddk.h>
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);
NTSTATUS DriverUnload(PDRIVER_OBJECT driver);
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
UNREFERENCED_PARAMETER(reg_path);
PEPROCESS pEprocess, pCurProcess;
PCHAR ImageFileName;
DbgBreakPoint();
__asm
{
mov eax, fs: [0x124] ; // 获取指向 _KTHREAD 的指针
mov eax, [eax + 0x44]; // 获取指向 _KPROCESS 的指针, 即EPROCESS 的首地址
mov pEprocess, eax;
}
pCurProcess = pEprocess;
do
{
ImageFileName = (PCHAR)pCurProcess + 0x174; // 进程名
if (strcmp(ImageFileName, "notepad.exe") == 0)
{
PLIST_ENTRY curNode;
curNode = (PLIST_ENTRY)((ULONG)pCurProcess + 0x88); // +88 指向链表结构体
curNode->Flink->Blink = curNode->Blink;
curNode->Blink->Flink = curNode->Flink;
DbgPrint("断链成功!n");
break;
}
pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88); // 更新进程
} while (pEprocess != pCurProcess);
driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
NTSTATUS DriverUnload(PDRIVER_OBJECT driver)
{
UNREFERENCED_PARAMETER(driver);
DbgPrint("驱动卸载成功n");
return STATUS_SUCCESS;
}
关键代码解释:
(1)更新pCurProcess的说明
// + 0x88:从 _LIST_ENTRY结构体中的Flink 指向的是下一个进程的双向循环链表结构体首地址
// - 0x88:链表指向的是下一个进程结构体+88的位置,-0x88让指针指向EPROCESS的首地址
pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88);
(2)获取EPRCESS的两种办法
第1种:通过内核函数 PsGetCurrentProcess。原理已在前面部分解释。
kd> u psgetcurrentprocess
nt!PsGetCurrentProcess:
804ef608 64a124010000 mov eax,dword ptr fs:[00000124h] // 获取指向 _KTHREAD 的指针
804ef60e 8b4044 mov eax,dword ptr [eax+44h] // 获取指向 _KPROCESS 的指针, 即EPROCESS 的首地址
第2种:
获取进程模块的步骤为:获取 KPCR -> KTHREAD-> ETHREAD -> EPROCESS
对应的汇编是:
__asm
{
mov eax, fs: [0x124] ; // 获取指向 _KTHREAD 的指针, 即 ETHREAD 的首地址。原理与上面一样
mov eax, [eax + 0x220]; // ETHREAD + 0x220 -> ThreadsProcess : Ptr32 _EPROCESS
mov pEprocess, eax;
}
_ETHREAD 结构体:
kd> dt _EThread
ntdll!_ETHREAD
+0x000 Tcb : _KTHREAD // _KTHREAD 结构体
+0x1c0 CreateTime : _LARGE_INTEGER
+0x1c0 NestedFaultCount : Pos 0, 2 Bits
+0x1c0 ApcNeeded : Pos 2, 1 Bit
+0x1c8 ExitTime : _LARGE_INTEGER
+0x1c8 LpcReplyChain : _LIST_ENTRY
+0x1c8 KeyedWaitChain : _LIST_ENTRY
+0x1d0 ExitStatus : Int4B
+0x1d0 OfsChain : Ptr32 Void
+0x1d4 PostBlockList : _LIST_ENTRY
+0x1dc TerminationPort : Ptr32 _TERMINATION_PORT
+0x1dc ReaperLink : Ptr32 _ETHREAD
+0x1dc KeyedWaitValue : Ptr32 Void
+0x1e0 ActiveTimerListLock : Uint4B
+0x1e4 ActiveTimerListHead : _LIST_ENTRY
+0x1ec Cid : _CLIENT_ID
+0x1f4 LpcReplySemaphore : _KSEMAPHORE
+0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
+0x208 LpcReplyMessage : Ptr32 Void
+0x208 LpcWaitingOnPort : Ptr32 Void
+0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION
+0x210 IrpList : _LIST_ENTRY
+0x218 TopLevelIrp : Uint4B
+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT
+0x220 ThreadsProcess : Ptr32 _EPROCESS // PPEB
+0x224 StartAddress : Ptr32 Void
... ...
3、总结
(1)隐藏模块或进程就是一个断链的过程,将自身节点从双向循环链表中删除就可以达到隐藏的效果。
(2)破坏双向循环链表的连续性就可以达到隐藏全部的效果。
(3)为什么删除进程的节点后,程序任然可以运行?这是因为CPU调度的基本单位是线程,和进程无关。
(4)找到EPROCESS也有多种方法。
看雪ID:ATrueMan
https://bbs.kanxue.com/user-home-968999.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):3环和0环断链隐藏分析