PLT/GotHook

  1. ELF文件格式
    1. Linker
    2. section
      1. .dynamic
      2. .dynsym
      3. Data
      4. Code
      5. 符号
      6. Hash table
  2. PLTHook
    1. 基本原理
  3. aqiyi xhook学习
    1. register
    2. refresh

ELF文件格式

ELF

ELF文件包括五部分,ELF-header,PHT,SHT,sections,segments

ELF文件开头是一个ELF-header,即文件头,描述了节头表SHT(section-header-table,关于连接视图)和程序头表PHT(Program-header-table,关于执行视图,描述了文件中的各种segment),包括他们在ELF文件中的起始位置和长度,ELF分为连接视图(Linking View)和执行视图(Execution View),连接视图是ELF被加载进内存执行前,以seciton为单位的数据组织形式,执行视图则是被加载到内存后,以segment为数据组织形式,PLTHook所关注的是执行视图,因为PLTHook是在运行时修改内存中的数据,inker会根据执行视图的信息,将ELF加载到内存,执行relocation将外部引用的绝对地址填入got表和DATA段,而PLTHook执行的时机是在Linker完全加载ELF之后,解析内存中的ELF数据,修改relocation(重定位)的结果

可以用readelf命令来查看elf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//example
n1ng@itachi:/tmp$ readelf
Usage: readelf <option(s)> elf-file(s)
Display information about the contents of ELF format files
Options are:
-a --all Equivalent to: -h -l -S -s -r -d -V -A -I
-h --file-header Display the ELF file header
-l --program-headers Display the program headers
--segments An alias for --program-headers
-S --section-headers Display the sections' header
--sections An alias for --section-headers
-g --section-groups Display the section groups
-t --section-details Display the section details
-e --headers Equivalent to: -h -l -S
-s --syms Display the symbol table

Linker

Linker(动态链接器)在加载ELF时最主要的工作就是relocation,使ELF的每个导入符号找到对应的外部符号(数据,函数)的绝对地址,最终将其写入got表(外部函数)和.data,.data.rel.ro(外部数据)

section

.dynamic

Dynamic里面包含很多信息比如你需要的动态库,以及是否立即加载以及符号表等,有Linker解析和加载ELF时会用到的各项数据的索引,Linker在解析完header和执行视图后就会开始解析.dynamic

.dynsym

保存了与动态链接相关的符号导入导出关系

Data

.bss:未初始化的数据。比如:没有赋初值的全局变量和静态变量。(.bss 不占用 ELF 文件体积)

.data:已初始化的非只读数据。比如:int g_value = 1;,或者 size_t (*strlen_ptr)(const char *) = strlen;(初始化过程需要 linker relocation 参与才能知道外部 strlen 函数的绝对地址)

.rodata:已初始化的只读数据,加载完成后所属内存页会被 linker 设置为只读。比如:const int g_value = 1;

.data.rel.ro:已初始化的只读数据,初始化过程需要 linker relocation 参与,加载完成后所属内存页会被 linker 设置为只读。比如:const size_t (*strlen)(const char *) = strlen;

Code

  • .text,大多数函数被编译为二进制机器指令后存放的位置
  • .init_array,在.init_array中调用某个类的构造函数就可以在ELF被加载后立刻自动执行该类的实例,也可以用_attribute_((constructor))定义单独的init函数
  • .plt,过程链接表,对外部或内部的符号的调用跳板,.plt会从.got或.data或.data.rel.ro中查询符号的绝对地址然后执行跳转

符号

  • 导出符号,当前ELF提供给外部使用的符号,如libc.so中的open就是libc.so的导出符号
  • 导入符号,当前ELF需要使用的外部符号

Hash table

通过哈希表可以快速确认ELF中是否存在某个动态链接符号,以及该符号对应的信息项在.dynsym中的偏移位置

包括.hash(SYSV hash,其中包含所有的动态链接符号)和.gun.hash(GNU hash,只包含动态链接库中的导出符号)

PLTHook

基本原理

