msync(void *addr,size_t len, int flags) 函数的使用问题

本文详细介绍了mmap和msync系统调用的功能及使用方法。mmap用于创建内存映射,允许进程通过内存访问文件,而msync确保内存中的修改同步到磁盘。文章还强调了munmap的作用及其与msync的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

msync(void *addr,size_t len, int flags) 函数使用问题
【注意】:
           只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小。

mmap系统调用共享内存

1. 解除内存映射函数
munmap

功能:用来取消参数 【映射的内存地址】所指向的内存起始地址,成功返回0,失败返回-1

#include<unistd.h>
#include<sys/mman.h>
int munmap( void * start, size_t len )


start 是调用mmap()时返回的地址,len是映射区的大小。当进程结束或使
用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭相应的
文件描述符不会解除映射。当映射关系解除后,对原来映射地址的访问将
导致段错误发生。

2. 刷新变化函数
msync

功能:进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,

往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上

文件内容与共享内存区的内容一致。成功返回0,失败返回-1

#include<unistd.h>
#include<sys/mman.h>
int msync ( void * addr /*刷新的地址*/, size_t len/*大小*/, int flags) /标志和权限/


3. 在使用过程中应该注意的地方:

1). 最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小;

2). 可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小。

注:在linux中,内存的保护是以页为基本单位的,即使被映射文件只有一

个字节大小,内核也会为映射分配一个页面大小的内存。当被映射文件小

于一个页面大小时,进程可以对从mmap()返回地址开始的一个页面大小进

行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,

则导致错误发生,后面将进一步描述。因此,可用于进程间通信的有效地

址空间大小不会超过文件大小及一个页面大小的和。

3)、文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一

内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址

空间的操作只在内存中有意义,只有在调用了munmap()后或者msync()

时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的

大小。

#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流程 [进程已结束 - 按回车关闭]修复好完整发给我 确认无任何报错后
最新发布
07-16
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值