Windows PEB

基础

在Windows中关于进程、线程的数据结构有以下

  • EPROCESS、KPROCESS、PEB
  • ETHREAD、KTHREAD、TEB

EPROCESS

在内核中即0环存在EPROCESS结构体,该结构用于存储有关进程的关键信息,例如进程的状态、进程的地址空间、进程的安全上下文以及它正在使用的其他资源。每个进程都有一个EPROCESS结构,windows通过一个链表将所有的EPROCESS串起来进行管理。修改此结构体需要驱动。

PEB

在用户模式中存在 进程环境块(Process Environment Block)是一种加载到每个进程中的内存结构,它存储了该进程范围内的全局信息。这些信息对于进程的整个生命周期都至关重要,例如进程加载了哪些动态链接库 (DLL)、完整的命令行参数、启动目录、可执行文件的内存基址、进程堆信息以及一些用于调试和系统兼容性的标志位。

//0x7c8 bytes (sizeof)
struct _PEB
{
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
VOID* Mutant; //0x8
VOID* ImageBaseAddress; //0x10
struct _PEB_LDR_DATA* Ldr; //0x18
struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x20
VOID* SubSystemData; //0x28
VOID* ProcessHeap; //0x30
struct _RTL_CRITICAL_SECTION* FastPebLock; //0x38
union _SLIST_HEADER* volatile AtlThunkSListPtr; //0x40
VOID* IFEOKey; //0x48
union
{
ULONG CrossProcessFlags; //0x50
struct
{
ULONG ProcessInJob:1; //0x50
ULONG ProcessInitializing:1; //0x50
ULONG ProcessUsingVEH:1; //0x50
ULONG ProcessUsingVCH:1; //0x50
ULONG ProcessUsingFTH:1; //0x50
ULONG ProcessPreviouslyThrottled:1; //0x50
ULONG ProcessCurrentlyThrottled:1; //0x50
ULONG ProcessImagesHotPatched:1; //0x50
ULONG ReservedBits0:24; //0x50
};
};
UCHAR Padding1[4]; //0x54
union
{
VOID* KernelCallbackTable; //0x58
VOID* UserSharedInfoPtr; //0x58
};
ULONG SystemReserved; //0x60
ULONG AtlThunkSListPtr32; //0x64
VOID* ApiSetMap; //0x68
ULONG TlsExpansionCounter; //0x70
UCHAR Padding2[4]; //0x74
VOID* TlsBitmap; //0x78
ULONG TlsBitmapBits[2]; //0x80
VOID* ReadOnlySharedMemoryBase; //0x88
VOID* SharedData; //0x90
VOID** ReadOnlyStaticServerData; //0x98
VOID* AnsiCodePageData; //0xa0
VOID* OemCodePageData; //0xa8
VOID* UnicodeCaseTableData; //0xb0
ULONG NumberOfProcessors; //0xb8
ULONG NtGlobalFlag; //0xbc
union _LARGE_INTEGER CriticalSectionTimeout; //0xc0
ULONGLONG HeapSegmentReserve; //0xc8
ULONGLONG HeapSegmentCommit; //0xd0
ULONGLONG HeapDeCommitTotalFreeThreshold; //0xd8
ULONGLONG HeapDeCommitFreeBlockThreshold; //0xe0
ULONG NumberOfHeaps; //0xe8
ULONG MaximumNumberOfHeaps; //0xec
VOID** ProcessHeaps; //0xf0
VOID* GdiSharedHandleTable; //0xf8
VOID* ProcessStarterHelper; //0x100
ULONG GdiDCAttributeList; //0x108
UCHAR Padding3[4]; //0x10c
struct _RTL_CRITICAL_SECTION* LoaderLock; //0x110
ULONG OSMajorVersion; //0x118
ULONG OSMinorVersion; //0x11c
USHORT OSBuildNumber; //0x120
USHORT OSCSDVersion; //0x122
ULONG OSPlatformId; //0x124
ULONG ImageSubsystem; //0x128
ULONG ImageSubsystemMajorVersion; //0x12c
ULONG ImageSubsystemMinorVersion; //0x130
UCHAR Padding4[4]; //0x134
ULONGLONG ActiveProcessAffinityMask; //0x138
ULONG GdiHandleBuffer[60]; //0x140
VOID (*PostProcessInitRoutine)(); //0x230
VOID* TlsExpansionBitmap; //0x238
ULONG TlsExpansionBitmapBits[32]; //0x240
ULONG SessionId; //0x2c0
UCHAR Padding5[4]; //0x2c4
union _ULARGE_INTEGER AppCompatFlags; //0x2c8
union _ULARGE_INTEGER AppCompatFlagsUser; //0x2d0
VOID* pShimData; //0x2d8
VOID* AppCompatInfo; //0x2e0
struct _UNICODE_STRING CSDVersion; //0x2e8
struct _ACTIVATION_CONTEXT_DATA* ActivationContextData; //0x2f8
struct _ASSEMBLY_STORAGE_MAP* ProcessAssemblyStorageMap; //0x300
struct _ACTIVATION_CONTEXT_DATA* SystemDefaultActivationContextData; //0x308
struct _ASSEMBLY_STORAGE_MAP* SystemAssemblyStorageMap; //0x310
ULONGLONG MinimumStackCommit; //0x318
VOID* SparePointers[4]; //0x320
ULONG SpareUlongs[5]; //0x340
VOID* WerRegistrationData; //0x358
VOID* WerShipAssertPtr; //0x360
VOID* pUnused; //0x368
VOID* pImageHeaderHash; //0x370
union
{
ULONG TracingFlags; //0x378
struct
{
ULONG HeapTracingEnabled:1; //0x378
ULONG CritSecTracingEnabled:1; //0x378
ULONG LibLoaderTracingEnabled:1; //0x378
ULONG SpareTracingBits:29; //0x378
};
};
UCHAR Padding6[4]; //0x37c
ULONGLONG CsrServerReadOnlySharedMemoryBase; //0x380
ULONGLONG TppWorkerpListLock; //0x388
struct _LIST_ENTRY TppWorkerpList; //0x390
VOID* WaitOnAddressHashTable[128]; //0x3a0
VOID* TelemetryCoverageHeader; //0x7a0
ULONG CloudFileFlags; //0x7a8
ULONG CloudFileDiagFlags; //0x7ac
CHAR PlaceholderCompatibilityMode; //0x7b0
CHAR PlaceholderCompatibilityModeReserved[7]; //0x7b1
struct _LEAP_SECOND_DATA* LeapSecondData; //0x7b8
union
{
ULONG LeapSecondFlags; //0x7c0
struct
{
ULONG SixtySecondEnabled:1; //0x7c0
ULONG Reserved:31; //0x7c0
};
};
ULONG NtGlobalFlag2; //0x7c4
};

