四种方法获取Teb和Peb

获取TEB和PEB的四种方法


TEB和PEB结构: TEB and PEB x64 and TEB and PEB x86

两种获取当前进程的TEB和PEB两种跨进程获取PEB,前两种方法可以获得当前进程的TEB和PEB,后两种方法是获得指定进程的PEB。第四种方法其实在x86体系下也可以获得TEB,同理x64肯定也行,但是暂时用不着,我也没去试试。

方法一:__readgsqword() 和 __readfsdword()

前置知识

__readgsqword(x)用于获取相对GS段偏移量为x处的八字节大小的值。同理__readfsdword(x)用于获取相对FS段开始偏移量为x处的四字节大小的值。

根据实验可知:

  • 在x64架构环境下,GS + 30h处存储的是Teb结构体的基地址,GS + 60h处存储的是Peb结构体的基地址。
  • 在x86架构环境下,FS + 18h处存储的是Teb结构体的基地址,FS + 30h处存储的是Peb结构体的基地址。

实现

语法:

/*
* 读GS
*/
unsigned char __readgsbyte(
   unsigned long Offset
);
unsigned short __readgsword(
   unsigned long Offset
);
unsigned long __readgsdword(
   unsigned long Offset
);
unsigned __int64 __readgsqword(
   unsigned long Offset
);


/*
* 读FS
*/
unsigned char __readfsbyte(
   unsigned long Offset
);
unsigned short __readfsword(
   unsigned long Offset
);
unsigned long __readfsdword(
   unsigned long Offset
);
unsigned __int64 __readfsqword(
   unsigned long Offset
);

具体实现与测试

#include <iostream>
#include <windows.h>

using namespace std;
void* GetTebAddress()
{
#ifdef _WIN64
	return (void*)__readgsqword(0x30);
#else
	return (void*)__readfsdword(0x18);
#endif
}

void* GetPebAddress()
{
#ifdef _WIN64
	return (void*)__readgsqword(0x60);
#else
	return (void*)__readfsdword(0x30);
#endif

}
void test()
{
    //to get the image base
	HANDLE imagebase = GetModuleHandle(NULL);
	if (imagebase != NULL) std::cout <<"imagebase:0x" << hex << imagebase << endl;
#ifdef _WIN64  

    //
    //
            /*x64 Architecture*/
    //
    //
    
    //get the address of teb and peb
    void* teb = GetTebAddress();
    void* peb = GetPebAddress();
    std::cout << "teb address:0x" << hex << (size_t)teb << endl;
    std::cout << "peb address:0x" << hex << (size_t)peb << endl;

    //get peb by teb to test if the return value of `GetTebAddress()` is the address of teb
    size_t pebByteb = *(size_t*)((size_t)teb + 0x60);
    if (pebByteb == (size_t)peb)
    {
        std::cout << "Yes the GetTebAddress works corresctly" << endl;
        std::cout << "pebByTeb:0x" << hex << pebByteb << endl;
    }
    else
    {
        std::cout << "No the of GetTebAddress works incorrectly" << endl;
        std::cout << "pebByTeb:0x" << hex << pebByteb << endl;
    }

    //get imagebase by peb test if the `GetPebAddress()` works correctly
    size_t imagebaseBypeb = *(size_t*)((size_t)peb + 0x10);
    if (imagebaseBypeb == (size_t)imagebase)
    {
        std::cout << "Yes the GetPebAddress works correctly" << endl;
        std::cout << "imagebaseBypeb:0x" << hex << imagebaseBypeb << endl;
    }
    else
    {
        std::cout << "No the GetPebAddress works incorrectly" << endl;
        std::cout << "imagebaseBypeb:0x" << hex << imagebaseBypeb << endl;
    }
#else
    //
    //
            /*x86 Architecture*/
    //
    //

    //get the address of teb and peb
    void* teb = GetTebAddress();
    void* peb = GetPebAddress();
    std::cout << "teb address:0x" << hex << teb << endl;
    std::cout << "peb address:0x" << hex << peb << endl;

    //get peb by teb to test if the return value of `GetTebAddress()` is the address of teb
    DWORD32 pebByteb = *(DWORD32*)((DWORD32)teb + 0x30);
    if (pebByteb == (size_t)peb)
    {
        std::cout << "Yes the return value of GetTebAddress is corresct" << endl;
        std::cout << "pebByTeb:0x" << hex << pebByteb << endl;
    }
    else
    {
        std::cout << "No the return value of GetTebAddress is incorrect" << endl;
        std::cout << "pebByTeb:0x" << hex << pebByteb << endl;
    }

    //get imagebase by peb test if the `GetPebAddress()` works correctly
    DWORD32 imagebaseBypeb = *(DWORD32*)((DWORD32)peb + 0x8);
    if (imagebaseBypeb == (DWORD32)imagebase)
    {
        std::cout << "Yes the GetPebAddress works correctly" << endl;
        std::cout << "imagebaseBypeb:0x" << hex << imagebaseBypeb << endl;
    }
    else
    {
        std::cout << "No the GetPebAddress works incorrectly" << endl;
        std::cout << "imagebaseBypeb:0x" << hex << imagebaseBypeb << endl;
    }
#endif
}

