在筆者上一篇文章《驅動開發(fā):內核取應用層模塊基地址》
中簡單為大家介紹了如何通過遍歷PLIST_ENTRY32
鏈表的方式獲取到32位
應用程序中特定模塊的基地址,由于是入門系列所以并沒有封裝實現(xiàn)太過于通用的獲取函數(shù),本章將繼續(xù)延申這個話題,并依次實現(xiàn)通用版GetUserModuleBaseAddress()
取遠程進程中指定模塊的基址和GetModuleExportAddress()
取遠程進程中特定模塊中的函數(shù)地址,此類功能也是各類安全工具中常用的代碼片段。
首先封裝一個lyshark.h
頭文件,此類頭文件中的定義都是微軟官方定義好的規(guī)范,如果您想獲取該結構的詳細說明文檔請參閱微軟官方,此處不做過多的介紹。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include <ntifs.h>
#include <ntimage.h>
#include <ntstrsafe.h>
// 導出未導出函數(shù)
NTKERNELAPI PPEB NTAPI PsGetProcessPeb(IN PEPROCESS Process);
NTKERNELAPI PVOID NTAPI PsGetProcessWow64Process(IN PEPROCESS Process);
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;
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 _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 KernelCallbackTable;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
PVOID ApiSetMap;
} PEB, *PPEB;
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 _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;
GetUserModuleBaseAddress(): 實現(xiàn)取進程中模塊基址,該功能在《驅動開發(fā):內核取應用層模塊基地址》
中詳細介紹過原理,這段代碼核心原理如下所示,此處最需要注意的是如果是32位進程
則我們需要得到PPEB32 Peb32
結構體,該結構體通??梢灾苯邮褂?code>PsGetProcessWow64Process()這個內核函數(shù)獲取到,而如果是64位進程
則需要將尋找PEB的函數(shù)替換為PsGetProcessPeb()
,其他的枚舉細節(jié)與上一篇文章中的方法一致。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include <ntifs.h>
#include <windef.h>
#include "lyshark.h"
// 獲取特定進程內特定模塊的基址
PVOID GetUserModuleBaseAddress(IN PEPROCESS EProcess, IN PUNICODE_STRING ModuleName, IN BOOLEAN IsWow64)
{
if (EProcess == NULL)
return NULL;
__try
{
// 設置延遲時間為250毫秒
LARGE_INTEGER Time = { 0 };
Time.QuadPart = -250ll * 10 * 1000;
// 如果是32位則執(zhí)行如下代碼
if (IsWow64)
{
// 得到PEB進程信息
PPEB32 Peb32 = (PPEB32)PsGetProcessWow64Process(EProcess);
if (Peb32 == NULL)
{
return NULL;
}
// 延遲加載等待時間
for (INT i = 0; !Peb32->Ldr && i < 10; i++)
{
KeDelayExecutionThread(KernelMode, TRUE, &Time);
}
// 沒有PEB加載超時
if (!Peb32->Ldr)
{
return NULL;
}
// 搜索模塊 InLoadOrderModuleList
for (PLIST_ENTRY32 ListEntry = (PLIST_ENTRY32)((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList.Flink; ListEntry != &((PPEB_LDR_DATA32)Peb32->Ldr)->InLoadOrderModuleList; ListEntry = (PLIST_ENTRY32)ListEntry->Flink)
{
UNICODE_STRING UnicodeString;
PLDR_DATA_TABLE_ENTRY32 LdrDataTableEntry32 = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
RtlUnicodeStringInit(&UnicodeString, (PWCH)LdrDataTableEntry32->BaseDllName.Buffer);
// 找到了返回模塊基址
if (RtlCompareUnicodeString(&UnicodeString, ModuleName, TRUE) == 0)
{
return (PVOID)LdrDataTableEntry32->DllBase;
}
}
}
// 如果是64位則執(zhí)行如下代碼
else
{
// 同理,先找64位PEB
PPEB Peb = PsGetProcessPeb(EProcess);
if (!Peb)
{
return NULL;
}
// 延遲加載
for (INT i = 0; !Peb->Ldr && i < 10; i++)
{
KeDelayExecutionThread(KernelMode, TRUE, &Time);
}
// 找不到PEB直接返回
if (!Peb->Ldr)
{
return NULL;
}
// 遍歷鏈表
for (PLIST_ENTRY ListEntry = Peb->Ldr->InLoadOrderModuleList.Flink; ListEntry != &Peb->Ldr->InLoadOrderModuleList; ListEntry = ListEntry->Flink)
{
// 將特定鏈表轉換為PLDR_DATA_TABLE_ENTRY格式
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
// 找到了則返回地址
if (RtlCompareUnicodeString(&LdrDataTableEntry->BaseDllName, ModuleName, TRUE) == 0)
{
return LdrDataTableEntry->DllBase;
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return NULL;
}
return NULL;
}
那么該函數(shù)該如何調用傳遞參數(shù)呢,如下代碼是DriverEntry
入口處的調用方法,首先要想得到特定進程的特定模塊地址則第一步就是需要PsLookupProcessByProcessId
找到模塊的EProcess
結構,接著通過PsGetProcessWow64Process
得到當前被操作進程是32位還是64位,通過調用KeStackAttachProcess
附加到進程內存中,然后調用GetUserModuleBaseAddress
并傳入需要獲取模塊的名字得到數(shù)據(jù)后返回給NtdllAddress
變量,最后調用KeUnstackDetachProcess
取消附加即可。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
HANDLE ProcessID = (HANDLE)7924;
PEPROCESS EProcess = NULL;
NTSTATUS Status = STATUS_SUCCESS;
KAPC_STATE ApcState;
DbgPrint("Hello LyShark.com \n");
// 根據(jù)PID得到進程EProcess結構
Status = PsLookupProcessByProcessId(ProcessID, &EProcess);
if (Status != STATUS_SUCCESS)
{
DbgPrint("獲取EProcessID失敗 \n");
return Status;
}
// 判斷目標進程是32位還是64位
BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;
// 驗證地址是否可讀
if (!MmIsAddressValid(EProcess))
{
DbgPrint("地址不可讀 \n");
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
// 將當前線程連接到目標進程的地址空間(附加進程)
KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);
__try
{
UNICODE_STRING NtdllUnicodeString = { 0 };
PVOID NtdllAddress = NULL;
// 得到進程內ntdll.dll模塊基地址
RtlInitUnicodeString(&NtdllUnicodeString, L"Ntdll.dll");
NtdllAddress = GetUserModuleBaseAddress(EProcess, &NtdllUnicodeString, IsWow64);
if (!NtdllAddress)
{
DbgPrint("沒有找到基址 \n");
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
DbgPrint("[*] 模塊ntdll.dll基址: %p \n", NtdllAddress);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
// 取消附加
KeUnstackDetachProcess(&ApcState);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
替換DriverEntry
入口函數(shù)處的ProcessID
并替換為當前需要獲取的應用層進程PID,運行驅動程序即可得到該進程內Ntdll.dll
的模塊基址,輸出效果如下;
GetModuleExportAddress(): 實現(xiàn)獲取特定模塊中特定函數(shù)的基地址,通常我們通過GetUserModuleBaseAddress()
可得到進程內特定模塊的基址,然后則可繼續(xù)通過GetModuleExportAddress()
獲取到該模塊內特定導出函數(shù)的內存地址,至于獲取導出表中特定函數(shù)的地址則可通過如下方式循環(huán)遍歷導出表函數(shù)獲取。
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
// 獲取特定模塊下的導出函數(shù)地址
PVOID GetModuleExportAddress(IN PVOID ModuleBase, IN PCCHAR FunctionName, IN PEPROCESS EProcess)
{
PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
PIMAGE_NT_HEADERS32 ImageNtHeaders32 = NULL;
PIMAGE_NT_HEADERS64 ImageNtHeaders64 = NULL;
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
ULONG ExportDirectorySize = 0;
ULONG_PTR FunctionAddress = 0;
// 為空則返回
if (ModuleBase == NULL)
{
return NULL;
}
// 是不是PE文件
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return NULL;
}
// 獲取NT頭
ImageNtHeaders32 = (PIMAGE_NT_HEADERS32)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
ImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)ModuleBase + ImageDosHeader->e_lfanew);
// 是64位則執(zhí)行
if (ImageNtHeaders64->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ExportDirectorySize = ImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
// 是32位則執(zhí)行
else
{
ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONG_PTR)ModuleBase);
ExportDirectorySize = ImageNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
}
// 得到導出表地址偏移和名字
PUSHORT pAddressOfOrds = (PUSHORT)(ImageExportDirectory->AddressOfNameOrdinals + (ULONG_PTR)ModuleBase);
PULONG pAddressOfNames = (PULONG)(ImageExportDirectory->AddressOfNames + (ULONG_PTR)ModuleBase);
PULONG pAddressOfFuncs = (PULONG)(ImageExportDirectory->AddressOfFunctions + (ULONG_PTR)ModuleBase);
// 循環(huán)搜索導出表
for (ULONG i = 0; i < ImageExportDirectory->NumberOfFunctions; ++i)
{
USHORT OrdIndex = 0xFFFF;
PCHAR pName = NULL;
// 搜索導出表下標索引
if ((ULONG_PTR)FunctionName <= 0xFFFF)
{
OrdIndex = (USHORT)i;
}
// 搜索導出表名字
else if ((ULONG_PTR)FunctionName > 0xFFFF && i < ImageExportDirectory->NumberOfNames)
{
pName = (PCHAR)(pAddressOfNames[i] + (ULONG_PTR)ModuleBase);
OrdIndex = pAddressOfOrds[i];
}
else
{
return NULL;
}
// 找到設置返回值并跳出
if (((ULONG_PTR)FunctionName <= 0xFFFF && (USHORT)((ULONG_PTR)FunctionName) == OrdIndex + ImageExportDirectory->Base) || ((ULONG_PTR)FunctionName > 0xFFFF && strcmp(pName, FunctionName) == 0))
{
FunctionAddress = pAddressOfFuncs[OrdIndex] + (ULONG_PTR)ModuleBase;
break;
}
}
return (PVOID)FunctionAddress;
}
如何調用此方法,首先將ProcessID
設置為需要讀取的進程PID,然后將上圖中所輸出的0x00007FF9553C0000
賦值給BaseAddress
接著調用GetModuleExportAddress()
并傳入BaseAddress
模塊基址,需要讀取的LdrLoadDll
函數(shù)名,以及當前進程的EProcess
結構。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
HANDLE ProcessID = (HANDLE)4144;
PEPROCESS EProcess = NULL;
NTSTATUS Status = STATUS_SUCCESS;
// 根據(jù)PID得到進程EProcess結構
Status = PsLookupProcessByProcessId(ProcessID, &EProcess);
if (Status != STATUS_SUCCESS)
{
DbgPrint("獲取EProcessID失敗 \n");
return Status;
}
PVOID BaseAddress = (PVOID)0x00007FF9553C0000;
PVOID RefAddress = 0;
// 傳入Ntdll.dll基址 + 函數(shù)名 得到該函數(shù)地址
RefAddress = GetModuleExportAddress(BaseAddress, "LdrLoadDll", EProcess);
DbgPrint("[*] 函數(shù)地址: %p \n", RefAddress);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行這段程序,即可輸出如下信息,此時也就得到了x64.exe
進程內ntdll.dll
模塊里面的LdrLoadDll
函數(shù)的內存地址,如下所示;文章來源:http://www.zghlxwxcb.cn/news/detail-503918.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-503918.html
到了這里,關于驅動開發(fā):取進程模塊的函數(shù)地址的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!