《编译系统-自底向上研究方法》用机器码来编程 - 弦外之音

/ 0评 / 0

源码下载地址:百度网盘,提取码:cat1 。

通过前面的文章《编译系统-自底向上研究方法》ELF符号段 ,基本已经知道了 符号表的 作用,就是能找到 变量,或者函数的位置。还是用 main 项目来分析,项目里面有个 sum.c 文件,里面有个 sum 函数。如下:

int sum(int *a,int n){
    int s = 0;
    for (int i = 0; i < n; i++) {
        s += a[i];
    }
    return s;
};

假设 sum 是一个 获取 武力值 的函数,我们想 hack 这款游戏,把他的武力值 加大 1000,那只需要 修改 sum 函数的返回值即可。假设我们没有 C 程序代码,只有一个 main 可执行文件,下面就展示一下如何用 机器码 来实现 加大 武力值 这个功能。

我们知道, 函数的返回值 一般都是放在 eax 寄存器,给上层用的,所以只需要把 eax 加 1000 即可。

这条指令add $1000,%eax 对应的机器码是 05 e8 03 00 00 ,所以只需要把这个 5 个字节的 机器码 插进去 sum 函数的末尾就行。通过符号表找到 sum 函数的位置,然后修改,如下:

上面我是用 sublime 加进去的,这样直接运行会报 Segmentation fault 错误。因为还有一些其他的地方没相应改动,我们知道 sum 符号有个大小,原来是 0x45 字节,现在加了 5 个字节,应该要改成 0x4a。需要用 xelfviewer 找到相应的位置 改成 0x4a:

还有一个地方,sum 符号是在 .text 段, .text 段也有一个大小,也要改。原来是 0x1e2,要改成 0x1e7,但是 .text 段大小变了之后,他会挤开其他的段,其他的段的偏移位还要改,还有对齐之类的,太麻烦,除非有软件自动处理偏移位,要不太难搞了。

所以不能加指令,只能再研究,把某些指令替换掉,不改变大小。

换一个思路,相对武力值不太好搞,直接返回绝对的武力值,直接返回 武力值 1000。相应的汇编指令是 mov $1000,%eax ,机器码是 b8 e8 03 00 00,这样只需要找到 正好是 5个字节的 指令,替换成 b8 e8 03 00 00 即可,这样就不会改变大小。

objdump -d main 可以看到以下图片:

为了方便操作,直接在 sum 函数的入口就返回 1000,所以要把 最后的两条指令 5d c3 往前面移动 。所以一共是 b8 e8 03 00 00 加上 5d c3 ,一共 7个字节,刚刚可以前面的 两条指令 48 89 e5 跟 48 89 7d 48 ,如果不够对消,可以用 nop 指令 90 来占位。

由于 我的 ubuntu 的 exit status 最大 是 232,echo $? 最大只显示 232 ,所以还是不用 1000 了,用 5 吧,机器码是 b8 05 00 00 00 ,运行情况如下:

逆向没有问题,确实返回 5,也没有报 Segmentation fault 错误,因为我们没有改动大小。

所以如果加机器码 影响的地方很多,所以通常逆向为了方便,会用同样大小的指令替换某一条指令。这样大小不变。

上面这些 汇编指令,如果你不太熟悉,可以看《汇编语言基于x86》。AT&T 跟 intel 风格的汇编差不多。

实际上,现代软件开发,基本不会直接写机器码来实现一个大功能,机器码据我了解,只有做逆向 破解的工程师才会用一下,但也是一小部分功能用汇编进行跳转或者抓取数据,跳转到的地方是用其他高级语言写的功能。

因为做逆向,没有源代码,不方便进行编译链接,只能写机器码来做跳转,干一些其他事情。

做SMID 优化的时候,用的最多的也是汇编指令,而不是机器码。


由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1,QQ:2338195090。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注