Pull to refresh

Работа с PEB и TEB

Reading time 6 min
Views 38K
PEB — структура процесса в windows, заполняется загрузчиком на этапе создания процесса, которая содержит информацию о окружении, загруженных модулях (LDR_DATA), базовой информации по текущему модулю и другие критичные данные необходимые для функционирования процесса. Многие системные api windows, получающие информацию о модулях (библиотеках) в процессе, вызывают ReadProcessMemory для считывания информации из PEB нужного процесса.

TEB — структура которая используется для хранения информации о потоках в текущем процессе, каждый поток имеет свой TEB. Wow64 процессы в Windows имеют два Process Environment Blocks и два Thread Environment Blocks. TEB создается функцией MmCreateTeb, PEB создается функцией MmCreatePeb, если интересен процесс создания, то можно посмотреть исходники ReactOS, или взять WinDBG и исследовать самостоятельно. Перед собой была поставлена цель в изменении PEB чужого процесса.

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;

в моем проекте используется следующая структура для 32 и 64 бит
//автор структуры - 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%x\n", status);
		exit(EXIT_FAILURE);
    }
    
    printf("PEB address : 0x%x\n", pbi.PebBaseAddress);

Интересное наблюдение, что если немного «испортить» LDR_DATA, такие api функции как GetModuleHandleEx и EnumProcessModules, QueryFullProcessImageName не будут выдавать нужный результат, так как они вызывают ReadProcessMemory для чтения PEB. Кода много, поэтому манипуляции с PEB (чтение из процесса, изменение и запись) оформлены в виде простого тестового класса, который можно найти тут.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+16
Comments 4
Comments Comments 4

Articles