AI描述PEB字段含义如下,未必正确

0x0 - 0x3: 基本状态标志
InheritedAddressSpace (0x0) - 是否继承父进程的地址空间
ReadImageFileExecOptions (0x1) - 是否读取可执行文件的特殊执行选项
BeingDebugged (0x2) - 进程是否正在被调试
BitField / 位域 (0x3)
ImageUsesLargePages - 是否使用大页内存映射
IsProtectedProcess - 是否受保护进程(如防止注入)
IsImageDynamicallyRelocated - 可执行映像是否被动态重定位
SkipPatchingUser32Forwarders - 是否跳过 User32 转发修补
IsPackagedProcess - 是否为打包应用(UWP/Store 应用)
IsAppContainer - 是否在 AppContainer 沙箱中运行
IsProtectedProcessLight - 轻量保护进程标志
IsLongPathAwareProcess - 是否支持长路径文件名

0x8 - 0x30: 核心指针
Mutant (0x8) - 内核同步对象指针(用于进程互斥)
ImageBaseAddress (0x10) - 可执行映像加载基址
Ldr (0x18) - 指向 _PEB_LDR_DATA,记录加载的模块列表
ProcessParameters (0x20) - 指向 _RTL_USER_PROCESS_PARAMETERS,存储命令行、环境变量等
SubSystemData (0x28) - 子系统数据指针(如 GUI/Console 子系统)
ProcessHeap (0x30) - 默认堆指针

