利用 预加载(PRELOAD)机制实现 Hook
elf程序在进行动态链接的时候,会将有相同符号名的符号覆盖成LD_PRELOAD指定的so文件中的符号。也就是说,我们可以用自己的so库中的函数替换原来库里有的函数,从而达到hook的目的。
下面我们尝试通过这种hook方式来实现任意地址读/写
linux平台下的测试
测试文件:
test.c:
#include <stdio.h> char xxx[] = "tlsn_wheeler"; int main(){ puts("nihao"); puts(xxx); return 0; } // gcc ./test.c -g -no-pie -o ./test
hook代码:
hook.c:
#include <stdio.h> #include <string.h> #include <dlfcn.h> unsigned char hooking[] = "hook write xD!!!!"; typedef int (*PFN_puts)(const char * s); int puts(const char * s) { void *handle = dlopen("libc.so.6", RTLD_LAZY); PFN_puts old_puts = dlsym(handle, "puts"); // 以备调用 if (!strcmp(s,"nihao")){ unsigned long long int hook_addr = 0x000000000404030; // 任意位置读数据 unsigned char res[20] ={0}; for(int i=0;i<10;i++){ res[i] = *(unsigned char*)(hook_addr+i); } printf("hook read: %s\n",res); // 任意位置写数据 for(int i=0;i<13;i++){ *(unsigned char*)(hook_addr+i) = hooking[i]; } *(unsigned char*)(hook_addr+13) = 0; }else{ return old_puts(s); } // printf("hooked!!!"); return 0; } // gcc -fPIC -shared -o hook.so hook.c -ldl // LD_PRELOAD=./hook.so ./test
效果:
Android平台下的测试
需要注意的是,Android5.0 之后不支持未开启pie的程序,因此,测试文件的编译选项中不能有 -no-pie选项了,没关系,我们可以通过读 /proc/self/maps 来获取程序加载的基址。
另一个需要注意的点就是 dlopen的库从 libc.so.6变为了 libc.so ,不要填错了!!!
测试文件:
test.c
#include <stdio.h> #include <stdlib.h> #include <sys/auxv.h> char xxx[] = "tlsn_wheeler"; int main(){ puts("nihao"); puts(xxx); return 0; } // '/home/tlsn/Btools/NDKXX/NDK24/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang' ./test.c -g -o ./test
hook文件
hook.c
#include <stdio.h> #include <string.h> #include <dlfcn.h> unsigned char hooking[] = "hook write xD!!!!"; typedef int (*PFN_puts)(const char * s); unsigned long long get_baseaddr(){ FILE *fp = fopen("/proc/self/maps", "r"); if (fp == NULL) { perror("fopen"); return 1; } char line[256]; unsigned long long start_addr =0; while (fgets(line, sizeof(line), fp) != NULL) { unsigned long start, end; if (sscanf(line, "%lx-%lx", &start, &end) == 2) { // printf("Program loaded at address range: %lx - %lx\n", start, end); start_addr = start; fclose(fp); return start_addr; } } } int puts(const char * s) { void *handle = dlopen("libc.so", RTLD_LAZY); PFN_puts old_puts = dlsym(handle, "puts"); // 以备调用 unsigned long long baseaddr = get_baseaddr(); printf("[*]baseaddr is 0x%llx\n",baseaddr); printf("[*]old_puts is 0x%llx\n",old_puts); printf("[*]HOOKED_puts is 0x%llx\n",puts); if (!strcmp(s,"nihao")){ unsigned long long int hook_addr_offset = 0x3998; unsigned long long int hook_addr = baseaddr + hook_addr_offset; // 任意位置读数据 unsigned char res[20] ={0}; for(int i=0;i<10;i++){ res[i] = *(unsigned char*)(hook_addr+i); } printf("hook read: %s\n",res); // 任意位置写数据 for(int i=0;i<13;i++){ *(unsigned char*)(hook_addr+i) = hooking[i]; } *(unsigned char*)(hook_addr+13) = 0; }else{ return old_puts(s); } // printf("hooked!!!"); return 0; } // '/home/tlsn/Btools/NDKXX/NDK24/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang' -fPIC -shared -o hook.so hook.c -ldl // LD_PRELOAD=./hook.so ./test
hook效果: