这里偷懒了,只获取了当前进程的模块信息,如果要获取其他进程的模块信息,只要得到其他进程的Peb结构地址即可,剩下流程都是一样的了
实验环境:
- Windows 10 21H1 家庭版
- Visual Studio 2019
参考自:[原创]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 模块名称
...
};
这里存储着的就是模块信息了
这三个链表之间的关系,我再拿一张某师傅的图:
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;
}