Work with PEB and TEB
PEB is the process structure in windows, it is filled by the loader at the stage of the process creation, which contains information about the environment, loaded modules (LDR_DATA), basic information on the current module and other critical data necessary for the process to function. Many system windows api that receive information about the modules (libraries) in a process call ReadProcessMemory to read the information from the PEB of the desired process.
TEB - a structure that is used to store information about threads in the current process, each thread has its own TEB. Wow64 processes on Windows have two Process Environment Blocks and two Thread Environment Blocks. TEB is created by the MmCreateTeb function, PEB is created by the MmCreatePeb function, if the creation process is interesting, you can look at the ReactOS sources, or take WinDBG and examine it yourself. The goal was to change the PEB of another process.
TEB has the following form:
To get the TEB of a specific thread, you can use NtQueryInformationThread.
on MSDN, PEB is described as follows for a 32 bit process:
and for 64 bit:
PEB can be obtained as follows:
Getting the kernel32 base and GetProcAddress address:
The base PEB address for a specific process is obtained as follows:
An interesting observation is that if you spoil LDR_DATA a little, api functions such as GetModuleHandleEx and EnumProcessModules, QueryFullProcessImageName will not produce the desired result, since they call ReadProcessMemory to read PEB. There is a lot of code, therefore manipulations with PEB (reading from the process, changing and writing) are arranged in the form of a simple test class, which can be found here .
TEB - a structure that is used to store information about threads in the current process, each thread has its own TEB. Wow64 processes on Windows have two Process Environment Blocks and two Thread Environment Blocks. TEB is created by the MmCreateTeb function, PEB is created by the MmCreatePeb function, if the creation process is interesting, you can look at the ReactOS sources, or take WinDBG and examine it yourself. The goal was to change the PEB of another process.
TEB has the following form:
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.
To get the TEB of a specific thread, you can use NtQueryInformationThread.
#include
#include
#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.
on MSDN, PEB is described as follows for a 32 bit process:
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;
and for 64 bit:
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;
my project uses the following structure for 32 and 64 bit
//автор структуры - http://blog.rewolf.pl/blog/?p=573
#pragma pack(push)
#pragma pack(1)
template
struct LIST_ENTRY_T
{
T Flink;
T Blink;
};
template
struct UNICODE_STRING_T
{
union
{
struct
{
WORD Length;
WORD MaximumLength;
};
T dummy;
};
T _Buffer;
};
template
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 CSDVersion;
T ActivationContextData;
T ProcessAssemblyStorageMap;
T SystemDefaultActivationContextData;
T SystemAssemblyStorageMap;
T MinimumStackCommit;
};
typedef _PEB_T PEB32;
typedef _PEB_T PEB64;
#pragma pack(pop)
PEB can be obtained as follows:
// воспользуемся 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]
Getting the kernel32 base and GetProcAddress address:
//х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.
The base PEB address for a specific process is obtained as follows:
typedef enum _PROCESSINFOCLASS {
ProcessBasicInformation = 0
} PROCESSINFOCLASS;
status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &dwLength);
if(status != 0x0)
{
printf("NtQueryInformationProcess Error 0x%x\n", status);
exit(EXIT_FAILURE);
}
printf("PEB address : 0x%x\n", pbi.PebBaseAddress);
An interesting observation is that if you spoil LDR_DATA a little, api functions such as GetModuleHandleEx and EnumProcessModules, QueryFullProcessImageName will not produce the desired result, since they call ReadProcessMemory to read PEB. There is a lot of code, therefore manipulations with PEB (reading from the process, changing and writing) are arranged in the form of a simple test class, which can be found here .