一个关于钩臂功能的谜题,通过修改ELF文件 [英] A puzzle about hook arm function, by modify ELF file

查看:30
本文介绍了一个关于钩臂功能的谜题,通过修改ELF文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过修改 elf 文件的 .text 二进制文件来挂钩一个函数,我的意思是用 'bl yyyy' 替换像 'bl xxxx' 这样的指令,'yyyy' 指向 elf 文件中的填充区域.跳转后,我保存寄存器并调用 dlopen&dlsym 来获取另一个库的新函数的地址,调用它,然后恢复寄存器并跳转回 'xxxx'.

I want to hook a function by modify elf file's .text binary, my mean is replace an instruction like 'bl xxxx' with 'bl yyyy', the 'yyyy' is point to an padding area in elf file. After jump, I save the registers and call dlopen&dlsym to get new function's addr of another lib, call it, then restore the registers and jump back to 'xxxx'.

这不是很难,我几乎成功了,除了一个问题:我的钩子函数中不能使用 64 位 var.int 类型没问题,但是当我 printf int64_t var 时,它总是显示错误的数字.

It is not very hard, and I have almost successful except for a problem: I cannot use 64-bits var in my hook function. The int type is no problem, but when I printf int64_t var, it's always shows a wrong number.

1 这是源代码:
test_ori

// test_ori.c
#include <stdio.h>
#include <dlfcn.h>

// I will hook sub and jump to myfn
void sub() {
  printf("sub called...\n");
}

// The purpose of sub2 is just for let me know the addr of dlopen&dlsym
void (*func)();
void sub2() {
  void *p = dlopen("/system/lib/libyyy.so", RTLD_NOW);
  func = (void (*)())dlsym(p,"myfn2");
  func();
}

int main(){
  sub();
  sub2();
  return 0;
}

libyyy.so

// yyy.c
#include <stdio.h>

void myfn() {
  int x = 1;
  uint32_t y = 2;
  uint64_t z = 3;
  printf("x=%d, y=%u, z=%llu\n", x, y, z);
}

void myfn2() {}


2 使用 objump 找到 dlopen&dlsym 的地址

// dlopen is 0x8440, dlsym is 0x844c
84a8:       f7ff efca       blx     8440 <dlopen@plt>
...
84b2:       f7ff efcc       blx     844c <dlsym@plt>

// sub is 0x84d4
84e0:       003c            movs    r4, r7
84e2:       0000            movs    r0, r0
84e4:       b510            push    {r4, lr}
84e6:       f7ff fff5       bl      84d4 <puts@plt+0x7c>
84ea:       f7ff ffd9       bl      84a0 <puts@plt+0x48>


3 找到填充区域并修改elf文件

// I use the offset 0x550(it's padding area) as my jump destination, the addr is 0x8550
// by the way, I also modify the segment's size field(0x580->0x600) so my new code can be loaded
ori  ->  84e6:       f7ff fff5       bl      84d4
new  ->  84e6:       f000 f833       bl      8550


4 从 0x8550 开始的钩子过程,由 asm:

1.  push {r0-r7}        //  save registers
2.  push {lr}           //  save lr
3.  mov  r1, #0         //  param2 of dlopen(RTLD_NOW)
4.  mov  r0, pc
5.  add  r0, #xx        //  param1 of dlopen(addr of "libyyy.so")
6.  blx  xxxx           //  call dlopen
7.  mov  r1, pc         
8.  add  r1, #xx        //  param2 of dlsym(addr of "myfn")
9.  blx  xxxx           //  call dlsym
10. blx  r0             //  call myfn
11. pop  {r3}           //  
12. mov  lr, r3         //  restore lr
13. pop  {r0-r7}        //  restore registers
14. b    xxxx           //  jump back


5 修改elf文件:将代码写入padding区域

// I convert the asm above to machine code and write it(and strings "libyyy.so" & "myfn") to file  
// then I check it in gdb:  
(gdb) x/20i 0x8550
   0x8550:      push    {r0, r1, r2, r3, r4, r5, r6, r7}
   0x8552:      push    {lr}
   0x8554:      movs    r1, #0
   0x8556:      mov     r0, pc
   0x8558:      adds    r0, #24
   0x855a:      blx     0x8440
   0x855e:      mov     r1, pc
   0x8560:      adds    r1, #26
   0x8562:      blx     0x844c
   0x8566:      blx     r0
   0x8568:      pop     {r3}
   0x856a:      mov     lr, r3
   0x856c:      pop     {r0, r1, r2, r3, r4, r5, r6, r7}
   0x856e:      b.w     0x84d4

6 结果

# ./test_new
x=1, y=2, z=12884901888
sub called...


如您所见,x 和 y 是正常的,但 z(uint64_t) 是错误的.它应该是 3,但我在这里得到 12884901888(0x300000000).z 的高/低寄存器似乎不正确,但您能告诉我为什么以及如何解决吗?感谢您的关注!


As you see, the x and y is normal, but z(uint64_t) is wrong. It shoule be 3, but I get 12884901888(0x300000000) here. It seems the high/low register for z is incorrect, but can you tell me why and how to fix it? Thanks for your attention!

推荐答案

这个问题的答案在 ARM 过程调用标准.

The answer to this question is in the ARM Procedure call standard.

 printf("x=%d, y=%u, z=%llu\n", x, y, z);

这有四个参数.format 字符串,32 位 xy 以及 3rd z 值.由于前三个参数将 z 放在一个奇数地址,编译器会填充一个空格,以便 ldrdstrd 指令可以工作;如果堆栈未对齐,这些 64 位加载将不起作用.

This has four arguments. The format string, the 32bit x and y and the 3rd z value. As the first three arguments land z at an odd address, the compiler pads a space so that ldrd and strd instruction can work; these 64-bit loads do not work if the stack is not aligned.

不清楚您是在为您编译的 C 代码显示汇编程序,还是在尝试修改生成的代码.您很可能没有确保 堆栈是八字节对齐的,因为 var args 必须放在堆栈上.

It is not clear whether you are showing assembler for the C code you compiled or if you are trying to modify the generated code. Most likely you do not ensure that the stack is eight byte aligned as var args must be put on the stack.

Ps:有一个关于 ARM 8 字节对齐的很好的堆栈溢出问题,但我现在找不到了.随时编辑我的问题或发表评论.

Ps: There is a good stack-overflow question about ARM 8-byte alignment, but I can not find it now. Feel free to edit my question or leave a comment.

这篇关于一个关于钩臂功能的谜题,通过修改ELF文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