ADBI框架在Android6.0平台上的使用

环境:
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
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
/**
* lookup the start address of a specific module
* return 0 if FAILED
*/

//函数功能:在指定pid的内存文件中查找包含module_path字符串的so,并返回其在内存中的起始地址
//成功返回so起始地址,失败返回0
uint32_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;

//若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;
}

在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
48
uint32_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
37
int 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
49
int 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;
}

源码之后整理一下会放出来,有什么问题可以直接来问我~