本次内容包含如何修改程序中的初始值,和如何转换内存和文件的地址。

修改初始值

问题
我们写了一个程序,可以输出一个结果,那么我们可以通过修改PE文件来改变这个输出结果吗?
对于有初始值的全局变量来说,是可以的。

我们在给全局变量赋初始值时,会在PE文件中记录其数据
没有初始值,无法找到

这里我们有如下一个程序:

1
2
3
4
5
6
#include<stdio.h>
int a = 0x12121212;
int main() {
printf("a=(十六进制)%X\n", a);
return 0;
}

执行结果如下:
在这里插入图片描述

以十六进制输出 a 的值。我们以二进制打开PE文件.这里我们将a先设置的特殊一点,便于寻找。
在这里插入图片描述
这里我们将结果12121212修改为00000000,然后另存为新文件。

在这里插入图片描述
然后再运行两个文件,发现值已经被修改:
在这里插入图片描述

RVA和FOA转换

RVA

RVA是PE文件在内存中的相对虚拟地址

比如全局变量a,在文件中是一个地址,然后映射到内存中,会有一个新地址,此时这个地址相对与基地址(也就是imageBase)的偏移量就是RVA。

FOA

FOA在PE文件在文件中的相对地址

全局变量a在文件中的地址,相对于文件起始地址的偏移量,上图中为B400h。

RVA和FOA的关系

由于两个都是相对偏移量,同时PE文件中又会记载文件对齐和内存对齐的地址信息。所有我们可以得到:

  1. 在文件头中时:RVA=FOA
  2. 在节数据中时:RVA - 内存中所在节的起始数据 = FOA - 文件中所在节的起始地址

在这里插入图片描述
在上面那个程序中,扩展PE头中,内存基址为02 00 00,内存对齐06 00 00,文件对齐0.
在这里插入图片描述
在标准PE头中记录节表数量
然后再去PE中查找节表的长度,所在节表是第几个。最后根据内存所在地址相对当前节偏移量 = 文件所在地址相对当前节偏移量就可以得到内存中的地址

注意

在windows中,如果使用vs直接去编译文件,查看地址,你会发现每一次修改你的地址都会不同。在ida,010等工具中你会发现得到的地址都不一样。
解决办法:

  1. 这是因为vs会对代码进行优化,在初学的时候可以将优化关闭
  2. 使用gcc编译,gcc在编译时没有敲额外命令不会产生优化或改变

推荐:

  1. 使用gcc编译
  2. 使用gdb去调试

在分析代码的时候可以使用ida,ida更加直观,但是具体地址,gdb会更加准确。

1
2
gcc -O0 test.c -o test.exe
//源文件 //生成文件

-O0:禁用优化。编译器不会执行任何优化,生成的机器代码与源代码几乎一致,方便调试源代码。
-O1:基本优化级别。编译器执行一些基本的优化,包括删除不可达代码、消除冗余赋值等。
-O2:中级优化级别。在基本优化级别的基础上进一步执行一些优化,例如内联函数、循环展开等。
-O3:高级优化级别。在中级优化级别的基础上进行更多的优化,包括函数内联、循环优化、向量化等。生成的代码可能更快,但编译时间可能较长。