PEB — структура процесса в windows, заполняется загрузчиком на этапе создания процесса, которая содержит информацию о окружении, загруженных модулях (LDR_DATA), базовой информации по текущему модулю и другие критичные данные необходимые для функционирования процесса. Многие системные api windows, получающие информацию о модулях (библиотеках) в процессе, вызывают ReadProcessMemory для считывания информации из PEB нужного процесса.
TEB — структура которая используется для хранения информации о потоках в текущем процессе, каждый поток имеет свой TEB. Wow64 процессы в Windows имеют два Process Environment Blocks и два Thread Environment Blocks. TEB создается функцией MmCreateTeb, PEB создается функцией MmCreatePeb, если интересен процесс создания, то можно посмотреть исходники ReactOS, или взять WinDBG и исследовать самостоятельно.
TEB имеет следующий вид:
typedef struct _CLIENT_ID {
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION {
typedef PVOID KPRIORITY;
NTSTATUS ExitStatus; PVOID TebBaseAddress; CLIENT_ID ClientId; KAFFINITY AffinityMask; KPRIORITY Priority; KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
[TEB+0] Указатель на первый SEH на стэке.
[TEB+4] Указатель на конец области памяти, выделенных на стеке.
[TEB+8] Указатель на начало области памяти выделенных на стеке, для контроля исключений переполнения стека.
[TEB+18] Адрес текущей TEB.
[TEB+30] Адрес PEB.
Для получения TEB конкретного потока можно воспользоваться NtQueryInformationThread.
#include <Windows.h>
#include <stdio.h>
#pragma comment(lib,"ntdll.lib")
typedef struct _CLIENT_ID {
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION {
typedef PVOID KPRIORITY;
NTSTATUS ExitStatus; PVOID TebBaseAddress; CLIENT_ID ClientId; KAFFINITY AffinityMask; KPRIORITY Priority; KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
typedef enum _THREADINFOCLASS
{
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair_Reusable,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger,
ThreadBreakOnTermination,
MaxThreadInfoClass
} THREADINFOCLASS;
THREADINFOCLASS ThreadInformationClass;
extern "C"
{
NTSTATUS WINAPI NtQueryInformationThread(
_In_ HANDLE ThreadHandle,
_In_ THREADINFOCLASS ThreadInformationClass,
_Inout_ PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_Out_opt_ PULONG ReturnLength
);
}
THREAD_BASIC_INFORMATION ThreadInfo;
DWORD ntstatus = NtQueryInformationThread(
GetCurrentThread(), // хэндл на поток
ThreadBasicInformation,
&ThreadInfo, //ThreadInfo.TebBaseAddress содержит адрес теба для указанного потока.
sizeof(THREAD_BASIC_INFORMATION),
0
);
// Если нужен teb только своего потока, использовать __readfsdword(0x18) в 32 бит или __readgsqword(0x30) в х64.
на MSDN PEB описывается следующим образом для 32 битного процесса:
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;
и для 64 битного:
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[21];
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved3[520];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved4[136];
ULONG SessionId;
} PEB;
//автор структуры - http://blog.rewolf.pl/blog/?p=573
#pragma pack(push)
#pragma pack(1)
template <class T>
struct LIST_ENTRY_T
{
T Flink;
T Blink;
};
template <class T>
struct UNICODE_STRING_T
{
union
{
struct
{
WORD Length;
WORD MaximumLength;
};
T dummy;
};
T _Buffer;
};
template <class T, class NGF, int A>
struct _PEB_T
{
union
{
struct
{
BYTE InheritedAddressSpace;
BYTE ReadImageFileExecOptions;
BYTE BeingDebugged;
BYTE _SYSTEM_DEPENDENT_01;
};
T dummy01;
};
T Mutant;
T ImageBaseAddress;
T Ldr;
T ProcessParameters;
T SubSystemData;
T ProcessHeap;
T FastPebLock;
T _SYSTEM_DEPENDENT_02;
T _SYSTEM_DEPENDENT_03;
T _SYSTEM_DEPENDENT_04;
union
{
T KernelCallbackTable;
T UserSharedInfoPtr;
};
DWORD SystemReserved;
DWORD _SYSTEM_DEPENDENT_05;
T _SYSTEM_DEPENDENT_06;
T TlsExpansionCounter;
T TlsBitmap;
DWORD TlsBitmapBits[2];
T ReadOnlySharedMemoryBase;
T _SYSTEM_DEPENDENT_07;
T ReadOnlyStaticServerData;
T AnsiCodePageData;
T OemCodePageData;
T UnicodeCaseTableData;
DWORD NumberOfProcessors;
union
{
DWORD NtGlobalFlag;
NGF dummy02;
};
LARGE_INTEGER CriticalSectionTimeout;
T HeapSegmentReserve;
T HeapSegmentCommit;
T HeapDeCommitTotalFreeThreshold;
T HeapDeCommitFreeBlockThreshold;
DWORD NumberOfHeaps;
DWORD MaximumNumberOfHeaps;
T ProcessHeaps;
T GdiSharedHandleTable;
T ProcessStarterHelper;
T GdiDCAttributeList;
T LoaderLock;
DWORD OSMajorVersion;
DWORD OSMinorVersion;
WORD OSBuildNumber;
WORD OSCSDVersion;
DWORD OSPlatformId;
DWORD ImageSubsystem;
DWORD ImageSubsystemMajorVersion;
T ImageSubsystemMinorVersion;
union
{
T ImageProcessAffinityMask;
T ActiveProcessAffinityMask;
};
T GdiHandleBuffer[A];
T PostProcessInitRoutine;
T TlsExpansionBitmap;
DWORD TlsExpansionBitmapBits[32];
T SessionId;
ULARGE_INTEGER AppCompatFlags;
ULARGE_INTEGER AppCompatFlagsUser;
T pShimData;
T AppCompatInfo;
UNICODE_STRING_T<T> CSDVersion;
T ActivationContextData;
T ProcessAssemblyStorageMap;
T SystemDefaultActivationContextData;
T SystemAssemblyStorageMap;
T MinimumStackCommit;
};
typedef _PEB_T<DWORD, DWORD64, 34> PEB32;
typedef _PEB_T<DWORD64, DWORD, 30> PEB64;
#pragma pack(pop)
PEB можно получить следующим образом:
// воспользуемся intrinsics функциями, так как в 12 студии инлайн асм для х64 компиляции отсутствует.
// адрес PEB - константа для всех процессов в системе.
#if defined _M_IX86
int offset = 0x30;
DWORD peb __readfsdword(PEB) //mov eax, fs:[0x30]
#elif defined _M_X64
//На 64 битных windows сегментный регистр GS хранит указатель на PEB в GS:[0x60]
int offset = 0x60;
DWORD64 peb =__readgsqword(PEB); //mov rax, gs:[0x60]
Получение базы для kernel32 и адрес GetProcAddress:
//х64, проверено, работать будет начиная с xp x64 sp2 до последней win 8.
typedef FARPROC (WINAPI * GetProcAddress_t) (HMODULE, const char *);
struct LDR_MODULE
{
LIST_ENTRY e[3];
HMODULE base;
void *entry;
UINT size;
UNICODE_STRING dllPath;
UNICODE_STRING dllname;
};
int offset = 0x60;
int ModuleList = 0x18;
int ModuleListFlink = 0x18;
int KernelBaseAddr = 0x10;
INT_PTR peb =__readgsqword(offset);
INT_PTR mdllist=*(INT_PTR*)(peb+ ModuleList);
INT_PTR mlink =*(INT_PTR*)(mdllist+ ModuleListFlink);
INT_PTR krnbase=*(INT_PTR*)(mlink+ KernelBaseAddr);
LDR_MODULE *mdl=(LDR_MODULE*)mlink;
do
{
mdl=(LDR_MODULE*)mdl->e[0].Flink;
if(mdl->base!=NULL)
{
if(!lstrcmpiW(mdl->dllname.Buffer,L"kernel32.dll")) //сравниваем имя библиотеки в буфере с необходимым
{
break;
}
}
} while (mlink!=(INT_PTR)mdl);
kernel32base = (HMODULE)mdl->base;
ULONG_PTR base = (ULONG_PTR) kernel32base;
IMAGE_NT_HEADERS * pe = PIMAGE_NT_HEADERS(base + PIMAGE_DOS_HEADER(base)->e_lfanew);
IMAGE_EXPORT_DIRECTORY * exportDir = PIMAGE_EXPORT_DIRECTORY(base + pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD * namePtr = (DWORD *) (base + exportDir->AddressOfNames); // Адрес имен функций.
WORD * ordPtr = (WORD *) (base + exportDir->AddressOfNameOrdinals); //Адрес имени для функции.
for(;_stricmp((const char *) (base +*namePtr), "GetProcAddress"); ++namePtr, ++ordPtr);
DWORD funcRVA = *(DWORD *) (base + exportDir->AddressOfFunctions + *ordPtr * 4);
auto myGetProcAddress = (GetProcAddress_t) (base + funcRVA); //получили адрес GetProcAddress.
Базовый адрес PEB для определенного процесса получаем так:
typedef enum _PROCESSINFOCLASS {
ProcessBasicInformation = 0
} PROCESSINFOCLASS;
status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &dwLength);
if(status != 0x0)
{
printf("NtQueryInformationProcess Error 0x%xn", status);
exit(EXIT_FAILURE);
}
printf("PEB address : 0x%xn", pbi.PebBaseAddress);
Интересное наблюдение, что если немного «испортить» LDR_DATA, такие api функции как GetModuleHandleEx и EnumProcessModules, QueryFullProcessImageName не будут выдавать нужный результат, так как они вызывают ReadProcessMemory для чтения PEB. Кода много, поэтому манипуляции с PEB оформлены в виде простого тестового класса, который можно найти тут.
Автор: Dinisoid