int main()
{
	test();
	return 0;
}

运行结果:

x86

在这里插入图片描述
x64
在这里插入图片描述

单词打错了,不想改了,请各位谅解【悲】
解释

GetModduleHandle(NULL)返回值为创建当前进程的可执行文件载入内存的基地址,由Teb结构可知starting address of teb + 60h(x64)/30h(x86)这个地址存储了Peb结构体的基地址。由Peb结构体可知starting address of peb + 10h(x64)/8h(x86)这个地址存储的值是image base。通过这些特性就可以通过APIGetModuleHandle()来测试得到的Teb和Peb的地址是否准确。

方法二:NtCurrentTeb()获取当前进程的Teb

前置知识

NtCurrentTeb()用于返回当前线程的线程环境块(TEB)指针。

  • 在x64架构环境下, address of TEB + 60h处存储的是PEB结构体的基地址。
  • 在x86架构环境下,address of TEB + 30h处存储的是PEB结构体的基地址。

实现

语法

_TEB * NtCurrentTeb();

具体实现与测试

#include <iostream>
#include <windows.h>

using namespace std;


void testfor_2()
{
    //to get the image base
    HANDLE imageBase = GetModuleHandle(NULL);
    if (imageBase != NULL) std::cout << "imageBase:0x" << hex << imageBase << endl;
#ifdef _WIN64
    
    //
    //
            /*x64 Architecture*/
    //
    //
    
    //use API NtCurrentTeb() to get the TEB of current thread
    size_t TebAddr = (size_t)NtCurrentTeb();
    //to get the PEB
    size_t PebAddr = *(size_t*)(TebAddr + 0x60);
    //to get the image base
    size_t imageBase_byAPI = *(size_t*)(PebAddr + 0x10);

    //test the imagebase I get
    if (imageBase_byAPI == (size_t)imageBase)
    {
        std::cout << "Yes the NtCurrentTeb works correctly\n";
        std::cout << "imageBase_byAPI:0x" << hex << imageBase_byAPI << endl;
    }
    else
    {
        std::cout << "No the NtCurrentTeb works incorrectly\n";
        std::cout << "imageBase_byAPI:0x" << hex << imageBase_byAPI << endl;
    }
#else
    
    //
    //
            /*x86 Architecture*/
    //
    //
    
    //use API NtCurrentTeb() to get the TEB of current thread
    DWORD32 TebAddr = (DWORD32)NtCurrentTeb();
    //to get the PEB
    DWORD32 PebAddr = *(DWORD32*)(TebAddr + 0x30);
    //to get the image base
    DWORD32 imageBase_byAPI = *(DWORD32*)(PebAddr + 0x8);

    //test the imagebase I get
    if (imageBase_byAPI == (DWORD32)imageBase)
    {
        std::cout << "Yes the NtCurrentTeb works correctly\n";
        std::cout << "imageBase_byAPI:0x" << hex << imageBase_byAPI << endl;
    }
    else
    {
        std::cout << "No the NtCurrentTeb works incorrectly\n";
        std::cout << "imageBase_byAPI:0x" << hex << imageBase_byAPI << endl;
    }
#endif 

}

int main()
{
	testfor_2();
	return 0;
}

x86

在这里插入图片描述

x64

在这里插入图片描述

解释

TEB与PEB的关系在第一种方法中已经讲了,可以通过TEB获得指向PEB的指针,再通过PEB获得程序的image base。请记住,这个方法将用于之后的方法准确性验证中。

方法三:NtQueryInformationProcess()

前置知识

  • 该函数在ntdll.dll中,所以需要先用LoadLibrary把这个dll导进来。
  • 这个函数用于获取指定进程的信息,可以用于获取某一个指定进程的信息,讲这些信息放到一个结构体中。

以下是存储指定进程信息的结构体

typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG_PTR AffinityMask;
    KPRIORITY BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;

可以惊讶地发现,第二个元素是该进程地PEB,而address of PEB + 0x10(x64)/0x8(x86)image base

补充:这种方式只能得到PEB,没有办法得到TEB,有些情况下该方法适用。

注意:在以下的实验中,我会先创建一个悬挂进程,然后去得到该悬挂进程的PEB。

实现

语法

