首页 > 编程语言 >C/C++ 实现VA与FOA之间的转换

C/C++ 实现VA与FOA之间的转换

时间:2023-07-06 15:31:32浏览次数:41  
标签:VA RVA C++ pSection each DWORD FOA


PE结构中的地址互转,这次再来系统的复习一下关于PE结构中各种地址的转换方式,最终通过编程来实现自动解析计算,最后将这个功能集成到我的迷你解析器中,本章中使用的工具是上次讲解PE结构文章中制作的CMD迷你结构解析器,如果不知道参数的基本使用请看前一篇。

PE工具的使用与下载:C/C++ 实现LyDebug动态PE调试器 - lyshark

将VA地址转换为FOA文件偏移: VA就是虚拟地址,转换为FOA文件偏移,其手工计算过程如下所示。

首先需要得到 ImageBase(镜像基址) 其次得到入口点地址,将两个地址相加即可得到VA,也就是实际装入地址。

C/C++ 实现VA与FOA之间的转换_相对地址

通过上方的已知条件我们就可以计算出程序实际装入内存后的入口地址了.

VA(实际装入地址) = ImageBase(基址) + RVA(偏移) => 00400000 + 0000158b = 0040158b

如果不放心,可以将源程序拖入X64DBG中观察是否一致,如下OEP为 0040158b 与我们的计算结果完全一致。

C/C++ 实现VA与FOA之间的转换_开发语言_02

接着我们需要得到 .text节 基地址以及 .text节 RVA,如下命令即可获取到。

C/C++ 实现VA与FOA之间的转换_开发语言_03

虚拟地址开始位置:节区基地址 + 节区RVA => 00400000 + 00001000 = 00401000

C/C++ 实现VA与FOA之间的转换_虚拟地址_04

虚拟地址结束位置:text节地址 + 节区尺寸 => 00401000 + 0x00000B44 = 00401B44

C/C++ 实现VA与FOA之间的转换_虚拟地址_05

计算得出,虚拟地址text节开始位置 00401000 结束位置是 00401B44 打开X64dbg验证没错 确实落在了 text节上。

判断是否落在了.text节的依据是 开始位置 >= 401000 结束位置 <= 402000 很明显:00401B44小于402000。

C/C++ 实现VA与FOA之间的转换_相对地址_06

接着我们就来计算一下,当前的VA地址0040158B其对应到文件中的偏移FOA位置是多少,计算公式如下。

C/C++ 实现VA与FOA之间的转换_开发语言_07

RVA(相对偏移) = VA - (.text节首地址) => 0040158B - 00401000 = 58B
FOA(文件偏移) = RVA + .text节对应到文件中的偏移 => 58B + 400 = 98B

C/C++ 实现VA与FOA之间的转换_相对地址_08

计算出结果了,我们使用WinHEX定位到98B处看看是否符合要求,机器码完全一致,符合要求。

C/C++ 实现VA与FOA之间的转换_开发语言_09

接着就是使用C语言来实现这一计算过程了,有了计算流程之后使用C语言实现就变得简单许多。

DWORD VA_To_FOA(HANDLE ImageBase,DWORD dwVA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD Section_Start = dwImageBase + pSection[each].VirtualAddress;                                  // 获取节的开始地址
		DWORD Section_Ends = dwImageBase + pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 获取节的结束地址

		if (dwVA >= Section_Start && dwVA <= Section_Ends)
		{
			DWORD RVA = dwVA - pNtHead->OptionalHeader.ImageBase;                                    // 计算RVA
			DWORD FOA = pSection[each].PointerToRawData + (RVA - pSection[each].VirtualAddress);     // 计算FOA
			return FOA;
		}
	}
	return -1;
}

将FOA文件偏移转换为VA地址: 将十六进制的文件偏移地址,反转为VA地址。

如下,通过公式计算一下文件偏移为0xF43的位置,其对应到VA虚拟地址是多少。

VPK(实际大小) = (text节首地址 - ImageBase) - 实际偏移 => 401000-400000-400 = C00

C/C++ 实现VA与FOA之间的转换_开发语言_10

VA(虚拟地址) = FOA(.text节) + ImageBase + VPK => F43 + 400000 + C00 = 401B43

C/C++ 实现VA与FOA之间的转换_c语言_11

计算后的结果F43对应到VA地址是401B43 验证一下,没错。

