本文博客地址:

后面深入学习了古河的Libinject注入Android进程,下边来深入学习一下作者ariesjzj的博文《Android中的so注入(inject)和挂钩(hook)-Forbothx86andarm》,注入的思路和古河的是一样的,而且代码的兼容性更好更好理解,适用于arm和x86模式下so注入和函数的Hook,这份代码自己也测试了一下,确实可以Hook目标函数成功,只是被Hook更改的目标函数,可能没有被系统调用,致使测试疗效和作者ariesjzj给出稍有区别。

一、Androidso注入和函数hook代码的说明1.为inject工程代码添加必要的include文件

根据作者ariesjzj提供的代码文件,在eclipse中建立一个ndk的Inject代码工程(用于实现so的注入)和一个ndk的Inject_so代码工程(被注入的so以及实现函数Hook),并将作者ariesjzj提供的代码分别导出到这两个ndk的代码工程中,并根据我在上面博客中提及的方式为ndk工程添加必要的include头文件(PathsandSymbols),添加方式如下:

windows环境下,NDK编译须要添加的include头文件(依据编译的版本须要进行更改):右击项目-->Properties-->两侧C/C++General-->PathsandSymbols-->两侧Includes-->GNUC++(.cpp)-->Add${NDKROOT}platformsandroid-19arch-armusrinclude${NDKROOT}sourcescxx-stlgnu-libstdc++4.8include${NDKROOT}sourcescxx-stlgnu-libstdc++4.8libsarmeabiinclude${NDKROOT}toolchainsarm-Linux-androideabi-4.8prebuiltwindowslibgccarm-linux-androideabi4.8include

2.在编译Inject的代码工程时,编译可能会报找不到头文件的错误

须要更改头文件#include为#include

linux include 找不到头文件_如何通过头文件找到函数_查找头文件路径

3.作者ariesjzj提供的Androidso注入Hook函数是基于ELF文件GOT表的Hook

说明的明白一点,就是被Hook的目标函数在该elf文件格式的so库文件中须要被导入,能被动态查找到(got表里储存的是外部符号的地址,不是所有符号的地址都能在got表找到)。其实了,通常在linux下,函数默认就是被导入的,顺便提一下,在so库文件中,函数没有被导入也是可以Hook的,由于函数调用地址在so库文件中的储存位置偏斜是可以手工估算下来,只不过麻烦一点点,通用性差点。有关so文件的非导入函数的调用地址的估算,可以参考《linux下调用共享库非导入函数》里的方式。

linux include 找不到头文件_查找头文件路径_如何通过头文件找到函数

生成共享库的代码share.c:

#include 
// 默认的导出函数
 void PrintABCD()	//__attribute__((visibility("hidden")))
{
  printf("ABCDn");
  sleep(-1);
}
// +++++++设置为非导出函数++++++++
__attribute__((visibility("hidden"))) void Printabcd()
{
  printf("abcdn");
  sleep(-1);
}
// 根据参数来确定print ABCD or abcd(默认的导出函数)
void PrintIt(int isPrintCapital)
{
  if(isPrintCapital)
  {
    PrintABCD();
  }
  else
  {
    Printabcd();
  }
  printf("over!n");
}

生成共享库share.so:

查找头文件路径_如何通过头文件找到函数_linux include 找不到头文件

gl-linux@ubuntu:~/Desktop/AYR$ gcc -fPIC -shared -o share.so share.c

测试代码test.c:

#include 
#include 
#include
#define DLL_FILE_NAME "/home/gl-linux/Desktop/AYR/share.so"
 int main()
 {
    long long addr = 0;
    void (*func)(int);
    void *handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
    if (handle == NULL)
    {
		fprintf(stderr, "Failed to open libaray %s error:%sn", DLL_FILE_NAME, dlerror());
		return -1;
    }
    
    addr = (long long)dlsym(handle, "PrintABCD"); 
    printf("%lld ",addr); 
       
    addr = (long long)dlsym(handle, "Printabcd"); 
    printf("%lld ",addr);
    
    addr = (long long)dlsym(handle, "PrintIt"); 
    printf("%lld ",addr);
    func = addr;
    func(1);
    
    dlclose(handle);
    sleep(-1);
    return 0;
 }