和relocation类似,PLTHook通过符号名在哈希表中找到符号信息(在.dynsym中),再找到对应的PLT信息(在.rel.plt或.rel.dyn等中),最后找到绝对地址信息(在.got.plt或.data或.data.rel.ro中)最后修改这个绝对地址的值为我们自己的代理函数的地址,在修改之前需要用mprotect将当前地址位置所在内存页设置为可写

/proc/{pid}/maps 是进程运行时的虚拟内存映射文件,可以通过这个文件来查看加载的动态库在内存中的绝对地址

1
2
3
4
5
6
7
8
9
10
11
//example
C:\Users\n1ng>adb shell
cepheus:/ $ su
cepheus:/ # cat proc/2826/maps | grep com.zj.wuaipojie
76b625f000-76b627f000 r--p 00000000 103:0f 5424721 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/oat/arm64/base.odex
76b627f000-76b6280000 r-xp 00020000 103:0f 5424721 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/oat/arm64/base.odex
76b6280000-76b6b9f000 r--p 00000000 103:0f 5424724 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/oat/arm64/base.vdex
76b6b9f000-76b6ba0000 r--p 00021000 103:0f 5424721 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/oat/arm64/base.odex
76b6ba0000-76b6ba1000 rw-p 00022000 103:0f 5424721 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/oat/arm64/base.odex
76bad31000-76baddb000 r--s 005e1000 103:0f 5423879 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/base.apk
774730d000-7747319000 r--s 0068c000 103:0f 5423879 /data/app/~~FzjZlM7c6EilYe9ou2G-2w==/com.zj.wuaipojie-d_gPd6cmPOwHcjx-vaJq5w==/base.apk

aqiyi xhook学习

register

注册hook信息,将每一个调用symbol的PLT入口点的地址替换为new_func,旧地址保存在old_func

1
2
3
4
5
6
7
//xhook.c
//返回了xh_coreregister
int xhook_register(const char *pathname_regex_str, const char *symbol,
void *new_func, void **old_func)
{
return xh_core_register(pathname_regex_str, symbol, new_func, old_func);
}
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
//xh_core.c

int xh_core_register(const char *pathname_regex_str, const char *symbol,
void *new_func, void **old_func)
{
xh_core_hook_info_t *hi;
regex_t regex;

if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;

if(xh_core_inited)
{
XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol);
return XH_ERRNO_INVAL;
}

if(0 != regcomp(&regex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;

if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM;

//复制symbol指针到hi->symbol
if(NULL == (hi->symbol = strdup(symbol)))
{
free(hi);
return XH_ERRNO_NOMEM;
}
#if XH_CORE_DEBUG

//复制pathname_regex_str指针到hi->pathname_regex_str
if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str)))
{
free(hi->symbol);
free(hi);
return XH_ERRNO_NOMEM;
}
#endif

//保存path,newfunc指针,oldfunc指针
hi->pathname_regex = regex;
hi->new_func = new_func;
hi->old_func = old_func;

//将hi添加到xh_core_hook_info
pthread_mutex_lock(&xh_core_mutex);//上锁
TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link);//添加
pthread_mutex_unlock(&xh_core_mutex);//解锁

return 0;
}

refresh

根据前面注册的hook信息,执行真正的hook操作

xhook 在内部维护了一个全局的缓存,用于保存最后一次从 /proc/self/maps 读取到的 ELF 加载信息。每次一调用 xhook_refresh 函数,这个缓存都将被更新。xhook 使用这个缓存来判断哪些 ELF 是这次新被加载到内存中的。我们每次只需要针对这些新加载的 ELF 做 hook 就可以了。

1
2
3
4
5
//xhook.c
int xhook_refresh(int async)
{
return xh_core_refresh(async);
}
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
//xh_core.c
int xh_core_refresh(int async)
{
//init
xh_core_init_once();
if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN;

if(async)//async=1,进行异步的hook
{
//init for async
xh_core_init_async_once();
if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN;

//refresh async
pthread_mutex_lock(&xh_core_mutex);
xh_core_refresh_thread_do = 1;//需要进行刷新
pthread_cond_signal(&xh_core_cond);
pthread_mutex_unlock(&xh_core_mutex);
}
else//进行同步的hook
{
//refresh sync
pthread_mutex_lock(&xh_core_refresh_mutex);
xh_core_refresh_impl();//调用xh_core_refresh_impl进行刷新
pthread_mutex_unlock(&xh_core_refresh_mutex);
}

return 0;
}