C/C++ 实现VA与FOA之间的转换_c++_12

通过C语言实现也很简单,只需要把这个计算过程流程化即可。

DWORD RVA_To_FOA(HANDLE ImageBase, DWORD dwRVA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD Section_Start = pSection[each].VirtualAddress;                                  // 计算RVA开始位置
		DWORD Section_Ends = pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 计算RVA结束位置

		if (dwRVA >= Section_Start && dwRVA <= Section_Ends)
		{
			DWORD VA = pNtHead->OptionalHeader.ImageBase + dwRVA;                                  // 得到VA地址
			DWORD FOA = pSection[each].PointerToRawData + (dwRVA - pSection[each].VirtualAddress); // 得到FOA
			return FOA;
		}
	}
	return -1;
}

将RVA相对地址转换为FOA文件偏移: RVA就是相对地址,将相对地址转换为FOA文件内偏移,例如将158b转换为FOA。

FOA = 实际偏移 + (RVA - 虚拟偏移) => 0x00000400 + (158b - 0x00001000) = 400 + 58b = 98B

C/C++ 实现VA与FOA之间的转换_c++_13

计算出结果158b虚拟地址对应到文件偏移为98B,验证一下看看。

C/C++ 实现VA与FOA之间的转换_开发语言_14

使用C语言实现此过程。

DWORD RVA_To_FOA(HANDLE ImageBase, DWORD dwRVA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD Section_Start = pSection[each].VirtualAddress;                                  // 计算RVA开始位置
		DWORD Section_Ends = pSection[each].VirtualAddress + pSection[each].Misc.VirtualSize; // 计算RVA结束位置

		if (dwRVA >= Section_Start && dwRVA <= Section_Ends)
		{
			DWORD VA = pNtHead->OptionalHeader.ImageBase + dwRVA;                                  // 得到VA地址
			DWORD FOA = pSection[each].PointerToRawData + (dwRVA - pSection[each].VirtualAddress); // 得到FOA
			return FOA;
		}
	}
	return -1;
}

将FOA文件偏移转换为VA地址: 将FOA文件偏移转换为VA内存装载地址,老样子,我们计算一下 FOA 98B 转为VA是多少?

首先计算RVA:RVA = pSection[each].VirtualAddress + (dwFOA - pSection[each].PointerToRawData);

RVA = 虚拟偏移 + (98B - 实际偏移) = 0x00001000 + (98B - 400) = 58B

C/C++ 实现VA与FOA之间的转换_开发语言_15

DWORD VA = RVA + pNtHead->OptionalHeader.ImageBase;

VA = 00400000 + 00001000 + 58B = 40158b

C/C++ 实现VA与FOA之间的转换_相对地址_16

结果就是40158b没错,接着看看如何使用C语言实现计算过程吧。

DWORD FOA_To_VA(HANDLE ImageBase, DWORD dwFOA)
{
	PIMAGE_NT_HEADERS pNtHead = NULL;
	PIMAGE_FILE_HEADER pFileHead = NULL;
	PIMAGE_SECTION_HEADER pSection = NULL;
	DWORD NumberOfSectinsCount = 0;
	DWORD dwImageBase = 0;

	pNtHead = GetNtHeader(ImageBase);
	pSection = IMAGE_FIRST_SECTION(pNtHead);

	dwImageBase = pNtHead->OptionalHeader.ImageBase;
	NumberOfSectinsCount = pNtHead->FileHeader.NumberOfSections;
	for (int each = 0; each < NumberOfSectinsCount; each++)
	{
		DWORD PointerRawStart = pSection[each].PointerToRawData;                                // 文件偏移开始位置
		DWORD PointerRawEnds = pSection[each].PointerToRawData + pSection[each].SizeOfRawData;  // 文件偏移结束位置

		if (dwFOA >= PointerRawStart && dwFOA <= PointerRawEnds)
		{
			DWORD RVA = pSection[each].VirtualAddress + (dwFOA - pSection[each].PointerToRawData);  // 计算出RVA
			DWORD VA = RVA + pNtHead->OptionalHeader.ImageBase;                                     // 计算出VA
			return VA;
		}
	}
	return -1;
}

实现无脑转换器(闭着眼转换): 为了节约大家的转换时间,以及让大家少动一些脑细胞,我决定将转换功能一并集成到解析器中,下面我给大家整体演示一遍使用方法。

