首页 > 其他分享 >驱动开发:PE导出函数与RVA转换

驱动开发:PE导出函数与RVA转换

时间:2023-06-07 10:37:38浏览次数:31  
标签:Index RVA 函数 FileOffset ++ 导出 pSectionHeader PE VirtualAddress

在笔者上篇文章《驱动开发:内核扫描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: [email protected]

// 从指定模块中得到特定函数的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->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);

		// 如果找到则返回RVA
		if (!_stricmp((const char *)FunctionName, FunName))
		{
			// 等于1则返回RVA
			if (Flag == 1)
			{
				TargetOff = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];
				// DbgPrint("索引 [ %p ] 函数名 [ %s ] 相对RVA [ %p ] \n", *AddressOfNameOrdinals, FunName, TargetOff);
				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)
{
	// 函数分别传入 [模块路径,函数名,标志=1] 返回该导出函数的RVA
	ULONG64 get_rva = GetRvaFromModuleName(L"\\SystemRoot\\system32\\ntoskrnl.exe", "NtReadFile", 1);
	DbgPrint("NtReadFile RVA = %p \n", get_rva);

	// 函数分别传入 [模块路径,函数名,标志=0] 返回该导出函数的ID下标
	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序号,解析出函数名,具体实现方法与如上函数基本一致,仅仅只是在过滤时做了调整。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

// 根据传入的函数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;

	// 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->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];

		// 等于1则通过RVA返回函数名
		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;

	// 传入函数RVA得到函数名
	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=0x5e5220Index=1472的函数名;

标签:Index,RVA,函数,FileOffset,++,导出,pSectionHeader,PE,VirtualAddress
From: https://www.cnblogs.com/LyShark/p/17154175.html

相关文章

  • NPOI导出Excel自适应行高
    开发环境:.NetCore3.1,NPOI2.6.0功能需求:导出的Excel的内容要自适应行高。先看模板,这是一张质量检测报告单,打码部分属于其他敏感数据。最终效果 需求分析:一顿网上查找“NPOI导出Excel自适应行高”,都是片段代码,很多参数不明,但是也提供了不错的思路,这里博主整理一下。之前......
  • 面向开发者的 ChatGPT 提示工程课程|吴恩达携手OpenAI 教你如何编写 prompt
    提示工程(PromptEngineering)是一门相对较新的学科,旨在开发和优化提示,从而高效地将语言模型(LM)用于各种应用和研究主题,并帮助开发人员更好地理解大型语言模型(LLM)的能力和局限。随着ChatGPT等大语言模型的爆火,提示工程在大模型中的重要性不言而喻。有效的提示工程需要考虑哪些......
  • 记录一次POI导出文件超时报错的问题
    后端日志错误信息![在这里插入图片描述](https://img-blog.csdnimg.cn/20200622165735646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxODM3OTkzNzAy,size_16,color_FFFFFF,t_70)解决办法在nginx的location中......
  • sqlServer用nacicat 导出表结构
    在我们写数据库设计文档时,我们可能得到一份特定表的表结构数据表格(如图),而Navicat并没有直接给我们提供这个功能,该怎么办呢?请看下面步骤1.打开Navicat,双击打开你要导出表结构的数据库(此时数据库名称变绿),点击“查询”——“新建查询”2、将以下SQL语句复制粘贴进查询编辑器,并修改数据......
  • poi根据模版导出多页word,带插入图片,并压缩下载
    工具类代码packagecn.edu.nfu.jw.srs.utils;importorg.apache.poi.xwpf.usermodel.*;importorg.apache.xmlbeans.XmlOptions;importorg.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;importjavax.servlet.http.HttpServletResponse;importjava.io.*;i......
  • Nginx出现403 forbidden (13: Permission denied)报错的解决办法
    一、由于启动用户和nginx工作用户不一致所致1、将nginx.config的user改为和启动用户一致,命令:viconf/nginx.conf二、缺少index.html或者index.php文件,就是配置文件中indexindex.htmlindex.htm这行中的指定的文件。server{listen80;server_namelocalhost;indexindex.p......
  • OpenOCD : Error: Error connecting DP: cannot read IDR
    没有连接单片机或是连接单片机没有开机。Warn:Failedtoopendevice:LIBUSB_ERROR_NOT_SUPPORTED:这个警告表示OpenOCD无法打开设备,因为设备不受支持。这通常是由于使用的调试适配器与OpenOCD或计算机的驱动程序不兼容所致。您可以尝试以下方法解决该问题:确保您使用的调试......
  • Python apend & extend 使用说明
    列表操作append()函数a.append(b):是将b原封不动的追加到a的末尾上,会改变a的值,其中,b可为列表、元组、字符串、一串数/字符/字符串append列表a=[1,2,3]b=['a','b']a.append(b)print(a)#[1,2,3,['a','b']]append元组a=[1,2,3]b=('a','b......
  • openwrt 搭建halo
    1、openwrt中安装docker2、更具情况在自己安装的目录里新建目录mkdir-p/opt/docker/Configs/.halomkdir-p/data/docker/.halo3、进入安装目录cd/opt/docker/Config/.halo4.创建容器并拉取镜像dockerrun-it-d--namehalo -p8090:8090 -v/opt/docker/Configs/.......
  • 在 SSM 中基于 MyBatis-PageHelper 分页插件的分页功能实现
    1引入分页插件2配置拦截器插件注意顺序!!!3插件使用serviceImpl.javacontroller.javajsp文件4效果测试pagehelper/Mybatis-PageHelper说明文档Spring4。X应用DEMO......