修改二进制可执行文件

在对程序逆向过程中,往往会遇到关键的逻辑使用的是C/C++函数实现的情况,这种情况下,通常有几种方法来实现修改,这里介绍修改二进制可执行文件的方法。

由于Hopper的个人许可证需要$99,免费的demo没有保存功能,不能导出新的可执行文件,但是可以查看修改的部分

环境准备

编译测试代码

首先来看一个常规的C语言源代码,这段代码通过判断变量i的值选择进入不同的分支执行相应的逻辑

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main(int argc, char **argv) {
int i = 1;
if (i == 0) {
printf("Hi, :)\n");
} else {
printf("Hi, (:\n");
}
return 0;
}

用clang编译程序后,导出hex文件:

1
2
3
clang app.c -o origin.bin
xxd -c 32 -g 1 -u origin.bin > origin.hex
xxd -c 32 -g 1 -u origin.bin > modify.hex

导出hex文件是为了方便后续修改,直接修改可执行文件不太方便
定位修改位置

将编译得到的origin.bin文件拖入Hopper中,查看对应的汇编代码,然后修改对应的部分即可(⌥+A或者在菜单栏Modify-Assemble Instruction...打开当前行的修改框)
修改前

修改后

这里将

1
int i = 1;

对应的汇编代码修改成了mov w8, wzr,wzr是零寄存器,值始终为0,等价C中的语句为 i = 0, 查看对应的机器码
对应机器码
为了避免字符串替换过程中替换到其他代码,这里拷贝32个字节
修改可执行文件

line: 3f40

before:

1
28 00 80 52 E8 0F 00 B9 E8 0F 40 B9 08 01 00 71 E8 07 9F 1A C8 00 00 37 01 00 00 14 00 00 00 90

after:

1
E8 03 1F 2A E8 0F 00 B9 E8 0F 40 B9 08 01 00 71 E8 07 9F 1A C8 00 00 37 01 00 00 14 00 00 00 90

所以可以得到下面的替换命令,这里对hex文件进行修改

1
2
3
4
5
6
7
8
9
OLD="28 00 80 52 E8 0F 00 B9 E8 0F 40 B9 08 01 00 71 E8 07 9F 1A C8 00 00 37 01 00 00 14 00 00 00 90"
NEW="E8 03 1F 2A E8 0F 00 B9 E8 0F 40 B9 08 01 00 71 E8 07 9F 1A C8 00 00 37 01 00 00 14 00 00 00 90"
echo "s|$OLD|$NEW|"|xargs -I % sed -i '' '%' modify.hex # 进行查找替换
查看修改后的差异,确保只有一处被修改,接下来重新转储为可执行文件并重新签名
diff modify.hex origin.hex # 比较修改前后差异
xxd -r -c 32 -g 1 -u modify.hex modify.bin # 转储为二进制可执行文件
codesign --sign - --force --deep modify.bin # 签名
chmod +x modify.bin # 添加执行权限
# ./modify.bin #执行modify.bin

可以执行modify.bin看看是否得到预期的结果

制作二进制补丁,便于分发

如果二进制文件很大,修改后不太便于分发,并且修改是对特定版本而言的,如果程序有更新,就需要重新确定修改位置,为了便于分发以及不同版本的修改保存。可以制作二进制补丁

最后就是根据两个二进制文件的差异生成补丁,这里用的是xdelta,需要自己安装

1
2
3
xdelta -e -s origin.bin modify.bin diff.patch #生成补丁
xdelta -d -s origin.bin diff.patch origin-patch.bin #应用补丁
chmod +x origin-patch.bin

完整修改命令