在笔者上篇文章《驱动开发:内核扫描SSDT挂钩状态》
中简单介绍了如何扫描被挂钩的SSDT函数,并简单介绍了如何解析导出表,本章将继续延申PE导出表的解析,实现一系列灵活的解析如通过传入函数名解析出函数的RVA偏移,ID索引,Index下标等参数,并将其封装为可直接使用的函数,以在后期需要时可以被直接引用,同样为了节约篇幅本章中的LoadKernelFile()
内存映射函数如需要使用请去前一篇文章中自行摘取。
首先实现GetRvaFromModuleName()
函数,当用户传入参数后自动将函数名解析为对应的RVA偏移或Index下标索引值,该函数接收三个参数传递,分别是wzFileName
模块名,FunctionName
所在模块内的函数名,Flag
标志参数,函数输出ULONG64
类型的数据。
// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
// 从指定模块中得到特定函数的RVA或相对序号相对偏移
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;
// DLL内存数据转成DOS头结构
pDosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
// 取出PE头结构
pNtHeaders = (PIMAGE_NT_HEADERS)((ULONGLONG)BaseAddress + pDosHeader->e_lfanew);
// 判断PE头导出表是否为空
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->AddressOf