继续看刷新的函数xh_core_refresh_impl

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
static void xh_core_refresh_impl()
{
char line[512];
FILE *fp;
uintptr_t base_addr;
uintptr_t prev_base_addr = 0;
char perm[5];
char prev_perm[5] = "---p";
unsigned long offset;
unsigned long prev_offset = 0;
int pathname_pos;
char *pathname;
char prev_pathname[512] = {0};
size_t pathname_len;
xh_core_map_info_t *mi, *mi_tmp;
xh_core_map_info_t mi_key;
xh_core_hook_info_t *hi;
xh_core_ignore_info_t *ii;
int match;
xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);

//读取/proc/self/maps,获取内存映射信息
if(NULL == (fp = fopen("/proc/self/maps", "r")))
{
XH_LOG_ERROR("fopen /proc/self/maps failed");
return;
}

while(fgets(line, sizeof(line), fp))
{
//获取addr,offset等信息
if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue;

// do not touch the shared memory
if (perm[3] != 'p') continue;

// Ignore permission PROT_NONE maps
if (perm[0] == '-' && perm[1] == '-' && perm[2] == '-')
continue;

//get pathname
while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1))
pathname_pos += 1;
if(pathname_pos >= (int)(sizeof(line) - 1)) continue;

//获取pathname指针
pathname = line + pathname_pos;
pathname_len = strlen(pathname);
if(0 == pathname_len) continue;
if(pathname[pathname_len - 1] == '\n')
{
pathname[pathname_len - 1] = '\0';
pathname_len -= 1;
}
if(0 == pathname_len) continue;
if('[' == pathname[0]) continue;

// Find non-executable map, we need record it. Because so maps can begin with
// an non-executable map.
if (perm[2] != 'x') {
prev_offset = offset;
prev_base_addr = base_addr;
memcpy(prev_perm, perm, sizeof(prev_perm));
strcpy(prev_pathname, pathname);
continue;
}

// Find executable map if offset == 0, it OK,
// or we need check previous map for base address.
if (offset != 0) {
if (strcmp(prev_pathname, pathname) || prev_offset != 0 || prev_perm[0] != 'r') {
continue;
}
// The previous map is real begin map
base_addr = prev_base_addr;
}

//check pathname,查看是否在要hook的queue中
match = 0;
TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
{
if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0))
{
TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
{
if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0))
{
if(NULL == ii->symbol)
goto check_finished;

if(0 == strcmp(ii->symbol, hi->symbol))
goto check_continue;
}
}

match = 1;
check_continue:
break;
}
}
check_finished:
if(0 == match) continue;

//check elf header format
//We are trying to do ELF header checking as late as possible.
if(0 != xh_core_check_elf_header(base_addr, pathname)) continue;

//check existed map item
mi_key.pathname = pathname;
if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))
{
//存在
RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);

//重复则保留第一个真正的基址
if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
{
#if XH_CORE_DEBUG
XH_LOG_DEBUG("repeated map info when update: %s", line);
#endif
free(mi->pathname);
free(mi);
continue;
}

//基址变化则重新继续hook
if(mi->base_addr != base_addr)
{
mi->base_addr = base_addr;
xh_core_hook(mi);
}
}
else
{
//not exist, create a new map info
if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue;
if(NULL == (mi->pathname = strdup(pathname)))
{
free(mi);
continue;
}
mi->base_addr = base_addr;

//repeated?
//We only keep the first one, that is the real base address
if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
{
#if XH_CORE_DEBUG
XH_LOG_DEBUG("repeated map info when create: %s", line);
#endif
free(mi->pathname);
free(mi);
continue;
}

//hook
xh_core_hook(mi); //hook
}
}
fclose(fp);

