环境:
    Android 6.0
问题:
    ADBI框架在Android5上可以稳定运行,但是在Android6.0中并不能得到函数在GOT表中保存的真正地址。Inline hook方式也会出现此问题,导致hook失败。
原因:
    经过对hook框架的研读,参考了网上对Android5-6的变化,我发现Android6.0里面的GOT HOOK框架有改变,Android6的动态库加载的基地址不再固定,即和/proc/id/maps里的地址不一致了。因此之前通过/proc/id/maps拿到的基地址不正确。
解决思路:
    通过soinfo结构拿到load_bias来作为基地址去算got函数地址的偏移。就是将module_base改成load_bias。
具体代码:
    分成两部分:一个是GOT hook,一个是inline hook.
1.GOT hook(hook.c)
在Android5.0中,get_module_base的代码如下:
| 1 | /** | 
在Android6.0中,在hook.c文件中添加soinfo的结构体定义:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 typedef struct link_map_t {
  uintptr_t l_addr;
  char*  l_name;
  uintptr_t l_ld;
  struct link_map_t* l_next;
  struct link_map_t* l_prev;
} link_map_t;
typedef void (*linker_function_t)();
#define SOINFO_NAME_LEN 128
typedef struct soinfo {
    char name[SOINFO_NAME_LEN];
    const Elf32_Phdr* phdr;
    size_t phnum;
    Elf32_Addr entry;
    Elf32_Addr base;
    unsigned size;
    uint32_t unused1;  // DO NOT USE, maintained for compatibility.
    Elf32_Dyn* dynamic;
    uint32_t unused2; // DO NOT USE, maintained for compatibility
    uint32_t unused3; // DO NOT USE, maintained for compatibility
    struct soinfo* next;
    unsigned flags;
    const char* strtab;
    Elf32_Sym* symtab;
    size_t nbucket;
    size_t nchain;
    unsigned* bucket;
    unsigned* chain;
    //------------------
  // This is only used by 32-bit MIPS, but needs to be here for
  // all 32-bit architectures to preserve binary compatibility.
  unsigned* plt_got;
  Elf32_Rel* plt_rel;
  size_t plt_rel_count;
  Elf32_Rel* rel;
  size_t rel_count;
  linker_function_t* preinit_array;
  size_t preinit_array_count;
  linker_function_t* init_array;
  size_t init_array_count;
  linker_function_t* fini_array;
  size_t fini_array_count;
  linker_function_t init_func;
  linker_function_t fini_func;
  // ARM EABI section used for stack unwinding.
  unsigned* ARM_exidx;
  size_t ARM_exidx_count;
  size_t ref_count;
  link_map_t link_map;
  int constructors_called;
  // When you read a virtual address from the ELF file, add this
  // value to get the corresponding address in the process' address space.
  Elf32_Addr load_bias;
} soinfo;
uint32_t load_bias_addr;
并修改get_module_base的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48uint32_t get_module_base(pid_t pid, const char *module_path)
{
    FILE *fp = NULL;
    char *pch = NULL;
    char filename[32];
    char line[512];
    uint32_t addr = 0;
    void *handle =  dlopen( LIBSF_PATH, RTLD_GLOBAL);
    soinfo *si = (soinfo*)handle; 
    addr = si->base;
    load_bias_addr = si->load_bias;
	//若pid为负值,proc/self/maps是查找本进程的内存映射信息
	//snprintf()对filename字串赋值
    if ( pid < 0 )
        snprintf(filename, sizeof(filename), "/proc/self/maps");
	//若pid为非负值,proc/pid/maps查找指定pid进程的内存映射信息
    else
        snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
    
	//处理打开内存文件失败
    if ( (fp = fopen(filename, "r")) == NULL )
    {
        LOGE("open %s failed!", filename);
        return 0;
    }
    
	//逐行读取内存文件maps
    while ( fgets(line, sizeof(line), fp) )
    {
        //如果在maps文件中搜索在module_path字符串(模块名称?)
        if ( strstr(line, module_path) )
        {
            LOGE("[+]:2");
			//strtok()把字串line按"-"分割,返回被分割出字符串的指针
            pch = strtok(line, "-");
			//strtoul()把字串pch转换成16进制
			//此处addr为模块所处内存空间的起始地址(XXX-YYY中的XXX)
            addr = strtoul(pch, NULL, 16);
            break;
        }
    }
    fclose(fp);
    return addr;
}
2.Inline hook(inlineHook.c)
在Android5.0中,其中find_name(参考文章点这里)的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37int find_name(pid_t pid, char *name, char *libn, unsigned long *addr)
{
	struct mm mm[1000];
	unsigned long libcaddr;
	int nmm;
	char libc[1024];
	symtab_t s;
	if (0 > load_memmap(pid, mm, &nmm)) {
		log("[-] cannot read memory map\n")
		return -1;
	} else 
		log("[+] success load memory map")
	if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) {
		log("[-] cannot find lib: %s\n", libn)
		return -1;
	} else 
		log("[+] success find lib name: 0x%X", libcaddr)
	log("[+] lib: >%s<\n", libc)
	s = load_symtab(libc);
	if (!s) {
	 	log("[-] cannot read symbol table\n")
		return -1;
	}else log("[+] success read symbol table")
	if (0 > lookup_func_sym(s, name, addr)) {
		log("[-] cannot find function: %s\n", name)
		return -1;
	}else log("[+] success find function: %s: addr - 0x%X, *addr - 0x%X", name, addr, *addr)
	 *addr += libcaddr;
	return 0;
}
在Android6.0中,在inlineHook.c文件中同样添加上述soinfo的结构体定义,并修改find_name函数为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49int find_name(pid_t pid, char *name, char *libn, unsigned long *addr, int syslib)
{
	struct mm mm[1000];
	unsigned long libcaddr;
	int nmm;
	char libc[1024];
	symtab_t s;
	if (0 > load_memmap(pid, mm, &nmm)) {
		log("[-] cannot read memory map\n")
		return -1;
	} else 
		log("[+] success load memory map")
	if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) {
		log("[-] cannot find lib: %s\n", libn)
		return -1;
	} else 
		log("[+] success find lib name: 0x%X", libcaddr)
	log("[+] lib: >%s<\n", libc)
	s = load_symtab(libc);
	if (!s) {
	 	log("[-] cannot read symbol table\n")
		return -1;
	 }else log("[+] success read symbol table")
	if (0 > lookup_func_sym(s, name, addr)) {
		log("[-] cannot find function: %s\n", name)
		return -1;
	}else log("[+] success find function: %s: addr - 0x%X, *addr - 0x%X", name, addr, *addr)
	log("[+] libcaddr is 0x%X\n", libcaddr)
	void *handle =  dlopen( LIBSF_PATH, RTLD_GLOBAL);
    soinfo *si = (soinfo*)handle; 
    load_bias_addr = si->load_bias;
	log("[+] load_bias_addr is 0x%X\n", load_bias_addr)
	if(syslib == 0){
		*addr += libcaddr;
	}else if(syslib == 1){
		*addr += load_bias_addr;
	}else{
		log("[-] parameter syslib is wrong !\n")
	}
	return 0;
}
源码之后整理一下会放出来,有什么问题可以直接来问我~