0x38 - 0x58: 同步和 Thunk 指针
FastPebLock (0x38) - PEB 快速锁,用于线程安全访问
AtlThunkSListPtr (0x40) - ATL 弹性链表指针,用于内部 thunk
IFEOKey (0x48) - Image File Execution Options 注册表键指针
CrossProcessFlags (0x50) - 进程状态标志集合(如 ProcessInJob、使用 VEH/FTH 等)

0x58 - 0x98: 内核回调与 API 重映射
KernelCallbackTable / UserSharedInfoPtr (0x58) - 内核回调表或用户共享信息
SystemReserved (0x60) - 系统保留
AtlThunkSListPtr32 (0x64) - 32 位 ATL 弹性链表指针
ApiSetMap (0x68) - API 重映射表
TlsExpansionCounter (0x70) - TLS 扩展计数器
TlsBitmap (0x78) & TlsBitmapBits (0x80) - TLS 使用情况位图
ReadOnlySharedMemoryBase (0x88) - 只读共享内存基址
SharedData (0x90) - KUSER_SHARED_DATA 全局共享数据指针
ReadOnlyStaticServerData (0x98) - 静态服务器只读数据

0xa0 - 0xb8: 字符编码和 CPU 信息
AnsiCodePageData (0xa0) - ANSI 编码页数据表
OemCodePageData (0xa8) - OEM 编码页数据表
UnicodeCaseTableData (0xb0) - Unicode 大小写映射表
NumberOfProcessors (0xb8) - 系统 CPU 核心数量

0xbc - 0xf0: 堆和同步超时
NtGlobalFlag (0xbc) - 内核调试标志
CriticalSectionTimeout (0xc0) - 临界区超时
HeapSegmentReserve (0xc8)、HeapSegmentCommit (0xd0) - 堆段保留与提交大小
HeapDeCommitTotalFreeThreshold (0xd8)、HeapDeCommitFreeBlockThreshold (0xe0) - 堆释放阈值
NumberOfHeaps (0xe8)、MaximumNumberOfHeaps (0xec) - 堆数量限制
ProcessHeaps (0xf0) - 所有进程堆数组

0xf8 - 0x110: GDI 和 Loader
GdiSharedHandleTable (0xf8) - GDI 共享句柄表
ProcessStarterHelper (0x100) - 内核启动辅助指针
GdiDCAttributeList (0x108) - GDI 设备上下文属性列表
LoaderLock (0x110) - 模块加载锁

0x118 - 0x134: OS 版本与子系统
OSMajorVersion/OSMinorVersion/OSBuildNumber/OSCSDVersion - 操作系统版本信息
OSPlatformId - 平台 ID
ImageSubsystem/ImageSubsystemMajorVersion/ImageSubsystemMinorVersion - 可执行文件子系统类型和版本
ActiveProcessAffinityMask (0x138) - CPU 亲和掩码

0x140 - 0x2c0: GDI 缓冲、初始化例程、TLS 扩展
GdiHandleBuffer (0x140) - GDI 本地句柄缓存
PostProcessInitRoutine (0x230) - 初始化后回调例程
TlsExpansionBitmap (0x238)、TlsExpansionBitmapBits (0x240) - TLS 扩展位图
SessionId (0x2c0) - Terminal Services / Session ID

