最后介绍的这种hook方式原理比较简单,只需要将GOT表中的目标函数地址替换为我们自己的函数地址即可,但它的缺点是只能对导入函数进行hook,还需要对elf文件的结构有所了解。

一、获取到GOT表在内存中的地址

要得到GOT表在内存中的地址首先要解析elf文件,获取其在文件中的偏移地址,内存地址就等于基地址加上文件偏移。

在elf的section header table中名为.got的节头记录着GOT表在文件中的偏移,所以第一件事就是获取到.got节头中的信息。

1、获取到section header table的入口地址

 Elf32_Ehdr elf_header;
memset(&elf_header, , sizeof(elf_header));
fseek(fp, , SEEK_SET);
fread(&elf_header, sizeof(elf_header), , fp);

文件指针fp对应打开的elf文件。结构体Elf32_Ehdr对应elf文件头,Elf32_Ehdr.e_shoff记录着节区头部表(section header table)在文件中的偏移。

2、获取字符串表

获取到section header table的起始地址后要通过名字来判断出哪一项为.got节头,这时就要用到字符串表。

 char* parse_string_table(FILE *fp)
{
Elf32_Ehdr elf_header;
Elf32_Shdr elf_setion_header;
memset(&elf_header, , sizeof(elf_header));
memset(&elf_setion_header, , sizeof(elf_setion_header));
  //读取elf头
fseek(fp, , SEEK_SET);
fread(&elf_header, sizeof(elf_header), , fp);
//字符串表头在节区头部表的第Elf32_Ehdr.e_shstrndx项
//通过节区头部表偏移和每个节区头的大小可以算出字符串表节头的地址
fseek(fp, elf_header.e_shoff + elf_header.e_shstrndx * elf_header.e_shentsize, SEEK_SET);
fread(&elf_setion_header, sizeof(elf_setion_header), , fp);
int size_string_table = elf_setion_header.sh_size;
char *buffer = malloc(size_string_table);
//通过字符串表节区头中记录的偏移地址读取字符串表
fseek(fp, elf_setion_header.sh_offset, SEEK_SET);
fread(buffer, size_string_table, , fp);
return buffer;
}

3、遍历节区头部表

遍历整个节区头部表,获取.got节区头,获取GOT表在文件中的偏移地址。

 void parse_got_table(FILE *fp, long *addr_got_table, long *size_got_table)
{
Elf32_Ehdr elf_header;
Elf32_Shdr elf_secion_header;
memset(&elf_header, , sizeof(elf_header));
memset(&elf_secion_header, , sizeof(elf_secion_header));
//读取elf头
fseek(fp, , SEEK_SET);
fread(&elf_header, sizeof(elf_header), , fp);
//获取字符串表
char *string_table = parse_string_table(fp);
fseek(fp, elf_header.e_shoff, SEEK_SET);
//遍历节区头部表
for (int i = ; i < elf_header.e_shnum; ++i) {
fread(&elf_secion_header, elf_header.e_shentsize, , fp);
if (elf_secion_header.sh_type == SHT_PROGBITS
&& == strcmp(".got", string_table + elf_secion_header.sh_name)) {
//返回GOT表偏移及大小
*addr_got_table = elf_secion_header.sh_addr;
*size_got_table = elf_secion_header.sh_size;
}
}
free(string_table);
}

4、获取elf文件在内存中的基址

在Linux系统中可以通过读取/proc/pid/maps来获取各个elf文件在内存中的加载基址。在之前的文章中已经反复用到,这里就不再重复了。

最后可以得到:GOT表内存地址 = elf文件内存基址 + GOT表文件偏移

二、修改GOT表中存放的导入函数的地址

知道GOT表在内存中的地址后我们就可以着手对其修改了,里边存放的全是外部符号地址(前三项有特殊作用,这里不做讨论),很显然每个表项占4个字节。接下来一个问题是我们怎么知道需要替换哪一个表项呢?如果我们知道需要hook的函数地址,就可以跟表里的地址进行逐一比对。如果是系统库中的函数,我们可以直接获取到函数地址,但如果是第三方库中的函数呢?我们可以借助dlsym函数。

void hook_got_make(const char *elf, const char *symbol, const char *library, void *func, void **old_func)
{
FILE *file = fopen(elf, "rb");
long addr_got_table;
long size_got_table;
parse_got_table(file, &addr_got_table, &size_got_table);
fclose(file);
void *handle = dlopen(library, RTLD_LAZY);
void *target = dlsym(handle, symbol);
dlclose(handle);
long addr_base = get_module_addr(-, elf);
for (int i = ; i < size_got_table; i += ) {
if (*(uint32_t *)(addr_base + addr_got_table + i) == (uint32_t)target) {
*old_func = target;
write_code(addr_base + addr_got_table + i, (uint32_t)func);
}
}
}

首先通过dlopen加载symbol(目标函数名)所在的可执行文件,当然这个文件肯定之前就已经加载到内存中了。然后通过dlsym获取symbol对应的函数地址。

获取到地址后首先保存到old_func中,然后用我们的新地址覆盖GOT表中的原地址。注意在更改GOT表的内容时首先要将所在内存地址的属性设为可写。

