x86x64用户层基于PEB遍历模块信息

selph
selph
发布于 2021-09-04 / 2290 阅读
0
0

x86x64用户层基于PEB遍历模块信息

这里偷懒了,只获取了当前进程的模块信息,如果要获取其他进程的模块信息,只要得到其他进程的Peb结构地址即可,剩下流程都是一样的了

实验环境:

  • Windows 10 21H1 家庭版
  • Visual Studio 2019

image.png

参考自:[原创]PEB 结构:获取模块 kernel32 基址技术及原理分析-软件逆向-看雪论坛-安全社区 | 安全招聘 |bbs.pediy.com

这里的图是 x86 系统的结构体,其实 x64 也差不多,只是位置不一样

找到模块信息的存储位置

PEB 是进程环境块,存储进程的信息的,地址在结构 TEB 中

PEB 中有一个结构是 LDR,LDR 里存储了进程模块的信息(存储着该进程所有模块数据的链表)

思路:这里要通过 TEB 找到 PEB 中的 LDR,然后遍历 LDR 里面的双向链表从而遍历所有模块

x86 下,fs 寄存器指的是 TEB 结构体首地址,fs:[18h]存的是 TEB 地址,fs:[30h]存的是 PEB 地址

x64 下,PEB 的位置在 gs:[60h]的位置

用到的结构体信息(以 x86 的结构体为例):

PEB:

struct _PEB
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2 反调试用的
...
    struct _PEB_LDR_DATA* Ldr;                                              //0xc LDR结构
...

这里的 0xC 偏移位置存储着 Ldr

Ldr:

//0x30 bytes (sizeof)
struct _PEB_LDR_DATA
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    VOID* SsHandle;                                                         //0x8
    struct _LIST_ENTRY InLoadOrderModuleList;                               //0xc
    struct _LIST_ENTRY InMemoryOrderModuleList;                             //0x14
    struct _LIST_ENTRY InInitializationOrderModuleList;                     //0x1c
    VOID* EntryInProgress;                                                  //0x24
    UCHAR ShutdownInProgress;                                               //0x28
    VOID* ShutdownThreadId;                                                 //0x2c
}; 

这里的 0xc 处存着模块链表,每个链表结点都指着一个 _LDR_DATA_TABLE_ENTRY 结构:

//0xa8 bytes (sizeof)
struct _LDR_DATA_TABLE_ENTRY
{
    struct _LIST_ENTRY InLoadOrderLinks;                                    //0x0
    struct _LIST_ENTRY InMemoryOrderLinks;                                  //0x8
    struct _LIST_ENTRY InInitializationOrderLinks;                          //0x10
    VOID* DllBase;                                                          //0x18 模块基址
    VOID* EntryPoint;                                                       //0x1c
    ULONG SizeOfImage;                                                      //0x20
    struct _UNICODE_STRING FullDllName;                                     //0x24 模块路径+名称
    struct _UNICODE_STRING BaseDllName;                                     //0x2c 模块名称
...
}; 

这里存储着的就是模块信息了

这三个链表之间的关系,我再拿一张某师傅的图:

image.png

entry.cpp:遍历模块

#include "entry.h"
void GetModuleInfo() {
	_PEB_LDR_DATA* pLdr = 0;
	__asm {
		mov eax, dword ptr fs : [0x30] ;
		add eax, 0x0C;
		mov eax,[eax];
		mov pLdr, eax;
	}
	_LIST_ENTRY* pModuleList = (_LIST_ENTRY*)&(pLdr->InLoadOrderModuleList);
	_LDR_DATA_TABLE_ENTRY* pLdrData = (_LDR_DATA_TABLE_ENTRY*)(pModuleList->Flink);
	while ((int*)pModuleList != (int*)pLdrData) {
		printf("Module Name:%ws\nModule Base = %08x\n", pLdrData->FullDllName.Buffer, pLdrData->DllBase);
		pLdrData = (_LDR_DATA_TABLE_ENTRY*)(pLdrData->InLoadOrderLinks.Flink);
	}
}
int main() {
	GetModuleInfo();
	return 0;
}

这里通过内联汇编来得到 Ldr 地址

代码实践

这里把 x86 和 x64 的写在一起了,就一起看吧

entry.h:

声明一下用到的结构体(因为 R3 下这些结构体 windows.h 里没有,需要自己声明一下)

注意:x86 和 x64 的结构体大小不一样,需要分开单独声明

#pragma once
#include <stdio.h>
#include <Windows.h>
#include <process.h>

#if defined(_WIN64)
extern "C" PVOID64 _cdecl GetPebLdr(void);

//0x10 bytes (sizeof)
struct _UNICODE_STRING
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    USHORT* Buffer;                                                         //0x8
};

//0x58 bytes (sizeof)
struct _PEB_LDR_DATA64
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    VOID* SsHandle;                                                         //0x8
    struct LIST_ENTRY64 InLoadOrderModuleList;                               //0x10
    struct LIST_ENTRY64 InMemoryOrderModuleList;                             //0x20
    struct LIST_ENTRY64 InInitializationOrderModuleList;                     //0x30
    VOID* EntryInProgress;                                                  //0x40
    UCHAR ShutdownInProgress;                                               //0x48
    VOID* ShutdownThreadId;                                                 //0x50
};

//0xe0 bytes (sizeof)
struct _LDR_DATA_TABLE_ENTRY64
{
    struct LIST_ENTRY64 InLoadOrderLinks;                                    //0x0
    struct LIST_ENTRY64 InMemoryOrderLinks;                                  //0x10
    struct LIST_ENTRY64 InInitializationOrderLinks;                          //0x20
    VOID* DllBase;                                                          //0x30
    VOID* EntryPoint;                                                       //0x38
    ULONG SizeOfImage;                                                      //0x40
    struct _UNICODE_STRING FullDllName;                                     //0x48
    struct _UNICODE_STRING BaseDllName;                                     //0x58
    ULONG Flags;                                                            //0x68
    USHORT LoadCount;                                                       //0x6c
    USHORT TlsIndex;                                                        //0x6e
    union
    {
        struct LIST_ENTRY64 HashLinks;                                       //0x70
        struct
        {
            VOID* SectionPointer;                                           //0x70
            ULONG CheckSum;                                                 //0x78
        };
    };
    union
    {
        ULONG TimeDateStamp;                                                //0x80
        VOID* LoadedImports;                                                //0x80
    };
    struct _ACTIVATION_CONTEXT* EntryPointActivationContext;                //0x88
    VOID* PatchInformation;                                                 //0x90
    struct LIST_ENTRY64 ForwarderLinks;                                      //0x98
    struct LIST_ENTRY64 ServiceTagLinks;                                     //0xa8
    struct LIST_ENTRY64 StaticLinks;                                         //0xb8
    VOID* ContextInformation;                                               //0xc8
    ULONGLONG OriginalBase;                                                 //0xd0
    union _LARGE_INTEGER LoadTime;                                          //0xd8
};
#else
//0x8 bytes (sizeof)
struct _UNICODE_STRING
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    USHORT* Buffer;                                                         //0x4
};

//0x30 bytes (sizeof)
struct _PEB_LDR_DATA
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    VOID* SsHandle;                                                         //0x8
    struct _LIST_ENTRY InLoadOrderModuleList;                               //0xc
    struct _LIST_ENTRY InMemoryOrderModuleList;                             //0x14
    struct _LIST_ENTRY InInitializationOrderModuleList;                     //0x1c
    VOID* EntryInProgress;                                                  //0x24
    UCHAR ShutdownInProgress;                                               //0x28
    VOID* ShutdownThreadId;                                                 //0x2c
};

//0x78 bytes (sizeof)
struct _LDR_DATA_TABLE_ENTRY
{
    struct _LIST_ENTRY InLoadOrderLinks;                                    //0x0
    struct _LIST_ENTRY InMemoryOrderLinks;                                  //0x8
    struct _LIST_ENTRY InInitializationOrderLinks;                          //0x10
    VOID* DllBase;                                                          //0x18
    VOID* EntryPoint;                                                       //0x1c
    ULONG SizeOfImage;                                                      //0x20
    struct _UNICODE_STRING FullDllName;                                     //0x24
    struct _UNICODE_STRING BaseDllName;                                     //0x2c
    ULONG Flags;                                                            //0x34
    USHORT LoadCount;                                                       //0x38
    USHORT TlsIndex;                                                        //0x3a
    union
    {
        struct _LIST_ENTRY HashLinks;                                       //0x3c
        struct
        {
            VOID* SectionPointer;                                           //0x3c
            ULONG CheckSum;                                                 //0x40
        };
    };
    union
    {
        ULONG TimeDateStamp;                                                //0x44
        VOID* LoadedImports;                                                //0x44
    };
    struct _ACTIVATION_CONTEXT* EntryPointActivationContext;                //0x48
    VOID* PatchInformation;                                                 //0x4c
    struct _LIST_ENTRY ForwarderLinks;                                      //0x50
    struct _LIST_ENTRY ServiceTagLinks;                                     //0x58
    struct _LIST_ENTRY StaticLinks;                                         //0x60
    VOID* ContextInformation;                                               //0x68
    ULONG OriginalBase;                                                     //0x6c
    union _LARGE_INTEGER LoadTime;                                          //0x70
};
#endif // defined(_WIN64)

fun.asm

x64 使用汇编找 Ldr 地址需要单独创建 asm 文件,然后导入汇编函数才能使用,具体详情自行搜索 64 位内联汇编

x86 不需要单独写 asm 文件,直接内联即可

.CODE
GetPebLdr PROC
	mov rax, gs:[60h];
	add rax, 18h;
	mov rax, [rax];
	ret;
GetPebLdr ENDP
END

entry.cpp

这里

#include "entry.h"

void GetModuleInfo() {
#if defined(_WIN64)
	_PEB_LDR_DATA64* pLdr = (_PEB_LDR_DATA64*)GetPebLdr();
	PLIST_ENTRY64 pModuleList = (PLIST_ENTRY64)&(pLdr->InLoadOrderModuleList);
	_LDR_DATA_TABLE_ENTRY64* pLdrData = (_LDR_DATA_TABLE_ENTRY64*)(pModuleList->Flink);
	while ((int*)pModuleList != (int*)pLdrData) {
		printf("Module Name:%ws\nModule Base = %016x\n",pLdrData->FullDllName.Buffer,pLdrData->DllBase);
		pLdrData = (_LDR_DATA_TABLE_ENTRY64*)(pLdrData->InLoadOrderLinks.Flink);
	}
#else
	_PEB_LDR_DATA* pLdr = 0;

	__asm {
		mov eax, dword ptr fs : [0x30] ;
		add eax, 0x0C;
		mov eax,[eax];
		mov pLdr, eax;
	}

	_LIST_ENTRY* pModuleList = (_LIST_ENTRY*)&(pLdr->InLoadOrderModuleList);
	_LDR_DATA_TABLE_ENTRY* pLdrData = (_LDR_DATA_TABLE_ENTRY*)(pModuleList->Flink);
	while ((int*)pModuleList != (int*)pLdrData) {
		printf("Module Name:%ws\nModule Base = %08x\n", pLdrData->FullDllName.Buffer, pLdrData->DllBase);
		pLdrData = (_LDR_DATA_TABLE_ENTRY*)(pLdrData->InLoadOrderLinks.Flink);
	}
#endif // 
}

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

评论