android的got表HOOK实现代码

作者:小道安全 时间:2023-11-18 17:04:51 

概述

对于android的so文件的hook根据ELF文件特性分为:Got表hook、Sym表hook和inline hook等。
全局符号表(GOT表)hook,它是通过解析SO文件,将待hook函数在got表的地址替换为自己函数的入口地址,这样目标进程每次调用待hook函数时,实际上是执行了我们自己的函数。

Androd so注入和函数Hook(基于got表)的步骤:

1.ptrace附加目标pid进程;
2.在目标pid进程中,查找内存空间(用于存放被注入的so文件的路径和so中被调用的函数的名称或者shellcode);
3.调用目标pid进程中的dlopen、dlsym等函数,用于加载so文件实现Android so的注入和函数的Hook;
4.释放附加的目标pid进程和卸载注入的so文件。

具体代码实现

以下以fopen函数进行got hook为例。


//获取模块地址功能实现
void* getModuleBase(pid_t pid, const char* module_name){
   FILE* fp;
   long address = 0;
   char* pch;
   char filename[32];
   char line[1024];

// 格式化字符串得到 "/proc/pid/maps"
   if(pid < 0){
       snprintf(filename, sizeof(filename), "/proc/self/maps");
   }else{
       snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
   }

// 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息
   fp = fopen(filename, "r");
   if(fp != NULL){
       // 每次一行,读取文件 /proc/pid/maps中内容
       while(fgets(line, sizeof(line), fp)){
           // 查找指定的so模块
           if(strstr(line, module_name)){
               // 分割字符串
               pch = strtok(line, "-");
               // 字符串转长整形
               address = strtoul(pch, NULL, 16);

}
               break;
           }
       }
   }
   fclose(fp);
   return (void*)address;
}

//hook fopen进行实现
//(libxxxx.so文件是ELF32文件)
#define LIBPATH "/data/app-lib/com.xxxx/libxxxx.so"

int hookFopen(){

// 获取目标pid中"/data/app-lib/com.xxxx/libxxxx.so"模块的加载地址
   void* base_addr = getModuleBase(getpid(), LIBPATH );
   // 保存Hook目标函数的原始调用地址
   old_fopen = fopen;
   int fd;
   // 用open打开内存模块文件"/data/app-lib/com.xxxx/libxxxx.so"
   fd = open(LIB_PATH, O_RDONLY);
   if(-1 == fd){
       return -1;
   }

// elf32文件的文件头结构体Elf32_Ehdr
   Elf32_Ehdr ehdr;
   // 读取elf32格式的文件"/data/app-lib/com.xxxx/libxxxx.so"的文件头信息
   read(fd, &ehdr, sizeof(Elf32_Ehdr));

// elf32文件中节区表信息结构的文件偏移
   unsigned long shdr_addr = ehdr.e_shoff;
   // elf32文件中节区表信息结构的数量
   int shnum = ehdr.e_shnum;
   // elf32文件中每个节区表信息结构中的单个信息结构的大小(描述每个节区的信息的结构体的大小)
   int shent_size = ehdr.e_shentsize;

// elf32文件节区表中每个节区的名称存放的节区名称字符串表,在节区表中的序号index
   unsigned long stridx = ehdr.e_shstrndx;

Elf32_Shdr shdr;
   lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET);
   // 读取elf32文件中的描述每个节区的信息的结构体(这里是保存elf32文件的每个节区的名称字符串的)
   read(fd, &shdr, shent_size);

// 为保存elf32文件的所有的节区的名称字符串申请内存空间
   char * string_table = (char *)malloc(shdr.sh_size);
   // 定位到具体存放elf32文件的所有的节区的名称字符串的文件偏移处
   lseek(fd, shdr.sh_offset, SEEK_SET);
   read(fd, string_table, shdr.sh_size);
   lseek(fd, shdr_addr, SEEK_SET);

int i;
   uint32_t out_addr = 0;
   uint32_t out_size = 0;
   uint32_t got_item = 0;
   int32_t got_found = 0;

