mach_vm_write()调用失败求助:mach_vm_protect()成功仍报KERN_INVALID_ADDRESS
解决Rosetta进程mach_vm_protect成功但mach_vm_write返回KERN_INVALID_ADDRESS的问题
可能的原因及对应解决方案
1. 内存保护操作未按页粒度执行
Mach的内存保护是按页粒度生效的,你当前只针对4字节范围调用mach_vm_protect,内核可能并未实际修改该地址所在页的权限。
修复代码:先计算目标地址所在页的起始地址,对整个页执行保护修改:
#include <mach/mach_vm.h> char bytes[] = {"\x03\x68\xF6\x3C\x03\x3C\x80\x3D\x02\x38\x80\x3D"}; vm_address_t nop_addr = find_bytes_in_rosetta(task, bytes, 12); char nop[] = {"\x1f\x20\x03\xd5"}; printf("%p\n", nop_addr); // 计算目标地址所在页的起始地址 vm_size_t page_size = vm_page_size; vm_address_t target_addr = nop_addr + 4; vm_address_t page_start = target_addr & ~(page_size - 1); // 对整个页设置可写权限并启用写时复制 kern_return_t prot_ret = mach_vm_protect(task, page_start, page_size, FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); printf("mach_vm_protect ret: %d\n", prot_ret); // 执行写入 kern_return_t write_ret = mach_vm_write(task, target_addr, (vm_address_t)nop, 4); printf("mach_vm_write ret: %d\n", write_ret);
2. 目标内存区域受特殊保护(Rosetta/SIP限制)
Rosetta作为Apple的转译层,其进程内存可能带有额外的内核级防护,即使mach_vm_protect返回成功,内核也可能阻止实际写入。此外,系统完整性保护(SIP)会限制对系统进程内存的修改。
排查步骤:
- 用
vm_region_recurse_64查看目标地址的内存属性,确认实际权限:
如果vm_address_t region_addr = target_addr; vm_size_t region_size; vm_region_submap_info_64 info; mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; kern_return_t region_ret = mach_vm_region_recurse_64(task, ®ion_addr, ®ion_size, &info, &info_count); if (region_ret == KERN_SUCCESS) { printf("Region protection: 0x%x\n", info.protection); printf("Max protection: 0x%x\n", info.max_protection); printf("Region flags: 0x%x\n", info.user_tag); }max_protection不包含VM_PROT_WRITE,说明该区域无法被修改。 - 临时关闭SIP(仅用于测试):重启Mac进入恢复模式,执行
csrutil disable,重启后再测试写入。测试完成后记得用csrutil enable重新开启SIP。
3. Task Port权限不足
确保你通过task_for_pid获取的目标进程task port拥有足够权限:
- 必须以root用户运行程序,或者拥有调试目标进程的权限(比如通过
lldb附加过目标进程)。
4. 尝试替代写入方式
如果直接mach_vm_write仍失败,可以尝试先在目标进程分配可写内存,写入数据后再复制到目标地址:
// 在目标进程分配内存 vm_address_t temp_addr; kern_return_t alloc_ret = mach_vm_allocate(task, &temp_addr, 4, VM_FLAGS_ANYWHERE); if (alloc_ret == KERN_SUCCESS) { // 写入临时内存 mach_vm_write(task, temp_addr, (vm_address_t)nop, 4); // 复制到目标地址 mach_vm_copy(task, temp_addr, 4, target_addr); // 释放临时内存 mach_vm_deallocate(task, temp_addr, 4); }
内容的提问来源于stack exchange,提问作者da_oldspice1
相关产品推荐
相关产品推荐

