一文带你掌握android的got表HOOK实现。

简介

对于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 LIB_PATH "/data/app-lib/com.xxxx/libxxxx.so"

int hookFopen(){

    // 获取目标pid进程中"/data/app-lib/com.xxxx/libxxxx.so"模块的加载地址
    void* base_addr = getModuleBase(getpid(), LIB_PATH);
    LOGD("[+] libxxxx.so address = %p \n", base_addr);

    // 保存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文件中节区表的每个单元信息结构体(描述每个节区的信息的结构体)
    Elf32_Shdr shdr;
    // elf32文件中定位到存放每个节区名称的字符串表的信息结构体位置.shstrtab
    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){
                        LOGD("[+] Found fopen in got.\n");
                        got_found = 1;
                        // 获取当前内存分页的大小
                        uint32_t page_size = getpagesize();
                        // 获取内存分页的起始地址(需要内存对齐)
                        uint32_t entry_page_start = (out_addr + j) & (~(page_size - 1));
                        LOGD("[+] entry_page_start = %lx, page size = %lx\n", entry_page_start, page_size);
                        // 修改内存属性为可读可写可执行
                        if(mprotect((uint32_t*)entry_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1){
                            LOGD("mprotect false.\n");
                            return -1;
                        }
                        LOGD("[+] %s, old_fopen = %lx, new_fopen = %lx\n", "before hook function", got_item, new_fopen);

                        // Hook函数为我们自己定义的函数
                        got_item = new_fopen;
                        LOGD("[+] %s, old_fopen = %lx, new_fopen = %lx\n", "after hook function", got_item, new_fopen);
                        // 恢复内存属性为可读可执行
                        if(mprotect((uint32_t*)entry_page_start, page_size, PROT_READ | PROT_EXEC) == -1){
                            LOGD("mprotect false.\n");
                            return -1;
                        }
                        break;
                    // 此时,目标函数的调用地址已经被Hook了
                    }else if(got_item == new_fopen){
                        LOGD("[+] Already hooked.\n");
                        break;
                    }
                }
                // Hook目标函数成功,跳出循环
                if(got_found)
                    break;
            }
        }
    }
    free(string_table);
    close(fd);
} 

学习资料分享

当然,只给予计划不给予学习资料的行为无异于耍流氓,### 如果你对网络安全入门感兴趣,那么你点击这里👉CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

如果你对网络安全感兴趣,学习资源免费分享,保证100%免费!!!(嘿客入门教程)

👉网安(嘿客)全套学习视频👈

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

img

👉网安(嘿客红蓝对抗)所有方向的学习路线****👈

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

img

学习资料工具包

压箱底的好资料,全面地介绍网络安全的基础理论,包括逆向、八层网络防御、汇编语言、白帽子web安全、密码学、网络安全协议等,将基础理论和主流工具的应用实践紧密结合,有利于读者理解各种主流工具背后的实现机制。

在这里插入图片描述

面试题资料

独家渠道收集京东、360、天融信等公司测试题!进大厂指日可待!
在这里插入图片描述

👉嘿客必备开发工具👈

工欲善其事必先利其器。学习客常用的开发软件都在这里了,给大家节省了很多时间。

这份完整版的网络安全(客)全套学习资料已经上传至CSDN官方,朋友们如果需要点击下方链接也可扫描下方微信二v码获取网络工程师全套资料【保证100%免费】

在这里插入图片描述

如果你对网络安全入门感兴趣,那么你点击这里👉CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值