elf ファイルの .text バイナリを変更して関数をフックしたいのですが、私の意味は、「bl xxxx」のような命令を「bl yyyy」に置き換えることです。「yyyy」は、elf ファイルのパディング領域を指しています。ジャンプ後、レジスタを保存し、dlopen&dlsym を呼び出して別のライブラリの新しい関数のアドレスを取得し、それを呼び出してから、レジスタを復元して「xxxx」に戻ります。
それほど難しいことではなく、問題を除いてほぼ成功しています。フック関数で 64 ビット var を使用できません。int 型は問題ありませんが、 int64_t varを printf すると、常に間違った数値が表示されます。
1 これは src コードです:
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 asm による 0x8550 からのフック プロセス:
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 ファイルの変更: コードをパディング領域に書き込みます
// 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 の高低レジスタが間違っているようですが、その理由と修正方法を教えてください。ご清聴ありがとうございました!