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:

    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 .

    Also popular now: