I have written a test program to implement something similar to self-modifying code on Apple Silicon.
int main() {
uint8_t *instr;
uint32_t instr1 = 0x8b000000; // add x0, x0, x0
uint32_t instr2 = 0xd65f03c0; // ret
if(pthread_jit_write_protect_supported_np() == 1)
printf("jit write supportedn");
else
printf("jit write not supportedn");
pthread_jit_write_protect_np(1);
instr = (uint8_t*)mmap(NULL, 1024, PROT_READ|PROT_EXEC|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_JIT, 0, 0) + 4084;
pthread_jit_write_protect_np(0);
if(instr == MAP_FAILED){
perror("mmap");
exit(-1);
}
printf("instr addr : %lxn", (uintptr_t)instr);
memcpy(instr, &instr, 4);
memcpy(instr+4, &instr2, 4);
printf("instr1 is %xn", *(uint32_t *)instr);
printf("instr2 is %xn", *(uint32_t *)(instr+4));
asm volatile(
"eor x0, x0, x0n"
"eor x1, x1, x1n"
"eor x2, x2, x2n"
"eor x3, x3, x3n"
);
asm volatile(
"ldr x1, %[ptr]n"
"br x1n"
::[ptr]"m"(instr)
);
return 0;
}
I allocate a 4KB memory region with mmap, allowing read, write, and execute permissions. Then, I use memcpy to write the two assembly instructions to this memory region. After that, I initialize registers x1~x3 with inline assembly and branch the program counter (pc) to the previously allocated page. After branching, execute instructions instr1 and instr2 sequentially. However, when branching and accessing memory region, the program aborts with an EXC_BAD_ACCESS code=2 error.
Through Google searches, I’ve come to realize that the issue lies with Apple codesign. It seems that access is denied for code running in memory on Apple Silicon if it is not codesigned. So, I’ve been googling to find a way to allow access through codesign. However, I have been unable to find a way to codesign the memory allocated through mmap to allow access. Is there any way to resolve this issue?
purple_potato is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.