-
-
未解决 [求助]求助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写入的
赞赏
他的文章
赞赏
雪币:
留言: