前言

本文将详细介绍在二进制层面修改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的二进制显示

打开IDA View视图的二进制显示

找到say函数,如下图所示:

say函数

函数say的参数name是通过寄存器R0传入的,我们通过系统调用号的方式直接调用execve,其中execve的系统调用号为0xB,如下图:

execve的系统调用号

结合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来转换.

Online ARM To Hex Converter

生成的二进制shellcode共占12字节,而say函数留给我们的空间是地址00008192到000081A3,足够放入我们的二进制shellcode了。

接下来找到地址00008192相应的文件偏移,并且用生成的二进制覆盖

修改相应文件偏移的二进制

保存main后重新用IDA打开,查看修改之后的say函数:

修改之后的say函数

测试

测试程序test的代码如下:

#include 
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上传到手机,并运行:

成功调用test

修改后的main成功运行测试程序test!

总结

读者可以进一步发散,譬如如何访问全局变量,如果可供修改的空间不足以放下我们的shellcode又怎么办等等。

另外,这里顺便推荐几款比较方便的IDA插件给大家:ida-patcher keypatch

https://jinyu00.github.io/2017/11/07/patch_file_methods.html