0x2c8 - 0x310: 兼容性、激活上下文、程序集
AppCompatFlags/AppCompatFlagsUser - 兼容性标志
pShimData/AppCompatInfo - 应用兼容性数据指针
CSDVersion - 服务包 Unicode 字符串
ActivationContextData/ProcessAssemblyStorageMap/
SystemDefaultActivationContextData/SystemAssemblyStorageMap - 激活上下文和程序集存储映射

0x318 - 0x370: 堆栈与调试指针
MinimumStackCommit - 最小栈提交大小
SparePointers / SpareUlongs - 保留指针和整数
WerRegistrationData / WerShipAssertPtr - Windows 错误报告数据
pUnused / pImageHeaderHash - 未使用或镜像哈希指针

0x378 - 0x3a0: 追踪和任务池
TracingFlags - Heap/CritSec/Loader 追踪开关
CsrServerReadOnlySharedMemoryBase - CSR 服务器共享内存
TppWorkerpListLock / TppWorkerpList - 线程池工作列表锁和列表
WaitOnAddressHashTable - 内核等待地址哈希表

0x7a0 - 0x7c4: 云文件、占位兼容、闰秒
TelemetryCoverageHeader - 遥测覆盖头
CloudFileFlags / CloudFileDiagFlags - 云文件标志
PlaceholderCompatibilityMode - 占位兼容模式
LeapSecondData / LeapSecondFlags - 闰秒数据及启用标志
NtGlobalFlag2 - 第二组内核标志(调试、追踪等)

其中重要的是Ldr字段,该字段指向 PEB_LDR_DATA 结构

struct _PEB_LDR_DATA* Ldr;  //0x18

PEB_LDR_DATA 结构包含三个双向链表

  • InLoadOrderModuleList - 加载顺序
  • InMemoryOrderModuleList - 内存布局顺序
  • InInitializationOrderModuleList - 初始化顺序
//0x58 bytes (sizeof)
struct _PEB_LDR_DATA
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
VOID* SsHandle; //0x8
struct _LIST_ENTRY InLoadOrderModuleList; //0x10
struct _LIST_ENTRY InMemoryOrderModuleList; //0x20
struct _LIST_ENTRY InInitializationOrderModuleList; //0x30
VOID* EntryInProgress; //0x40
UCHAR ShutdownInProgress; //0x48
VOID* ShutdownThreadId; //0x50
};

每个双向链表中 _LIST_ENTRY 中的 Flink/Blink 指针,指向了一个 LDR_DATA_TABLE_ENTRY 结构体,该结构是描述加载模块的信息,例如DLL基址、DLL名称。每一个加载的模块(.dll)都有一个这样的结构体。该结构体中分别有三个_LIST_ENTRY类型的InLoadOrderLinks、InMemoryOrderLinks、InInitializationOrderLinks 它们又指向了下一个 LDR_DATA_TABLE_ENTRY 结构体

//0x120 bytes (sizeof)
struct _LDR_DATA_TABLE_ENTRY
{
struct _LIST_ENTRY InLoadOrderLinks; //0x0
struct _LIST_ENTRY InMemoryOrderLinks; //0x10
struct _LIST_ENTRY InInitializationOrderLinks; //0x20
VOID* DllBase; //0x30
VOID* EntryPoint; //0x38
ULONG SizeOfImage; //0x40
struct _UNICODE_STRING FullDllName; //0x48
struct _UNICODE_STRING BaseDllName; //0x58
union
{
UCHAR FlagGroup[4]; //0x68
ULONG Flags; //0x68
struct
{
...
};
};
USHORT ObsoleteLoadCount; //0x6c
USHORT TlsIndex; //0x6e
struct _LIST_ENTRY HashLinks; //0x70
ULONG TimeDateStamp; //0x80
struct _ACTIVATION_CONTEXT* EntryPointActivationContext; //0x88
VOID* Lock; //0x90
struct _LDR_DDAG_NODE* DdagNode; //0x98
struct _LIST_ENTRY NodeModuleLink; //0xa0
struct _LDRP_LOAD_CONTEXT* LoadContext; //0xb0
VOID* ParentDllBase; //0xb8
VOID* SwitchBackContext; //0xc0
struct _RTL_BALANCED_NODE BaseAddressIndexNode; //0xc8
struct _RTL_BALANCED_NODE MappingInfoIndexNode; //0xe0
ULONGLONG OriginalBase; //0xf8
union _LARGE_INTEGER LoadTime; //0x100
ULONG BaseNameHashValue; //0x108
enum _LDR_DLL_LOAD_REASON LoadReason; //0x10c
ULONG ImplicitPathOptions; //0x110
ULONG ReferenceCount; //0x114
ULONG DependentLoadFlags; //0x118
UCHAR SigningLevel; //0x11c
};

