📜 ⬆️ ⬇️

Work with PEB and TEB

PEB - structure of the process in windows, filled with the loader at the stage of creating the process, which contains information about the environment, loaded modules (LDR_DATA), basic information on the current module and other critical data necessary for the operation of the process. Many system api windows that receive information about modules (libraries) in a process call ReadProcessMemory to read 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 in 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 view the ReactOS sources, or take WinDBG and examine it yourself. The goal was to change the PEB of an alien process.

TEB is as follows:

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 stream, you can use 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. 

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 bits
 //  - 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 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 the address 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. 

The base address of PEB for a specific process is obtained as

 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 bit, api functions like GetModuleHandleEx and EnumProcessModules, QueryFullProcessImageName will not produce the desired result, since they call ReadProcessMemory to read PEB. There is a lot of code, so the manipulations with PEB (reading from the process, changing and writing) are designed as a simple test class, which can be found here .

Source: https://habr.com/ru/post/187226/


All Articles