__kernel_entry NTSTATUS NtQueryInformationProcess(
  [in]            HANDLE           ProcessHandle,
  [in]            PROCESSINFOCLASS ProcessInformationClass,
  [out]           PVOID            ProcessInformation,
  [in]            ULONG            ProcessInformationLength,
  [out, optional] PULONG           ReturnLength
);

具体实现

#include <windows.h>
#include<iostream>
#include<winternl.h>
using namespace std;

typedef NTSTATUS(WINAPI *fnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
void testfor_3()
{
#ifdef DEBUG
    size_t susProImageBase;
#else
    DWORD32 susProImageBase;
#endif

    //create a suspended process by program2.exe
    //the program2.exe is just for test don't care what it is
    TCHAR appName[] = TEXT("program2.exe");
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    ZeroMemory(&pi, sizeof(pi));
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(STARTUPINFO);
    bool sucess = CreateProcess(
        appName,
        NULL,
        NULL,
        NULL,
        FALSE,
        CREATE_SUSPENDED,
        NULL,
        NULL,
        &si,
        &pi
    );
    //if false return
    if (sucess == FALSE) {
        printf("failed to create new process\n");
        return;
    }

    //load the ntdll.dll into the current process.
    HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
    if (hNtdll == NULL) {
        printf("Error:fail to connect ntdll.dll\n");
        return;
    }

    //get the target function NtQueryInformationProcess
    fnNtQueryInformationProcess fNtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");
    //if fail, return
    if (fNtQueryInformationProcess == NULL) {
        printf("Error:we can not get the function named ZwUnmapViewOfSection or fNtQueryInformationProcess\n");
        return;
    }

    //get the PEB of the suspended process
    PROCESS_BASIC_INFORMATION info{ 0 };
    NTSTATUS stat = fNtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &info, sizeof(info), NULL);
}

解释

这个需要和第四种方法进行同步测试,相互验证以确保这个确实能够得到指定进程的PEB。所以具体的验证和解释将在下一种方法的代码中。已知第三种方法是肯定能获取这个悬挂了进程的PEB的,于是读者可以通过第三种方法去获取PEB以测试第四种方法获得的PEB地址是否准确。

方法四:通过GetThreadContext()和CONTEXT获得PEB

前置知识

说明:这种方法也可以跨进程获取TEB和PEB(甚至可以说是这四种方法中最高明的一种方法),下面的测试会创建一个进程并且获取它的PEB(不写TEB的主要原因是我困了)。

  • 通过APIGetThreadContext()获取指定线程的context,context中Ebx(x86)/Rdx(x64)中存储了PEB的地址。

实现

语法

BOOL GetThreadContext(
  [in]      HANDLE    hThread,
  [in, out] LPCONTEXT lpContext
);

//
//x64
//

typedef struct _CONTEXT {
  DWORD64 P1Home;
  DWORD64 P2Home;
  DWORD64 P3Home;
  DWORD64 P4Home;
  DWORD64 P5Home;
  DWORD64 P6Home;
  DWORD   ContextFlags;
  DWORD   MxCsr;
  WORD    SegCs;
  WORD    SegDs;
  WORD    SegEs;
  WORD    SegFs;
  WORD    SegGs;
  WORD    SegSs;
  DWORD   EFlags;
  DWORD64 Dr0;
  DWORD64 Dr1;
  DWORD64 Dr2;
  DWORD64 Dr3;
  DWORD64 Dr6;
  DWORD64 Dr7;
  DWORD64 Rax;
  DWORD64 Rcx;
  DWORD64 Rdx;
  DWORD64 Rbx;
  DWORD64 Rsp;
  DWORD64 Rbp;
  DWORD64 Rsi;
  DWORD64 Rdi;
  DWORD64 R8;
  DWORD64 R9;
  DWORD64 R10;
  DWORD64 R11;
  DWORD64 R12;
  DWORD64 R13;
  DWORD64 R14;
  DWORD64 R15;
  DWORD64 Rip;
  union {
    XMM_SAVE_AREA32 FltSave;
    NEON128         Q[16];
    ULONGLONG       D[32];
    struct {
      M128A Header[2];
      M128A Legacy[8];
      M128A Xmm0;
      M128A Xmm1;
      M128A Xmm2;
      M128A Xmm3;
      M128A Xmm4;
      M128A Xmm5;
      M128A Xmm6;
      M128A Xmm7;
      M128A Xmm8;
      M128A Xmm9;
      M128A Xmm10;
      M128A Xmm11;
      M128A Xmm12;
      M128A Xmm13;
      M128A Xmm14;
      M128A Xmm15;
    } DUMMYSTRUCTNAME;
    DWORD           S[32];
  } DUMMYUNIONNAME;
  M128A   VectorRegister[26];
  DWORD64 VectorControl;
  DWORD64 DebugControl;
  DWORD64 LastBranchToRip;
  DWORD64 LastBranchFromRip;
  DWORD64 LastExceptionToRip;
  DWORD64 LastExceptionFromRip;
} CONTEXT, *PCONTEXT;


