PE结构中的地址互转,这次再来系统的复习一下关于PE结构中各种地址的转换方式,最终通过编程来实现自动解析计算,最后将这个功能集成到我的迷你解析器中,本章中使用的工具是上次讲解PE结构文章中制作的CMD迷你结构解析器,如果不知道参数的基本使用请看前一篇。
将VA地址转换为FOA文件偏移: VA就是虚拟地址,转换为FOA文件偏移,其手工计算过程如下所示。
首先需要得到 ImageBase(镜像基址)
其次得到入口点地址,将两个地址相加即可得到VA,也就是实际装入地址。
通过上方的已知条件我们就可以计算出程序实际装入内存后的入口地址了.
VA(实际装入地址) = ImageBase(基址) + RVA(偏移) => 00400000 + 0000158b = 0040158b
如果不放心,可以将源程序拖入X64DBG中观察是否一致,如下OEP为 0040158b
与我们的计算结果完全一致。
接着我们需要得到 .text节
基地址以及 .text节
RVA,如下命令即可获取到。
虚拟地址开始位置:节区基地址 + 节区RVA => 00400000 + 00001000 = 00401000
虚拟地址结束位置:text节地址 + 节区尺寸 => 00401000 + 0x00000B44 = 00401B44
计算得出,虚拟地址text节开始位置 00401000
结束位置是 00401B44
打开X64dbg验证没错 确实落在了 text节上。
判断是否落在了.text节的依据是 开始位置 >= 401000 结束位置 <= 402000 很明显:00401B44小于402000。
接着我们就来计算一下,当前的VA地址0040158B
其对应到文件中的偏移FOA位置是多少,计算公式如下。
RVA(相对偏移) = VA - (.text节首地址) => 0040158B - 00401000 = 58B
FOA(文件偏移) = RVA + .text节对应到文件中的偏移 => 58B + 400 = 98B
计算出结果了,我们使用WinHEX定位到98B
处看看是否符合要求,机器码完全一致,符合要求。
接着就是使用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
VA(虚拟地址) = FOA(.text节) + ImageBase + VPK => F43 + 400000 + C00 = 401B43
计算后的结果F43
对应到VA地址是401B43
验证一下,没错。
通过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
计算出结果158b
虚拟地址对应到文件偏移为98B
,验证一下看看。
使用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
DWORD VA = RVA + pNtHead->OptionalHeader.ImageBase;
VA = 00400000 + 00001000 + 58B = 40158b
结果就是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地址是多少?
输入命令 GetPE.exe c://lyshark.exe --VaToFoa 0040158b
完成计算,对应FOA 是 0000098B
计算结果完全一致。
在反转回去将FOA0000098B
转为VA地址,完全一致,没问题了,无脑乱搞。