Files
coruna/src/bootstrap/memory.m
T
2026-03-12 21:09:59 +07:00

287 lines
10 KiB
Objective-C

/*
* memory.m - Cache, buffer allocation, and memory region management
*
* Decompiled from bootstrap.dylib offsets 0x5dc8-0x5fe8, 0x8298-0x842c
*/
#import "bootstrap.h"
#import <string.h>
#import <unistd.h>
#import <mach/mach.h>
#import <libkern/OSAtomic.h>
extern void sys_dcache_flush(void *addr, size_t size);
extern void sys_icache_invalidate(void *addr, size_t size);
extern int proc_pidinfo(int pid, int flavor, uint64_t arg,
void *buf, int bufsize);
/* ── flush_icache (0x5dc8) ───────────────────────────────────────── */
void flush_icache(bootstrap_ctx_t *ctx, void *addr, uint32_t size)
{
uint8_t *base;
uint32_t total;
if (addr && size) {
base = (uint8_t *)addr;
total = size;
} else {
total = ctx->exec_size;
if (total == 0) return;
base = ctx->exec_base;
}
print_log("[bootstrap] flush_icache: base=%p size=0x%x", base, total);
uint32_t offset = 0;
uint32_t remaining = total;
while (total > offset) {
uint32_t chunk = remaining;
if (chunk > 0x50000)
chunk = 0x50000;
sys_dcache_flush(base + offset, chunk);
sys_icache_invalidate(base + offset, chunk);
offset += 0x50000;
remaining = total - offset;
}
}
/* ── alloc_buffer (0x5e78) ───────────────────────────────────────── */
uint32_t alloc_buffer(bootstrap_ctx_t *ctx, uint32_t size)
{
print_log("[bootstrap] alloc_buffer: size=0x%x", size);
uint8_t *exec_base = ctx->exec_base;
uint32_t exec_size = ctx->exec_size;
uint32_t *state = ctx->atomic_state;
if (!state)
state = (uint32_t *)(exec_base + exec_size - 4);
for (;;) {
uint32_t current = __atomic_load_n(state, __ATOMIC_ACQUIRE);
uint32_t add = size;
if (current == 0)
add += 4;
uint32_t new_val = current + add;
if (new_val > exec_size) {
print_log("[bootstrap] alloc_buffer: FAIL size exceeds exec region (0x%x > 0x%x)", new_val, exec_size);
return 0xc003;
}
uint32_t expected = current;
if (__atomic_compare_exchange_n(state, &expected, new_val,
0, __ATOMIC_ACQ_REL,
__ATOMIC_ACQUIRE))
{
uint8_t *ptr = exec_base + exec_size - new_val;
ctx->buffer_ptr = ptr;
ctx->buffer_remaining = size;
bzero(ptr, (size_t)size);
if (ctx->fn_icache_flush)
ctx->fn_icache_flush(ctx, ctx->buffer_ptr, ctx->buffer_remaining);
print_log("[bootstrap] alloc_buffer: OK ptr=%p", ptr);
return 0;
}
}
}
/* ── consume_buffer (0x5f4c) ─────────────────────────────────────── */
void *consume_buffer(bootstrap_ctx_t *ctx, uint32_t size)
{
if (ctx->buffer_remaining < size)
return NULL;
uint8_t *old = ctx->buffer_ptr;
ctx->buffer_ptr = old + size;
ctx->buffer_remaining -= size;
return old;
}
/* ── secure_bzero (0x5fa0) ───────────────────────────────────────── */
uint32_t secure_bzero(bootstrap_ctx_t *ctx, void *ptr, uint32_t size)
{
if (ptr && size)
bzero(ptr, size);
return 0;
}
/* ── ensure_rwx_protection ───────────────────────────────────────── */
/* On modern iOS (14.5+), JIT regions may have current protection ---
* with max protection rwx. We need to vm_protect them to rwx before use.
*/
static uint32_t ensure_rwx_protection(uint64_t addr, uint64_t size,
uint32_t cur_prot, uint32_t max_prot)
{
if (cur_prot == 0x7) {
/* Already rwx */
return 0;
}
if (max_prot != 0x7) {
print_log("[bootstrap] ensure_rwx_protection: max_prot=0x%x (not rwx), cannot fix", max_prot);
return 0xc001;
}
print_log("[bootstrap] ensure_rwx_protection: promoting ---/rwx -> rwx/rwx at 0x%llx size=0x%llx",
(unsigned long long)addr, (unsigned long long)size);
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)addr,
(vm_size_t)size, 0,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
if (kr != KERN_SUCCESS) {
print_log("[bootstrap] ensure_rwx_protection: vm_protect FAIL kr=0x%x", kr);
return kr | 0x80000000;
}
return 0;
}
/* ── setup_memory (0x8298) ───────────────────────────────────────── */
uint32_t setup_memory(bootstrap_ctx_t *ctx)
{
print_log("[bootstrap] setup_memory: flag_a=%d flag_b=%d flag_direct_mem=%d", ctx->flag_a, ctx->flag_b, ctx->flag_direct_mem);
if (ctx->flag_a || ctx->flag_b || ctx->flag_direct_mem) {
ctx->fn_icache_flush = (fn_icache_flush_t)flush_icache;
if (!ctx->flag_b && !ctx->flag_direct_mem) {
ctx->buffer_remaining = 0;
return 0;
}
return 0;
}
uint32_t os_ver = ctx->os_version;
print_log("[bootstrap] setup_memory: os_ver=0x%x", os_ver);
if (os_ver >= 0x0F0000) {
/* iOS 15+: use proc_pidinfo (PROC_PIDREGIONINFO = 7) */
/* Returns struct proc_regioninfo (0x60 bytes):
* +0x00: pri_protection (uint32_t)
* +0x04: pri_max_protection (uint32_t)
* ...
* +0x50: pri_address (uint64_t)
* +0x58: pri_size (uint64_t)
*/
struct {
uint8_t data[0x60];
} info;
int ret = proc_pidinfo(getpid(), 7,
(uint64_t)ctx->buffer_ptr,
&info, 0x60);
if (ret <= 0) {
print_log("[bootstrap] setup_memory: proc_pidinfo FAIL ret=%d", ret);
return 0x80000005;
}
uint64_t region_addr = *(uint64_t *)((uint8_t *)&info + 0x50);
uint64_t region_size = *(uint64_t *)((uint8_t *)&info + 0x58);
uint32_t prot = *(uint32_t *)((uint8_t *)&info);
uint32_t max_prot = *(uint32_t *)((uint8_t *)&info + 4);
print_log("[bootstrap] setup_memory: region addr=0x%llx size=0x%llx prot=0x%x max_prot=0x%x",
(unsigned long long)region_addr, (unsigned long long)region_size, prot, max_prot);
if (!ctx->mmap_secondary && prot != 0x7) {
/* Current protection is not rwx — try to promote if max allows */
uint32_t fix_err = ensure_rwx_protection(region_addr, region_size, prot, max_prot);
if (fix_err) {
print_log("[bootstrap] setup_memory: cannot get rwx protection");
return fix_err;
}
}
if (!region_addr || !region_size)
return 0xc003;
uint64_t half = region_size >> 1;
if (half > region_size)
return 0xc003;
uint32_t *end = (uint32_t *)(region_addr + half);
end--;
if (half < *end)
return 0xc003;
ctx->atomic_state = end;
ctx->exec_base = (uint8_t *)(region_addr + half);
uint32_t usable;
if (ctx->flag_new_ios)
usable = (uint32_t)(half - 0x100000);
else
usable = (uint32_t)(region_size >> 2);
ctx->exec_size = usable;
ctx->fn_alloc_buffer = (fn_alloc_buffer_t)alloc_buffer;
print_log("[bootstrap] setup_memory: iOS15+ exec_base=%p exec_size=0x%x", ctx->exec_base, usable);
goto install_funcs;
}
/* Pre-iOS 15: use vm_region_64 */
{
mach_vm_address_t addr = (mach_vm_address_t)ctx->buffer_ptr;
mach_vm_size_t size_out = 0;
vm_region_flavor_t flavor = 9;
vm_region_basic_info_data_64_t info;
mach_msg_type_number_t count = 9;
mach_port_t object_name;
kern_return_t kr = vm_region_64(mach_task_self(),
(vm_address_t *)&addr,
(vm_size_t *)&size_out,
flavor,
(vm_region_info_t)&info,
&count,
&object_name);
if (kr != KERN_SUCCESS) {
print_log("[bootstrap] setup_memory: vm_region_64 FAIL kr=0x%x", kr);
return kr | 0x80000000;
}
uint32_t prot = info.protection;
uint32_t max_prot = info.max_protection;
print_log("[bootstrap] setup_memory: region addr=0x%llx size=0x%llx prot=0x%x max_prot=0x%x",
(unsigned long long)addr, (unsigned long long)size_out, prot, max_prot);
if (!ctx->mmap_secondary && prot != 0x7) {
/* Current protection is not rwx — try to promote if max allows */
uint32_t fix_err = ensure_rwx_protection(addr, size_out, prot, max_prot);
if (fix_err) {
print_log("[bootstrap] setup_memory: cannot get rwx protection");
return fix_err;
}
}
if (!addr || !size_out)
return 0xc003;
uint64_t half = size_out >> 1;
uint32_t *end = (uint32_t *)(addr + half);
end--;
if (half < *end)
return 0xc003;
ctx->atomic_state = end;
ctx->exec_base = (uint8_t *)(addr + half);
uint32_t usable;
if (ctx->flag_new_ios)
usable = (uint32_t)(half - 0x100000);
else
usable = (uint32_t)(size_out >> 2);
ctx->exec_size = usable;
ctx->fn_alloc_buffer = (fn_alloc_buffer_t)alloc_buffer;
print_log("[bootstrap] setup_memory: pre-15 exec_base=%p exec_size=0x%x", ctx->exec_base, usable);
}
install_funcs:
ctx->fn_icache_flush = (fn_icache_flush_t)flush_icache;
print_log("[bootstrap] setup_memory: OK");
return 0;
}