首页
社区
课程
招聘
未解决 [求助]求助linux下注入so,为何时而成功时而失败? 10雪币
发表于: 2025-6-29 22:47 266

未解决 [求助]求助linux下注入so,为何时而成功时而失败? 10雪币

2025-6-29 22:47
266

在学习,so注入,下面代码基本上是ai给的


/root/code/c/so_injector.c

#define _GNU_SOURCE // 必须定义在所有 include 之前,以启用 RTLD_DEFAULT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <signal.h>

// 函数:获取指定库在目标进程中的基地址
void* get_lib_base_addr(pid_t pid, const char* lib_name) {
    char maps_path[256];
    snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", pid);

    FILE* maps_file = fopen(maps_path, "r");
    if (!maps_file) {
        perror("fopen maps");
        return NULL;
    }

    char line[512];
    void* base_addr = NULL;
    while (fgets(line, sizeof(line), maps_file)) {
        // 我们寻找 r-xp (可读-可执行) 的内存段,这是代码段
        if (strstr(line, lib_name) && strstr(line, "r-xp")) {
            base_addr = (void*)strtoul(line, NULL, 16);
            break;
        }
    }

    fclose(maps_file);
    return base_addr;
}

// 函数:通过 ptrace 向目标进程写入数据
int ptrace_write(pid_t pid, void* addr, const void* data, size_t len) {
    const long* src = (const long*)data;
    // POKEDATA 一次写入一个字 (word),所以我们循环写入
    for (size_t i = 0; i < len; i += sizeof(long)) {
        if (ptrace(PTRACE_POKEDATA, pid, (char*)addr + i, *src++) == -1) {
            perror("ptrace_write POKEDATA");
            return -1;
        }
    }
    return 0;
}

// --- 以下是用于自动管理目标进程的辅助函数 ---
pid_t get_target_pid() {
    FILE *fp = fopen("/tmp/target.pid", "r");
    if (!fp) return -1;
    pid_t pid = 0;
    fscanf(fp, "%d", &pid);
    fclose(fp);
    return pid;
}

void kill_old_target() {
    pid_t old_pid = get_target_pid();
    if (old_pid > 0) {
        kill(old_pid, SIGKILL);
    }
    remove("/tmp/target.pid");
}

