拿到vmaddr和ASLR之后,就可以正确计算出Section的内存地址,然后重新写入数据。不过这里为了演示简便,我们直接把解密之后的数据放在了壳程序的代码中,其实这样调试起来也更方便。
解密之后的数据:
unsignedint sectionData[] = {0xA0E1000E,0xE0800001,0xE12FFF30}; // Encrypt Decrypt ASM // 0xCB927360 0xA0E1000E mov r0, lr // 0x8BF3736F 0xE0800001 add r0, r0, r1 // 0x8A5C8C5E 0xE12FFF30 blx r0
计算正确的内存地址,更改段属性,写入解密数据,最后跳转执行。
vm_address_t addr = myseg->vmaddr + reinterpret_cast<unsigned long>(info.dli_fbase) - FILE_BASE_ADDR; #if __arm64__ || __x86_64__ printf("__[data deleted],__code at 0x%lX\n",addr); #else printf("__[data deleted],__code at 0x%X\n",addr); #endif kern_return_t err = 0; // 更改段属性为RWE err = vm_protect(mach_task_self_, addr, myseg->vmsize, 0, VM_PROT_ALL); if (err == KERN_SUCCESS) { printf("[vm_protect] KERN_SUCCESS![0]\n"); } else { printf("[vm_protect] KERN_ERROR![%d]\n",err); } // 写入数据 err = vm_write(mach_task_self_, addr, (vm_offset_t)§ionData, sizeof(sectionData)); if (err == KERN_SUCCESS) { printf("[vm_write] KERN_SUCCESS![0]\n"); } else { printf("[vm_write] KERN_ERROR![%d]\n",err); } // 跳转执行 (*((void (*)())(addr)))();
编译好了之后记得用MachOView改段属性的Max Protect:
执行结果:
成功了♪───O(≧∇≦)O────♪