生成可执行文件test:

gl-linux@ubuntu:~/Desktop/AYR$ gcc test.c -o test -ldl

运行可执行文件test:

查找头文件路径_如何通过头文件找到函数_linux include 找不到头文件

139725025953621 0 139725025953687 ABCD

从里面的输出结果可以看出,非导入函数Printabcd的地址没有被dlsym函数获取到,通常非导入函数也被叫做内部函数,应当和函数的生命周期也就是有效范围类似,其实了这个访问的限制是可以被突破的。

查看share.so中的导入函数偏斜:

0000000000201048 B __bss_start
                 w __cxa_finalize
0000000000201048 D _edata
0000000000201050 B _end
00000000000007cc T _fini
                 w __gmon_start__
00000000000005e8 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000000755 T PrintABCD
0000000000000797 T PrintIt
                 U puts
                 U sleep

已知导入函数的偏斜地址,又晓得模块加载基址,如何能晓得非导入函数的地址呢?

linux include 找不到头文件_查找头文件路径_如何通过头文件找到函数

使用估算后的地址,调用非导入函数Printabcd(),更改测试程序代码TEST.c如下:

#include 
#include 
#include
#define DLL_FILE_NAME "/home/gl-linux/Desktop/AYR/share.so"
 int main()
 {
    long long addr = 0;
    void (*func)();
    void *handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
    if (handle == NULL)
    {
	fprintf(stderr, "Failed to open libaray %s error:%sn", DLL_FILE_NAME, dlerror());
	return -1;
    }
    
    addr = (long long)dlsym(handle, "PrintABCD"); 
    printf("%lld ",addr);    
    
    addr = (long long)dlsym(handle, "Printabcd"); 
    printf("%lld ",addr);
    
    addr = (long long)dlsym(handle, "PrintIt"); 
    printf("%lld ",addr);
    // 调用共享库share.so中的非导出函数。
    func = addr-33;
    func();
    
    dlclose(handle);
    sleep(-1);
    return 0;
 }

运行结果:

gl-linux@ubuntu:~/Desktop/AYR$ ./TEST 
139670664996693 0 139670664996759 abcd

按照前面的输出结果linux教程下载,很其实调用share.so文件中的非导入函数Printabcd成功。

有网友提示,下边这个Linux版本的nm可以查看so库文件中非导入函数的位置偏斜:

如何通过头文件找到函数_查找头文件路径_linux include 找不到头文件

Linuxubuntu3.17.1#1SMPFriOct2409:08:26PDT2014x86_64GNU/Linux版本nm-Dxx.solinux include 找不到头文件,可以见到非导入函数的偏斜,只不过是t不是T

0068cTPrintABCD

006ceTPrintIt006adtPrintabcd

虽然so库文件中的非导入函数的调用地址很简单linux include 找不到头文件,将so文件推入到IDA中剖析,找到静态下非导入函数和导入函数的函数调用地址的位置偏斜offsetOfFunciton或则非导入函数调用地址offsetOfBaseAddrlinux 安装,之后so库文件中非导入函数的地址,即为动态显存的导入函数地址+offsetOfFunciton或则offsetOfBaseAddr+base(so文件的显存加载基地址)。

谢谢联接:

4.作者ariesjzj提供的Androidso注入Hook目标函数的代码的兼容性的考虑的诱因

在arm模式和x86模式下,函数的堆栈工作原理是一样的,而且在具体到工作的实现细节上还是有好多的区别,arm模式的函数是基于寄存器传参,x86模式的函数是基于堆栈传参。

arm模式下,当函数的参数高于4个时,通过R0~R3寄存器传递参数;当函数的参数超过4个时,前4个参数通过R0~R3寄存器传递,超过4个的参数通过函数的堆栈进行传递,函数的程序计数寄存器为PC(储存即将执行的指令)

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复