为了 Hook Android 应用程序的 libc 函数(即 app_process32
),我首先读取保存在 /proc/self/maps
中的整个地址空间以及每个加载的 ELF ,我覆盖匹配的重定位条目。
读取一行/proc/self/maps
:
Elf32_Addr start, end;
sscanf(line, "%8x-%8x", &start, &end);
然后我通过检查魔法来检查它是否是 ELF。如果它是 ELF,我读取它的 PT_DYNAMIC 段并循环遍历它的条目:
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) start;
Elf32_Phdr *phdr = (Elf32_Phdr *) ((unsigned char *) ehdr + ehdr->e_phoff);
Elf32_Half phnum = ehdr->e_phnum;
Elf32_Addr dynamic = 0;
for (; phnum > 0; --phnum, ++phdr) {
if (phdr->p_type == PT_DYNAMIC) {
dynamic = start + phdr->p_vaddr;
break;
}
}
以下是我迭代动态条目的方法:
Elf32_Dyn *dyn;
for (dyn = (Elf32_Dyn *) dynamic; dyn->d_tag; dyn++) {
Elf32_Addr addr = dyn->d_un.d_ptr;
Elf32_Sword val = dyn->d_un.d_val;
switch (dyn->d_tag) {
// rest of the code.
}
}
对于某些共享对象来说,这很好,但是对于某些共享对象,我在检查 for 循环条件 dyn->d_tag
时收到 SIGSEGV。为什么 PT_DYNAMIC 指向我无法读取的位置?我还注意到 dynamic
通常是 > end
,可以吗?
我使用的设备运行 32 位 Android 6.0。
最佳答案
这个计算:
dynamic = start + phdr->p_vaddr;
仅适用于在地址 0 处链接的 ELF 镜像,这对于共享库和位置无关的可执行文件来说是典型的,但不是必需的。
我猜测您有一些共享库已预先链接到非 0 地址。
该库未预先链接:
readelf -Wl foo.so | egrep 'LOAD|DYNAMIC'
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000684 0x000684 R E 0x200000
LOAD 0x000e40 0x0000000000200e40 0x0000000000200e40 0x0001e0 0x0001e8 RW 0x200000
DYNAMIC 0x000e50 0x0000000000200e50 0x0000000000200e50 0x000190 0x000190 RW 0x8
prelink -r 0x120000 foo.so
之后的相同库:
readelf -Wl foo.so | egrep 'LOAD|DYNAMIC'
LOAD 0x000000 0x0000000012000000 0x0000000012000000 0x000684 0x000684 R E 0x200000
LOAD 0x000e40 0x0000000012200e40 0x0000000012200e40 0x0001e0 0x0001e8 RW 0x200000
DYNAMIC 0x000e50 0x0000000012200e50 0x0000000012200e50 0x000190 0x000190 RW 0x8
如果预链接库加载到应用程序的预链接地址 (start==0x12200000
),您最终会得到 dynamic == 0x12200e50+0x12200000
这显然是假的。
要解决这个问题,您需要:
dynamic = start + phdr->p_vaddr - first_pt_load->p_vaddr;
其中 first_pt_load
是最低的 PT_LOAD
段(对于非预链接库,其具有 .p_vaddr == 0
,并且 0x12000000
对于预链接的)。
关于android - 在 Android 6.0 上读取 Elf32 的 PT_DYNAMIC 条目时出现内存访问错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55180723/