通过PEB枚举进程中所有模块

背景知识网上可搜到

这儿有个视频讲得比较详细:http://www.52pojie.cn/thread-178258-1-1.html

废话不说,上代码

首先是一些结构体的定义:

  1 typedef LONG KPRIORITY;
  2 typedef void** PPVOID;
  3 
  4 typedef struct _LDR_DATA_TABLE_ENTRY
  5 {
  6     LIST_ENTRY InLoadOrderLinks;
  7     LIST_ENTRY InMemoryOrderLinks;
  8     LIST_ENTRY InInitializationOrderLinks;
  9     PVOID DllBase;
 10     PVOID EntryPoint;
 11     ULONG SizeOfImage;
 12     UNICODE_STRING FullDllName;
 13     UNICODE_STRING BaseDllName;
 14     ULONG Flags;
 15     WORD LoadCount;
 16     WORD TlsIndex;
 17     union
 18     {
 19         LIST_ENTRY HashLinks;
 20         struct
 21         {
 22             PVOID SectionPointer;
 23             ULONG CheckSum;
 24         };
 25     };
 26     union
 27     {
 28         ULONG TimeDateStamp;
 29         PVOID LoadedImports;
 30     };
 31     DWORD EntryPointActivationContext; //_ACTIVATION_CONTEXT * EntryPointActivationContext;
 32     PVOID PatchInformation;
 33     LIST_ENTRY ForwarderLinks;
 34     LIST_ENTRY ServiceTagLinks;
 35     LIST_ENTRY StaticLinks;
 36 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
 37 
 38 
 39 typedef struct _PEB_LDR_DATA {
 40     ULONG Length; 
 41     BOOLEAN Initialized; 
 42     PVOID SsHandle; 
 43     LIST_ENTRY InLoadOrderModulevector; 
 44     LIST_ENTRY InMemoryOrderModulevector; 
 45     LIST_ENTRY InInitializationOrderModulevector;
 46 } PEB_LDR_DATA, *PPEB_LDR_DATA;
 47 
 48 
 49 typedef struct _PEB
 50 {
 51     BOOLEAN InheritedAddressSpace; 
 52     BOOLEAN ReadImageFileExecOptions; 
 53     BOOLEAN BeingDebugged; 
 54     BOOLEAN Spare; 
 55     HANDLE Mutant; 
 56     PVOID ImageBaseAddress; 
 57     PPEB_LDR_DATA LoaderData; 
 58     PVOID ProcessParameters; //PRTL_USER_PROCESS_PARAMETERS ProcessParameters; 
 59     PVOID SubSystemData; 
 60     PVOID ProcessHeap; 
 61     PVOID FastPebLock; 
 62     PVOID FastPebLockRoutine; //PPEBLOCKROUTINE FastPebLockRoutine; 
 63     PVOID FastPebUnlockRoutine; //PPEBLOCKROUTINE FastPebUnlockRoutine; 
 64     ULONG EnvironmentUpdateCount; 
 65     PPVOID KernelCallbackTable; 
 66     PVOID EventLogSection; 
 67     PVOID EventLog; 
 68     DWORD Freevector; //PPEB_FREE_BLOCK Freevector; 
 69     ULONG TlsExpansionCounter; 
 70     PVOID TlsBitmap; 
 71     ULONG TlsBitmapBits[0x2]; 
 72     PVOID ReadOnlySharedMemoryBase; 
 73     PVOID ReadOnlySharedMemoryHeap; 
 74     PPVOID ReadOnlyStaticServerData; 
 75     PVOID AnsiCodePageData; 
 76     PVOID OemCodePageData; 
 77     PVOID UnicodeCaseTableData; 
 78     ULONG NumberOfProcessors; 
 79     ULONG NtGlobalFlag; 
 80     BYTE Spare2[0x4]; 
 81     LARGE_INTEGER CriticalSectionTimeout; 
 82     ULONG HeapSegmentReserve; 
 83     ULONG HeapSegmentCommit; 
 84     ULONG HeapDeCommitTotalFreeThreshold; 
 85     ULONG HeapDeCommitFreeBlockThreshold; 
 86     ULONG NumberOfHeaps; 
 87     ULONG MaximumNumberOfHeaps; 
 88     PPVOID *ProcessHeaps; 
 89     PVOID GdiSharedHandleTable; 
 90     PVOID ProcessStarterHelper; 
 91     PVOID GdiDCAttributevector; 
 92     PVOID LoaderLock; 
 93     ULONG OSMajorVersion; 
 94     ULONG OSMinorVersion; 
 95     ULONG OSBuildNumber; 
 96     ULONG OSPlatformId; 
 97     ULONG ImageSubSystem; 
 98     ULONG ImageSubSystemMajorVersion; 
 99     ULONG ImageSubSystemMinorVersion; 
100     ULONG GdiHandleBuffer[0x22]; 
101     ULONG PostProcessInitRoutine; 
102     ULONG TlsExpansionBitmap; 
103     BYTE TlsExpansionBitmapBits[0x80]; 
104     ULONG SessionId;
105 } PEB, *PPEB;

用到一个读内存的函数:

 1 int readMemory(const void * adresseBase, void * adresseDestination, size_t longueur, HANDLE handleProcess)
 2 {
 3     if(handleProcess == INVALID_HANDLE_VALUE)
 4     {
 5         return (memcpy_s(adresseDestination, longueur, adresseBase, longueur) == 0);
 6     }
 7     else
 8     {
 9         SIZE_T dwBytesRead = 0;
10         return ((ReadProcessMemory(handleProcess, adresseBase, adresseDestination, longueur, &dwBytesRead) != 0) && (dwBytesRead == longueur));
11     }
12 }

获取PEB的方法,网上大都是用汇编,读fs寄存器获得TEB,通过偏移获取PEB。

这里用Windows在ntdll中一个未公开的函数:NtQueryInformationProcess

函数指针定义如下:

typedef NTSTATUS (WINAPI * PNT_QUERY_INFORMATION_PROCESS)    (__in HANDLE ProcessHandle, __in PROCESSINFOCLASS ProcessInformationClass, __out PVOID ProcessInformation, __in ULONG ProcessInformationLength, __out_opt  PULONG ReturnLength);

其中第二个参数可取如下值:

 1 typedef enum _PROCESSINFOCLASS {
 2     ProcessBasicInformation,
 3     ProcessQuotaLimits,
 4     ProcessIoCounters,
 5     ProcessVmCounters,
 6     ProcessTimes,
 7     ProcessBasePriority,
 8     ProcessRaisePriority,
 9     ProcessDebugPort,
10     ProcessExceptionPort,
11     ProcessAccessToken,
12     ProcessLdtInformation,
13     ProcessLdtSize,
14     ProcessDefaultHardErrorMode,
15     ProcessIoPortHandlers,          // Note: this is kernel mode only
16     ProcessPooledUsageAndLimits,
17     ProcessWorkingSetWatch,
18     ProcessUserModeIOPL,
19     ProcessEnableAlignmentFaultFixup,
20     ProcessPriorityClass,
21     ProcessWx86Information,
22     ProcessHandleCount,
23     ProcessAffinityMask,
24     ProcessPriorityBoost,
25     ProcessDeviceMap,
26     ProcessSessionInformation,
27     ProcessForegroundInformation,
28     ProcessWow64Information,
29     ProcessImageFileName,
30     ProcessLUIDDeviceMapsEnabled,
31     ProcessBreakOnTermination,
32     ProcessDebugObjectHandle,
33     ProcessDebugFlags,
34     ProcessHandleTracing,
35     ProcessIoPriority,
36     ProcessExecuteFlags,
37     ProcessTlsInformation,
38     ProcessCookie,
39     ProcessImageInformation,
40     ProcessCycleTime,
41     ProcessPagePriority,
42     ProcessInstrumentationCallback,
43     ProcessThreadStackAllocation,
44     ProcessWorkingSetWatchEx,
45     ProcessImageFileNameWin32,
46     ProcessImageFileMapping,
47     ProcessAffinityUpdateMode,
48     ProcessMemoryAllocationMode,
49     ProcessGroupInformation,
50     ProcessTokenVirtualizationEnabled,
51     ProcessConsoleHostProcess,
52     ProcessWindowInformation,
53     MaxProcessInfoClass             // MaxProcessInfoClass should always be the last enum
54 } PROCESSINFOCLASS;

这里用ProcessBasicInformation,获得信息的结构如下:

1 typedef struct _PROCESS_BASIC_INFORMATION {
2     NTSTATUS ExitStatus;
3     PPEB PebBaseAddress;
4     ULONG_PTR AffinityMask;
5     KPRIORITY BasePriority;
6     ULONG_PTR UniqueProcessId;
7     ULONG_PTR InheritedFromUniqueProcessId;
8 } PROCESS_BASIC_INFORMATION,*PPROCESS_BASIC_INFORMATION;

下面是具体的获取PEB的代码:

1     PEB this_peb;
2     PROCESS_BASIC_INFORMATION mesInfos;
3     HANDLE thisHandle = GetCurrentProcess();    //本进程的HANDLE
4     ULONG sizeReturn;
5     PNT_QUERY_INFORMATION_PROCESS NtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS)(GetProcAddress(GetModuleHandle(TEXT("ntdll")), "NtQueryInformationProcess"));
6     NtQueryInformationProcess(thisHandle, ProcessBasicInformation, &mesInfos, sizeof(PROCESS_BASIC_INFORMATION), &sizeReturn);
7     readMemory(mesInfos.PebBaseAddress, &this_peb, sizeof(PEB), thisHandle);

