Skip to content

Instantly share code, notes, and snippets.

@JJTech0130
Last active March 4, 2025 06:10
Show Gist options
  • Save JJTech0130/142aee0f7bda9c61a421140d17afbdeb to your computer and use it in GitHub Desktop.
Save JJTech0130/142aee0f7bda9c61a421140d17afbdeb to your computer and use it in GitHub Desktop.
Improved method of using a debugger for JIT on iOS... Uses split rx/rw regions, and works on iOS 18.4b1
#import <Foundation/Foundation.h>
#import <mach/mach.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#include <libkern/OSCacheControl.h>
const int REGION_SIZE = 0x4000*1;
void write_instructions(void* page)
{
uint32_t instructions[] = {
0x52800540, // mov w0, #42
0xD65F03C0 // ret
};
memcpy(page, instructions, sizeof(instructions));
}
void write_instructions2(void* page)
{
uint32_t instructions[] = {
0x52800C60, // mov w0, #99
0xD65F03C0 // ret
};
memcpy(page, instructions, sizeof(instructions));
}
int main(int argc, char* argv[])
{
void* page = mmap(
0, REGION_SIZE, PROT_READ | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0);
vm_address_t buf_rw = (vm_address_t)page;
vm_address_t buf_rx = 0;
vm_prot_t cur_prot, max_prot;
kern_return_t ret = vm_remap(mach_task_self(), &buf_rx, REGION_SIZE, 0,
VM_FLAGS_ANYWHERE, mach_task_self(), buf_rw, false, &cur_prot,
&max_prot, VM_INHERIT_NONE);
if (ret != KERN_SUCCESS) {
fprintf(stderr, "Failed to remap RX region %d\n", ret);
return 1;
}
// Protect region as RX
ret = vm_protect(mach_task_self(), buf_rx, REGION_SIZE, FALSE,
VM_PROT_READ | VM_PROT_EXECUTE);
if (ret != KERN_SUCCESS) {
fprintf(stderr, "Failed to set RX protection %d\n", ret);
return 1;
}
// Make executable region a debug map
// You just need to have the debugger write to any pages you want to JIT i.e. "mem w buf_rx 0"
// TIP: Here is a nice script you can insert into Xcode's breakpoint box
// script lldb.target.process.WriteMemory(int(lldb.frame.FindVariable("buf_rx").GetValue()), b'\x00' * int(lldb.frame.module.FindFirstGlobalVariable(lldb.target, "REGION_SIZE").GetValue()), lldb.SBError())
// Protect region as RW
ret = vm_protect(mach_task_self(), buf_rw, REGION_SIZE, FALSE,
VM_PROT_READ | VM_PROT_WRITE);
if (ret != KERN_SUCCESS) {
fprintf(stderr, "Failed to set RW protection %d\n", ret);
return 1;
}
// Write new instructions
write_instructions((void*)buf_rw);
int (*func)(void) = (int (*)(void))buf_rx;
int result = func();
printf("Executed JIT-ed function, result: %d\n", result); // Should print 42
// Write new instructions
write_instructions2((void*)buf_rw);
// This is important
sys_icache_invalidate((void*)buf_rx, REGION_SIZE);
int result2 = func();
printf("Executed JIT-ed function, result: %d\n", result2); // Should print 99
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment