最后介绍的这种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. Pycharm连接远程服务器并进行代码上传+远程调试

    前提:需要有一个远程服务器,知道他的ip.port.user.password 一.连接远程服务器 进入配置页面 Pycharm菜单栏,如下图所示,依次点击 Tools -> Deploymen ...

  2. 【翻译】Flink Table Api & SQL — SQL

    本文翻译自官网:SQL https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/sql.html Flink Tab ...

  3. 170道python面试题(转)

    作者:麋鹿链接:https://www.zhihu.com/question/54513391/answer/779646691来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  4. [LeetCode] 149. Max Points on a Line 共线点个数

    Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. ...

  5. ThreadPool.QueueUserWorkItem 简单示例,显示当前时间

    1.线程池  添加方法 作为一个线程运行 class Program { static void Main(string[] args) { ThreadPool.QueueUserWorkItem( ...

  6. python实践项目三:将列表添加到字典

    1.创建一个字典,其中键是字符串,描述一个物品,值是一个整型值,说明有多少该物品.例如,字典值{'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, ...

  7. LeetCode 2. 两数相加(Add Two Numbers)

    2. 两数相加 2. Add Two Numbers 题目描述 You are given two non-empty linked lists representing two non-negati ...

  8. python三大器之装饰器的练习

    装饰器 加载顺序从下至上 执行顺序从上至下 ''' 多层装饰器 ''' def deco1(func): #func=deco2 def wrapper1(*args, **kwargs): '''t ...

  9. day30——socket套接字(完全版)

    day30 基于TCP协议的socket循环通信 server import socket phone = socket.socket() phone.bind(("127.0.0.1&qu ...

  10. gensim快速上手教程

    1 gensim是什么?        gensim是一个Python常用的的自然语言处理开发包, 主要用于词向量训练和加载词向量,以下解释其正确使用姿势. 2 正确使用姿势 from gensim. ...