利用 预加载(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效果:

 

posted @ 2025-02-09 22:56  TLSN  阅读(108)  评论(0)    收藏  举报