Android GOT Hook的更多相关文章

  1. 使用cydia substrate 来进行android native hook

      cydia不仅可以hook java代码,同样可以hook native代码,下面举一个例子来进行android native hook 我是在网上找到的supermathhook这个项目,在他基 ...

  2. Android Exception Hook

    承接上一篇文章Android Inline Hook,接下来我们看一下android系统中基于异常的hook方式,这种方式与inline hook相比实现较为简单,但执行效率是它的短板. except ...

  3. Android Xpose Hook(一)

    实验环境:     Droid4x模拟器 (目前Android版本4.2.2)     Android Studio 1.下载相关工具 XposedInstaller下载 http://repo.xp ...

  4. android inline hook

    最近终于沉下心来对着书把hook跟注入方面的代码敲了一遍,打算写几个博客把它们记录下来. 第一次介绍一下我感觉难度最大的inline hook,实现代码参考了腾讯GAD的游戏安全入门. inline ...

  5. android ART hook

    0x00 前言 之前一直都是在Dalvik 虚拟机上在折腾,从Android 4.4开始开始引入ART,到5.0已经成为默认选择.而且最近看到阿里开源的 Dexposed 框架,已经提供了对于andr ...

  6. Android Native Hook技术(二)

    Hook技术应用 已经介绍了安卓 Native hook 原理,这里介绍 hook 技术的应用,及 Cyida Substrate 框架. 分析某APP,发现其POST请求数据经过加密,我们希望还原其 ...

  7. Android Native Hook技术(一)

    原理分析 ADBI是一个著名的安卓平台hook框架,基于 动态库注入 与 inline hook 技术实现.该框架主要由2个模块构成:1)hijack负责将so注入到目标进程空间,2)libbase是 ...

  8. android的hook方面知识点

    android hook分为另种: native层hook---理解ELF文件 java层---虚拟机特性和Java上的反射的作用 注入代码: 存放在哪? 用mmap函数分配临时内存来完成代码存放,对 ...

  9. Android逆向进阶(7)——揭开Hook的神秘面纱

    本文作者:i春秋作家——HAI_ 0×00 前言 HAI_逆向使用手册(想尝试一下新的写法) 其他 Android逆向进阶 系列课程 <<<<<<< 人物说明 ...

随机推荐

  1. plsql 记录型变量

    set serveroutput on declare emplist emp%rowtype; begin ; dbms_output.put_line(emplist.ename||'的薪水是'| ...

  2. 改进初学者的PID-测量的比例介绍

    最近看到了Brett Beauregard发表的有关PID的系列文章,感觉对于理解PID算法很有帮助,于是将系列文章翻译过来!在自我提高的过程中,也希望对同道中人有所帮助.作者Brett Beaure ...

  3. 画图前端:mermaid。时序图/类图/甘特图/流程图/状态图/饼图。类似工具:Typora

    文档 https://mermaidjs.github.io/#/ cdn https://www.bootcdn.cn/mermaid/ 在线编辑 https://mermaidjs.github. ...

  4. Harbor的安装和基本使用

    Harbor是一个开源的云原生registry工程.Harbor对开源的Docker Distribution扩进行了扩展,支持registries之间镜像的复制功能,而且还提供了一些高级的安全方面的 ...

  5. 【CUDA开发】 Check failed: error == cudaSuccess (8 vs. 0) invalid device function

    最近在复现R-CNN一系列的实验时,配置代码环境真是花费了不少时间.由于对MATLAB不熟悉,实验采用的都是github上rbg大神的Python版本.在配置Faster R-CNN时,编译没有问题, ...

  6. 《PHP - 信号/基本操作/配置》

    一:PHP 信号 - SIGINT / SIGTERM / SIGQUIT - 退出FPM,在master收到退出信号后将向所有的worker进程发送退出信号,然后master退出. - SIGUSR ...

  7. 一起来学Spring Cloud | 第七章:分布式配置中心(Spring Cloud Config)

    上一章节,我们讲解了服务网关zuul,本章节我们从git和本地两种存储配置信息的方式来讲解springcloud的分布式配置中心-Spring Cloud Config. 一.Spring Cloud ...

  8. dubbo学习之路-SPI机制

    dubbo学习之路-SPI机制 1.SPI 1.1Java SPI 原理 SPI是service provider interface简称.在java JDK中 内置的一种服务提供发现机制.它解决在一 ...

  9. Java开发笔记(一百零八)JSON串的定义和解析

    前面提到URL尾巴支持添加请求参数,具体格式形如“参数A名称=A参数值&参数B名称=B参数值”,可是这种格式只能传递简单的键值对信息,不能传递结构化数据,也无法传递数组形式的参数,因而它不适用 ...

  10. 100天搞定机器学习|Day55 最大熵模型

    1.熵的定义 熵最早是一个物理学概念,由克劳修斯于1854年提出,它是描述事物无序性的参数,跟热力学第二定律的宏观方向性有关:在不加外力的情况下,总是往混乱状态改变.熵增是宇宙的基本定律,自然的有序状 ...