最后介绍的这种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. 解决Python模块报错:ModuleNotFoundError: No module name 'StringIO'

    下面是我在学习中遇到的问题,给大家分享一下:   ''' 这里是测试代码 '''# coding = utf-8from selenium import webdriverfrom selenium. ...

  2. DHCP、DHCP Snooping及DHCP relay工作原理入门及实践(转)

    原文https://blog.51cto.com/5167020/2312718 序:DHCP服务相对简单,写本文的目的是为了讲一些DHCP安全方面的技术. 1.DHCP基础 DHCP 全称动态主机配 ...

  3. DHCP的配置方法

    1.实验拓扑图: 2.配置命令: AR2: <Huawei>system-view[Huawei]dhcp enable      #开启DHCP服务[Huawei]interface G ...

  4. [案例一] Spring中的事件驱动模型(机制)

    事件驱动模型是观察者模式的另一种形态,观察者相当于监听器,被观察者相当于事件源 事件源产生事件,监听器监听事件 以用户注册时候,要发送邮件和发送短信举例说明 定义一个事件 /** * spring会自 ...

  5. controller进行数据保存以及作用域

    controller进行数据保存以及作用域 一.request域 1.ModelAndView 在ModelAndView中进行存键值对,也可以进行跳转的地址存储,但是返回类型必须是ModelAndV ...

  6. 二次剩余定理及Cipolla算法入门到自闭

    二次剩余定义: 在维基百科中,是这样说的:如果q等于一个数的平方模 n,则q为模 n 意义下的二次剩余.例如:x2≡n(mod p).否则,则q为模n意义下的二次非剩余. Cipolla算法:一个解决 ...

  7. free(分层图最短路)(2019牛客暑期多校训练营(第四场))

    示例: 输入: 3 2 1 3 11 2 12 3 2 输出:1 题意:求s,t最短路,可将k条边权值置零. 题解:分层图最短路原题 #include<bits/stdc++.h> usi ...

  8. hdu 1022 Train Problem I【模拟出入栈】

    Train Problem I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  9. gdocrimg04从库无法重启问题

    1.查看error.logVersion: '5.6.25-log'  socket: '/tmp/mysqld.3324_gdocrimg04.sock'  port: 3324  Source d ...

  10. 第13章 Salesforce Lightning应用程序

    13.1 Lightning应用程序 13.1.1 什么是闪电应用程序 Salesforce应用程序有两种风格:经典应用程序和闪电应用程序.经典应用程序在Salesforce Classic中创建和管 ...