int main(int argc, char* argv[]) {
    // --- 自动管理目标进程 ---
    printf("[+] Managing target process...\n");
    kill_old_target(); // 杀死任何已存在的 target 进程

    pid_t target_pid = fork();
    if (target_pid == 0) {
        // 子进程:执行 target 程序,不重定向输出,让输出可见
        // 如果需要记录输出到文件,可以打开一个文件进行重定向
        int fd = open("/tmp/target_output.txt", O_CREAT | O_WRONLY | O_APPEND, 0644);
        if (fd != -1) {
            dup2(fd, 2); // 只重定向stderr,保留stdout的可见性
            close(fd);
        }
        execl("./target", "./target", (char*)NULL);
        perror("execl"); // 如果 execl 返回,说明出错了
        exit(1);
    } else if (target_pid < 0) {
        perror("fork");
        return 1;
    }

    // 父进程:保存 PID 并等待
    FILE* fp = fopen("/tmp/target.pid", "w");
    if(fp) {
        fprintf(fp, "%d", target_pid);
        fclose(fp);
    }

    printf("[+] Target process started with PID: %d\n", target_pid);
   
    // =========================================================================
    // === 关键修改: 将路径指向一个权限宽松的目录 ===
    // =========================================================================
    const char* payload_path = "/root/code/c/payload.so"; 
    // =========================================================================
   
    const char* libc_map_name = "/libc-";

    // 轮询等待目标进程加载 libc.so
    printf("[+] Waiting for target to load libc.so...\n");
    void* target_libc_base = NULL;
    int attempts = 0;
    const int max_attempts = 50; // 5 秒超时

    while (attempts < max_attempts) {
        target_libc_base = get_lib_base_addr(target_pid, libc_map_name);
        if (target_libc_base) {
            printf("[+] libc.so found in target process after %dms.\n", attempts * 100);
            break;
        }
        usleep(100000); // 等待 100ms
        attempts++;
    }

    if (!target_libc_base) {
        fprintf(stderr, "Timeout: Failed to find libc.so in target process.\n");
        kill(target_pid, SIGKILL);
        return 1;
    }
    printf("    -> Target's libc base:   %p\n", target_libc_base);

    // 1. 计算 dlopen 在目标进程中的地址 (利用ASLR偏移)
    printf("[+] Calculating address of dlopen in target process...\n");
    void* self_libc_base = get_lib_base_addr(getpid(), libc_map_name);
    if (!self_libc_base) { fprintf(stderr, "Failed to get self libc base address.\n"); kill(target_pid, SIGKILL); return 1; }
    printf("    -> Injector's libc base: %p\n", self_libc_base);

    void* self_dlopen_addr = dlsym(RTLD_DEFAULT, "dlopen"); // 使用 RTLD_DEFAULT 更具可移植性
    if (!self_dlopen_addr) { fprintf(stderr, "dlsym failed for dlopen: %s\n", dlerror()); kill(target_pid, SIGKILL); return 1; }
    printf("    -> Injector's dlopen addr: %p\n", self_dlopen_addr);

    long dlopen_offset = (long)self_dlopen_addr - (long)self_libc_base;
    printf("    -> dlopen offset: 0x%lx\n", dlopen_offset);

    void* target_dlopen_addr = (void*)((long)target_libc_base + dlopen_offset);
    printf("[+] Calculated target dlopen address: %p\n", target_dlopen_addr);

    // 2. Attach to target and save registers
    printf("[+] Attaching to process %d...\n", target_pid);
    if (ptrace(PTRACE_ATTACH, target_pid, NULL, NULL) == -1) { perror("ptrace attach"); return 1; }
    waitpid(target_pid, NULL, 0);
    printf("[+] Attached.\n");

    struct user_regs_struct old_regs;
    if (ptrace(PTRACE_GETREGS, target_pid, NULL, &old_regs) == -1) { perror("ptrace GETREGS"); ptrace(PTRACE_DETACH, target_pid, NULL, NULL); return 1; }
    printf("[+] Saved original registers. RIP: 0x%llx\n", old_regs.rip);

    // 3. Write payload path to target's memory (stack)
    void* remote_path_addr = (void*)(old_regs.rsp - strlen(payload_path) - 256); // 在栈上分配空间
    printf("[+] Writing payload path to remote stack at %p\n", remote_path_addr);
    if (ptrace_write(target_pid, remote_path_addr, payload_path, strlen(payload_path) + 1) == -1) {
        ptrace(PTRACE_DETACH, target_pid, NULL, NULL); return 1;
    }
   
    // =========================================================================
    // === 新增部分: 1. 设置软件断点 (INT 3) ===
    // =========================================================================
    printf("[+] Setting software breakpoint at original RIP: %p\n", (void*)old_regs.rip);
   
    // 读取并保存原始指令
    long original_instruction = ptrace(PTRACE_PEEKTEXT, target_pid, (void*)old_regs.rip, NULL);
    if (original_instruction == -1) { perror("ptrace PEEKTEXT"); ptrace(PTRACE_DETACH, target_pid, NULL, NULL); return 1; }
    printf("    -> Original instruction: 0x%lx\n", original_instruction);

    // 构造断点指令 (0xCC)
    long breakpoint_instruction = (original_instruction & ~0xFF) | 0xCC;

    // 写入断点指令
    if (ptrace(PTRACE_POKETEXT, target_pid, (void*)old_regs.rip, (void*)breakpoint_instruction) == -1) {
        perror("ptrace POKETEXT for breakpoint"); ptrace(PTRACE_DETACH, target_pid, NULL, NULL); return 1;
    }
    printf("    -> Breakpoint instruction 0x%lx set.\n", breakpoint_instruction);
    // =========================================================================

    // 4. 设置寄存器以调用 dlopen
    struct user_regs_struct new_regs = old_regs;
    new_regs.rip = (unsigned long long)target_dlopen_addr; // 指令指针指向 dlopen
    new_regs.rdi = (unsigned long long)remote_path_addr;   // 第一个参数 (rdi) 是 so 路径
    new_regs.rsi = RTLD_LAZY;                              // 第二个参数 (rsi) 是 flag
   
    // 模拟 CALL 指令: 将伪造的返回地址 (即我们的断点地址) 压入栈中
    unsigned long long aligned_rsp = new_regs.rsp & -16LL; // 16字节对齐栈
    new_regs.rsp = aligned_rsp - sizeof(long); // 为返回地址分配空间
    ptrace_write(target_pid, (void*)new_regs.rsp, &old_regs.rip, sizeof(long));

    printf("[+] Setting registers for remote call. RIP: 0x%llx, RDI: 0x%llx\n", new_regs.rip, new_regs.rdi);
    if (ptrace(PTRACE_SETREGS, target_pid, NULL, &new_regs) == -1) { perror("ptrace SETREGS"); ptrace(PTRACE_DETACH, target_pid, NULL, NULL); return 1; }

    // 5. 继续执行以触发 dlopen
    printf("[+] Continuing process to execute dlopen...\n");
    if (ptrace(PTRACE_CONT, target_pid, NULL, NULL) == -1) { perror("ptrace CONT"); return 1; }
   
    // 等待,直到我们的断点被命中 (触发 SIGTRAP)
    waitpid(target_pid, NULL, 0); 
    printf("[+] Breakpoint hit! dlopen call has finished.\n");

    // =========================================================================
    // === 新增部分: 2. 检查结果并恢复现场 ===
    // =========================================================================
    // 获取 dlopen 调用后的寄存器状态
    struct user_regs_struct post_call_regs;
    if(ptrace(PTRACE_GETREGS, target_pid, NULL, &post_call_regs) == -1) { perror("ptrace GETREGS post-call"); }
   
    // 函数的返回值通常在 rax 寄存器中
    printf("[+] dlopen returned 0x%llx (0 means error, non-zero means success).\n", post_call_regs.rax);
    if (post_call_regs.rax == 0) {
        fprintf(stderr, "[!] Injection failed! dlopen returned an error.\n");
    } else {
        printf("[+] Injection successful! Handle: 0x%llx\n", post_call_regs.rax);
    }

    // 恢复被我们修改为断点的原始指令
    printf("[+] Restoring original instruction at %p...\n", (void*)old_regs.rip);
    if (ptrace(PTRACE_POKETEXT, target_pid, (void*)old_regs.rip, (void*)original_instruction) == -1) {
        perror("ptrace POKETEXT to restore instruction");
    }
    // =========================================================================

    // 6. 恢复原始寄存器并分离
    printf("[+] Restoring original registers and detaching...\n");
    if (ptrace(PTRACE_SETREGS, target_pid, NULL, &old_regs) == -1) { perror("ptrace restore SETREGS"); }
    if (ptrace(PTRACE_DETACH, target_pid, NULL, NULL) == -1) { perror("ptrace DETACH"); }

    // =========================================================================
    // === 关键修改: 增加延时,给构造函数执行时间 ===
    // =========================================================================
    printf("[+] Detached. Giving payload 1 second to run its constructor...\n");
    sleep(1);
    // =========================================================================

    printf("[+] Done! Killing target process for cleanup.\n");
    kill(target_pid, SIGKILL);

    return 0;
}