//free all missing map item, maybe dlclosed?
RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
{
#if XH_CORE_DEBUG
XH_LOG_DEBUG("remove missing map info: %s", mi->pathname);
#endif
RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
if(mi->pathname) free(mi->pathname);
free(mi);
}

//save the new refreshed map info tree
xh_core_map_info = map_info_refreshed;

XH_LOG_INFO("map refreshed");

#if XH_CORE_DEBUG
RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info)
XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname);
#endif
}

static void *xh_core_refresh_thread_func(void *arg)
{
(void)arg;

pthread_setname_np(pthread_self(), "xh_refresh_loop");

while(xh_core_refresh_thread_running)
{
//waiting for a refresh task or exit
pthread_mutex_lock(&xh_core_mutex);
while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running)
{
pthread_cond_wait(&xh_core_cond, &xh_core_mutex);
}
if(!xh_core_refresh_thread_running)
{
pthread_mutex_unlock(&xh_core_mutex);
break;
}
xh_core_refresh_thread_do = 0;
pthread_mutex_unlock(&xh_core_mutex);

//refresh
pthread_mutex_lock(&xh_core_refresh_mutex);
xh_core_refresh_impl();
pthread_mutex_unlock(&xh_core_refresh_mutex);
}

return NULL;
}

static void xh_core_init_once()
{
if(xh_core_inited) return;

pthread_mutex_lock(&xh_core_mutex);

if(xh_core_inited) goto end;

xh_core_inited = 1;

//dump debug info
XH_LOG_INFO("%s\n", xh_version_str_full());
#if XH_CORE_DEBUG
xh_core_hook_info_t *hi;
TAILQ_FOREACH(hi, &xh_core_hook_info, link)
XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str,
hi->new_func, hi->old_func);
xh_core_ignore_info_t *ii;
TAILQ_FOREACH(ii, &xh_core_ignore_info, link)
XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ",
ii->pathname_regex_str);
#endif

//register signal handler
if(0 != xh_core_add_sigsegv_handler()) goto end;

//OK
xh_core_init_ok = 1;

end:
pthread_mutex_unlock(&xh_core_mutex);
}

接下来看xh_core_hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void xh_core_hook(xh_core_map_info_t *mi)
{
if(!xh_core_sigsegv_enable)
{
xh_core_hook_impl(mi);
}
else
{
xh_core_sigsegv_flag = 1;
if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
{
xh_core_hook_impl(mi);
}
else
{
XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname);
}
xh_core_sigsegv_flag = 0;
}
}

调用的是xh_core_hook_impl

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
static void xh_core_hook_impl(xh_core_map_info_t *mi)
{
//init
if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return;

//hook
xh_core_hook_info_t *hi;
xh_core_ignore_info_t *ii;
int ignore;
//查看白名单是否有要忽略的
TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
{
if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0))
{
ignore = 0;
TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
{
if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0))
{
if(NULL == ii->symbol) //ignore all symbols
return;

if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol
{
ignore = 1;
break;
}
}
}

//不忽略则继续xh_elf_hook
if(0 == ignore)
xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func);
}
}
}
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
//xh_elf_hook
int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func)
{
uint32_t symidx;
void *rel_common;
xh_elf_plain_reloc_iterator_t plain_iter;
xh_elf_packed_reloc_iterator_t packed_iter;
int found;
int r;

if(NULL == self->pathname)
{
XH_LOG_ERROR("not inited\n");
return XH_ERRNO_ELFINIT; //not inited?
}

if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;

XH_LOG_INFO("hooking %s in %s\n", symbol, self->pathname);

//find symbol index by symbol name
if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0;
//对plt数组对应的got位置的数据进行替换
//replace for .rel(a).plt
if(0 != self->relplt)
{
xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela);
while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))
{
if(0 != (r = xh_elf_find_and_replace_func(self,
(self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1,
symbol, new_func, old_func,
symidx, rel_common, &found))) return r;
if(found) break;
}
}

//replace for .rel(a).dyn
if(0 != self->reldyn)
{
xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela);
while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))
{
if(0 != (r = xh_elf_find_and_replace_func(self,
(self->is_use_rela ? ".rela.dyn" : ".rel.dyn"), 0,
symbol, new_func, old_func,
symidx, rel_common, NULL))) return r;
}
}