三个链表记录加载模块的顺序如下:

InLoadOrderModuleList

notepad.exe - ntdll.dll - kernel32.dll - kernelbase.dll

InMemoryOrderModuleList

notepad.exe - ntdll.dll - kernel32.dll - kernelbase.dll

InInitializationOrderModuleList

ntdll.dll - kernelbase.dll - kernel32.dll

TEB

在用户模式中存在 线程环境块(Thread Environment Block),每个线程都具有一个独立的TEB,它存储了与单个线程相关的特定信息,例如 线程ID (TID)、线程的栈基址和栈顶限制、指向线程局部存储 (Thread Local Storage, TLS) 的指针。

// AI总结TEB结构成员意义,未必正确

0x0 - 0x38: NT_TIB
NtTib - 包含异常链表、堆栈边界、线程信息块核心指针,用于结构化异常处理和栈管理

0x38 - 0x60: 环境与线程标识
EnvironmentPointer - 指向环境变量块
ClientId - 线程和进程 ID
ActiveRpcHandle - 活动 RPC 会话句柄
ThreadLocalStoragePointer - TLS 基址
ProcessEnvironmentBlock - 指向所属进程的 PEB

0x68 - 0x108: 状态与保留字段
LastErrorValue - 上一次调用 SetLastError/Win32 API 的错误码
CountOfOwnedCriticalSections - 线程拥有的临界区数量
CsrClientThread - CSR 服务器客户端线程指针
Win32ThreadInfo - Win32 子系统线程信息
User32Reserved / UserReserved - User32 和用户保留区
WOW32Reserved - WOW64 子系统保留
CurrentLocale - 当前线程区域设置
FpSoftwareStatusRegister - 浮点状态寄存器模拟标志

0x110 - 0x290: 调试和占位相关
ReservedForDebuggerInstrumentation - 调试器使用的保留指针数组
SystemReserved1 - 系统保留
PlaceholderCompatibilityMode / PlaceholderHydrationAlwaysExplicit / PlaceholderReserved - 占位兼容模式和保留
ProxiedProcessId - 被代理的进程 ID
_ActivationStack - 激活上下文栈信息
WorkingOnBehalfTicket - 权限代理票据

0x2c0 - 0x2e8: 异常与上下文
ExceptionCode - 线程异常代码
ActivationContextStackPointer - 当前激活上下文栈指针
InstrumentationCallbackSp/Pc/PreviousSp - 仿真/回调上下文保存
TxFsContext - 事务性文件系统上下文
InstrumentationCallbackDisabled - 仿真回调是否禁用
UnalignedLoadStoreExceptions - 未对齐访问异常标志

0x2f0 - 0x7f8: GDI 与客户端信息
GdiTebBatch - 批量 GDI 绘图缓存
RealClientId - 实际线程和进程 ID
GdiCachedProcessHandle / GdiClientPID / GdiClientTID - GDI 相关缓存
GdiThreadLocalInfo - 线程本地 GDI 信息
Win32ClientInfo - Win32 客户端信息数组