/root/code/c/payload.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dlfcn.h>

// 使用 __attribute__((constructor)) 定义一个构造函数
// 这个函数会在 .so 文件被加载时自动执行
__attribute__((constructor))
void payload_init() {
    // 创建文件
    int fd = open("/root/code/c/injected.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd != -1) {
        const char* msg = "Payload injected and constructor executed successfully!\n";
        write(fd, msg, strlen(msg));
       
        // 2. 检查并记录dlerror(),这是一个非常有用的调试技巧
        const char* dlsym_error = dlerror();
        if (dlsym_error) {
            write(fd, "\nDLERROR at constructor time: ", strlen("\nDLERROR at constructor time: "));
            write(fd, dlsym_error, strlen(dlsym_error));
        }
       
        close(fd);
    }
}

/root/code/c/target.c

#include <stdio.h>
#include <unistd.h>

void victim_func() {
    printf("I am the victim! Please don't hook me!\n");
}

int main() {
    printf("My PID is: %d\n", getpid());
    while(1) {
        victim_func();
        sleep(2);
    }
    return 0;
}


运行日志,大概90%几率失败,10%成功

(py39) > gcc -o target target.c && gcc -shared -fPIC -o payload.so payload.c && gcc -o so_injector so_injector.c -ldl && ./so_injector ./target /root/code/c/payload.so && sleep 1 && cat /root/code/c/injected.txt
[+] Managing target process...
[+] Target process started with PID: 148276
[+] Waiting for target to load libc.so...
My PID is: 148276
I am the victim! Please don't hook me!
[+] libc.so found in target process after 100ms.
    -> Target's libc base:   0x792c2cc47000
