0x01 结构探究
先在win7 x86下通过windbg来探究通过peb来得到进程模块的步骤:
命令!process 0 0 exeplorer.exe 先获取到explorer.exe的EPROCESS的地址,如图我们可以看到EPROCESS的地址为:0x87782d40 ,PEB的地址为:0x7ffdf000
使用 .process /p /r 87ede940 命令切换到explorer.exe进程后才能够访问它的peb用户地址空间:
切换到explorer.exe后,使用命令dt _EPROCESS 877822d40 查看EPROCESS,可以看到在偏移0x1a8的位置就是_PEB,地址为0x7ffdf000
继续查看_PEB结构,使用命令 dt _PEB 0x7ffdf000查看PEB的信息,可以看到在偏移0x00c的位置就是_PEB_LDR_DATA,地址为: 0x77697880:
继续查看_PEB_LDR_DATA结构,使用命令dt _PEB_LDR_DATA 0x77697880 查看_PEB_LDR_DATA结构,可以看到一共有3个_LIST_ENTRY,这三个链表只是结点的排列顺序不一样,总的内容是相同的。我利用的是第一个InLoadOrderModuleList:
现在再来看一个最后的关键结构:_LDR_DATA_TABLE_ENTRY
使用命令dt _LDR_DATA_TABLE_ENTRY查看_LDR_DATA_TABLE_ENTRY的结构:
从结构中可以看到InLoadOrderModuleList位于结构体的首部,所以我们获取到的_LIST_ENTRY地址就直接指向了_LDR_DATA_TABLE_ENTRY!(这就是为什么选择InLoadOrderModuleList这条链表的原因。)
回头看一下眼链表的第一个结点的地址位0x3316a8,使用命令dt _LDR_DATA_TABLE_ENTRY 0x3316a8查看检验一下:
可以看到看到explore.exe这个进程本身的完整路径!因为InLoadOrderModuleList这条链表是按照模块加载顺序遍历的,所以第一个模块儿就是进程本身了,加上0x80的偏移向后看一个进行检验:dt _LDR_DATA_TABLE_ENTRY 0x331ae8
大功告成~
0x02 步骤实现
1.先通过NtQueryInformationProcess函数根据传入的进程句柄找到进程的ProcessBasicInformation,就得到ProcessBasicInfo.PebBaseAddress,再传入ProcessBasicInfo.PebBaseAddress借由ReadProcessMemory函数读取出peb结构。
(附:获得peb的方法有其他,比如去读fs的0x30处等:
__asm { //1、通过fs:[30h]获取当前进程的_PEB结构 mov eax,dword ptr fs:[30h]; mov pPeb,eax }
)
2.得到peb之后,传入peb的成员_PEB_LDR_DATA的基地址,通过ReadProcessMemory函数得到_PEB_LDR_DATA
3.再次使用ReadProcessMemory函数得到_PEB_LDR_DATA的成员变量InLoadOrderModuleList链表进行遍历,由于InLoadOrderModuleList的基地址也是_LDR_DATA_TABLE_ENTRY的第一成员,所以InLoadOrderModuleList的基地址也就是_LDR_DATA_TABLE_ENTRY的基地址。_LDR_DATA_TABLE_ENTRY中就含有进程加载模块的名称和完整路径,分别是FullDllName和BaseDlllName两个成员。
介绍一下ReadProcessMemory函数:
BOOL ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead );
参数
hProcess
目标进程的句柄,该句柄必须对目标进程具有PROCESS_VM_READ
的访问权限。
lpBaseAddress
从目标进程中读取数据的起始地址。 在读取数据前,系统将先检验该地址的数据是否可读,如果不可读,函数将调用失败。
lpBuffer
用来接收数据的缓存区地址。
nSize
从目标进程读取数据的字节数。
lpNumberOfBytesRead
记录实际读取的字节数的变量地址。如果对这个值 不关心,填入NULL即可。
返回值
如果函数执行成功,返回值非零。
如果函数执行失败,返回值为零。调用
GetLastError 函数可以获取该函数执行错误的信息。
如果要读取一个进程中不可访问空间的数据,该函数就会失败。
注意:ReadProcessMemory 函数从目标进程复制指定大小的数据到自己进程的缓存区,任何拥有PROCESS_VM_READ
权限句柄的进程都可以调用该函数,目标进程的地址空间很显然要是可读的,但也并不是必须的,如果目标进程处于被调试状态的话。
0x03 代码
头文件
#pragma once #include <windows.h> #include <winternl.h> #include <ntstatus.h> #include <TlHelp32.h> #include <Psapi.h> #include <vector> using namespace std; enum MODULE_TYPE { MODULE_X86, MODULE_X64, }; struct _PROCESS_MODULE_INFORMATION_ { ULONG64 ModuleBase; //操作系统中可能存在64位与32位的程序 size_t ModuleSize; WCHAR ModuleFullPathData[MAX_PATH]; MODULE_TYPE ModuleType; }; template <typename T> struct _LIST_ENTRY_ { T Flink; T Blink; }; template <typename T> struct _UNICODE_STRING_ { WORD BufferLength; WORD MaximumLength; T BufferData; }; template <typename T, typename NGF, int A> struct _PEB_ { typedef T type; union { struct { BYTE InheritedAddressSpace; BYTE ReadImageFileExecOptions; BYTE BeingDebugged; BYTE BitField; }; T dummy01; }; T Mutant; T ImageBaseAddress; T Ldr; T ProcessParameters; T SubSystemData; T ProcessHeap; T FastPebLock; T AtlThunkSListPtr; T IFEOKey; T CrossProcessFlags; T UserSharedInfoPtr; DWORD SystemReserved; DWORD AtlThunkSListPtr32; T ApiSetMap; T TlsExpansionCounter; T TlsBitmap; DWORD TlsBitmapBits[2]; T ReadOnlySharedMemoryBase; T HotpatchInformation; 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; 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; T FlsCallback; _LIST_ENTRY_<T> FlsListHead; T FlsBitmap; DWORD FlsBitmapBits[4]; T FlsHighIndex; T WerRegistrationData; T WerShipAssertPtr; T pContextData; T pImageHeaderHash; T TracingFlags; T CsrServerReadOnlySharedMemoryBase; }; typedef _PEB_<DWORD, DWORD64, 34> _PEB32_; typedef _PEB_<DWORD64, DWORD, 30> _PEB64_; template<typename T> struct _PEB_T { typedef typename std::conditional<std::is_same<T, DWORD>::value, _PEB32_, _PEB64_>::type type; }; template<typename T> struct _PEB_LDR_DATA_ { unsigned long Length; unsigned char Initialized; T SsHandle; _LIST_ENTRY_<T> InLoadOrderModuleList; _LIST_ENTRY_<T> InMemoryOrderModuleList; _LIST_ENTRY_<T> InInitializationOrderModuleList; T EntryInProgress; unsigned char ShutdownInProgress; T ShutdownThreadId; }; template<typename T> struct _LDR_DATA_TABLE_ENTRY_ { _LIST_ENTRY_<T> InLoadOrderLinks; _LIST_ENTRY_<T> InMemoryOrderLinks; _LIST_ENTRY_<T> InInitializationOrderLinks; T DllBase; T EntryPoint; unsigned long SizeOfImage; _UNICODE_STRING_<T> FullDllName; _UNICODE_STRING_<T> BaseDllName; unsigned long Flags; unsigned short LoadCount; unsigned short TlsIndex; _LIST_ENTRY_<T> HashLinks; unsigned long TimeDateStamp; T EntryPointActivationContext; T PatchInformation; }; template<typename T> struct _PROCESS_BASIC_INFORMATION_ { NTSTATUS ExitStatus; ULONG Reserved0; T PebBaseAddress; T AffinityMask; LONG BasePriority; ULONG Reserved1; T UniqueProcessId; T InheritedFromUniqueProcessId; }; typedef decltype(&NtQueryInformationProcess) LPFN_NTQUERYINFORMATIONPROCESS; typedef NTSTATUS(NTAPI *LPFN_NTWOW64QUERYINFORMATIONPROCESS64)( IN HANDLE ProcessHandle, IN ULONG ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL); typedef NTSTATUS(NTAPI *LPFN_NTWOW64READVIRTUALMEMORY64)( IN HANDLE ProcessHandle, IN ULONG64 BaseAddress, OUT PVOID BufferData, IN ULONG64 BufferLength, OUT PULONG64 ReturnLength OPTIONAL); struct _PROCESS_INFORMATION_ { ULONG ProcessID; char ImageNameData[MAX_PATH]; char ProcessFullPathData[MAX_PATH]; }; typedef struct { DWORD ExitStatus; DWORD PebBaseAddress; DWORD AffinityMask; DWORD BasePriority; ULONG UniqueProcessId; ULONG InheritedFromUniqueProcessId; } __PROCESS_BASIC_INFORMATION__; typedef LONG(__stdcall *PROCNTQSIP)(HANDLE, UINT, PVOID, ULONG, PULONG); SIZE_T SeEnumProcessModuleList(ULONG ProcessID, vector<_PROCESS_MODULE_INFORMATION_>& ProcessModuleInfo); SIZE_T SeEnumModuleInfoByPeb(ULONG ProcessID, vector<_PROCESS_MODULE_INFORMATION_>& ProcessModuleInfo); template<typename T> SIZE_T EnumModuleInfoByPeb(HANDLE ProcessHandle, vector<_PROCESS_MODULE_INFORMATION_>& ProcessModuleInfo); ULONG64 GetPeb(HANDLE ProcessHandle, _PEB64_* Peb); ULONG32 GetPeb(HANDLE ProcessHandle, _PEB32_* Peb); BOOL SeReadProcessMemory(HANDLE ProcessHandle, DWORD BaseAddress, LPVOID BufferData, size_t BufferLength, DWORD64* ReturnLength); BOOL SeReadProcessMemory(HANDLE ProcessHandle, DWORD64 BaseAddress, LPVOID BufferData, size_t BufferLength, DWORD64 *ReturnLength);
源文件:
BOOL EnumDllLoaderedProcess(vector<_PROCESS_INFORMATION_> ProcessInfo, string DllName) { OnInitMember(); vector<_PROCESS_INFORMATION_>::iterator i; string DllFullPath; int j = 0; for (i = ProcessInfo.begin(); i != ProcessInfo.end(); i++) { ULONG ProcessID = i->ProcessID; vector<_PROCESS_MODULE_INFORMATION_>ProcessModuleInfomationVector; SeEnumProcessModuleList(ProcessID, ProcessModuleInfomationVector); int index = 0; vector<_PROCESS_MODULE_INFORMATION_>::iterator m; for (m = ProcessModuleInfomationVector.begin(); m != ProcessModuleInfomationVector.end(); m++) { DllFullPath = SeUtf16ToUtf8(m->ModuleFullPathData); index = DllFullPath.find(DllName); if (index == -1) { continue; } else { printf("%s", i->ProcessFullPathData); printf("----------------------------- "); break; } } } return TRUE; } SIZE_T SeEnumProcessModuleList(ULONG ProcessID, vector<_PROCESS_MODULE_INFORMATION_>& ProcessModuleInfo) { SeEnumModuleInfoByPeb(ProcessID, ProcessModuleInfo); return ProcessModuleInfo.size(); } SIZE_T SeEnumModuleInfoByPeb(ULONG ProcessID, vector<_PROCESS_MODULE_INFORMATION_>& ProcessModuleInfo) { HANDLE ProcessHandle = NULL; BOOL IsWow64 = FALSE; ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessID); int a = GetLastError(); if (ProcessHandle == NULL) { goto Error; } IsWow64Process(ProcessHandle, &IsWow64); if (IsWow64 == TRUE) { EnumModuleInfoByPeb<DWORD>(ProcessHandle, ProcessModuleInfo); } else { EnumModuleInfoByPeb<DWORD64>(ProcessHandle, ProcessModuleInfo); } Error: if (ProcessHandle != NULL) { CloseHandle(ProcessHandle); ProcessHandle = NULL; } return ProcessModuleInfo.size(); } template<typename T> SIZE_T EnumModuleInfoByPeb(HANDLE ProcessHandle, vector<_PROCESS_MODULE_INFORMATION_>& ProcessModuleInfo) { typename _PEB_T<T>::type Peb = { { { 0 } } }; _PEB_LDR_DATA_<T> PebLdrData = { 0 }; ProcessModuleInfo.clear(); if (GetPeb(ProcessHandle, &Peb) != 0 && SeReadProcessMemory(ProcessHandle, Peb.Ldr, &PebLdrData, sizeof(PebLdrData), 0) == TRUE) { for (T CheckPtr = PebLdrData.InLoadOrderModuleList.Flink; CheckPtr != (Peb.Ldr + FIELD_OFFSET(_PEB_LDR_DATA_<T>, InLoadOrderModuleList)); SeReadProcessMemory(ProcessHandle, static_cast<ULONG64>(CheckPtr), &CheckPtr, sizeof(CheckPtr), 0)) { _PROCESS_MODULE_INFORMATION_ v1; wchar_t ModuleFullPathData[MAX_PATH] = { 0 }; _LDR_DATA_TABLE_ENTRY_<T> LdrDataTableEntry = { { 0 } }; SeReadProcessMemory(ProcessHandle, CheckPtr, &LdrDataTableEntry, sizeof(LdrDataTableEntry), 0); SeReadProcessMemory(ProcessHandle, LdrDataTableEntry.FullDllName.BufferData, ModuleFullPathData, LdrDataTableEntry.FullDllName.BufferLength, 0); v1.ModuleBase = LdrDataTableEntry.DllBase; v1.ModuleSize = LdrDataTableEntry.SizeOfImage; wmemcpy(v1.ModuleFullPathData, ModuleFullPathData, MAX_PATH); printf("%ls ", v1.ModuleFullPathData); v1.ModuleType = std::is_same<T, DWORD>::value ? MODULE_X86 : MODULE_X64; ProcessModuleInfo.emplace_back(v1); } } return ProcessModuleInfo.size(); } ULONG64 GetPeb(HANDLE ProcessHandle, _PEB64_* Peb) { _PROCESS_BASIC_INFORMATION_<DWORD64> ProcessBasicInfo = { 0 }; ULONG ReturnLength = 0; if (NT_SUCCESS(__NtWow64QueryInformationProcess64(ProcessHandle, ProcessBasicInformation, &ProcessBasicInfo, (ULONG)sizeof(ProcessBasicInfo), &ReturnLength)) && Peb) { __NtWow64ReadVirtualMemory64(ProcessHandle, ProcessBasicInfo.PebBaseAddress, Peb, sizeof(_PEB64_), NULL); } return ProcessBasicInfo.PebBaseAddress; } ULONG32 GetPeb(HANDLE ProcessHandle, _PEB32_* Peb) { PROCESS_BASIC_INFORMATION ProcessBasicInfo = { 0 }; ULONG ReturnLength = 0; if (NT_SUCCESS(__NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &ProcessBasicInfo, (ULONG)sizeof(ProcessBasicInfo), &ReturnLength)) && Peb) { ReadProcessMemory(ProcessHandle, ProcessBasicInfo.PebBaseAddress, Peb, sizeof(_PEB32_), NULL); } return reinterpret_cast<ULONG32>(ProcessBasicInfo.PebBaseAddress); } BOOL SeReadProcessMemory(HANDLE ProcessHandle, DWORD BaseAddress, LPVOID BufferData, size_t BufferLength, DWORD64* ReturnLength) { return ReadProcessMemory(ProcessHandle, reinterpret_cast<LPVOID>(BaseAddress), BufferData, BufferLength, reinterpret_cast<SIZE_T*>(ReturnLength)); } BOOL SeReadProcessMemory(HANDLE ProcessHandle, DWORD64 BaseAddress, LPVOID BufferData, size_t BufferLength, DWORD64 *ReturnLength) { if (__NtWow64ReadVirtualMemory64(ProcessHandle, BaseAddress, BufferData, BufferLength, ReturnLength) != STATUS_SUCCESS) { return FALSE; } return TRUE; } BOOL OnInitMember() { HMODULE NtdllModuleBase = NULL; NtdllModuleBase = GetModuleHandle("Ntdll.dll"); if (NtdllModuleBase == NULL) { return FALSE; } __NtWow64QueryInformationProcess64 = (LPFN_NTWOW64QUERYINFORMATIONPROCESS64)GetProcAddress(NtdllModuleBase, "NtWow64QueryInformationProcess64"); __NtWow64ReadVirtualMemory64 = (LPFN_NTWOW64READVIRTUALMEMORY64)GetProcAddress(NtdllModuleBase, "NtWow64ReadVirtualMemory64"); __NtQueryInformationProcess = (LPFN_NTQUERYINFORMATIONPROCESS)GetProcAddress(NtdllModuleBase, "NtQueryInformationProcess"); if (__NtWow64QueryInformationProcess64 == NULL || __NtWow64ReadVirtualMemory64 == NULL || __NtQueryInformationProcess == NULL) { return FALSE; } return TRUE; }