//replace for .rel(a).android
if(0 != self->relandroid)
{
xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela);
while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter)))
{
if(0 != (r = xh_elf_find_and_replace_func(self,
(self->is_use_rela ? ".rela.android" : ".rel.android"), 0,
symbol, new_func, old_func,
symidx, rel_common, NULL))) return r;
}
}

return 0;
}
1
2
3
4
//replace for .rea(a).plt
xh_elf_find_and_replace_func(self,(self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1,
symbol, new_func, old_func,
symidx, rel_common, &found)
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
//查找替换函数
static int xh_elf_find_and_replace_func(xh_elf_t *self, const char *section,
int is_plt, const char *symbol,
void *new_func, void **old_func,
uint32_t symidx, void *rel_common,
int *found)
{
ElfW(Rela) *rela;
ElfW(Rel) *rel;
ElfW(Addr) r_offset;
size_t r_info;
size_t r_sym;
size_t r_type;
ElfW(Addr) addr;
int r;

if(NULL != found) *found = 0;

if(self->is_use_rela)
{
//获取对应got的偏移r_offset
rela = (ElfW(Rela) *)rel_common;
r_info = rela->r_info;
r_offset = rela->r_offset;
}
else
{
rel = (ElfW(Rel) *)rel_common;
r_info = rel->r_info;
r_offset = rel->r_offset;
}

//check sym
r_sym = XH_ELF_R_SYM(r_info);
if(r_sym != symidx) return 0;

//check type
r_type = XH_ELF_R_TYPE(r_info);
if(is_plt && r_type != XH_ELF_R_GENERIC_JUMP_SLOT) return 0;
if(!is_plt && (r_type != XH_ELF_R_GENERIC_GLOB_DAT && r_type != XH_ELF_R_GENERIC_ABS)) return 0;

//we found it
XH_LOG_INFO("found %s at %s offset: %p\n", symbol, section, (void *)r_offset);
if(NULL != found) *found = 1;

//do replace
//找到got对应的地址,进行指针替换
addr = self->bias_addr + r_offset;
if(addr < self->base_addr) return XH_ERRNO_FORMAT;
if(0 != (r = xh_elf_replace_function(self, symbol, addr, new_func, old_func)))
{
XH_LOG_ERROR("replace function failed: %s at %s\n", symbol, section);
return r;
}

return 0;
}

调用了xh_elf_replace_function进行地址指针的替换

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
//xh_elf_replace_function
static int xh_elf_replace_function(xh_elf_t *self, const char *symbol, ElfW(Addr) addr, void *new_func, void **old_func)
{
void *old_addr;
unsigned int old_prot = 0;
unsigned int need_prot = PROT_READ | PROT_WRITE;
int r;

//already replaced?
//here we assume that we always have read permission, is this a problem?
if(*(void **)addr == new_func) return 0;

//get old prot
if(0 != (r = xh_util_get_addr_protect(addr, self->pathname, &old_prot)))
{
XH_LOG_ERROR("get addr prot failed. ret: %d", r);
return r;
}

if(old_prot != need_prot)
{
//set new prot
if(0 != (r = xh_util_set_addr_protect(addr, need_prot)))
{
XH_LOG_ERROR("set addr prot failed. ret: %d", r);
return r;
}
}

//save old func
old_addr = *(void **)addr;
if(NULL != old_func) *old_func = old_addr;

//指令替换
*(void **)addr = new_func; //segmentation fault sometimes

if(old_prot != need_prot)
{
//restore the old prot
if(0 != (r = xh_util_set_addr_protect(addr, old_prot)))
{
XH_LOG_WARN("restore addr prot failed. ret: %d", r);
}
}

//清除cpu 指令缓存
xh_util_flush_instruction_cache(addr);

XH_LOG_INFO("XH_HK_OK %p: %p -> %p %s %s\n", (void *)addr, old_addr, new_func, symbol, self->pathname);
return 0;
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 3049155267@qq.com

💰

×

Help us with donation