//
//x86
//

typedef struct _CONTEXT {

    DWORD ContextFlags;
    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;
    FLOATING_SAVE_AREA FloatSave;
    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;
    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;
    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;
    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;


注意:在这里我们只需要关注RdxEbx

具体代码实现以及测试

代码实现

#include <windows.h>
#include<iostream>
#include<winternl.h>
using namespace std;

void testfor_4()
{
#ifdef _WIN64
#define PEBpointer Rdx
#else
#define PEBpointer Ebx
#endif

    //create a suspended process by program2.exe
    //the program2.exe is just for test don't care what it is
    TCHAR appName[] = TEXT("program2.exe");
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    ZeroMemory(&pi, sizeof(pi));
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(STARTUPINFO);
    bool sucess = CreateProcess(
        appName,
        NULL,
        NULL,
        NULL,
        FALSE,
        CREATE_SUSPENDED,
        NULL,
        NULL,
        &si,
        &pi
    );
    //if false return
    if (sucess == FALSE) {
        printf("failed to create new process\n");
        return;
    }


    // Get the context of the suspended process
    CONTEXT context;
    context.ContextFlags = CONTEXT_FULL;
    GetThreadContext(pi.hThread, &context);

    //close the handle
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

}


int main()
{
	testfor_4();
	return 0;
}

测试

注意:在测试的时候用来创建悬挂进程的程序,需要使用x86程序和x64程序两类程序测试。

#include <windows.h>
#include<iostream>
#include<winternl.h>
using namespace std;

void testfor_4()
{
#ifdef _WIN64
#define PEBpointer Rdx
#define format size_t
#else
#define PEBpointer Ebx
#define format DWORD32
#endif

    //create a suspended process by program2.exe
    //the program2.exe is just for test don't care what it is
    TCHAR appName[] = TEXT("program2.exe");
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    ZeroMemory(&pi, sizeof(pi));
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(STARTUPINFO);
    bool sucess = CreateProcess(
        appName,
        NULL,
        NULL,
        NULL,
        FALSE,
        CREATE_SUSPENDED,
        NULL,
        NULL,
        &si,
        &pi
    );
    //if false return
    if (sucess == FALSE) {
        printf("failed to create new process\n");
        return;
    }


    HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
    if (hNtdll == NULL) {
        printf("Error:fail to connect ntdll.dll\n");
        return;
    }

    //get the target function NtQueryInformationProcess
    fnNtQueryInformationProcess fNtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");
    //if fail, return
    if (fNtQueryInformationProcess == NULL) {
        printf("Error:we can not get the function named ZwUnmapViewOfSection or fNtQueryInformationProcess\n");
        return;
    }

    //get the PEB of the suspended process
    PROCESS_BASIC_INFORMATION info{ 0 };
    NTSTATUS stat = fNtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &info, sizeof(info), NULL);


    // Get the context of the suspended process
    CONTEXT context;
    context.ContextFlags = CONTEXT_FULL;
    GetThreadContext(pi.hThread, &context);

    if (context.PEBpointer == (format)info.PebBaseAddress)
    {
        cout << "it works\n";
        cout << "PEBpointer:0x" << hex << context.PEBpointer << endl;
        cout << "info.PebBaseAddress:0x" << hex << context.PEBpointer << endl;
    }
    else
    {
        cout << "it doesn't work\n";
        cout << "PEBpointer:0x" << hex << context.PEBpointer << endl;
        cout << "info.PebBaseAddress:0x" << hex << info.PebBaseAddress << endl;
    }

    //close the handle
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

}


int main()
{
	testfor_4();
	return 0;
}

x86

在这里插入图片描述
x64

在这里插入图片描述

解释

首先,用CreateProcess()创建了一个进程,然后通过GetThreadContext()得到了指定进程的CONTEXT然后通过结构体中的相关值获得了指定进程的PEB地址。通过方法三和方法四的相互验证可以得出一个相对准确的结论——这两种方法都能获取指定进程的PEB。

总结

留下来了一个问题:如何获取指定线程的TEB,这个目前只知道32位如何获得,64位的估计也是通过方法四更容易,但是TEB的地址存在CONTEXT的哪个元素里面我还没去找(太困了)。

以此记录我这段时间学到的四种获取TEB和PEB的方式,其实有很多细节可以深究但是目前没有时间深究,先挖个坑,到时候有时间了填上去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值