NDK注入存档

这篇博客介绍了如何使用NDK进行动态注入,通过修改libsurfaceflinger.so中的eglSwapBuffers函数来实现。首先获取libsurfaceflinger.so在进程中的基地址,然后读取ELF文件头和节区信息,定位到.GOT.PLT或.GOT段,替换原始函数地址为自定义的新函数,从而实现hook。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <android/log.h>  
#include <EGL/egl.h>  
#include <GLES/gl.h>  
#include <elf.h>  
#include <fcntl.h>  
#include <sys/mman.h>  
  
#define LOG_TAG "DEBUG"  
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)    
  
EGLBoolean (*old_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf) = -1;  
  
EGLBoolean new_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)  
{  
    LOGD("New eglSwapBuffers\n");  
    if (old_eglSwapBuffers == -1)  
        LOGD("error\n");  
    return old_eglSwapBuffers(dpy, surface);  
}  
  
void* get_module_base(pid_t pid, const char* module_name)  
{  
    FILE *fp;  
    long addr = 0;  
    char *pch;  
    char filename[32];  
    char line[1024];  
  
    if (pid < 0) {  
        /* self process */  
        snprintf(filename, sizeof(filename), "/proc/self/maps", pid);  
    } else {  
        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);  
    }  
  
    fp = fopen(filename, "r");  
  
    if (fp != NULL) {  
        while (fgets(line, sizeof(line), fp)) {  
            if (strstr(line, module_name)) {  
                pch = strtok( line, "-" );  
                addr = strtoul( pch, NULL, 16 );  
  
                if (addr == 0x8000)  
                    addr = 0;  
  
                break;  
            }  
        }  
  
        fclose(fp) ;  
    }  
  
    return (void *)addr;  
}  
  
#define LIBSF_PATH  "/system/lib/libsurfaceflinger.so"    
int hook_eglSwapBuffers()    
{    
    old_eglSwapBuffers = eglSwapBuffers;    
    LOGD("Orig eglSwapBuffers = %p\n", old_eglSwapBuffers);    
    void * base_addr = get_module_base(getpid(), LIBSF_PATH);   //获取 libsurfaceflinger.so 加载到 本进程后的地址
    LOGD("libsurfaceflinger.so address = %p\n", base_addr);    
  
    int fd;    
    fd = open(LIBSF_PATH, O_RDONLY);             //打开  libsurfaceflinger.so文件
    if (-1 == fd) {    
        LOGD("error\n");    
        return -1;    
    }    
  
    Elf32_Ehdr ehdr;                         //elf 文件头结构
	/*
	  #define EI_NIDENT
  typedef struct{
  unsigned char e_ident[EI_NIDENT];
  Elf32_Half e_type;
  Elf32_Half e_machine;
  Elf32_Word e_version;
  Elf32_Addr e_entry;
  Elf32_Off e_phoff;
  Elf32_Off e_shoff;
  Elf32_Word e_flags;
  Elf32_Half e_ehsize;
  Elf32_Half e_phentsize;
  Elf32_Half e_phnum;
  Elf32_Half e_shentsize;
  Elf32_Half e_shnum;
  Elf32_Half e_shstrndx;
  }Elf32_Ehdr;
  */
	//ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。
    read(fd, &ehdr, sizeof(Elf32_Ehdr));                //从  libsurfaceflinger.so 中读取so  库的 elf 文件头 
  
    unsigned long shdr_addr = ehdr.e_shoff;      //节区头部表格的偏移量(按字节计算)
    int shnum = ehdr.e_shnum;                           //节区头部表格的表项数目。可以为0。
    int shent_size = ehdr.e_shentsize;                 //e_shentsize 表示Section header table中的每一个条目的大小
    unsigned long stridx = ehdr.e_shstrndx;       //包含节名称的字符串是第几个节(从零开始计数)  
  
    Elf32_Shdr shdr;      //每个“Elf32_Shdr”结构体对应一个段   Elf32_Shdr”又被称为段描述符(Section Descriptor)
	/* 
	  typedef struct {
					  Elf32_Word sh_name;
					  Elf32_Word sh_type;
					  Elf32_Word sh_flags;
					  Elf32_Addr sh_addr;			 假如该section将出现在进程的内存映象空间里,该成员给出了一个该section在内存中的位置。
					  Elf32_Off sh_offset;			 该section的字节偏移量(从文件开始计数)
					  Elf32_Word sh_size;			 该成员给你了section的字节大小
					  Elf32_Word sh_link;
					  Elf32_Word sh_info;
					  Elf32_Word sh_addralign;
					  Elf32_Word sh_entsize;		一些sections保存着一张固定大小入口的表,就象符号表。对于这样一个 section来说,该成员给出了每个入口的字节大小。
	} Elf32_Shdr;
  */
    lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET);        //SEEK_SET 将读写位置指向文件头后再增加offset个位移量  
    read(fd, &shdr, shent_size);                                               //lseek 定位到 某一节区  后读取 
  
    char * string_table = (char *)malloc(shdr.sh_size);            //创建字符表 大小为Elf段的大小
    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;    
  
    for (i = 0; i < shnum; i++) {                                             //遍历所有的段
        read(fd, &shdr, shent_size);      
        if (shdr.sh_type == SHT_PROGBITS) {                        //如果段的类型是程序段
            int name_idx = shdr.sh_name;                               //段的名字
            if (strcmp(&(string_table[name_idx]), ".got.plt") == 0 || strcmp(&(string_table[name_idx]), ".got") == 0) { 

                out_addr = base_addr + shdr.sh_addr;             //so库加载的地址+段的地址   
                out_size = shdr.sh_size;									  //大小仍然不变
                LOGD("out_addr = %lx, out_size = %lx\n", out_addr, out_size); 
  
                for (i = 0; i < out_size; i += 4) {						 //步长为4位  
                    got_item = *(uint32_t *)(out_addr + i);    
                    if (got_item  == old_eglSwapBuffers) {      
                        LOGD("Found eglSwapBuffers in got\n");     //已经找到了交换缓冲区
                        got_found = 1;  

						//对于uint32_t类型的说明 http://blog.youkuaiyun.com/kiddy19850221/article/details/6655066
                        uint32_t page_size = getpagesize();                                                                //4字节
                        uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1));                   //找到进入页面的地址
                        mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);  
                        *(uint32_t *)(out_addr + i) = new_eglSwapBuffers;											//替换地址
  
                        break;      
                    } else if (got_item == new_eglSwapBuffers) {                            //如果已经替换成新的缓冲交换表
                        LOGD("Already hooked\n");    
                        break;      
                    }      
                }     
                if (got_found)   
                    break;  
            }     
        }      
    }      
  
    free(string_table);      
    close(fd);    
}    
  
