在笔者上篇文章《内核扫描SSDT挂钩状态》
中简单介绍了如何扫描被挂钩的SSDT函数,并简单介绍了如何解析导出表,本章将继续延申PE导出表的解析,实现一系列灵活的解析如通过传入函数名解析出函数的RVA偏移,ID索引,Index下标等参数,并将其封装为可直接使用的函数,以在后期需要时可以被直接引用,同样为了节约篇幅本章中的LoadKernelFile()
内存映射函数如需要使用请去前一篇文章中自行摘取。
首先实现GetRvaFromModuleName()
函数,当用户传入参数后自动将函数名解析为对应的RVA偏移或Index下标索引值,该函数接收三个参数传递,分别是wzFileName
模块名,FunctionName
所在模块内的函数名,Flag
标志参数,函数输出ULONG64
类型的数据。
ULONG64 GetRvaFromModuleName(WCHAR *wzFileName, UCHAR *FunctionName, INT Flag) { PVOID BaseAddress = LoadKernelFile(wzFileName);
PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNtHeaders; PIMAGE_SECTION_HEADER pSectionHeader; ULONGLONG FileOffset; PIMAGE_EXPORT_DIRECTORY pExportDirectory;
pDosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)BaseAddress + pDosHeader->e_lfanew);
if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) { return 0; }
FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONGLONG)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;
for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } }
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONGLONG)BaseAddress + FileOffset);
PULONG AddressOfFunctions; FileOffset = pExportDirectory->AddressOfFunctions;
pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } AddressOfFunctions = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
PUSHORT AddressOfNameOrdinals; FileOffset = pExportDirectory->AddressOfNameOrdinals;
pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } AddressOfNameOrdinals = (PUSHORT)((ULONGLONG)BaseAddress + FileOffset);
PULONG AddressOfNames; FileOffset = pExportDirectory->AddressOfNames;
pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } AddressOfNames = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
ULONG uOffset; LPSTR FunName; ULONG uAddressOfNames; ULONG TargetOff = 0;
for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++) { uAddressOfNames = *AddressOfNames; pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } FunName = (LPSTR)((ULONGLONG)BaseAddress + uOffset);
if (!_stricmp((const char *)FunctionName, FunName)) { if (Flag == 1) { TargetOff = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals]; return TargetOff; } else if (Flag == 0) { return *AddressOfNameOrdinals; } } }
ExFreePoolWithTag(BaseAddress, (ULONG)"LyShark"); return 0; }
|
调用该函数很容易,传入模块路径以及该模块内的函数名,解析出RVA地址或Index下标。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { ULONG64 get_rva = GetRvaFromModuleName(L"\\SystemRoot\\system32\\ntoskrnl.exe", "NtReadFile", 1); DbgPrint("NtReadFile RVA = %p \n", get_rva);
ULONG64 get_id = GetRvaFromModuleName(L"\\SystemRoot\\system32\\ntoskrnl.exe", "NtReadFile", 0); DbgPrint("NtReadFile ID = %d \n", get_id);
Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
|
编译并运行程序,分别获取到ntoskrnl.exe
模块内NtReadFile
函数的RVA,Index索引,调用效果如下;
第二个函数GetModuleNameFromRVA()
则实现传入RVA或者函数Index序号,解析出函数名,具体实现方法与如上函数基本一致,仅仅只是在过滤时做了调整。
PCHAR GetModuleNameFromRVA(WCHAR *wzFileName, ULONG64 uRVA, INT Flag) { PVOID BaseAddress = LoadKernelFile(wzFileName);
PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNtHeaders; PIMAGE_SECTION_HEADER pSectionHeader; ULONGLONG FileOffset; PIMAGE_EXPORT_DIRECTORY pExportDirectory;
pDosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)BaseAddress + pDosHeader->e_lfanew);
if (pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) { return 0; }
FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONGLONG)pNtHeaders + sizeof(IMAGE_NT_HEADERS)); PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;
for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } }
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONGLONG)BaseAddress + FileOffset);
PULONG AddressOfFunctions; FileOffset = pExportDirectory->AddressOfFunctions;
pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } AddressOfFunctions = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
PUSHORT AddressOfNameOrdinals; FileOffset = pExportDirectory->AddressOfNameOrdinals;
pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } AddressOfNameOrdinals = (PUSHORT)((ULONGLONG)BaseAddress + FileOffset);
PULONG AddressOfNames; FileOffset = pExportDirectory->AddressOfNames;
pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } } AddressOfNames = (PULONG)((ULONGLONG)BaseAddress + FileOffset);
ULONG uOffset; LPSTR FunName; ULONG uAddressOfNames; ULONG TargetOff = 0;
for (ULONG uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++) { uAddressOfNames = *AddressOfNames; pSectionHeader = pOldSectionHeader; for (UINT16 Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++) { if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData) { uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData; } }
FunName = (LPSTR)((ULONGLONG)BaseAddress + uOffset); TargetOff = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];
if (Flag == 1) { if (uRVA == TargetOff) { return FunName; } } else if (Flag == 0) { if (uRVA == *AddressOfNameOrdinals) { return FunName; } } }
ExFreePoolWithTag(BaseAddress, (ULONG)"LyShark"); return "None"; }
|
调用GetModuleNameFromRVA()
并传入相应的RVA偏移或Index下标。
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint("hello lyshark.com \n");
PCHAR function_name;
function_name = GetModuleNameFromRVA(L"\\SystemRoot\\system32\\ntoskrnl.exe", 0x5e5220, 1); DbgPrint("根据RVA得到函数名 = %s \n", function_name);
function_name = GetModuleNameFromRVA(L"\\SystemRoot\\system32\\ntoskrnl.exe", 1472, 0); DbgPrint("根据Index得到函数名 = %s \n", function_name);
Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
|
编译并运行程序,调用后分别获取到RVA=0x5e5220
或Index=1472
的函数名;