前言
本文将详细介绍在二进制层面修改android的so库,以实现so逻辑的修改。
为了用最短的文字把问题说的最透彻,我们尽可能的简化,这里采用可执行程序来作为例子,需要修改的可执行程序main很简单,代码如下:
void say(char* name)
{
printf("Hello %s , let`s hurt each other!\n" , name);
}
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("you must tell me your name!\n");
return -1;
}
say(argv[1]);
return 0;
}
假设我们现在有个需求,就是我们要把argv[1]这个命令行参数作为某个可执行程序test的路径,让main调用execve去执行这个程序test。
修改
我们先用IDA打开可执行程序main,并且打开IDA View的二进制显示
找到say函数,如下图所示:
函数say的参数name是通过寄存器R0传入的,我们通过系统调用号的方式直接调用execve,其中execve的系统调用号为0xB,如下图:
结合say函数的特点我们选择在地址为00008192处开始修改汇编代码(注意维持栈平衡),插入的汇编代码shellcode如下:
@execve的函数声明:int execve(const char* filename,char *const argv[],char *const envp[]);
MOVS R0, R0 @filename,say函数的参数name传给它
MOVS R1, #0 @argv,我们设为NULL
MOVS R2, R1 @envp,我们设为NULL
MOV R7, #0xB @execve的系统调用号0xB通过寄存器R7传入
SVC 0 @State change Supervisor Call
POP {R3 ,PC} @维持栈平衡
接下来需要把它转换成二进制,我们在可以在http://armconverter.com/上转换,也可以用一个很不错的开源项目miasm来转换.
生成的二进制shellcode共占12字节,而say函数留给我们的空间是地址00008192到000081A3,足够放入我们的二进制shellcode了。
接下来找到地址00008192相应的文件偏移,并且用生成的二进制覆盖
保存main后重新用IDA打开,查看修改之后的say函数:
测试
测试程序test的代码如下:
#include <stdio.h>
int main(int argc, char** argv)
{
int i = 0;
while(i < 100)
{
printf("test is running!---->pid:%d, ppid:%d, uid:%d, gid:%d\n",
getpid(), getppid(), getuid(), getgid());
sleep(1);
i++;
}
}
把修改后的main和测试程序test上传到手机,并运行:
修改后的main成功运行测试程序test!
总结
读者可以进一步发散,譬如如何访问全局变量,如果可供修改的空间不足以放下我们的shellcode又怎么办等等。
另外,这里顺便推荐几款比较方便的IDA插件给大家:ida-patcher keypatch
https://jinyu00.github.io/2017/11/07/patch_file_methods.html