// 循环遍历elf32文件的节区表(描述每个节区的信息的结构体)
   for(i = 0; i<shnum; i++){
       // 依次读取节区表中每个描述节区的信息的结构体
       read(fd, &shdr, shent_size);
       // 判断当前节区描述结构体描述的节区是否是SHT_PROGBITS类型
       //类型为SHT_PROGBITS的.got节区包含全局偏移表
       if(shdr.sh_type == SHT_PROGBITS){
           // 获取节区的名称字符串在保存所有节区的名称字符串段.shstrtab中的序号
           int name_idx = shdr.sh_name;

// 判断节区的名称是否为".got.plt"或者".got"
           if(strcmp(&(string_table[name_idx]), ".got.plt") == 0
               || strcmp(&(string_table[name_idx]), ".got") == 0){
               // 获取节区".got"或者".got.plt"在内存中实际数据存放地址
               out_addr = base_addr + shdr.sh_addr;
               // 获取节区".got"或者".got.plt"的大小
               out_size = shdr.sh_size;

int j = 0;
               // 遍历节区".got"或者".got.plt"获取保存的全局的函数调用地址
               for(j = 0; j<out_size; j += 4){
                   // 获取节区".got"或者".got.plt"中的单个函数的调用地址
                   got_item = *(uint32_t*)(out_addr + j);
                   // 判断节区".got"或者".got.plt"中函数调用地址是否是将要被Hook的目标函数地址
                   if(got_item == old_fopen){
                       got_found = 1;
                       // 获取当前内存分页的大小
                       uint32_t page_size = getpagesize();
                       // 获取内存分页的起始地址(需要内存对齐)
                       uint32_t entry_page_start = (out_addr + j) & (~(page_size - 1));

// 修改内存属性为可读可写可执行
                       if(mprotect((uint32_t*)entry_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1){

return -1;
                       }

// Hook的函数,是我们自己定义的函数
                       got_item = new_fopen;

// 进行恢复内存属性为可读可执行
                       if(mprotect((uint32_t*)entry_page_start, page_size, PROT_READ | PROT_EXEC) == -1){

return -1;
                       }
                       break;
                   // 目标函数的调用地址已经被Hook了
                   }else if(got_item == new_fopen){
                       break;
                   }
               }
               // 对目标函数HOOk成功,跳出循环
               if(got_found)
                   break;
           }
       }
   }
   free(string_table);
   close(fd);
}

来源:https://blog.csdn.net/c_kongfei/article/details/119620172

标签:android,HOOK,got表
0
投稿

猜你喜欢

  • Android可筛选的弹窗控件CustomFiltControl

    2023-01-21 09:01:46
  • 详解Android自定义控件属性

    2023-09-27 18:00:34
  • java处理图片背景颜色的方法

    2023-11-27 04:38:20
  • Java设计模式之原型模式详解

    2021-09-06 19:04:49
  • c#使用Dataset读取XML文件动态生成菜单的方法

    2022-07-14 22:52:20
  • 基于C#实现屏幕取色器

    2023-02-16 04:17:53
  • Android开发之开发者头条(二)实现左滑菜单

    2022-02-28 11:18:31
  • Studio 编译报错:compileSdkVersion 'android-24' requires JDK 1.8 or later to compile.的解决办法

    2023-06-19 17:19:41
  • C# WinForm导出Excel方法介绍

    2022-12-01 13:25:57
  • java实现FTP文件上传与文件下载

    2023-08-16 08:28:38
  • android连续拖动导致挂起的解决方法

    2021-08-06 07:11:39
  • Netty分布式FastThreadLocal的set方法实现逻辑剖析

    2021-08-22 04:51:54
  • Java中遍历Map集合的5种方式总结

    2023-04-29 13:14:25
  • c# Parallel类的使用

    2022-09-09 12:30:21
  • Android自定义wheelview随机选号效果

    2021-09-12 06:36:53
  • Java double转BigDecimal的注意事项说明

    2022-12-13 14:44:38
  • 设置Android设备WIFI在休眠时永不断开的代码实现

    2022-08-26 09:03:00
  • Spring Boot 定义系统启动任务的多种方式

    2023-11-24 13:25:33
  • 优化spring boot应用后6s内启动内存减半

    2021-09-13 02:47:59
  • springboot使用redis对单个对象进行自动缓存更新删除的实现

    2023-06-30 19:14:36
  • asp之家 软件编程 m.aspxhome.com