下面要用到一个很奇怪的技巧(上面那个视频中讲得很好):

PEB中有个LoaderData,类型为PPEB_LDR_DATA。而PEB_LDR_DATALDR_DATA_TABLE_ENTRY两种结构的三个InLoadOrderLinks、InMemoryOrderLinks、InInitializationOrderLinks是共用的。(PS:我的理解,就是本来是一个结构体,然后被人们用两个结构体表示了)

然后是根据peb中的LoaderData中的某个双向链表找到LDR_DATA_TABLE_ENTRY结构体里面的成员,其中有模块的名字——BaseDllName。它是UNICODE_STRING类型的,打印出来就是了。

下面是自己写的代码,写的很挫,没有注释,但是应该不难理解:

 1 PEB_LDR_DATA my_Ldr;
 2     readMemory(&this_peb.LoaderData, &my_Ldr, sizeof(PEB_LDR_DATA), thisHandle);
 3     LIST_ENTRY* tmp = this_peb.LoaderData->InLoadOrderModulevector.Flink;
 4     LDR_DATA_TABLE_ENTRY* pLdr_table = (LDR_DATA_TABLE_ENTRY*)(unsigned char *)(tmp);
 5     //取出模块名字
 6     wchar_t* module_name = (wchar_t*)malloc(pLdr_table->BaseDllName.Length+2);
 7     ZeroMemory(module_name, pLdr_table->BaseDllName.Length+2);
 8     readMemory(pLdr_table->BaseDllName.Buffer, module_name, pLdr_table->BaseDllName.Length, thisHandle);
 9     wprintf(L"%s\n", module_name);
10     free(module_name);
11 
12     LIST_ENTRY* bianli = tmp->Flink;
13     while (bianli->Flink != tmp)
14     {
15         pLdr_table = (LDR_DATA_TABLE_ENTRY*)(bianli);
16         wchar_t* module_name = (wchar_t*)malloc(pLdr_table->BaseDllName.Length+2);
17         ZeroMemory(module_name, pLdr_table->BaseDllName.Length+2);
18         readMemory(pLdr_table->BaseDllName.Buffer, module_name, pLdr_table->BaseDllName.Length, thisHandle);
19         wprintf(L"%s\n", module_name);
20         free(module_name);
21         bianli = bianli->Flink;
22     }

总结:

貌似PEB很强大的(看里面的变量就能知道),这里仅仅用来枚举所有的模块了。做安全的话,不得不知道这些。。

原文地址:https://www.cnblogs.com/02xiaoma/p/2989104.html