先来演示一下VA转换为RVA的过程,将VA地址40158B转换为FOA地址是多少?

C/C++ 实现VA与FOA之间的转换_c++_17

输入命令 GetPE.exe c://lyshark.exe --VaToFoa 0040158b 完成计算,对应FOA 是 0000098B

C/C++ 实现VA与FOA之间的转换_相对地址_18

计算结果完全一致。

C/C++ 实现VA与FOA之间的转换_相对地址_19

在反转回去将FOA0000098B 转为VA地址,完全一致,没问题了,无脑乱搞。

C/C++ 实现VA与FOA之间的转换_c++_20

标签:VA,RVA,C++,pSection,each,DWORD,FOA
From: https://blog.51cto.com/lyshark/6642799

相关文章

  • C/C++学生成绩管理系统[2023-07-06]
    C/C++学生成绩管理系统[2023-07-06]学生成绩管理系统开发一个可以管理学生成绩以及学生基本信息的一个信息系统,至少实现如下功能:信息管理,支持信息的增、删、改、查操作,具体信息类型如下:(1) 管理学生信息 ,包括学号,姓名,年龄,班级等等信息。(2) 班级信息,包括班级编号、班级人数,......
  • C++内存模型&空指针、野指针、函数指针和回调函数
    C++内存模型&空指针、野指针、函数指针和回调函数C++内存模型栈与堆的区别:1.管理方式不同栈是系统自动管理的,在超出作用域后,将自动被释放堆是手动释放,若程序中不释放,程序结束后将由操作系统回收2.空间大小不同堆的大小受限于物理内存范围栈小的可怜,一般为8M(可通过更改......
  • java http大文件断点续传上传问题
    ​ 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数下面直接贴代码吧,一些难懂的我大部分都加上注释了:上传文件实体类:看得出来,实体类中已经有很多我们需要的功能了,还有实用的属性。如MD5秒传的信息。pub......
  • C++电影评分系统[2023-07-06]
    C++电影评分系统[2023-07-06]程序设计综合课程设计指导书一、题目:电影评分系统二、设计内容及要求:根据C++课程所学的概念、理论和方法,按照C++程序设计的基本步骤,设计出一个适当规模的程序来实现设计课程内容中的全部功能。本系统要求模拟实现电影评分系统,其中包括电影资源管......
  • 小学期C++实践
    一、链表1、#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#defineN100010structnode{intval;structnode*next;};structnode*head=NULL;intx;structnode*reverseList(structnode*head){structnode*h......
  • 【ChernoC++笔记】指针和引用
    指针【16】C++指针▶️指针的类型不影响指针的本质:任何type的指针都是保存着内存地址的整数(integer)。指针的type只用来使人更好理解。//一个最简单的void类型指针,储存内存地址0void*ptr=0;void*ptr=NULL;void*ptr=nullptr; //C++11//使ptr存储var的内存地......
  • 报错 Cannot construct instance of `java.time.LocalDate` LocalDateTime
    原因:报错的原因就是导入了JacksonObjectMapper对象映射器,导致不知道怎么将LocalDateTime转换成Json类型的数据了,在没有导入使用JacksonObjectMapper的时候是不会报错的。解决方式:指定LocalDateTime类型的数据如何进行序列化就好了,给实体类中LocalDateTime的属性加上注解就可以了:......
  • java中http请求-okhttp使用连接池优化
    愿历尽千帆,归来仍是少年原因:避免频繁频繁的开关连接。1.Maven添加依赖<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>3.10.0</version></dependency>2.OkHttpConfiguration配置类......
  • C++程序课程设计任务书[2023-07-06]
    C++程序课程设计任务书[2023-07-06]C++程序课程设计任务书班级学号姓名一、实践目的该实践在系统学习《C++程序设计基础》课程后进行。通过本实践,培养学生使用C++解决实际问题的能力。二、实践任务与要求(任选一个任务,独立完成)任务一:(一)......
  • JAVA_DAY_01
    第一天1.jdk针对Java开发员的软件开发工具包。1.5:{自动拆箱和装箱、foreach循环、可变参数}​1.8:{1、Lamdba表达式2、函数式接口3、方法引用和构造引用4、StreamAPI}​java的运行机制,编写定义为.java格式的源代码。​通过javac命令格式调用编译器(JDK)对源代码进行编......