int hook_entry(char * a){  
    LOGD("Hook success\n");  
    LOGD("Start hooking\n");  
    hook_eglSwapBuffers();    
    return 0;  
} 



/*#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <android/log.h>  
#include <elf.h>  
#include <fcntl.h> 
#include <sys/user.h>    
#include <asm/ptrace.h>    
#include <sys/ptrace.h>    
#include <sys/wait.h>    
#include <sys/mman.h>    
#include <dlfcn.h>    
#include <dirent.h>    
   
#include <string.h>    
   

#define LOG_TAG "DEBUG"  
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)    

uint32_t* got;
int  hook_api() {
//使用打开动态库的方式得到动态库的soinfo结构
	soinfo* si =(soinfo*)::dlopen("/system/bin/libbinder.so",RTLD_NOW);
   if(si == NULL || si->strtab == NULL || si->plt_rel == NULL)
   {
       return -1 ;
   }
for (uint32_t i = 0; i < si->plt_rel_count;i++) {
       //查找重定位表中ioctl所在的项
if(strcmp(si->symtab[ELF32_R_SYM(si->plt_rel[i].r_info)].st_name+ si->strtab, "ioctl") == 0){
           //计算对应的GOT表项的地址
            got = (uint32_t*)(si->base +si->plt_rel[i].r_offset);
           if(*(got) != new_ioctl) {
                //把GOT表项的地址属性改为可写
                uint32_tpagesize = sysconf(_SC_PAGE_SIZE);
                void*start =(void*)(((uint32_t)got)/pagesize*pagesize);
                if(mprotect(start, pagesize * 2, PROT_READ|PROT_WRITE) == -1){
                    returnfalse;
                }
                *(got) = new_ioctl;  //填入新地址
                mprotect(start,pagesize * 2, PROT_READ|PROT_WRITE);
           }
           return 0;
       }
   }
}  
//******************************************************************************************************************************

void new_ioctl()
{

}

int hook_entry(char * a){  
    LOGD("Hook success, pid = %d\n", getpid());  
    LOGD("Hello %s\n", a);  
    return 0;  
} */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值