0x9f0 - 0x1250: OpenGL 和状态
glDispatchTable / glReserved1/2 - OpenGL 调用表及保留
glSectionInfo, glSection, glTable - OpenGL 线程上下文
glCurrentRC / glContext - 当前 OpenGL 渲染上下文
LastStatusValue - 上一次 NT API 状态返回值
StaticUnicodeString / StaticUnicodeBuffer - 静态 Unicode 字符串缓存

0x1478 - 0x1698: 堆栈与 TLS
DeallocationStack - 栈释放指针
TlsSlots - 线程局部存储槽
TlsLinks - TLS 链表,用于线程切换
Vdm - 虚拟 DOS 子系统指针
ReservedForNtRpc - NT RPC 保留
DbgSsReserved - 调试子系统保留
HardErrorMode - 严重错误处理模式
Instrumentation - 调试/监控回调保留

0x1710 - 0x1748: 活动、性能、GDI
ActivityId - 活动 GUID,用于事件追踪
SubProcessTag - 子进程标记
PerflibData / EtwTraceData - 性能库和 ETW 事件数据
WinSockData - Winsock 数据
GdiBatchCount - GDI 批量计数
CurrentIdealProcessor / IdealProcessorValue - 理想处理器编号
GuaranteedStackBytes - 最小栈保证

0x1750 - 0x17e8: 线程池、TLS 扩展、语言与模拟
ReservedForPerf / ReservedForOle - 性能和 OLE 保留
WaitingOnLoaderLock - 是否在等待 LoaderLock
SavedPriorityState - 保存线程优先级状态
ReservedForCodeCoverage - 代码覆盖工具使用
ThreadPoolData - 线程池信息
TlsExpansionSlots - TLS 扩展槽
DeallocationBStore / BStoreLimit - 备用栈存储指针及限制
MuiGeneration - MUI 数据版本
IsImpersonating - 是否模拟用户
NlsCache - 本地化信息缓存
pShimData - 应用兼容性 Shim 数据
HeapData - 堆相关数据
CurrentTransactionHandle / ActiveFrame - 事务处理句柄和上下文
FlsData - Fiber 本地存储
PreferredLanguages, UserPrefLanguages, MergedPrefLanguages - 线程语言首选项
MuiImpersonation - MUI 模拟状态
CrossTebFlags / SameTebFlags - TEB 标志位,如 SafeThunkCall, InDebugPrint, InitialThread 等
TxnScopeEnterCallback / TxnScopeExitCallback / TxnScopeContext - 事务回调和上下文

0x1808 - 0x1828: 资源与容器
LockCount - 线程锁计数
WowTebOffset - WOW64 TEB 偏移
ResourceRetValue - 资源返回值缓存
ReservedForWdf / ReservedForCrt - WDF 和 CRT 保留
EffectiveContainerId - 有效容器 GUID(AppContainer 标识)

其中 ProcessEnvironmentBlock 即PEB,相对TEB地址偏移为:

  • x86:0x30
  • x64:0x60

获取PEB

windows设计了段寄存器来指向TEB地址,因此可以直接读取该值获取TEB地址,再通过偏移获取PEB地址

  • x86 架构:FS 段寄存器 指向当前线程 TEB 的基址
    • FS:[0x18]:TEB 自身地址
    • FS:[0x30]: 指向 PEB 的指针
  • x64 架构: GS 段寄存器 指向当前线程 TEB 的基址
    • GS:[0x30]: TEB 自身地址
    • GS:[0x60]: 指向 PEB 的指针

汇编

x86直接使用汇编

__asm {
mov eax, fs:[0x30]
mov pPeb, eax
}

x64默认无法使用汇编,需要新建asm文件

.CODE
GetInInitializationOrderModuleList PROC
mov rax,gs:[60h]
ret
GetInInitializationOrderModuleList ENDP
END

内联函数

在 C 语言中,获取 PEB 的方法会因编译器而异。Visual Studio 在编译 64 位程序时不允许使用内联汇编,但提供了一些替代宏。而 GCC 则要求使用内联汇编

  • x86:使用 __readfsdword(offset)
  • x64:使用 __readgsqword(offset)
