#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/types.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
// ARM64缓存类型定义
#ifndef CACHE_TYPE_INSTRUCTION
#define CACHE_TYPE_INSTRUCTION 0
#endif
#ifndef CACHE_TYPE_DATA
#define CACHE_TYPE_DATA 1
#endif
// 系统调用号数组(适配Linux 5系列内核)
#if defined(__aarch64__)
const int cache_flush_syscalls[] = {118, 123, 124, 125, 126, 0};
#endif
typedef uint64_t ADDRESS;
// 全局变量(存储目标进程信息)
struct ProcessInfo {
int pid;
uint64_t so_base;
} processInfo = {-1, 0};
typedef struct {
uint64_t start_addr;
uint64_t end_addr;
char permissions[5];
} ModuleRange;
typedef struct {
ModuleRange ranges[200];
int count;
} ModuleInfo;
// 获取目标进程PID
int getPID(const char *packageName) {
int id = -1;
DIR *dir = opendir("/proc");
if (!dir) {
perror("打开/proc目录失败");
return -1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
id = atoi(entry->d_name);
if (id > 0) {
char filename[64], cmdline[64];
snprintf(filename, sizeof(filename), "/proc/%d/cmdline", id);
FILE *fp = fopen(filename, "r");
if (fp) {
if (fgets(cmdline, sizeof(cmdline), fp)) {
cmdline[strcspn(cmdline, "\n")] = '\0';
if (strcmp(packageName, cmdline) == 0) {
fclose(fp);
closedir(dir);
return id;
}
}
fclose(fp);
}
}
}
}
closedir(dir);
return -1;
}
// 获取模块内存信息(增强权限检查)
ModuleInfo get_module_info(int pid, const char *module_name) {
ModuleInfo info = {0};
FILE *fp;
char line[8192];
char filename[32];
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
if ((fp = fopen(filename, "r")) == NULL) {
perror("打开maps文件失败");
return info;
}
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, module_name)) {
uint64_t start, end;
char perms[5], offset[17], dev[6], inode[11], pathname[256];
// 增强解析逻辑
if (sscanf(line, "%lx-%lx %4s %16s %5s %10s %255[^\n]",
&start, &end, perms, offset, dev, inode, pathname) >= 6) {
if (start < end) {
if (info.count < 200) {
info.ranges[info.count].start_addr = start;
info.ranges[info.count].end_addr = end;
strncpy(info.ranges[info.count].permissions, perms, 4);
info.ranges[info.count].permissions[4] = '\0';
info.count++;
} else {
printf("警告:模块映射段过多(超过200段),部分段未记录\n");
break;
}
}
}
}
}
fclose(fp);
return info;
}
// 使用ptrace安全写入DWORD
int SafeWriteDword(int pid, uint64_t addr, uint32_t value) {
// 附加到目标进程
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
perror("ptrace附加失败");
return -1;
}
// 等待进程暂停
int status;
waitpid(pid, &status, 0);
if (!WIFSTOPPED(status)) {
perror("进程未暂停");
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return -1;
}
// 逐字写入(避免对齐问题)
for (int i = 0; i < sizeof(uint32_t); i++) {
uint8_t byte = (value >> (i * 8)) & 0xFF;
long data = ptrace(PTRACE_PEEKTEXT, pid, addr + i, NULL);
if (data == -1 && errno) {
perror("ptrace读取失败");
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return -1;
}
// 修改单个字节
long new_data = (data & ~0xFF) | byte;
if (ptrace(PTRACE_POKETEXT, pid, addr + i, new_data) == -1) {
perror("ptrace写入失败");
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return -1;
}
}
// 分离目标进程
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
perror("ptrace分离失败");
return -1;
}
return 0;
}
// 使用ptrace安全读取DWORD
int SafeReadDword(int pid, uint64_t addr, uint32_t *value) {
*value = 0;
// 附加到目标进程
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
perror("ptrace附加失败");
return -1;
}
// 等待进程暂停
int status;
waitpid(pid, &status, 0);
if (!WIFSTOPPED(status)) {
perror("进程未暂停");
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return -1;
}
// 逐字读取(避免对齐问题)
for (int i = 0; i < sizeof(uint32_t); i++) {
long data = ptrace(PTRACE_PEEKTEXT, pid, addr + i, NULL);
if (data == -1 && errno) {
perror("ptrace读取失败");
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return -1;
}
*value |= ((uint32_t)(data & 0xFF)) << (i * 8);
}
// 分离目标进程
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
perror("ptrace分离失败");
return -1;
}
return 0;
}
// 使用__builtin___clear_cache刷新缓存
int flush_with_builtin(uint64_t addr, size_t size) {
char *start = (char *)(addr & ~(4095ULL));
char *end = start + size;
__builtin___clear_cache(start, end);
printf("成功: 使用__builtin___clear_cache刷新指令缓存\n");
return 0;
}
// 五级强刷新机制
int enhanced_flush_cache(uint64_t addr, size_t size) {
uint64_t aligned_addr = addr & ~(4095ULL);
size_t aligned_size = ((addr + size - aligned_addr + 4095) / 4096) * 4096;
#ifdef __aarch64__
// 1. 优先使用__builtin___clear_cache
if (flush_with_builtin(aligned_addr, aligned_size) == 0) {
return 0;
}
// 2. 尝试系统调用刷新
const int *syscall_num = cache_flush_syscalls;
while (*syscall_num != 0) {
int ret_instr = syscall(*syscall_num, aligned_addr, aligned_size, CACHE_TYPE_INSTRUCTION);
if (ret_instr == 0) {
printf("成功: 使用syscall %d 刷新缓存\n", *syscall_num);
return 0;
}
syscall_num++;
}
// 3. mprotect内存属性修改方案
void *page_start = (void *)aligned_addr;
int pagesize = sysconf(_SC_PAGESIZE);
unsigned long page_count = (aligned_size + pagesize - 1) / pagesize;
int protection = PROT_READ | PROT_WRITE | PROT_EXEC;
if (mprotect(page_start, page_count * pagesize, protection) == 0) {
printf("成功: 通过mprotect修改内存保护属性刷新缓存\n");
return 0;
}
// 4. msync强制同步方案
if (msync((void *)aligned_addr, aligned_size, MS_SYNC | MS_INVALIDATE) == 0) {
printf("成功: 使用msync同步刷新缓存\n");
return 0;
}
// 5. 终极方案:重建内存映射
void *tmp = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (tmp) {
memcpy(tmp, (void *)aligned_addr, aligned_size);
munmap((void *)aligned_addr, aligned_size);
mmap((void *)aligned_addr, aligned_size, PROT_READ | PROT_EXEC,
MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
memcpy((void *)aligned_addr, tmp, aligned_size);
munmap(tmp, aligned_size);
printf("成功: 通过重建内存映射刷新缓存\n");
return 0;
}
#endif
perror("所有缓存刷新方法均失败");
return -1;
}
// 暂停目标进程
int pause_process(int pid) {
if (kill(pid, SIGSTOP) == -1) {
perror("发送SIGSTOP失败");
return -1;
}
// 等待进程真正停止
int status;
waitpid(pid, &status, WUNTRACED);
if (!WIFSTOPPED(status)) {
perror("进程未正确停止");
return -1;
}
printf("进程已暂停(PID: %d)\n", pid);
return 0;
}
// 恢复目标进程
int resume_process(int pid) {
if (kill(pid, SIGCONT) == -1) {
perror("发送SIGCONT失败");
return -1;
}
printf("进程已恢复运行(PID: %d)\n", pid);
return 0;
}
// 主Hook函数
void hook_il2cpp() {
const char *packageName = "com.tencent.tmgp.cf";
const uint64_t HOOK_OFFSET = 0x7EFD168;
const uint32_t HOOK_VALUE = 505416143;
const uint32_t ORIGINAL_VALUE = 505416142;
processInfo.pid = getPID(packageName);
if (processInfo.pid <= 0) {
printf("获取PID失败\n");
return;
}
printf("获取到目标进程PID: %d\n", processInfo.pid);
ModuleInfo module_info = get_module_info(processInfo.pid, "libil2cpp.so");
if (module_info.count == 0) {
printf("获取SO模块地址失败(可能无权限或模块未加载)\n");
return;
}
// 使用第一个可执行段的起始地址作为模块基址
uint64_t module_base = 0;
for (int i = 0; i < module_info.count; i++) {
if (strstr(module_info.ranges[i].permissions, "x")) {
module_base = module_info.ranges[i].start_addr;
break;
}
}
if (module_base == 0) {
printf("未找到可执行段,使用第一个段作为基址\n");
module_base = module_info.ranges[0].start_addr;
}
uint64_t target_addr = module_base + HOOK_OFFSET;
printf("\nlibil2cpp.so 模块基址: 0x%016lx\n", module_base);
printf("目标Hook地址: 0x%016lx (基址 + 0x%lx)\n", target_addr, HOOK_OFFSET);
// 验证目标地址是否在模块范围内且具有写权限
int in_range = 0;
int has_write_permission = 0;
for (int i = 0; i < module_info.count; i++) {
if (target_addr >= module_info.ranges[i].start_addr &&
target_addr <= module_info.ranges[i].end_addr) {
in_range = 1;
if (strstr(module_info.ranges[i].permissions, "w")) {
has_write_permission = 1;
}
break;
}
}
if (!in_range) {
printf("\n目标地址0x%016lx超出模块范围,退出操作\n", target_addr);
return;
}
if (!has_write_permission) {
printf("\n警告:目标地址无写权限,尝试修改内存保护属性\n");
}
// 暂停进程进行内存操作
if (pause_process(processInfo.pid) != 0) {
printf("暂停进程失败,退出Hook流程\n");
return;
}
// 读取原始值并验证
uint32_t original_val;
if (SafeReadDword(processInfo.pid, target_addr, &original_val) != 0) {
printf("读取原始值失败,恢复进程并退出\n");
resume_process(processInfo.pid);
return;
}
printf("原始值: %u (预期: %u)\n", original_val, ORIGINAL_VALUE);
if (original_val != ORIGINAL_VALUE) {
printf("警告:原始值(%u)与预期值(%u)不匹配,继续操作但结果可能异常\n",
original_val, ORIGINAL_VALUE);
}
// 写入新值
if (SafeWriteDword(processInfo.pid, target_addr, HOOK_VALUE) != 0) {
printf("修改失败(可能因内存保护或权限问题),恢复进程并退出\n");
resume_process(processInfo.pid);
return;
}
printf("成功写入新值: %u\n", HOOK_VALUE);
// 刷新指令缓存
if (enhanced_flush_cache(target_addr & ~(4095), 4096) != 0) {
printf("警告:缓存刷新失败,可能存在执行风险\n");
}
// 验证新值
uint32_t new_val;
if (SafeReadDword(processInfo.pid, target_addr, &new_val) == 0) {
printf("验证修改: 新值 = %u (预期: %u)\n", new_val, HOOK_VALUE);
if (new_val == HOOK_VALUE) {
printf("\033[32m验证成功:值已修改为预期值\033[0m\n");
} else {
printf("\033[31m验证失败:新值(%u)与预期值(%u)不匹配\033[0m\n", new_val, HOOK_VALUE);
}
} else {
printf("验证修改失败(无法读取新值)\n");
}
// 恢复进程运行
if (resume_process(processInfo.pid) != 0) {
printf("恢复进程失败\n");
return;
}
printf("进程已恢复运行\n");
// 等待进程稳定
sleep(2);
// 验证恢复后的值
uint32_t restored_val;
if (SafeReadDword(processInfo.pid, target_addr, &restored_val) == 0) {
printf("恢复后的值: %u (预期: %u)\n", restored_val, ORIGINAL_VALUE);
if (restored_val == ORIGINAL_VALUE) {
printf("\033[32m验证成功:值已恢复为原始值\033[0m\n");
} else {
printf("\033[31m验证失败:恢复后的值(%u)与原始值(%u)不匹配\033[0m\n",
restored_val, ORIGINAL_VALUE);
}
} else {
printf("验证恢复值失败(可能无权限或地址不可读)\n");
}
}
int main() {
if (getuid() != 0) {
printf("需要root权限运行!\n");
return 1;
}
hook_il2cpp();
return 0;
}获取到目标进程PID: 24442
libil2cpp.so 模块基址: 0x0000006f46366000
目标Hook地址: 0x0000006f4e263168 (基址 + 0x7efd168)
警告:目标地址无写权限,尝试修改内存保护属性
进程未正确停止: No child processes
暂停进程失败,退出Hook流程
[进程已结束 - 按回车关闭]修复好完整发给我 确认无任何报错后
最新发布