imgui-mirror/injector.cpp
2026-07-23 08:41:49 +08:00

301 lines
9.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* injector.cpp – Android arm64 ptrace 注入器(纯 ptrace 方式)
* 编译: aarch64-linux-android-clang++ -static -std=c++17 injector.cpp -o injector
* 用法: ./injector <so_path> <package_name|pid>
* 示例: ./injector /data/1/libMyMenu.so com.tencent.jkchess
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <linux/elf.h>
#include <asm/ptrace.h>
#include <ctype.h>
#include <string>
#include <vector>
#include <algorithm>
// ---------- 查找进程 ----------
int find_pid_by_name(const char* proc_name) {
DIR* dir = opendir("/proc");
if (!dir) return -1;
struct dirent* entry;
int pid = -1;
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type != DT_DIR) continue;
int tmp_pid = atoi(entry->d_name);
if (tmp_pid <= 0) continue;
char cmdline_path[256];
snprintf(cmdline_path, sizeof(cmdline_path), "/proc/%d/cmdline", tmp_pid);
FILE* fp = fopen(cmdline_path, "r");
if (fp) {
char cmdline[256] = {0};
size_t n = fread(cmdline, 1, sizeof(cmdline) - 1, fp);
fclose(fp);
if (n > 0) {
char* pkg = cmdline;
char* last_slash = strrchr(pkg, '/');
if (last_slash) pkg = last_slash + 1;
if (strcmp(pkg, proc_name) == 0) {
pid = tmp_pid;
break;
}
}
}
}
closedir(dir);
return pid;
}
// ---------- 远程内存读写(纯 ptrace) ----------
long ptrace_readdata(int pid, unsigned long addr, void* buffer, size_t size) {
for (size_t i = 0; i < size; i += sizeof(long)) {
long data = ptrace(PTRACE_PEEKDATA, pid, addr + i, 0);
if (data == -1 && errno) return -1;
memcpy((char*)buffer + i, &data, std::min(sizeof(long), size - i));
}
return 0;
}
long ptrace_writedata(int pid, unsigned long addr, const void* buffer, size_t size) {
for (size_t i = 0; i < size; i += sizeof(long)) {
long data = 0;
memcpy(&data, (char*)buffer + i, std::min(sizeof(long), size - i));
if (ptrace(PTRACE_POKEDATA, pid, addr + i, data) == -1)
return -1;
}
return 0;
}
// ---------- 执行系统调用(ptrace) ----------
int remote_syscall(int pid, struct user_pt_regs& regs) {
struct iovec iov = { &regs, sizeof(regs) };
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
perror("setregset before syscall");
return -1;
}
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
perror("ptrace syscall");
return -1;
}
int status;
waitpid(pid, &status, 0);
if (!WIFSTOPPED(status)) {
fprintf(stderr, "process not stopped after syscall\n");
return -1;
}
// 再次 syscall 以获取返回值
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1) {
perror("ptrace syscall exit");
return -1;
}
waitpid(pid, &status, 0);
if (!WIFSTOPPED(status)) {
fprintf(stderr, "process not stopped after syscall exit\n");
return -1;
}
// 读取返回值
struct user_pt_regs ret_regs;
iov.iov_base = &ret_regs; iov.iov_len = sizeof(ret_regs);
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
perror("getregset for return");
return -1;
}
regs = ret_regs;
return 0;
}
// ---------- 注入主函数 ----------
int inject_so(int pid, const char* so_path) {
// 1. 附加进程
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
perror("ptrace attach");
return -1;
}
int status;
waitpid(pid, &status, 0);
if (!WIFSTOPPED(status)) {
fprintf(stderr, "process not stopped\n");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
// 2. 获取原始寄存器
struct user_pt_regs orig_regs, regs;
struct iovec iov = { &orig_regs, sizeof(orig_regs) };
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
perror("getregset original");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
regs = orig_regs;
// 3. 在远程进程分配内存(mmap)
size_t path_len = strlen(so_path) + 1;
regs.regs[0] = 0;
regs.regs[1] = path_len;
regs.regs[2] = PROT_READ | PROT_WRITE;
regs.regs[3] = MAP_PRIVATE | MAP_ANONYMOUS;
regs.regs[4] = -1;
regs.regs[5] = 0;
regs.regs[8] = 222; // __NR_mmap (arm64)
if (remote_syscall(pid, regs) != 0) {
fprintf(stderr, "mmap syscall failed\n");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
long remote_addr = regs.regs[0];
if (remote_addr <= 0 || remote_addr == -1) {
fprintf(stderr, "mmap returned invalid address: 0x%lx\n", remote_addr);
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
printf("[+] Allocated memory at 0x%lx (size=%zu)\n", remote_addr, path_len);
// 4. 写入 so 路径(使用 ptrace)
if (ptrace_writedata(pid, remote_addr, so_path, path_len) == -1) {
perror("ptrace_writedata");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
printf("[+] Wrote SO path into remote memory\n");
// 5. 计算远程 dlopen 地址
void* local_dlopen = (void*)dlopen;
unsigned long local_libdl_base = 0;
FILE* fp = fopen("/proc/self/maps", "r");
if (!fp) {
perror("open self maps");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
char line[512];
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "libdl.so")) {
sscanf(line, "%lx", &local_libdl_base);
break;
}
}
fclose(fp);
if (local_libdl_base == 0) {
fprintf(stderr, "can't find local libdl.so base\n");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
unsigned long dlopen_offset = (unsigned long)local_dlopen - local_libdl_base;
// 获取远程 libdl.so 基址
char remote_maps_path[64];
snprintf(remote_maps_path, sizeof(remote_maps_path), "/proc/%d/maps", pid);
fp = fopen(remote_maps_path, "r");
if (!fp) {
perror("open remote maps");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
unsigned long remote_libdl_base = 0;
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "libdl.so")) {
sscanf(line, "%lx", &remote_libdl_base);
break;
}
}
fclose(fp);
if (remote_libdl_base == 0) {
fprintf(stderr, "can't find remote libdl.so base\n");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
unsigned long remote_dlopen = remote_libdl_base + dlopen_offset;
printf("[+] Remote dlopen address: 0x%lx\n", remote_dlopen);
// 6. 设置远程寄存器执行 dlopen
regs = orig_regs;
regs.regs[0] = remote_addr; // path
regs.regs[1] = RTLD_LAZY; // flag
regs.pc = remote_dlopen;
regs.regs[30] = 0x0; // LR = 0 (导致 segfault)
iov.iov_base = &regs; iov.iov_len = sizeof(regs);
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
perror("setregset for dlopen");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
// 7. 继续执行
if (ptrace(PTRACE_CONT, pid, 0, 0) == -1) {
perror("ptrace cont");
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
waitpid(pid, &status, 0);
if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) {
printf("[+] dlopen executed (expected segfault due to LR=0)\n");
} else {
fprintf(stderr, "Unexpected stop: status=%d\n", status);
ptrace(PTRACE_DETACH, pid, 0, 0);
return -1;
}
// 8. 恢复寄存器并 detach
iov.iov_base = &orig_regs; iov.iov_len = sizeof(orig_regs);
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
perror("restore registers");
}
if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
perror("ptrace detach");
return -1;
}
printf("[+] Injection successful!\n");
return 0;
}
// ---------- 主程序 ----------
int main(int argc, char** argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <so_path> <package_name|pid>\n", argv[0]);
return 1;
}
const char* so_path = argv[1];
const char* target = argv[2];
int pid = 0;
bool is_digit = true;
for (int i = 0; target[i]; i++) {
if (!isdigit(target[i])) { is_digit = false; break; }
}
if (is_digit) {
pid = atoi(target);
printf("[+] Using specified PID: %d\n", pid);
} else {
printf("[+] Searching for process: %s\n", target);
pid = find_pid_by_name(target);
if (pid <= 0) {
fprintf(stderr, "Process '%s' not found. Make sure it's running.\n", target);
return 1;
}
printf("[+] Found PID: %d\n", pid);
}
if (access(so_path, F_OK) != 0) {
fprintf(stderr, "SO file '%s' does not exist\n", so_path);
return 1;
}
printf("[+] SO file: %s\n", so_path);
return inject_so(pid, so_path);
}