内存修改技术的原理及实现 第三节 - 如何寻找偏移量

如何寻找偏移量

笔者近几个月没有经营博客,对各位读者说声抱歉 :(

这段时间在搞CTF,只入门了几个月,今后可能会将CTF的内容放入博客中。

寻找偏移量的两种方式

上节我们讲过,偏移量的实质是一个对象或结构体中各数据的相对位置,那么我们通过找出一个偏移量便可以定位一个对象的位置,因此我们从寻找单一变量的指针路径入手。

1. 传统方式——调试器

教学篇:一级指针

打开CE的文字教程第六关,界面如下

image-20201201223259033

首先我们通过第二节的方法找到地址,然后通过调试器来找到偏移量。

image-20201201223254470

我们在地址点击右键,点击找到是哪个指令修改了这个地址,在点击Change Value,改变这个内存的值,这时便可以在调试器中看到修改这个值的指令。

image-20201201223328005

image-20201201223314692

注:(个人建议)尽量不要选找到是哪个指令访问了这个地址,除非没有方法改变这个内存地址的值,否则可能会有很多指令对分析造成干扰。以下图片是该选项的结果

image-20201201223342812

可以看到,修改这个地址的指令是mov [edx], eax,将eax的值放入edx存放的地址中,此时由于我们的edx直接指向目标地址,所以这一阶段的偏移量为0

接下来我们在CE中搜索edx的值,在调试器中点击指令便会显示此时各寄存器的值,此时edx的值为0x1822170

image-20201201223403055

搜索结果如下

image-20201201223430751

我们可以看到,搜索到的地址以exe文件名开头(在CE中用来表示基址),因此我们达到了访问目标地址最初的指针,偏移量为+2426B0,这是一个一级指针。

多级指针同理,只需要重复以上步骤直到找到基址开头的地址即可。

注:偏移量有用负值表示的,记录时最好记录正负值。

我们用更直观的方式表示该一级指针。

1
基址 + 0x2426B0 -> 01822170 -> 目标地址

实战篇:多级指针

我们以四级指针为例,在CE教程的第八关,界面相似。

image-20201201230149073

首先需要找到该变量的地址,随后重复以上步骤。

image-20201201211134078

偏移量从后向前分别为0x0C 0x14 0 0x18

image-20201201211309727

image-20201201211443675

在中间有一步,会有多个不同的内存存放了同一地址,然而我们只需要一个有效的指针路径,因此我们需要手动操作这三个内存地址,分别用调试器找出哪一个地址会被程序访问到,即可得到有效的偏移量。

image-20201201211717481

image-20201201211953373

image-20201201212523502

可以发现,这是一个五级指针,我们可以通过CE的手动添加地址信息功能让CE自动寻找指针路径从而可以持续性的控制同一变量。

image-20201201212638407

image-20201201213043739

可以通过锁定该变量为5000来达到通关目的。

image-20201201213133731

2. 指针扫描

原理

相比于调试器寻址,指针扫描的过程更加简单、自动化。

指针扫描不同于使用调试器的逆向过程,指针扫描更像是一种爆破的过程,通过设置最大的指针级数和最大的偏移量来将参数从0开始穷举到最大值,如果走到了内存中的有效数据存放区域(堆栈区域和拷贝在内存中的应用程序的数据区域),即得到一个指针路径,如果走到了无法取址的区域(死路),该区域结束。该过程用递归函数的方法实现,但实现方式不作为重点。下图为基本过程:

graph LR;
起始地址 --> A地址 --> B地址 --> 目的地址;
起始地址 --> C地址 --> D地址 --> E地址 --> F地址 --> 目的地址;
起始地址 --> G地址 --> H地址 --> B地址;
起始地址 --> I地址 --> 无法取地址;
起始地址 --> J地址 --> K地址 --> 无法取地址;
目的地址 --> 目标变量;

基本操作方式

首先选择Generate pointermap,生成Pointermap,随便存放一个位置。

重启程序(只要能让变量所在的地址变更即可,在游戏中可选择重开地图等操作,此处我们点击Change pointer),重新寻找变量地址。

首先使用调试器方法,找出最后一级的偏移量:0x18。

image-20201201211309727

在地址处右键选择Pointer Scan

image-20201201214957984

选择与其它的Pointermap比较,并选择Pointers must end with specific offsets,填入最后一级的偏移量(当然如果你有更多偏移量的信息,可以通过点击Add按钮进行添加)。例如此时笔者使用调试器找到了最后两级的偏移量,于是我们填入18和0

image-20201201221834101

点击OK进行扫描

即可得到指针路径,此处比较理想,只得到了一个有效路径

image-20201201220919230

误差处理

若结果有几千个甚至更多,我们可以通过右上角的Pointer scanner的Rescan选项重新扫描,可以指定目的地址或目的变量的值,从而剔除无效的指针路径。

image-20201201221206297

重复以上步骤十次左右或指针路径数量在几十个左右,剩下的指针路径就基本就全都可用了,当然我们尽量需要找到级数更小的指针路径(偏移量数量更少的)即可。