码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 驱动开发:PE导出函数与RVA转换


    合集 - Windows 内核安全编程技术实践(97)
    1.驱动开发:运用VAD隐藏R3内存思路2021-07-162.驱动开发:应用DeviceIoContro模板精讲06-293.驱动开发:取进程模块的函数地址06-284.驱动开发:内核读写内存多级偏移06-275.驱动开发:内核物理内存寻址读写06-266.驱动开发:内核远程线程实现DLL注入06-257.驱动开发:摘除InlineHook内核钩子06-248.驱动开发:内核中进程与句柄互转06-239.驱动开发:内核注册表增删改查06-2110.驱动开发:基于事件同步的反向通信06-2011.驱动开发:文件微过滤驱动入门06-1912.驱动开发:内核RIP劫持实现DLL注入06-1613.驱动开发:内核解锁与强删文件06-1514.驱动开发:内核ShellCode线程注入06-1415.驱动开发:内核LoadLibrary实现DLL注入06-1316.驱动开发:内核遍历文件或目录06-1217.驱动开发:内核文件读写系列函数06-0918.驱动开发:内核封装WFP防火墙入门06-08
    19.驱动开发:PE导出函数与RVA转换06-07
    20.驱动开发:内核扫描SSDT挂钩状态06-0621.驱动开发:内核实现SSDT挂钩与摘钩06-0522.驱动开发:内核PE结构VA与FOA转换06-0223.驱动开发:内核解析PE结构节表06-0124.驱动开发:内核解析PE结构导出表05-3125.驱动开发:内核读写内存浮点数05-3026.驱动开发:内核解析内存四级页表05-2927.驱动开发:内核实现进程汇编与反汇编05-2328.驱动开发:通过应用堆实现多次通信05-1929.驱动开发:内核远程堆分配与销毁05-1530.驱动开发:通过MDL映射实现多次通信04-2931.驱动开发:内核使用IO/DPC定时器04-0432.驱动开发:探索DRIVER_OBJECT驱动对象04-0333.驱动开发:配置Visual Studio驱动开发环境03-1334.驱动开发:内核封装TDI网络通信接口2022-11-0335.驱动开发:内核封装WSK网络通信接口2022-11-0336.驱动开发:内核层InlineHook挂钩函数2022-10-3137.驱动开发:内核LDE64引擎计算汇编长度2022-10-3138.驱动开发:内核强制结束进程运行2022-10-2939.驱动开发:内核监控FileObject文件回调2022-10-2840.驱动开发:内核监控Register注册表回调2022-10-2741.驱动开发:内核运用LoadImage屏蔽驱动2022-10-2642.驱动开发:内核监视LoadImage映像回调2022-10-2543.驱动开发:内核无痕隐藏自身分析2022-10-2444.驱动开发:内核注册并监控对象回调2022-10-2445.驱动开发:内核监控进程与线程回调2022-10-2346.驱动开发:内核测试模式过DSE签名2022-10-2247.驱动开发:内核枚举进程与线程ObCall回调2022-10-2248.驱动开发:内核枚举Registry注册表回调2022-10-2149.驱动开发:内核枚举LoadImage映像回调2022-10-2050.驱动开发:内核枚举ShadowSSDT基址2022-10-2051.驱动开发:Win10枚举完整SSDT地址表2022-10-1952.驱动开发:Win10内核枚举SSDT表基址2022-10-1953.驱动开发:内核特征码扫描PE代码段2022-10-1854.驱动开发:内核枚举Minifilter微过滤驱动2022-10-1855.驱动开发:内核特征码搜索函数封装2022-10-1756.驱动开发:内核枚举驱动内线程(答疑篇)2022-10-1657.驱动开发:内核枚举PspCidTable句柄表2022-10-1658.驱动开发:内核枚举DpcTimer定时器2022-10-1659.驱动开发:如何枚举所有SSDT表地址2022-10-1460.驱动开发:内核枚举IoTimer定时器2022-10-1461.驱动开发:内核遍历进程VAD结构体2022-10-1362.驱动开发:内核中实现Dump进程转储2022-10-1163.驱动开发:内核R3与R0内存映射拷贝2022-10-1164.驱动开发:内核通过PEB得到进程参数2022-10-1065.驱动开发:内核取应用层模块基地址2022-10-0966.驱动开发:内核取ntoskrnl模块基地址2022-10-0967.驱动开发:判断自身是否加载成功2022-10-0868.驱动开发:应用DeviceIoContro开发模板2022-10-0369.驱动开发:通过Async反向与内核通信2022-10-0370.驱动开发:通过PIPE管道与内核层通信2022-10-0171.驱动通信:通过PIPE管道与内核层通信2022-10-0172.驱动开发:通过ReadFile与内核层通信2022-09-3073.驱动开发:内核字符串拷贝与比较2022-09-2974.驱动开发:内核字符串转换方法2022-09-2875.驱动开发:内核中的自旋锁结构2022-09-2876.驱动开发:内核CR3切换读写内存2022-09-2577.驱动开发:内核中的链表与结构体2022-09-2378.驱动开发:摘链DKOM进程隐藏2022-08-3179.驱动开发:WinDBG 枚举SSDT以及SSSDT地址2022-04-2880.驱动开发:实现驱动加载卸载工具2021-07-1681.驱动开发:断链隐藏驱动程序自身2021-07-1682.驱动开发:通过内存拷贝读写内存2021-07-1283.驱动开发:内核MDL读写进程内存2021-07-0584.驱动开发:内核监控进程与线程创建2021-05-0685.驱动开发:监控进程与线程对象操作2020-06-1486.驱动开发:WinDBG 常用调试命令总结2020-06-1087.驱动开发:通过SystemBuf与内核层通信2020-04-1388.驱动开发:对象回调监控文件访问2019-11-0189.驱动开发:内核中枚举进线程与模块2019-10-2190.驱动开发:DKOM 实现进程隐藏2019-10-1191.驱动开发:内核读取SSDT表基址2019-10-0992.驱动开发:驱动与应用的简单通信2019-09-2393.驱动开发:恢复SSDT内核钩子2019-09-2194.驱动开发:挂接SSDT内核钩子2019-09-2095.驱动开发:WinDBG 配置内核双机调试2019-09-1996.VS2013+WDK8.1 驱动开发环境配置2019-09-1897.驱动开发:派遣函数与设备对象2019-09-14
    收起

    在笔者上篇文章《驱动开发:内核扫描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->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: me@lyshark.com
    
    // 根据传入的函数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=0x5e5220或Index=1472的函数名;

  • 相关阅读:
    手写RPC框架-第五天 支持HTTP协议
    Java--Spring之AOP面向切面编程
    向量化编程书籍推荐
    将vue项目打包成安卓app
    京东商品如何同步主图到长主图
    java毕业设计宠物销售管理系统Mybatis+系统+数据库+调试部署
    算法基础-----【动态规划】
    写作一笔记
    推荐一个Java学习路线图
    Java开发快递物流项目
  • 原文地址:https://www.cnblogs.com/LyShark/p/17154175.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号