简介 在很多情况下,我们需要获取某进程所加载的DLL信息,以及DLL中的导出函数地址, 这里参照 Blackbone
的部分代码进行修改:https://github.com/DarthTon/Blackbone
工作原理 1.获取进程ID 在驱动中遍历进程需要使用 ZwQuerySystemInformation
函数,指定其class参数为5(SystemProcessesAndThreadsInformation)
时,可以获取一个全部进程的快照链表
1 2 3 4 5 NTSTATUS NTAPI ZwQuerySystemInformation ( IN ULONG SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL) ;
对应快照链表的结构体如下所示,这里只定义出了一些关键参数名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 typedef struct _SYSTEM_PROCESS_INFO { ULONG NextEntryOffset; ULONG NumberOfThreads; UCHAR Reserved1[48 ]; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; PVOID Reserved2; ULONG HandleCount; ULONG SessionId; PVOID Reserved3; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG Reserved4; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; PVOID Reserved5; SIZE_T QuotaPagedPoolUsage; PVOID Reserved6; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved7[6 ]; } SYSTEM_PROCESS_INFO, *PSYSTEM_PROCESS_INFO;
这里以 explorer.exe
进程为例,获取其PID信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 NTSTATUS GetExplorerProcessId (OUT HANDLE *ProcessId) { ULONG RetLen = 0 ; PVOID Buffer = NULL ; NTSTATUS Status = STATUS_SUCCESS; PSYSTEM_PROCESS_INFO ProcInfo = NULL ; if (!ProcessId) return STATUS_UNSUCCESSFUL; Status = ZwQuerySystemInformation(5 , NULL , 0 , &RetLen); if (Status != STATUS_INFO_LENGTH_MISMATCH) return Status; Buffer = ExAllocatePool(NonPagedPoolNx, RetLen * 2 ); if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES; Status = ZwQuerySystemInformation(5 , Buffer, RetLen * 2 , &RetLen); if (!NT_SUCCESS(Status)) { ExFreePool(Buffer); return Status; } Status = STATUS_UNSUCCESSFUL; ProcInfo = (PSYSTEM_PROCESS_INFO)Buffer; while (1 ) { if (ProcInfo->SessionId && (ProcInfo->UniqueProcessId > (HANDLE)4 )) { if (ProcInfo->ImageName.Buffer && ProcInfo->ImageName.Length) { if (!_wcsnicmp(ProcInfo->ImageName.Buffer, L"explorer.exe" , ProcInfo->ImageName.Length / 2 )) { *ProcessId = ProcInfo->UniqueProcessId; Status = STATUS_SUCCESS; break ; } } } if (!ProcInfo->NextEntryOffset) break ; ProcInfo = (PSYSTEM_PROCESS_INFO)( (PCHAR)ProcInfo + ProcInfo->NextEntryOffset); } ExFreePool(Buffer); return Status; }
2.获取DLL地址 进程所加载的DLL列表在 PEB->LDR
中保存,我们可以遍历LDR链表来查询DLL模块地址, 注意:在驱动中访问应用层地址,必须加 try/except
和 ProbeForRead/Write
来检测有效性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 typedef struct _PEB_LDR_DATA { ULONG Length; UCHAR Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; } PEB_LDR_DATA, *PPEB_LDR_DATA; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; LIST_ENTRY HashLinks; ULONG TimeDateStamp; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; typedef struct _PEB { UCHAR InheritedAddressSpace; UCHAR ReadImageFileExecOptions; UCHAR BeingDebugged; UCHAR BitField; PVOID Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; PVOID ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PVOID FastPebLock; PVOID AtlThunkSListPtr; PVOID IFEOKey; PVOID CrossProcessFlags; PVOID UserSharedInfoPtr; ULONG SystemReserved; ULONG AtlThunkSListPtr32; PVOID ApiSetMap; } PEB, *PPEB;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 typedef struct _PEB_LDR_DATA32 { ULONG Length; UCHAR Initialized; ULONG SsHandle; LIST_ENTRY32 InLoadOrderModuleList; LIST_ENTRY32 InMemoryOrderModuleList; LIST_ENTRY32 InInitializationOrderModuleList; } PEB_LDR_DATA32, *PPEB_LDR_DATA32; typedef struct _LDR_DATA_TABLE_ENTRY32 { LIST_ENTRY32 InLoadOrderLinks; LIST_ENTRY32 InMemoryOrderLinks; LIST_ENTRY32 InInitializationOrderLinks; ULONG DllBase; ULONG EntryPoint; ULONG SizeOfImage; UNICODE_STRING32 FullDllName; UNICODE_STRING32 BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; LIST_ENTRY32 HashLinks; ULONG TimeDateStamp; } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32; typedef struct _PEB32 { UCHAR InheritedAddressSpace; UCHAR ReadImageFileExecOptions; UCHAR BeingDebugged; UCHAR BitField; ULONG Mutant; ULONG ImageBaseAddress; ULONG Ldr; ULONG ProcessParameters; ULONG SubSystemData; ULONG ProcessHeap; ULONG FastPebLock; ULONG AtlThunkSListPtr; ULONG IFEOKey; ULONG CrossProcessFlags; ULONG UserSharedInfoPtr; ULONG SystemReserved; ULONG AtlThunkSListPtr32; ULONG ApiSetMap; } PEB32, *PPEB32;
需要用到的已导出但未文档化相关函数的声明
1 2 3 NTSYSAPI BOOLEAN NTAPI PsIsProtectedProcess (IN PEPROCESS Process) ; NTSYSAPI PVOID NTAPI PsGetProcessPeb (IN PEPROCESS Process) ; NTSYSAPI PVOID NTAPI PsGetProcessWow64Process (IN PEPROCESS Process) ;
这里以获取 explorer.exe
程序的 kernel32.dll
模块地址为例,需要注意的是: 遍历某进程的模块,需要先 KeStackAttachProcess
到目标进程中,但是受保护的进程不能Attach, 访问 PEB
的成员信息,要使用 __try{} __except(EXCEPTION_EXECUTE_HANDLER){}
来检测有效性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 PVOID GetKernel32Address (IN PEPROCESS Process, IN BOOLEAN IsWow64) { LARGE_INTEGER Timeout = { 0 }; __try { #ifdef AMD64 if (IsWow64) { PLIST_ENTRY32 ListEntry32 = NULL ; PPEB_LDR_DATA32 PebLdrData32 = NULL ; PLDR_DATA_TABLE_ENTRY32 TableEntry32 = NULL ; PPEB32 Peb32 = (PPEB32)PsGetProcessWow64Process(Process); if (!Peb32) return NULL ; Timeout.QuadPart = -10L L * 1000 * 250 ; for (INT i = 0 ; (!Peb32->Ldr) && (i < 10 ); i++) KeDelayExecutionThread(KernelMode, FALSE, &Timeout); if (!Peb32->Ldr) return NULL ; PebLdrData32 = (PPEB_LDR_DATA32)Peb32->Ldr; ListEntry32 = (PLIST_ENTRY32)PebLdrData32->InLoadOrderModuleList.Flink; while (ListEntry32 != &PebLdrData32->InLoadOrderModuleList) { TableEntry32 = CONTAINING_RECORD( ListEntry32, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks); if (TableEntry32->BaseDllName.Buffer && TableEntry32->BaseDllName.Length) { if (!_wcsnicmp((PWCHAR)TableEntry32->BaseDllName.Buffer, L"Kernel32.dll" , TableEntry32->BaseDllName.Length / 2 )) { return (PVOID)TableEntry32->DllBase; } } ListEntry32 = (PLIST_ENTRY32)ListEntry32->Flink; } } else { #endif PLIST_ENTRY ListEntry = NULL ; PPEB_LDR_DATA PebLdrData = NULL ; PLDR_DATA_TABLE_ENTRY TableEntry = NULL ; PPEB Peb = PsGetProcessPeb(Process); if (!Peb) return NULL ; #ifndef AMD64 UNREFERENCED_PARAMETER(IsWow64); #endif Timeout.QuadPart = -10L L * 1000 * 250 ; for (INT i = 0 ; (!Peb->Ldr) && (i < 10 ); i++) KeDelayExecutionThread(KernelMode, FALSE, &Timeout); if (!Peb->Ldr) return NULL ; PebLdrData = (PPEB_LDR_DATA)Peb->Ldr; ListEntry = (PLIST_ENTRY)PebLdrData->InLoadOrderModuleList.Flink; while (ListEntry != &PebLdrData->InLoadOrderModuleList) { TableEntry = CONTAINING_RECORD( ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (TableEntry->BaseDllName.Buffer && TableEntry->BaseDllName.Length) { if (!_wcsnicmp(TableEntry->BaseDllName.Buffer, L"Kernel32.dll" , TableEntry->BaseDllName.Length / 2 )) { return (PVOID)TableEntry->DllBase; } } ListEntry = (PLIST_ENTRY)ListEntry->Flink; } #ifdef AMD64 } #endif } __except (EXCEPTION_EXECUTE_HANDLER) {} return NULL ; }
3.获取导出函数 获取导出表的信息需要解析PE文件结构,如下为相关定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 #define IMAGE_DOS_SIGN 0x5A4D #define IMAGE_NT_SIGN 0x00004550 #define IMAGE_NT_OPT_HDR32_MAGIC 0x10B #define IMAGE_NT_OPT_HDR64_MAGIC 0x20B typedef struct _IMAGE_DOS_HDR { USHORT e_magic; USHORT e_cblp; USHORT e_cp; USHORT e_crlc; USHORT e_cparhdr; USHORT e_minalloc; USHORT e_maxalloc; USHORT e_ss; USHORT e_sp; USHORT e_csum; USHORT e_ip; USHORT e_cs; USHORT e_lfarlc; USHORT e_ovno; USHORT e_res[4 ]; USHORT e_oemid; USHORT e_oeminfo; USHORT e_res2[10 ]; LONG e_lfanew; } IMAGE_DOS_HDR, *PIMAGE_DOS_HDR; typedef struct _IMAGE_FILE_HDR { USHORT Machine; USHORT NumberOfSections; ULONG TimeDateStamp; ULONG PointerToSymbolTable; ULONG NumberOfSymbols; USHORT SizeOfOptionalHeader; USHORT Characteristics; } IMAGE_FILE_HDR, *PIMAGE_FILE_HDR; typedef struct _IMAGE_DATA_DIR { ULONG VirtualAddress; ULONG Size; } IMAGE_DATA_DIR, *PIMAGE_DATA_DIR; typedef struct _IMAGE_OPT_HDR64 { USHORT Magic; UCHAR MajorLinkerVersion; UCHAR MinorLinkerVersion; ULONG SizeOfCode; ULONG SizeOfInitializedData; ULONG SizeOfUninitializedData; ULONG AddressOfEntryPoint; ULONG BaseOfCode; ULONGLONG ImageBase; ULONG SectionAlignment; ULONG FileAlignment; USHORT MajorOperatingSystemVersion; USHORT MinorOperatingSystemVersion; USHORT MajorImageVersion; USHORT MinorImageVersion; USHORT MajorSubsystemVersion; USHORT MinorSubsystemVersion; ULONG Win32VersionValue; ULONG SizeOfImage; ULONG SizeOfHeaders; ULONG CheckSum; USHORT Subsystem; USHORT DllCharacteristics; ULONGLONG SizeOfStackReserve; ULONGLONG SizeOfStackCommit; ULONGLONG SizeOfHeapReserve; ULONGLONG SizeOfHeapCommit; ULONG LoaderFlags; ULONG NumberOfRvaAndSizes; IMAGE_DATA_DIR DataDirectory[16 ]; } IMAGE_OPT_HDR64, *PIMAGE_OPT_HDR64; typedef struct _IMAGE_OPT_HDR32 { USHORT Magic; UCHAR MajorLinkerVersion; UCHAR MinorLinkerVersion; ULONG SizeOfCode; ULONG SizeOfInitializedData; ULONG SizeOfUninitializedData; ULONG AddressOfEntryPoint; ULONG BaseOfCode; ULONG BaseOfData; ULONG ImageBase; ULONG SectionAlignment; ULONG FileAlignment; USHORT MajorOperatingSystemVersion; USHORT MinorOperatingSystemVersion; USHORT MajorImageVersion; USHORT MinorImageVersion; USHORT MajorSubsystemVersion; USHORT MinorSubsystemVersion; ULONG Win32VersionValue; ULONG SizeOfImage; ULONG SizeOfHeaders; ULONG CheckSum; USHORT Subsystem; USHORT DllCharacteristics; ULONG SizeOfStackReserve; ULONG SizeOfStackCommit; ULONG SizeOfHeapReserve; ULONG SizeOfHeapCommit; ULONG LoaderFlags; ULONG NumberOfRvaAndSizes; IMAGE_DATA_DIR DataDirectory[16 ]; } IMAGE_OPT_HDR32, *PIMAGE_OPT_HDR32; typedef struct _IMAGE_NT_HDR32 { ULONG Signature; IMAGE_FILE_HDR FileHeader; IMAGE_OPT_HDR32 OptHeader32; } IMAGE_NT_HDR32, *PIMAGE_NT_HDR32; typedef struct _IMAGE_NT_HDR64 { ULONG Signature; IMAGE_FILE_HDR FileHeader; IMAGE_OPT_HDR64 OptHeader64; } IMAGE_NT_HDR64, *PIMAGE_NT_HDR64; typedef struct _IMAGE_EXPORT_DIR { ULONG Characteristics; ULONG TimeDateStamp; USHORT MajorVersion; USHORT MinorVersion; ULONG Name; ULONG Base; ULONG NumberOfFunctions; ULONG NumberOfNames; ULONG AddressOfFunctions; ULONG AddressOfNames; ULONG AddressOfNameOrdinals; } IMAGE_EXPORT_DIR, *PIMAGE_EXPORT_DIR; typedef struct _IMAGE_SECTION_HDR { UCHAR Name[8 ]; union { ULONG PhysicalAddress; ULONG VirtualSize; } Misc; ULONG VirtualAddress; ULONG SizeOfRawData; ULONG PointerToRawData; ULONG PointerToRelocations; ULONG PointerToLinenumbers; USHORT NumberOfRelocations; USHORT NumberOfLinenumbers; ULONG Characteristics; } IMAGE_SECTION_HDR, *PIMAGE_SECTION_HDR;
这里以获取 Kernel32.dll
的 WinExec
函数地址为例, 注意:在访问不确定的地址时,一定要记得加 __try
异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 PVOID GetExportFuncAddr (IN PVOID DllBase, IN PCSTR FuncName) { PULONG Ent = NULL ; PULONG Eat = NULL ; PUSHORT Eot = NULL ; PIMAGE_DOS_HDR DosHdr = NULL ; PIMAGE_NT_HDR32 NtHdr32 = NULL ; PIMAGE_NT_HDR64 NtHdr64 = NULL ; PIMAGE_EXPORT_DIR ExportDir = NULL ; if (!DllBase || !FuncName) return NULL ; __try { DosHdr = (PIMAGE_DOS_HDR)DllBase; if (DosHdr->e_magic != IMAGE_DOS_SIGN) return NULL ; NtHdr32 = (PIMAGE_NT_HDR32)((PUCHAR)DllBase + DosHdr->e_lfanew); NtHdr64 = (PIMAGE_NT_HDR64)((PUCHAR)DllBase + DosHdr->e_lfanew); if (NtHdr32->Signature != IMAGE_NT_SIGN) return NULL ; if (NtHdr32->OptHeader32.Magic == IMAGE_NT_OPT_HDR32_MAGIC) { ExportDir = (PIMAGE_EXPORT_DIR)(NtHdr32->OptHeader32. DataDirectory[0 ].VirtualAddress + (ULONG_PTR)DllBase); } else { ExportDir = (PIMAGE_EXPORT_DIR)(NtHdr64->OptHeader64. DataDirectory[0 ].VirtualAddress + (ULONG_PTR)DllBase); } Eot = (PUSHORT)(ExportDir->AddressOfNameOrdinals + (ULONG_PTR)DllBase); Ent = (PULONG)(ExportDir->AddressOfNames + (ULONG_PTR)DllBase); Eat = (PULONG)(ExportDir->AddressOfFunctions + (ULONG_PTR)DllBase); for (ULONG i = 0 ; i < ExportDir->NumberOfNames; i++) { if (!strcmp ((PCHAR)(Ent[i] + (ULONG_PTR)DllBase), FuncName)) return (PVOID)(Eat[Eot[i]] + (ULONG_PTR)DllBase); } } __except (EXCEPTION_EXECUTE_HANDLER) {} return NULL ; }