[+] Calculating address of dlopen in target process...
    -> Injector's libc base: 0x7a15a1634000
    -> Injector's dlopen addr: 0x7a15a1805390
    -> dlopen offset: 0x1d1390
[+] Calculated target dlopen address: 0x792c2ce18390
[+] Attaching to process 148276...
[+] Attached.
[+] Saved original registers. RIP: 0x792c2cd021b4
[+] Writing payload path to remote stack at 0x7ffc18220969
[+] Setting software breakpoint at original RIP: 0x792c2cd021b4
    -> Original instruction: 0x840475eaf883c289
    -> Breakpoint instruction 0x840475eaf883c2cc set.
[+] Setting registers for remote call. RIP: 0x792c2ce18390, RDI: 0x7ffc18220969
[+] Continuing process to execute dlopen...
[+] Breakpoint hit! dlopen call has finished.
[+] dlopen returned 0xdb (0 means error, non-zero means success).
[+] Injection successful! Handle: 0xdb
[+] Restoring original instruction at 0x792c2cd021b4...
[+] Restoring original registers and detaching...
[+] Detached. Giving payload 1 second to run its constructor...
[+] Done! Killing target process for cleanup.
cat: /root/code/c/injected.txt: 没有那个文件或目录 # 失败了
(py39) > gcc -o target target.c && gcc -shared -fPIC -o payload.so payload.c && gcc -o so_injector so_injector.c -ldl && ./so_injector ./target /root/code/c/payload.so && sleep 1 && cat /root/code/c/injected.txt
[+] Managing target process...
[+] Target process started with PID: 148326
[+] Waiting for target to load libc.so...
[+] libc.so found in target process after 0ms.
    -> Target's libc base:   0x7531a5cba000
[+] Calculating address of dlopen in target process...
    -> Injector's libc base: 0x7531a5cba000
    -> Injector's dlopen addr: 0x7531a5e8b390
    -> dlopen offset: 0x1d1390
[+] Calculated target dlopen address: 0x7531a5e8b390
[+] Attaching to process 148326...
[+] Attached.
[+] Saved original registers. RIP: 0x7531a5d7af3f
[+] Writing payload path to remote stack at 0x7ffcaa9e5079
[+] Setting software breakpoint at original RIP: 0x7531a5d7af3f
    -> Original instruction: 0x870ffffff0003d48
    -> Breakpoint instruction 0x870ffffff0003dcc set.
[+] Setting registers for remote call. RIP: 0x7531a5e8b390, RDI: 0x7ffcaa9e5079
[+] Continuing process to execute dlopen...
[+] Breakpoint hit! dlopen call has finished.
[+] dlopen returned 0x617f469c68b0 (0 means error, non-zero means success).
[+] Injection successful! Handle: 0x617f469c68b0
[+] Restoring original instruction at 0x7531a5d7af3f...
[+] Restoring original registers and detaching...
[+] Detached. Giving payload 1 second to run its constructor...
My PID is: 148326
I am the victim! Please don't hook me!
[+] Done! Killing target process for cleanup.
Payload injected and constructor executed successfully! # 成功了 cat读取到payload.so写入的



传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回