#include <intrin.h>

PTEB GetTEB(void) {
#ifdef _M_IX86
return (PTEB)__readfsdword(0x18); // FS:[0x18] = Self
#elif defined(_M_X64)
return (PTEB)__readgsqword(0x30); // GS:[0x30] = Self
#else
#error "Unsupported architecture"
#endif
}

PPEB GetPEB(void) {
#ifdef _M_IX86
return (PPEB)__readfsdword(0x30); // FS:[0x30] = PEB指针
#elif defined(_M_X64)
return (PPEB)__readgsqword(0x60); // GS:[0x60] = PEB指针
#else
#error "Unsupported architecture"
#endif
}

Win API

NtCurrentTeb、RtlGetCurrentPeb、NtQueryInformationProcess 获取PEB地址

武器化

在获取到PEB在内存中的地址后,可以通过修改结构体实现攻防对抗

动态API

获取PEB后通过Ldr结构定位加载的模块例如ntdll,解析导出表可以动态使用API函数,实现隐藏程序的IAT表

peb.h,定义所需要的结构体

#pragma once

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;

typedef struct _PEB {
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
VOID* Mutant; //0x8
VOID* ImageBaseAddress; //0x10
struct _PEB_LDR_DATA* Ldr; //0x18
} PEB, * PPEB;

typedef struct _PEB_LDR_DATA {
ULONG Length; //0x0
UCHAR Initialized; //0x4
VOID* SsHandle; //0x8
struct _LIST_ENTRY InLoadOrderModuleList; //0x10
struct _LIST_ENTRY InMemoryOrderModuleList; //0x20
struct _LIST_ENTRY InInitializationOrderModuleList; //0x30
VOID* EntryInProgress; //0x40
UCHAR ShutdownInProgress; //0x48
VOID* ShutdownThreadId; //0x50
} PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY {
struct _LIST_ENTRY InLoadOrderLinks; //0x0
struct _LIST_ENTRY InMemoryOrderLinks; //0x10
struct _LIST_ENTRY InInitializationOrderLinks; //0x20
VOID* DllBase; //0x30
VOID* EntryPoint; //0x38
ULONG SizeOfImage; //0x40
struct _UNICODE_STRING FullDllName; //0x48
struct _UNICODE_STRING BaseDllName; //0x58
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

PEB.cpp

#include <iostream>
#include <windows.h>
#include <peb.h>

PPEB GetPeb() {
#ifdef _WIN64
return (PPEB)__readgsqword(0x60);
#else
return (PPEB)__readfsdword(0x30);
#endif
}

LPVOID EnumModule(const wchar_t* target) {
LPVOID address = nullptr;
PPEB peb = GetPeb();
PPEB_LDR_DATA Ldr = peb->Ldr;

_LIST_ENTRY* head = &Ldr->InLoadOrderModuleList;
_LIST_ENTRY* current = Ldr->InLoadOrderModuleList.Flink;

while (head != current) {
PLDR_DATA_TABLE_ENTRY ldr_data = (PLDR_DATA_TABLE_ENTRY)current;
printf("[*] name: %S base_address: %p\n", ldr_data->BaseDllName.Buffer, ldr_data->DllBase);
if (wcscmp(ldr_data->BaseDllName.Buffer, target) == 0) {
address = ldr_data->DllBase;
printf("[!]Found module\n");
}
current = current->Flink;
}
return address;
}

FARPROC GetExportedFunctionAddress(LPVOID pBaseAddr, const char* functionName) {
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)pBaseAddr;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)pBaseAddr + dosHeader->e_lfanew);

// 获取导出表 RVA (Relative Virtual Address)
PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)pBaseAddr + ntHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);

// 获取名称表和地址表
DWORD* nameRvas = (DWORD*)((BYTE*)pBaseAddr + exportDir->AddressOfNames);
DWORD* addrRvas = (DWORD*)((BYTE*)pBaseAddr + exportDir->AddressOfFunctions);
WORD* ordinals = (WORD*)((BYTE*)pBaseAddr + exportDir->AddressOfNameOrdinals);

// 遍历名称表,找到函数名
for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) {
// 获取导出函数名
char* exportedFuncName = (char*)pBaseAddr + nameRvas[i];
if (strcmp(exportedFuncName, functionName) == 0) {
// 找到函数,返回函数地址
DWORD funcRva = addrRvas[ordinals[i]];
FARPROC funcAddr = (FARPROC)((BYTE*)pBaseAddr + funcRva);
return funcAddr;
}
}
return NULL;
}

int main()
{
const wchar_t *target_module = L"ntdll.dll";
LPVOID module_addr = EnumModule(target_module);

FARPROC paddr = GetExportedFunctionAddress(module_addr, "NtCreateProcess");
printf("[*]Addr from PEB: %p\n", paddr);

FARPROC ntaddr = GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateProcess");
printf("[*]Addr from API: %p\n", ntaddr);

return 0;
}

模块隐藏

同样通过Ldr结构修改链表,实现隐藏加载的模块。核心逻辑如下

// 我后面的下一个要指向我的下一个
ldr->InLoadOrderModuleList.Blink->Flink = ldr->InLoadOrderModuleList.Flink;
// 我下一个的后面要指向我的后面
ldr->InLoadOrderModuleList.Flink->Blink = ldr->InLoadOrderModuleList.Blink;

进程伪装

修改PEB结构体中ProcessParameters,实现伪装命令行参数、伪装启动路径等信息,结构体如下

struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                 //0x20

//0x440 bytes (sizeof)
struct _RTL_USER_PROCESS_PARAMETERS
{
ULONG MaximumLength; //0x0
ULONG Length; //0x4
ULONG Flags; //0x8
ULONG DebugFlags; //0xc
VOID* ConsoleHandle; //0x10
ULONG ConsoleFlags; //0x18
VOID* StandardInput; //0x20
VOID* StandardOutput; //0x28
VOID* StandardError; //0x30
struct _CURDIR CurrentDirectory; //0x38
struct _UNICODE_STRING DllPath; //0x50
struct _UNICODE_STRING ImagePathName; //0x60
struct _UNICODE_STRING CommandLine; //0x70
VOID* Environment; //0x80
ULONG StartingX; //0x88
ULONG StartingY; //0x8c
ULONG CountX; //0x90
ULONG CountY; //0x94
ULONG CountCharsX; //0x98
ULONG CountCharsY; //0x9c
ULONG FillAttribute; //0xa0
ULONG WindowFlags; //0xa4
ULONG ShowWindowFlags; //0xa8
struct _UNICODE_STRING WindowTitle; //0xb0
struct _UNICODE_STRING DesktopInfo; //0xc0
struct _UNICODE_STRING ShellInfo; //0xd0
struct _UNICODE_STRING RuntimeData; //0xe0
struct _RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32]; //0xf0
ULONGLONG EnvironmentSize; //0x3f0
ULONGLONG EnvironmentVersion; //0x3f8
VOID* PackageDependencyData; //0x400
ULONG ProcessGroupId; //0x408
ULONG LoaderThreads; //0x40c
struct _UNICODE_STRING RedirectionDllName; //0x410
struct _UNICODE_STRING HeapPartitionName; //0x420
ULONGLONG* DefaultThreadpoolCpuSetMasks; //0x430
ULONG DefaultThreadpoolCpuSetMaskCount; //0x438
ULONG DefaultThreadpoolThreadMaximum; //0x43c
};

参考

https://www.vergiliusproject.com/

《寒江独钓》内核学习笔记(2)

《寒江独钓》内核学习笔记(3)

《寒江独钓》内核学习笔记(4)

Peb小结

Windows安全攻防-PEB&TEB