diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad0c814 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +arm64_runner +mach/ +dyld +*.a +*.dylib +rt_spawn +DobbyX +dobby.h diff --git a/CydiaSubstrate.tbd b/CydiaSubstrate.tbd new file mode 100644 index 0000000..e13f92c --- /dev/null +++ b/CydiaSubstrate.tbd @@ -0,0 +1,13 @@ +--- +archs: [ armv7, armv7s, arm64, arm64e, i386, x86_64 ] +platform: ios +install-name: /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate +current-version: 0.0.0 +compatibility-version: 0.0.0 +exports: + - archs: [ armv7, armv7s, arm64, arm64e, i386, x86_64 ] + symbols: [ _MSCloseImage, _MSDebug, _MSFindAddress, _MSFindSymbol, + _MSGetImageByName, _MSHookClassPair, _MSHookFunction, + _MSHookMemory, _MSHookMessageEx, _MSImageAddress, + _MSMapImage ] +... diff --git a/README.md b/README.md new file mode 100644 index 0000000..15ea90c --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# Reductant Translator +**Translate and patch arm64e binaries or macOS arm64 binaries to run on an arm64 iPhones at runtime.** + +As Macs move to ARM, there are many official command-line tools from Apple, Homebrew or others. But the instructions in these binaries are at least ARMv8.3+ since the first arm Mac was based on A12z. +You can run these utilities directly on arm64e iDevices, if there are dependent libraries on iOS. If not, it's this tool's turn: + +- If your iDevice is arm64, you can run them with this tool. +- If your binaries are linked with macOS frameworks, you can run them with this tool and a dyld_shared_caches_arm64e from macOS. + +You can replace **these binaries provided by me**: +- A patched dyld from macOS 11.4. +- A customized [Dobby](https://github.com/jmpews/Dobby) Framework. +- [mimalloc.a](https://github.com/microsoft/mimalloc) built for arm64 iOS. + +## Build options in `build.sh` + +- enable_dobby_hook Use a custom [Dobby Framework](https://github.com/jmpews/Dobby) to optimize a (CAS + B) sequence. This obviously improve performance. + +Requirements: Xcode 12 or later. + +## Environment variables + +- `RT_OUT_OF_PROCESS=[whatever]` Do not inject threads in target process. +- `RT_EMULATE_TIME=[whatever]` Do not hook mach_continuous_time. +- `RT_DYLD_SHARED_CACHE_DIR=[path to dyld_share_cache]` Specify the dyld_share_cache path. It defaults to "/System/macOSSupport/dyld" if exists. Otherwise use the system one. +- `RT_DYLD_INSERT_LIBRARIES=[libraries to insert]` Pass this env to dyld as `DYLD_INSERT_LIBRARIES`. Default is `/System/macOSSupport/usr/lib/rt_hooker.dylib` to support in-process translating and hooking, and other necessary patches. +- `RT_DYLD_ROOT_PATH=[libraries to insert]` Pass this env to dyld as `DYLD_ROOT_PATH`. +- `RT_ARM64_RUNNER_PATH=[path to a binary]` Creating a process with a arm64 binary. Then map the real binary arm64e default) into this process. +- `RT_FORCE_ARM64_RUNNER=[whatever]` Creating a process with a arm64 binary. Then force map the real binary into this process. (arm64e only runs in this mode.) +- `RT_DYLD_PATH=[dyld path]` Dyld path, default use a builtin dyld. +- `RT_DISABLE_DOBBY_HOOK=[whatever]` Disable dobby hook if the build option `enable_dobby_hook` is enabled. + +The detail behaviors can be easly gotten in codes. + +## Known issues + +- NSURLSession will get a `kCFURLErrorCannotFindHost` error with macOS 11 dyld_shared_cache on iOS 13. (iOS 14 is OK.) +- Metal is not available except A12z or M1 iPad due to missing binary for `/System/Library/Extensions/AGXMetalxxx` in dyld_shared_cache. (I have not tested these iPad, maybe Metal parallel computing works.) +- `fork()` and `spawn()` is not work properly which means you can not use bash/zsh or other utilities depended on them from macOS. (This can be fixed by hooking.) +- Some libproc-based (such as `proc_pidinfo(PROC_PIDPATHINFO)`) processes viewer will only treat `arm64_runner` as the process executable. This cannot be fixed because XNU uses the vnode of the executable when `spawn()`. +- Code signing issues are dependent on your jailbreak tools. +- A bad mach-o may crash this tool. + +## Example + +``` sh +mobile@iPhone-7-Plus ~ % ./rt_spawn ./geekbench_aarch64 +Geekbench 5.4.2 Corporate : https://www.geekbench.com/ + +System Information + Operating System macOS 14.8 (Build 18H17) + Model D11AP + Model ID D11AP + Motherboard D11AP + +Processor Information + Name Apple processor + Topology 1 Processor, 2 Cores + Identifier Apple processor + Base Frequency 2.33 GHz + L1 Instruction Cache 64.0 KB + L1 Data Cache 64.0 KB + L2 Cache 3.00 MB + +Memory Information + Size 2.93 GB + + +Single-Core + AES-XTS 1121 1.91 GB/sec + Text Compression 682 3.45 MB/sec + Image Compression 744 35.2 Mpixels/sec + Navigation 727 2.05 MTE/sec + HTML5 724 849.8 KElements/sec + SQLite 806 252.6 Krows/sec + PDF Rendering 112 6.07 Mpixels/sec + Text Rendering 824 262.7 KB/sec + Clang 673 5.24 Klines/sec + Camera 760 8.82 images/sec + N-Body Physics 413 517.1 Kpairs/sec + Rigid Body Physics 836 5181.6 FPS + Gaussian Blur 598 32.9 Mpixels/sec + Face Detection 896 6.90 images/sec + Horizon Detection 990 24.4 Mpixels/sec + Image Inpainting 1159 56.8 Mpixels/sec + HDR 1276 17.4 Mpixels/sec + Ray Tracing 934 750.1 Kpixels/sec + Structure from Motion 608 5.45 Kpixels/sec + Speech Recognition 545 17.4 Words/sec + Machine Learning 66 2.54 images/sec + +Multi-Core + AES-XTS 1963 3.35 GB/sec + Text Compression 1097 5.55 MB/sec + Image Compression 1371 64.8 Mpixels/sec + Navigation 1222 3.44 MTE/sec + HTML5 1299 1.53 MElements/sec + SQLite 1438 450.7 Krows/sec + PDF Rendering 177 9.60 Mpixels/sec + Text Rendering 1455 463.5 KB/sec + Clang 1103 8.59 Klines/sec + Camera 1340 15.5 images/sec + N-Body Physics 723 904.0 Kpairs/sec + Rigid Body Physics 1496 9268.8 FPS + Gaussian Blur 1058 58.2 Mpixels/sec + Face Detection 1623 12.5 images/sec + Horizon Detection 1771 43.6 Mpixels/sec + Image Inpainting 1852 90.9 Mpixels/sec + HDR 2336 31.8 Mpixels/sec + Ray Tracing 1721 1.38 Mpixels/sec + Structure from Motion 1082 9.69 Kpixels/sec + Speech Recognition 832 26.6 Words/sec + Machine Learning 118 4.55 images/sec + +Benchmark Summary + Single-Core Score 634 + Crypto Score 1121 + Integer Score 601 + Floating Point Score 623 + Multi-Core Score 1095 + Crypto Score 1963 + Integer Score 1030 + Floating Point Score 1091 + +Upload results to the Geekbench Browser? [Y/n] +``` + +## Credits +- [iOS-run-macOS-executables-tools](https://github.com/zhuowei/iOS-run-macOS-executables-tools) for the idea and some implementation details. +- [Dobby](https://github.com/jmpews/Dobby) for the idea of single instruction hook. diff --git a/arm64.h b/arm64.h new file mode 100644 index 0000000..5f72322 --- /dev/null +++ b/arm64.h @@ -0,0 +1,78 @@ +#ifndef __ARM64_h__ +#define __ARM64_h__ + +#define BIT_MASK(bit_num) ((1 << ((bit_num))) - 1) + +#define ARMv8_REG_MASK BIT_MASK(ARMv8_REG_BITS) +#define ARMv8_REG_BITS 5 +#define ARMv8_REG_LR 30 + +#define ARMv8_RET_mask 0xfffffc1f +#define ARMv8_RET_base 0xd65f0000 + +#define ARMv8_RET(...) _ARMv8_RET_IMPL(0, ##__VA_ARGS__, ARMv8_REG_LR) +#define _ARMv8_RET_IMPL(ph, reg, ...) (0xd65f0000 | ((reg) << ARMv8_REG_BITS)) +#define ARMv8_is_RET(instr) (((instr) & ARMv8_RET_mask) == ARMv8_RET_base) + +#define ARMv8_NOP_mask 0xffffffff +#define ARMv8_NOP_base 0xd503201f +#define ARMv8_NOP() (ARMv8_NOP_base) +#define ARMv8_is_NOP(instr) ((instr) == ARMv8_NOP()) + +#define ARMv8_BLR_mask 0xfffffc1f +#define ARMv8_BLR_base 0xd63f0000 +#define ARMv8_BLR(xs) (0xd63f0000 | ((xs) << ARMv8_REG_BITS)) +#define ARMv8_is_BLR(instr) (((instr) & ARMv8_BLR_mask) == ARMv8_BLR_base) + +#define ARMv8_BR_mask 0xfffffc1f +#define ARMv8_BR_base 0xd61f0000 +#define ARMv8_BR(xs) (0xd61f0000 | ((xs) << ARMv8_REG_BITS)) +#define ARMv8_is_BR(instr) (((instr) & ARMv8_BR_mask) == ARMv8_BR_base) + +#define ARMv8_LDRi64_preindex_mask 0xffe00c00 +#define ARMv8_LDRi64_preindex_base 0xf8400c00 +#define ARMv8_LDRi64_preindex(xs, xd, imm12) (ARMv8_LDRi64_preindex_base | (imm12 << 12) | (xs << ARMv8_REG_BITS) | (xd)) +#define ARMv8_is_LDRi64_preindex(instr) (((instr) & ARMv8_LDRi64_preindex_mask) == ARMv8_LDRi64_preindex_base) + +#define ARMv8_LDRiu64_mask 0xffc00000 +#define ARMv8_LDRiu64_base 0xf9400000 +#define ARMv8_LDRiu64(Xt, Xn_SP, imm12) (ARMv8_LDRiu64_base | ((imm12) << 10) | ((Xn_SP) << 5) | (Xt)) +#define ARMv8_is_LDRiu64(instr) (((instr) & ARMv8_LDRiu64_mask) == ARMv8_LDRiu64_base) + +#define ARMv8_LDUR64_mask 0xffe00c00 +#define ARMv8_LDUR64_base 0xf8400000 +#define ARMv8_LDUR64(Xt, Xn_SP, imm9) (ARMv8_LDUR64_base | ((imm9) << 12) | ((Xn_SP) << 5) | (Xt)) +#define ARMv8_is_LDUR64(instr) (((instr) & ARMv8_LDUR64_mask) == ARMv8_LDUR64_base) + +#define ARMv8_LDAR_mask 0x3ffffc00 +#define ARMv8_LDAR_base 0x8dffc00 +#define ARMv8_LDAR(size, Rn_SP, Rt) (ARMv8_LDAR_base | ((size) << 30) | ((Rn_SP) << 5) | (Rt)) +#define ARMv8_is_LDAR(instr) (((instr) & ARMv8_LDAR_mask) == ARMv8_LDAR_base) + +#define ARMv8_CMP_er_mask 0x7fe0001f +#define ARMv8_CMP_er_base 0x6b20001f +#define ARMv8_is_CMP_er(instr) (((instr) & ARMv8_CMP_er_mask) == ARMv8_CMP_er_base) +#define ARMv8_CMP_i_mask 0x7f80001f +#define ARMv8_CMP_i_base 0x7100001f +#define ARMv8_is_CMP_i(instr) (((instr) & ARMv8_CMP_i_mask) == ARMv8_CMP_i_base) +#define ARMv8_CMP_sr_mask 0x7f20001f +#define ARMv8_CMP_sr_base 0x6b00001f +#define ARMv8_is_CMP_sr(instr) (((instr) & ARMv8_CMP_sr_mask) == ARMv8_CMP_sr_base) + +#define ARMv8_is_CAS_family(instr) (((instr) & 0x3fa07c00) == 0x8a07c00) + +#define ARMv8_is_CASP_family(instr) (((instr) & 0xbfa07c00) == 0x8207c00) + +#define ARMv8_is_SWP_family(instr) (((instr) & 0x3f20fc00) == 0x38208000) + +#define ARMv8_is_ldadd_family(instr) (((instr) & 0x3f20fc00) == 0x38200000) + +#define ARMv8_is_LDSET_family(instr) (((instr) & 0x3f20fc00) == 0x38203000) + +#define ARMv8_is_LDCLR_family(instr) (((instr) & 0x3f20fc00) == 0x38201000) + +#define ARMv8_is_LDEOR_family(instr) (((instr) & 0x3f20fc00) == 0x38202000) + +#define ARMv8_is_LDAPR_family(instr) (((instr) & 0x3ffffc00) == 0x38bfc000) + +#endif //__ARM64_h__ \ No newline at end of file diff --git a/arm64_runner.s b/arm64_runner.s new file mode 100644 index 0000000..e69de29 diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..60ff703 --- /dev/null +++ b/build.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e + +rm -rf mach +mkdir mach +pushd mach +mig -arch arm64 -DXNU_KERNEL_PRIVATE $(xcrun -sdk macosx --show-sdk-path)/usr/include/mach/mach_exc.defs +mig -arch arm64 -DXNU_KERNEL_PRIVATE $(xcrun -sdk macosx --show-sdk-path)/usr/include/mach/task.defs +mig -arch arm64 -DXNU_KERNEL_PRIVATE $(xcrun -sdk macosx --show-sdk-path)/usr/include/mach/mach_port.defs +mig -arch arm64 -DXNU_KERNEL_PRIVATE $(xcrun -sdk macosx --show-sdk-path)/usr/include/mach/mach_vm.defs +mig -arch arm64 -DXNU_KERNEL_PRIVATE $(xcrun -sdk macosx --show-sdk-path)/usr/include/mach/thread_act.defs +mig -arch arm64 -DXNU_KERNEL_PRIVATE $(xcrun -sdk macosx --show-sdk-path)/usr/include/mach/vm_map.defs +popd + +clang -O3 -std=gnu11 -flto -target arm64-apple-ios7.0 -Wall \ + -isysroot "$(xcrun -sdk iphoneos --show-sdk-path)" \ + -o rt_spawn \ + littlespawn.c main.c dyldloader.c translator.c mach/mach_excServer.c -mcpu=apple-a7 -Wl,-sectcreate,__DATA,builtin_dyld,dyld +strip rt_spawn +ldid -Sent.xml rt_spawn +echo build hooker +hooker_sdk=iphoneos +if [ x$hooker_sdk == xiphoneos ] +then + libinject=CydiaSubstrate.tbd +else + libinject=libsubstitute.dylib +fi + +enable_dobby_hook=true +if [ x$enable_dobby_hook == xtrue ] +then + CFLAGS="libmimalloc.a DobbyX -lc++ -DENABLE_DOBBY_HOOK=1" +else + CFLAGS="" +fi + +clang -O3 -std=gnu11 -flto -march=armv8-a -target arm64-apple-ios9.0 -Wall \ + -isysroot "$(xcrun -sdk $hooker_sdk --show-sdk-path)" \ + -o rt_hooker.dylib \ + hooker.c translator.c mach/mach_excServer.c libSystem/syscall.s libSystem/syscall.c mach/mach_vmUser.c mach/mach_portUser.c libSystem/libc.c mach/vm_mapUser.c mach/taskUser.c mach/thread_actUser.c libSystem/mach_time_legacy.c -DIN_PROCESS=1 -shared $libinject -fno-stack-check -fno-stack-protector -D_FORTIFY_SOURCE=0 -mcpu=apple-a7 -framework IOKit $CFLAGS +ldid -S rt_hooker.dylib + +as arm64_runner.s -o arm64_runner.o -target arm64-apple-ios7.0 -isysroot "$(xcrun -sdk $hooker_sdk --show-sdk-path)" +ld -o arm64_runner arm64_runner.o -syslibroot "$(xcrun -sdk $hooker_sdk --show-sdk-path)" -e __mh_execute_header -lSystem -arch arm64 -platform_version ios 7.0.0 7.0.0 -dead_strip_dylibs +rm arm64_runner.o +strip arm64_runner +ldid -S arm64_runner \ No newline at end of file diff --git a/dyldloader.c b/dyldloader.c new file mode 100644 index 0000000..ec9bd4e --- /dev/null +++ b/dyldloader.c @@ -0,0 +1,544 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "reductant.h" + +static void +set_all_images_offset_maybe(struct segment_command_64* seg_cmd, + size_t* all_images_offset) { + struct section_64* sections = (void*)seg_cmd + sizeof(*seg_cmd); + for (unsigned int index = 0; index < seg_cmd->nsects; index++) { + struct section_64* section = §ions[index]; + if (!strncmp(section->sectname, "__all_image_info", + sizeof(section->sectname))) { + if (all_images_offset != NULL) { + *all_images_offset = section->addr; + } + } + } +} + +static size_t +get_execute_address_space_size(void* executable_region, + size_t* all_images_offset, + size_t* sigcmd_dataoff, + size_t* sigcmd_datasize) { + struct mach_header_64* mh = executable_region; + struct load_command* cmd = executable_region + sizeof(struct mach_header_64); + if (all_images_offset != NULL) { + *all_images_offset = 0; + } + + uint64_t min_addr = ~0; + uint64_t max_addr = 0; + for (unsigned int index = 0; index < mh->ncmds; index++) { + switch (cmd->cmd) { + case LC_SEGMENT_64: { + struct segment_command_64* seg_cmd = (struct segment_command_64*)cmd; + if (!strncmp(seg_cmd->segname, "__PAGEZERO", sizeof(seg_cmd->segname))) { + break; + } + min_addr = MIN(min_addr, seg_cmd->vmaddr); + max_addr = MAX(max_addr, seg_cmd->vmaddr + seg_cmd->vmsize); + if (all_images_offset != NULL && !strncmp(seg_cmd->segname, "__DATA", sizeof(seg_cmd->segname))) { + set_all_images_offset_maybe(seg_cmd, all_images_offset); + } + break; + } + case LC_CODE_SIGNATURE: { + struct linkedit_data_command* signature_cmd = + (struct linkedit_data_command*)cmd; + *sigcmd_dataoff = signature_cmd->dataoff; + *sigcmd_datasize = signature_cmd->datasize; + } + } + cmd = (struct load_command*)((char*)cmd + cmd->cmdsize); + } + return max_addr - min_addr; +} + +#ifdef __arm64__ +static const cpu_type_t kPreferredCpuType = CPU_TYPE_ARM64; +#else +static const cpu_type_t kPreferredCpuType = CPU_TYPE_X86_64; +#endif + +static off_t +get_fat_offset(void* fat_region) { + struct fat_header* fat_header = fat_region; + if (fat_header->magic != FAT_CIGAM) { + //fprintf(stderr, "Not a FAT executable. Assume raw.\n"); + return 0; + } + struct fat_arch* fat_arches = fat_region + sizeof(struct fat_header); + for (unsigned int index = 0; index < ntohl(fat_header->nfat_arch); index++) { + struct fat_arch* fat_arch = &fat_arches[index]; + if (ntohl(fat_arch->cputype) == kPreferredCpuType) { + return ntohl(fat_arch->offset); + } + } + fprintf(stderr, "No preferred slice\n"); + return -1; +} + +static int +remap_into_process(task_t target_task, void* executable_region, + vm_address_t target_base, int executable_fd, + off_t fat_offset) { + struct mach_header_64* mh = executable_region; + struct load_command* cmd = executable_region + sizeof(struct mach_header_64); + kern_return_t err; + + for (unsigned int index = 0; index < mh->ncmds; index++) { + switch (cmd->cmd) { + case LC_SEGMENT_64: { + struct segment_command_64* seg_cmd = (struct segment_command_64*)cmd; + vm_address_t source_address = + (vm_address_t)(executable_region + seg_cmd->fileoff); + + if (!strncmp(seg_cmd->segname, "__PAGEZERO", sizeof(seg_cmd->segname))) { + target_base -= seg_cmd->vmsize; + goto next_loop; + } + if (seg_cmd->filesize == 0) { + goto next_loop; + } + if (executable_fd >= 0) { + void* remap = mmap(NULL, seg_cmd->filesize, seg_cmd->initprot, MAP_PRIVATE, executable_fd, fat_offset + seg_cmd->fileoff); + if (remap == MAP_FAILED) { + fprintf(stderr, "remap failed: %s\n", strerror(errno)); + return 1; + } + source_address = (vm_address_t)remap; + if (seg_cmd->fileoff == 0) { + struct mach_header_64 *header = remap; + err = vm_protect(mach_task_self(), source_address, seg_cmd->filesize, true, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + if (err != KERN_SUCCESS) { + fprintf(stderr, "%dCan't protect into process: %s addr: %lx initprot %d size %llx\n", + __LINE__, mach_error_string(err), + source_address, seg_cmd->initprot, seg_cmd->vmsize); + return 1; + } + err = vm_protect(mach_task_self(), source_address, seg_cmd->filesize, false, VM_PROT_READ | VM_PROT_WRITE); + if (err != KERN_SUCCESS) { + fprintf(stderr, "%dCan't protect into process: %s addr: %lx initprot %d size %llx\n", + __LINE__, mach_error_string(err), + source_address, seg_cmd->initprot, seg_cmd->vmsize); + return 1; + } + if (header->magic == MH_MAGIC_64 && header->cputype == CPU_TYPE_ARM64 && (header->cpusubtype & CPU_SUBTYPE_ARM64E) == CPU_SUBTYPE_ARM64E && header->filetype == MH_EXECUTE) { + header->cpusubtype = 0; + } + } + } else { + vm_address_t addr = 0; + kern_return_t kr = vm_allocate(mach_task_self(), &addr, seg_cmd->filesize, VM_FLAGS_ANYWHERE); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "remap_into_process: vm_allocate %s\n", mach_error_string(kr)); + return 1; + } + memcpy((void*)addr, executable_region + seg_cmd->fileoff, seg_cmd->filesize); + source_address = addr; + } + vm_address_t target_address = target_base + seg_cmd->vmaddr; + vm_prot_t cur_protection; + vm_prot_t max_protection; + if (seg_cmd->filesize) { + err = vm_remap(target_task, &target_address, seg_cmd->filesize, + /*mask=*/0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + mach_task_self(), source_address, + /*copy=*/false, &cur_protection, &max_protection, + VM_INHERIT_COPY); + if (err) { + fprintf(stderr, "Can't map into process: %s\n", mach_error_string(err)); + return 1; + } + } + err = vm_protect(target_task, target_address, seg_cmd->vmsize, false, seg_cmd->initprot); + if (err) { + fprintf(stderr, "Can't protect into process: %s addr: %lx initprot %d size %llx\n", + mach_error_string(err), + target_address, seg_cmd->initprot, seg_cmd->vmsize); + return 1; + } + if (executable_fd >= 0) { + munmap((void*)source_address, seg_cmd->filesize); + } else { + vm_deallocate(mach_task_self(), source_address, seg_cmd->filesize); + } + break; + } + // TODO(zhuowei): handle unixthread (we currently assume dylds have the + // same unixthread) + } +next_loop: + cmd = (struct load_command*)((char*)cmd + cmd->cmdsize); + } + return 0; +} + +static thread_t +task_first_thread(task_t target_task) { + kern_return_t err; + thread_act_t* thread_array; + mach_msg_type_number_t num_threads; + err = task_threads(target_task, &thread_array, &num_threads); + if (err) { + fprintf(stderr, "Failed to get threads: %s\n", mach_error_string(err)); + return MACH_PORT_NULL; + } + return thread_array[0]; +} + +static vm_address_t +set_entry_point(task_t target_task, vm_address_t entry_point) { + kern_return_t err; + vm_address_t ret = 0; + thread_t main_thread = task_first_thread(target_task); + if (main_thread == MACH_PORT_NULL) { + return ret; + } +#ifdef __x86_64__ + x86_thread_state64_t thread_state; + mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; + err = thread_get_state(main_thread, x86_THREAD_STATE64, + (thread_state_t)&thread_state, &thread_state_count); + if (err) { + fprintf(stderr, "Failed to get thread state: %s\n", mach_error_string(err)); + goto end; + } + thread_state.__rip = entry_point; + err = thread_set_state(main_thread, x86_THREAD_STATE64, + (thread_state_t)&thread_state, thread_state_count); + if (err) { + fprintf(stderr, "Failed to set thread state: %s\n", mach_error_string(err)); + goto end; + } + ret = thread_state.__rsp; +#elif __arm64__ + arm_thread_state64_t thread_state; + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; + err = thread_get_state(main_thread, ARM_THREAD_STATE64, + (thread_state_t)&thread_state, &thread_state_count); + if (err) { + fprintf(stderr, "Failed to get thread state: %s\n", mach_error_string(err)); + goto end; + } + thread_state.__pc = entry_point; + err = thread_set_state(main_thread, ARM_THREAD_STATE64, + (thread_state_t)&thread_state, thread_state_count); + if (err) { + fprintf(stderr, "Failed to set thread state: %s\n", mach_error_string(err)); + goto end; + } + ret = thread_state.__sp; +#endif +end: + if (main_thread != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), main_thread); + } + return ret; +} + +static vm_address_t +dyld_all_images_address_ptr(task_t target_task) { + kern_return_t err; + // https://opensource.apple.com/source/dyld/dyld-195.6/unit-tests/test-cases/all_image_infos-cache-slide/main.c + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + err = task_info(target_task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count); + if (err) { + fprintf(stderr, "Failed to get task info: %s\n", mach_error_string(err)); + return 0; + } + return task_dyld_info.all_image_info_addr; +} + +static int +prepare_dyld_map(const char *dyld_path, void **map, int *fd, off_t *dyld_fat_offset, size_t *dyld_map_size) { + void *dyld_map = NULL; + struct stat dyld_stat; + int dyld_fd = -1; + kern_return_t err = 0; + if (dyld_path != NULL) { + dyld_fd = open(dyld_path, O_RDONLY); + if (FD_INVALID(dyld_fd)) { + fprintf(stderr, "Can't open %s: %s", dyld_path, strerror(errno)); + err = 1; + goto err; + } + if (fstat(dyld_fd, &dyld_stat)) { + fprintf(stderr, "Can't stat %s: %s", dyld_path, strerror(errno)); + err = 1; + goto err; + } + dyld_map = mmap(NULL, dyld_stat.st_size, PROT_READ, MAP_PRIVATE, dyld_fd, 0); + if (dyld_map == MAP_FAILED) { + fprintf(stderr, "Can't map %s: %s", dyld_path, strerror(errno)); + err = 1; + goto err; + } + } else { + size_t size; + dyld_map = getsectiondata(&_mh_execute_header, "__DATA", "builtin_dyld", &size); + } + off_t fat_offset = get_fat_offset(dyld_map); + if (fat_offset == -1) { + err = 1; + goto err; + } + *map = dyld_map; + *fd = dyld_fd; + *dyld_fat_offset = fat_offset; + *dyld_map_size = dyld_stat.st_size; + return 0; +err: + if (FD_VALID(dyld_fd)) { + if (dyld_map != MAP_FAILED) { + munmap(dyld_map, dyld_stat.st_size); + } + close(dyld_fd); + } + return err; +} + +#define EXECUTABLE_KEY "executable_path" +#define EXECUTABLE_KEY_LEN (sizeof(EXECUTABLE_KEY) - 1) + +static int +set_executable_path(task_t target_task, vm_address_t stack_ptr, const char *argv0) { + int target_argc; + vm_address_t target_argc_ptr = stack_ptr + 8; + mach_vm_size_t sz; + mach_vm_read_overwrite(target_task, target_argc_ptr, sizeof(target_argc), (vm_address_t)&target_argc, &sz); + vm_address_t argv = stack_ptr + 16; + vm_address_t envp = argv + (target_argc + 1) * sizeof(uintptr_t); + vm_address_t apple_ptr = envp; + + uintptr_t apple = 0; + mach_vm_read_overwrite(target_task, apple_ptr, sizeof(apple), (vm_address_t)&apple, &sz); + while (apple != 0) { + apple_ptr += 8; + mach_vm_read_overwrite(target_task, apple_ptr, sizeof(apple), (vm_address_t)&apple, &sz); + } + apple_ptr += 8; + + mach_vm_read_overwrite(target_task, apple_ptr, sizeof(apple), (vm_address_t)&apple, &sz); + + for (vm_address_t p = apple_ptr; p != 0 && apple != 0; ) { + const char buffer[MAXPATHLEN + EXECUTABLE_KEY_LEN]; + mach_vm_read_overwrite(target_task, apple, sizeof(buffer), (vm_address_t)buffer, &sz); + + size_t p_len = strlen(buffer); + if (p_len >= EXECUTABLE_KEY_LEN && (memcmp(buffer, EXECUTABLE_KEY, EXECUTABLE_KEY_LEN) == 0) && buffer[EXECUTABLE_KEY_LEN] == '=') { + kern_return_t kr = mach_vm_write(target_task, apple + EXECUTABLE_KEY_LEN + 1, (vm_offset_t)argv0, strlen(argv0) + 1); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "overwriting ececutable_path failed: %s\n", mach_error_string(kr)); + } + return 0; + } + p += 8; + mach_vm_read_overwrite(target_task, p, sizeof(apple), (vm_address_t)&apple, &sz); + } + + return 0; +} + +int map_dyld_and_main_executable(int target_pid, const char *dyld_path, const char *argv0, int fd, void *exe_map, size_t map_size) { + task_t target_task = MACH_PORT_NULL; + kern_return_t err = 0; + int dyld_fd = -1; + err = task_for_pid(mach_task_self(), target_pid, &target_task); + if (err) { + fprintf(stderr, "Failed to get task port: %s\n", mach_error_string(err)); + err = 1; + goto err; + } + void *dyld_map = MAP_FAILED; + size_t dyld_size = 0; + off_t fat_offset = 0; + if (prepare_dyld_map(dyld_path, &dyld_map, &dyld_fd, &fat_offset, &dyld_size) != 0) { + err = 0; + goto err; + } + void* dyld_executable_map = dyld_map + fat_offset; + + size_t new_dyld_all_images_offset = 0; + size_t sigcmd_dataoff = 0; + size_t sigcmd_datasize = 0; + size_t address_space_size = get_execute_address_space_size(dyld_executable_map, &new_dyld_all_images_offset, &sigcmd_dataoff, &sigcmd_datasize); + if (!new_dyld_all_images_offset) { + fprintf(stderr, "can't find all images\n"); + err = 1; + goto err; + } + + if (dyld_fd >= 0) { + fsignatures_t siginfo; + siginfo.fs_file_start = fat_offset; // start of mach-o slice in fat file + siginfo.fs_blob_start = + (void*)(sigcmd_dataoff); // start of CD in mach-o file + siginfo.fs_blob_size = sigcmd_datasize; // size of CD + if (fcntl(dyld_fd, F_ADDFILESIGS_RETURN, &siginfo) == -1) { + fprintf(stderr, "can't add signatures: %s\n", strerror(errno)); + err = 1; + goto err; + } + } + + vm_address_t dyld_target_address = dyld_all_images_address_ptr(target_task) - new_dyld_all_images_offset; + if (!dyld_target_address) { + err = 1; + goto err; + } + //fprintf(stderr, "mapping dyld at %p\n", (void*)dyld_target_address); + err = vm_allocate(target_task, &dyld_target_address, address_space_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); + if (err) { + fprintf(stderr, "Can't allocate into process: %s\n", + mach_error_string(err)); + err = 1; + goto err; + } + if (remap_into_process(target_task, dyld_map + fat_offset, dyld_target_address, dyld_fd, fat_offset)) { + err = 1; + goto err; + } + vm_address_t stack_ptr = set_entry_point(target_task, dyld_target_address + 0x1000); + if (stack_ptr == 0) { + fprintf(stderr, "failed to get main execute address\n"); + err = 1; + goto err; + } + vm_address_t target_main_execute; + + size_t exe_sigcmd_dataoff = 0; + size_t exe_sigcmd_datasize = 0; + off_t main_offset = get_fat_offset(exe_map); + if (main_offset == -1) { + err = 1; + goto err; + } + size_t exe_space_size = get_execute_address_space_size(exe_map + main_offset, NULL, &exe_sigcmd_dataoff, &exe_sigcmd_datasize); + + err = vm_allocate(target_task, &target_main_execute, exe_space_size, VM_FLAGS_ANYWHERE); + if (err) { + fprintf(stderr, "Can't allocate main execute into process: %s\n", + mach_error_string(err)); + err = 1; + goto err; + } + //fprintf(stderr, "main execute address: %lx size: %lx\n", target_main_execute, exe_space_size); + uintptr_t tmp = (uintptr_t)target_main_execute; + set_executable_path(target_task, stack_ptr, argv0); + mach_vm_write(target_task, stack_ptr, (vm_offset_t)&tmp, sizeof(tmp)); + if (remap_into_process(target_task, exe_map + main_offset, target_main_execute, fd, main_offset)) { + err = 1; + goto err; + } +err: + if (target_task != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), target_task); + } + if (dyld_fd >= 0) { + if (dyld_map != NULL) { + munmap(dyld_map, dyld_size); + } + close(dyld_fd); + } + return err; +} + +int map_dyld(int target_pid, const char* dyld_path) { + task_t target_task = MACH_PORT_NULL; + kern_return_t err = 0; + int dyld_fd = -1; + + err = task_for_pid(mach_task_self(), target_pid, &target_task); + if (err || target_task == MACH_PORT_NULL) { + fprintf(stderr, "Failed to get task port: %s\n", mach_error_string(err)); + err = 1; + goto err; + } + void *dyld_map = MAP_FAILED; + size_t dyld_size = 0; + off_t fat_offset = 0; + if (prepare_dyld_map(dyld_path, &dyld_map, &dyld_fd, &fat_offset, &dyld_size) != 0) { + err = 0; + goto err; + } + + void* executable_map = dyld_map + fat_offset; + size_t new_dyld_all_images_offset = 0; + size_t sigcmd_dataoff = 0; + size_t sigcmd_datasize = 0; + size_t address_space_size = get_execute_address_space_size(executable_map, &new_dyld_all_images_offset, &sigcmd_dataoff, &sigcmd_datasize); + if (!new_dyld_all_images_offset) { + fprintf(stderr, "can't find all images\n"); + err = 1; + goto err; + } + // ImageLoaderMachO::loadCodeSignature, Loader::mapImage + if (dyld_fd >= 0) { + fsignatures_t siginfo; + siginfo.fs_file_start = fat_offset; // start of mach-o slice in fat file + siginfo.fs_blob_start = + (void*)(sigcmd_dataoff); // start of CD in mach-o file + siginfo.fs_blob_size = sigcmd_datasize; // size of CD + if (fcntl(dyld_fd, F_ADDFILESIGS_RETURN, &siginfo) == -1) { + fprintf(stderr, "can't add signatures: %s\n", strerror(errno)); + err = 1; + goto err; + } + } + // TODO(zhuowei): this _only_ works if ASLR is enabled + // (since we try to align the image infos of the new dyld on top of the old, + // and that would overwrite the executable if ASLR is off and dyld is right + // behind the executable) at least detect if we would overwrite an existing + // mapping... + vm_address_t target_address = dyld_all_images_address_ptr(target_task) - new_dyld_all_images_offset; + if (!target_address) { + err = 1; + goto err; + } + //fprintf(stderr, "mapping dyld at %p\n", (void*)target_address); + err = vm_allocate(target_task, &target_address, address_space_size, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); + if (err) { + fprintf(stderr, "Can't allocate into process: %s\n", + mach_error_string(err)); + err = 1; + goto err; + } + if (remap_into_process(target_task, executable_map + fat_offset, target_address, dyld_fd, fat_offset)) { + err = 1; + goto err; + } + // TODO(zhuowei): grab entry point from unixthread + if (!set_entry_point(target_task, target_address + 0x1000)) { + err = 1; + goto err; + } +err: + if (target_task != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), target_task); + } + if (dyld_fd >= 0) { + if (dyld_map != NULL) { + munmap(dyld_map, dyld_size); + } + close(dyld_fd); + } + return err; +} \ No newline at end of file diff --git a/ent.xml b/ent.xml new file mode 100644 index 0000000..7132136 --- /dev/null +++ b/ent.xml @@ -0,0 +1,21 @@ + + + + +platform-application + +get-task-allow + +com.apple.system-task-ports + +task_for_pid-allow + +com.apple.private.security.no-container + +com.apple.private.security.no-sandbox + +com.apple.private.skip-library-validation + + + diff --git a/hooker.c b/hooker.c new file mode 100644 index 0000000..e6d3bb3 --- /dev/null +++ b/hooker.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef const void *MSImageRef; + +MSImageRef MSGetImageByName(const char *file); +void *MSFindSymbol(MSImageRef image, const char *name); + +void MSHookFunction(void *symbol, void *replace, void **result); + +int sysctlbyname(const char *, void *, size_t *, void *, size_t); + +static int (*orig_sysctlbyname)(const char *, void *, size_t *, void *, size_t); + +int translator(task_t t); +extern void translator_loop(mach_port_t port); + +extern void *translator_subthread(void *arg); + +static int +new_sysctlbyname(const char *name, void *oldp, size_t *oldenp, void *newp, size_t newlen) { + if (name != NULL && oldp != NULL && (strcmp(name, "kern.osvariant_status") == 0)) { + *(unsigned long long *)oldp = 0x70010000f388828a; + return 0; + } + return orig_sysctlbyname(name, oldp, oldenp, newp, newlen); +} + +void *IOServiceMatching(const char *name); +static void *(*orig_IOServiceMatching)(const char *name); +static void *new_IOServiceMatching(const char *name) { + if (strcmp("IOSurfaceRoot", name) == 0) { + static const char tmp[] = "IOCoreSurfaceRoot"; + return orig_IOServiceMatching(tmp); + } else if (strcmp("IOAccelerator", name) == 0) { + static const char tmp[] = "IOAcceleratorES"; + return orig_IOServiceMatching(tmp); + } + return orig_IOServiceMatching(name); +} + +void *IOServiceNameMatching(const char *name); +static void *(*orig_IOServiceNameMatching)(const char *name); +static void *new_IOServiceNameMatching(const char *name) { + if (strcmp("IOSurfaceRoot", name) == 0) { + static const char tmp[] = "IOCoreSurfaceRoot"; + return orig_IOServiceNameMatching(tmp); + } else if (strcmp("IOAccelerator", name) == 0) { + static const char tmp[] = "IOAcceleratorES"; + return orig_IOServiceNameMatching(tmp); + } + return orig_IOServiceNameMatching(name); +} + +extern mach_port_t mach_thread_self(void); + +static void __attribute__((constructor)) +$ctor(void) { + MSHookFunction(sysctlbyname, new_sysctlbyname, (void**)&orig_sysctlbyname); + MSHookFunction(IOServiceMatching, new_IOServiceMatching, (void**)&orig_IOServiceMatching); + MSHookFunction(IOServiceNameMatching, new_IOServiceNameMatching, (void**)&orig_IOServiceNameMatching); + // iOS 14 and above use modern mach_continuous_time. + // If this binary is linked with macOS dynamic libraries, the version checked here is macOS 14. + if (__builtin_available(macOS 14, iOS 14, *)) { + } else { + if (!getenv("RT_EMULATE_TIME")) { + uint64_t mach_continuous_time_iOS13(void); + MSHookFunction(mach_continuous_time, mach_continuous_time_iOS13, NULL); + } + } + if (!getenv("RT_OUT_OF_PROCESS")) { + pthread_t pth; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, 1); + pthread_create(&pth, &attr, (void*)translator_subthread, NULL); + } +} \ No newline at end of file diff --git a/libSystem/libc.c b/libSystem/libc.c new file mode 100644 index 0000000..cc14969 --- /dev/null +++ b/libSystem/libc.c @@ -0,0 +1,89 @@ +#include +#include + +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +void * memcpy(void *dst0, const void *src0, size_t length) { + char *dst = dst0; + const char *src = src0; + size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (uintptr_t)src; /* only need low bits */ + if ((t | (uintptr_t)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (uintptr_t)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (uintptr_t)src; + if ((t | (uintptr_t)dst) & wmask) { + if ((t ^ (uintptr_t)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: + return (dst0); +} + + + +int strcmp(const char *X, const char *Y) { + while (*X) { + // if characters differ, or end of the second string is reached + if (*X != *Y) { + break; + } + + // move to the next pair of characters + X++; + Y++; + } + + // return the ASCII difference after converting `char*` to `unsigned char*` + return *(const unsigned char*)X - *(const unsigned char*)Y; +} \ No newline at end of file diff --git a/libSystem/mach_time_legacy.c b/libSystem/mach_time_legacy.c new file mode 100644 index 0000000..f5f9b8a --- /dev/null +++ b/libSystem/mach_time_legacy.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#define USER_TIMEBASE_NONE 0 +#define USER_TIMEBASE_SPEC 1 +#define USER_TIMEBASE_NOSPEC 2 + +#define _COMM_PAGE64_BASE_ADDRESS (0x0000000FFFFFC000ULL) /* In TTBR0 */ +#define _COMM_PAGE_START_ADDRESS (_COMM_PAGE64_BASE_ADDRESS) +#define _COMM_PAGE_CONT_HW_TIMEBASE (_COMM_PAGE_START_ADDRESS+0x0A8) // uint64_t base for mach_continuous_time() relative to CNT[PV]CT +#define _COMM_PAGE_CONT_TIMEBASE (_COMM_PAGE_START_ADDRESS+0x098) // uint64_t base for mach_continuous_time() relative to mach_absolute_time() +#define _COMM_PAGE_CONT_HWCLOCK (_COMM_PAGE_START_ADDRESS+0x091) // uint8_t is always-on hardware clock present for mach_continuous_time() +#define _COMM_PAGE_USER_TIMEBASE (_COMM_PAGE_START_ADDRESS+0x090) // uint8_t is userspace mach_absolute_time supported (can read the timebase) + +uint64_t +_mach_continuous_time_base(void) +{ +#if !defined(__x86_64__) && !defined(__arm64__) + // Deal with the lack of 64-bit loads on arm32 (see mach_approximate_time.s) + while (1) { + volatile uint64_t *base_ptr = (volatile uint64_t*)_COMM_PAGE_CONT_TIMEBASE; + uint64_t read1, read2; + read1 = *base_ptr; +#if defined(__arm__) + __asm__ volatile ("dsb sy" ::: "memory"); +#elif defined(__i386__) + __asm__ volatile ("lfence" ::: "memory"); +#else +#error "unsupported arch" +#endif + read2 = *base_ptr; + + if (__builtin_expect((read1 == read2), 1)) { + return read1; + } + } +#else // 64-bit + return *(volatile uint64_t*)_COMM_PAGE_CONT_TIMEBASE; +#endif // 64-bit +} + +#if 0 +// CNTVCT_EL0 is enabled in iOS 14 above and macOS 11 above. +#define CNTVCTSS_EL0 "S3_3_c14_c0_6" + +__attribute__((visibility("hidden"))) +kern_return_t +_mach_continuous_hwclock(uint64_t *cont_time __unused) +{ +#if defined(__arm64__) +#define ISB_SY 0xf + uint8_t cont_hwclock = *((uint8_t*)_COMM_PAGE_CONT_HWCLOCK); + if (cont_hwclock) { + volatile uint64_t *base_ptr = (volatile uint64_t*)_COMM_PAGE_CONT_HW_TIMEBASE; + + boolean_t has_cntvctss_el0 = *((uint8_t*)_COMM_PAGE_USER_TIMEBASE) == USER_TIMEBASE_NOSPEC; + if (has_cntvctss_el0) { + *cont_time = __builtin_arm_rsr64(CNTVCTSS_EL0) + *base_ptr; + return KERN_SUCCESS; + } + + + __builtin_arm_isb(ISB_SY); + *cont_time = __builtin_arm_rsr64("CNTVCT_EL0") + *base_ptr; + return KERN_SUCCESS; + } +#endif + return KERN_NOT_SUPPORTED; +} +#else +__attribute__((visibility("hidden"))) +kern_return_t +_mach_continuous_hwclock(uint64_t *cont_time __unused) +{ +#if defined(__arm64__) +#define ISB_SY 0xf + uint8_t cont_hwclock = *((uint8_t*)_COMM_PAGE_CONT_HWCLOCK); + if (cont_hwclock) { + __builtin_arm_isb(ISB_SY); + *cont_time = __builtin_arm_rsr64("CNTPCT_EL0"); + return KERN_SUCCESS; + } +#endif + return KERN_NOT_SUPPORTED; +} +#endif + +__attribute__((visibility("hidden"))) +kern_return_t +_mach_continuous_time(uint64_t* absolute_time, uint64_t* cont_time) +{ + volatile uint64_t *base_ptr = (volatile uint64_t*)_COMM_PAGE_CONT_TIMEBASE; + volatile uint64_t read1, read2; + volatile uint64_t absolute; + + do { + read1 = *base_ptr; + absolute = mach_absolute_time(); +#if defined(__arm__) || defined(__arm64__) + /* + * mach_absolute_time() contains an instruction barrier which will + * prevent the speculation of read2 above this point, so we don't + * need another barrier here. + */ +#endif + read2 = *base_ptr; + } while (__builtin_expect((read1 != read2), 0)); + + if (absolute_time) { + *absolute_time = absolute; + } + if (cont_time) { + *cont_time = absolute + read1; + } + + return KERN_SUCCESS; +} + +uint64_t +mach_continuous_time_iOS13(void) +{ + uint64_t cont_time; + if (_mach_continuous_hwclock(&cont_time) != KERN_SUCCESS) { + _mach_continuous_time(NULL, &cont_time); + } + return cont_time; +} \ No newline at end of file diff --git a/libSystem/syscall.c b/libSystem/syscall.c new file mode 100755 index 0000000..d35d58a --- /dev/null +++ b/libSystem/syscall.c @@ -0,0 +1,1505 @@ +#include "../mach/vm_map.h" +#include "../mach/mach_port.h" +#include +#include + +extern kern_return_t _kernelrpc_mach_vm_allocate_trap( + mach_port_name_t target, + mach_vm_offset_t *addr, + mach_vm_size_t size, + int flags); + +extern kern_return_t _kernelrpc_mach_vm_deallocate_trap( + mach_port_name_t target, + mach_vm_address_t address, + mach_vm_size_t size + ); + +extern kern_return_t _kernelrpc_mach_vm_protect_trap( + mach_port_name_t target, + mach_vm_address_t address, + mach_vm_size_t size, + boolean_t set_maximum, + vm_prot_t new_protection + ); + +extern kern_return_t _kernelrpc_mach_vm_map_trap( + mach_port_name_t target, + mach_vm_offset_t *address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + vm_prot_t cur_protection + ); + +extern kern_return_t _kernelrpc_mach_vm_allocate( + vm_map_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + int flags +); + +extern kern_return_t _kernelrpc_mach_vm_deallocate( + vm_map_t target, + mach_vm_address_t address, + mach_vm_size_t size +); + +extern kern_return_t _kernelrpc_mach_vm_protect( + vm_map_t target_task, + mach_vm_address_t address, + mach_vm_size_t size, + boolean_t set_maximum, + vm_prot_t new_protection +); + +extern kern_return_t _kernelrpc_mach_vm_read( + vm_map_read_t target_task, + mach_vm_address_t address, + mach_vm_size_t size, + vm_offset_t *data, + mach_msg_type_number_t *dataCnt +); + +extern kern_return_t _kernelrpc_mach_vm_map( + vm_map_t target_task, + mach_vm_address_t *address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + mem_entry_name_port_t object, + memory_object_offset_t offset, + boolean_t copy, + vm_prot_t cur_protection, + vm_prot_t max_protection, + vm_inherit_t inheritance +); + +extern kern_return_t _kernelrpc_mach_vm_remap( + vm_map_t target_task, + mach_vm_address_t *target_address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + vm_map_t src_task, + mach_vm_address_t src_address, + boolean_t copy, + vm_prot_t *cur_protection, + vm_prot_t *max_protection, + vm_inherit_t inheritance +); + +extern kern_return_t _kernelrpc_mach_vm_purgable_control( + vm_map_t target_task, + mach_vm_address_t address, + vm_purgable_t control, + int *state +); + +extern kern_return_t _kernelrpc_mach_vm_purgable_control_trap( + mach_port_name_t target, + mach_vm_offset_t address, + vm_purgable_t control, + int *state); + +extern kern_return_t _kernelrpc_mach_port_allocate_trap( + mach_port_name_t target, + mach_port_right_t right, + mach_port_name_t *name + ); + +extern kern_return_t _kernelrpc_mach_port_deallocate_trap( + mach_port_name_t target, + mach_port_name_t name + ); + +extern kern_return_t _kernelrpc_mach_port_mod_refs_trap( + mach_port_name_t target, + mach_port_name_t name, + mach_port_right_t right, + mach_port_delta_t delta + ); + +extern kern_return_t _kernelrpc_mach_port_move_member_trap( + mach_port_name_t target, + mach_port_name_t member, + mach_port_name_t after + ); + +extern kern_return_t _kernelrpc_mach_port_insert_right_trap( + mach_port_name_t target, + mach_port_name_t name, + mach_port_name_t poly, + mach_msg_type_name_t polyPoly + ); + +extern kern_return_t _kernelrpc_mach_port_get_attributes_trap( + mach_port_name_t target, + mach_port_name_t name, + mach_port_flavor_t flavor, + mach_port_info_t port_info_out, + mach_msg_type_number_t *port_info_outCnt + ); + +extern kern_return_t _kernelrpc_mach_port_insert_member_trap( + mach_port_name_t target, + mach_port_name_t name, + mach_port_name_t pset + ); + +extern kern_return_t _kernelrpc_mach_port_extract_member_trap( + mach_port_name_t target, + mach_port_name_t name, + mach_port_name_t pset + ); + +extern kern_return_t _kernelrpc_mach_port_construct_trap( + mach_port_name_t target, + mach_port_options_t *options, + uint64_t context, + mach_port_name_t *name + ); + +extern kern_return_t _kernelrpc_mach_port_destruct_trap( + mach_port_name_t target, + mach_port_name_t name, + mach_port_delta_t srdelta, + uint64_t guard + ); + +extern kern_return_t _kernelrpc_mach_port_guard_trap( + mach_port_name_t target, + mach_port_name_t name, + uint64_t guard, + boolean_t strict + ); + +extern kern_return_t _kernelrpc_mach_port_unguard_trap( + mach_port_name_t target, + mach_port_name_t name, + uint64_t guard + ); + +extern kern_return_t mach_vm_allocate( + mach_port_name_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + int flags); + +extern kern_return_t mach_vm_protect( + mach_port_name_t task, + mach_vm_address_t address, + mach_vm_size_t size, + boolean_t set_maximum, + vm_prot_t new_protection); + +extern kern_return_t mach_vm_deallocate( + mach_port_name_t target, + mach_vm_address_t address, + mach_vm_size_t size); + +extern kern_return_t mach_generate_activity_id( + mach_port_name_t target, + int count, + uint64_t *activity_id + ); + +extern kern_return_t macx_swapon( + uint64_t filename, + int flags, + int size, + int priority); + +extern kern_return_t macx_swapoff( + uint64_t filename, + int flags); + +extern kern_return_t macx_triggers( + int hi_water, + int low_water, + int flags, + mach_port_t alert_port); + +extern kern_return_t macx_backing_store_suspend( + boolean_t suspend); + +extern kern_return_t macx_backing_store_recovery( + int pid); + +extern boolean_t swtch_pri(int pri); + +extern boolean_t swtch(void); + +extern kern_return_t thread_switch( + mach_port_name_t thread_name, + int option, + mach_msg_timeout_t option_time); + +extern mach_port_name_t task_self_trap(void); + +extern kern_return_t host_create_mach_voucher_trap( + mach_port_name_t host, + mach_voucher_attr_raw_recipe_array_t recipes, + int recipes_size, + mach_port_name_t *voucher); + +extern kern_return_t mach_voucher_extract_attr_recipe_trap( + mach_port_name_t voucher_name, + mach_voucher_attr_key_t key, + mach_voucher_attr_raw_recipe_t recipe, + mach_msg_type_number_t *recipe_size); + +extern kern_return_t _kernelrpc_mach_port_type_trap( + ipc_space_t task, + mach_port_name_t name, + mach_port_type_t *ptype); + +extern kern_return_t _kernelrpc_mach_port_request_notification_trap( + ipc_space_t task, + mach_port_name_t name, + mach_msg_id_t msgid, + mach_port_mscount_t sync, + mach_port_name_t notify, + mach_msg_type_name_t notifyPoly, + mach_port_name_t *previous); + +kern_return_t +mach_vm_allocate( + mach_port_name_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + int flags) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_vm_allocate_trap(target, address, size, flags); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_vm_allocate(target, address, size, flags); + } + + //int userTagFlags = flags & VM_FLAGS_ALIAS_MASK; + /*if (__syscall_logger && rv == KERN_SUCCESS && (userTagFlags != VM_MAKE_TAG(VM_MEMORY_STACK))) { + __syscall_logger(stack_logging_type_vm_allocate | userTagFlags, (uintptr_t)target, (uintptr_t)size, 0, (uintptr_t)*address, 0); + }*/ + + return rv; +} + +kern_return_t +mach_vm_deallocate( + mach_port_name_t target, + mach_vm_address_t address, + mach_vm_size_t size) +{ + kern_return_t rv; + + /*if (__syscall_logger) { + __syscall_logger(stack_logging_type_vm_deallocate, (uintptr_t)target, (uintptr_t)address, (uintptr_t)size, 0, 0); + }*/ + + rv = _kernelrpc_mach_vm_deallocate_trap(target, address, size); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_vm_deallocate(target, address, size); + } + + return rv; +} + +kern_return_t +mach_vm_protect( + mach_port_name_t task, + mach_vm_address_t address, + mach_vm_size_t size, + boolean_t set_maximum, + vm_prot_t new_protection) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_vm_protect_trap(task, address, size, set_maximum, + new_protection); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_vm_protect(task, address, size, + set_maximum, new_protection); + } + + return rv; +} + +kern_return_t +vm_allocate( + mach_port_name_t task, + vm_address_t *address, + vm_size_t size, + int flags) +{ + kern_return_t rv; + mach_vm_address_t mach_addr; + + mach_addr = (mach_vm_address_t)*address; + rv = mach_vm_allocate(task, &mach_addr, size, flags); +#if defined(__LP64__) + *address = mach_addr; +#else + *address = (vm_address_t)(mach_addr & ((vm_address_t)-1)); +#endif + + return rv; +} + +kern_return_t +vm_deallocate( + mach_port_name_t task, + vm_address_t address, + vm_size_t size) +{ + kern_return_t rv; + + rv = mach_vm_deallocate(task, address, size); + + return rv; +} + +kern_return_t +vm_protect( + mach_port_name_t task, + vm_address_t address, + vm_size_t size, + boolean_t set_maximum, + vm_prot_t new_protection) +{ + kern_return_t rv; + + rv = mach_vm_protect(task, address, size, set_maximum, new_protection); + + return rv; +} + +kern_return_t +mach_vm_map( + mach_port_name_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + mem_entry_name_port_t object, + memory_object_offset_t offset, + boolean_t copy, + vm_prot_t cur_protection, + vm_prot_t max_protection, + vm_inherit_t inheritance) +{ + kern_return_t rv = MACH_SEND_INVALID_DEST; + + if (object == MEMORY_OBJECT_NULL && max_protection == VM_PROT_ALL && + inheritance == VM_INHERIT_DEFAULT) { + rv = _kernelrpc_mach_vm_map_trap(target, address, size, mask, flags, + cur_protection); + } + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_vm_map(target, address, size, mask, flags, object, + offset, copy, cur_protection, max_protection, inheritance); + } + + //int userTagFlags = flags & VM_FLAGS_ALIAS_MASK; + /*if (__syscall_logger && rv == KERN_SUCCESS && (userTagFlags != VM_MAKE_TAG(VM_MEMORY_STACK))) { + int eventTypeFlags = stack_logging_type_vm_allocate | stack_logging_type_mapped_file_or_shared_mem; + __syscall_logger(eventTypeFlags | userTagFlags, (uintptr_t)target, (uintptr_t)size, 0, (uintptr_t)*address, 0); + }*/ + + return rv; +} + +kern_return_t +mach_vm_remap( + mach_port_name_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + mach_port_name_t src_task, + mach_vm_address_t src_address, + boolean_t copy, + vm_prot_t *cur_protection, + vm_prot_t *max_protection, + vm_inherit_t inheritance) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_vm_remap(target, address, size, mask, flags, + src_task, src_address, copy, cur_protection, max_protection, + inheritance); + + /*if (__syscall_logger && rv == KERN_SUCCESS) { + int eventTypeFlags = stack_logging_type_vm_allocate | stack_logging_type_mapped_file_or_shared_mem; + int userTagFlags = flags & VM_FLAGS_ALIAS_MASK; + __syscall_logger(eventTypeFlags | userTagFlags, (uintptr_t)target, (uintptr_t)size, 0, (uintptr_t)*address, 0); + }*/ + + return rv; +} + +kern_return_t +mach_vm_read( + mach_port_name_t target, + mach_vm_address_t address, + mach_vm_size_t size, + vm_offset_t *data, + mach_msg_type_number_t *dataCnt) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_vm_read(target, address, size, data, dataCnt); + + /*if (__syscall_logger && rv == KERN_SUCCESS) { + int eventTypeFlags = stack_logging_type_vm_allocate | stack_logging_type_mapped_file_or_shared_mem; + // The target argument is the remote task from which data is being read, + // so pass mach_task_self() as the destination task receiving the allocation. + __syscall_logger(eventTypeFlags, (uintptr_t)mach_task_self(), (uintptr_t)*dataCnt, 0, *data, 0); + }*/ + + return rv; +} + +kern_return_t +vm_map( + mach_port_name_t target, + vm_address_t *address, + vm_size_t size, + vm_offset_t mask, + int flags, + mem_entry_name_port_t object, + vm_offset_t offset, + boolean_t copy, + vm_prot_t cur_protection, + vm_prot_t max_protection, + vm_inherit_t inheritance) +{ + kern_return_t rv; + + rv = _kernelrpc_vm_map(target, address, size, mask, flags, object, + offset, copy, cur_protection, max_protection, inheritance); + + /*if (__syscall_logger && rv == KERN_SUCCESS) { + int eventTypeFlags = stack_logging_type_vm_allocate | stack_logging_type_mapped_file_or_shared_mem; + int userTagFlags = flags & VM_FLAGS_ALIAS_MASK; + __syscall_logger(eventTypeFlags | userTagFlags, (uintptr_t)target, (uintptr_t)size, 0, (uintptr_t)*address, 0); + }*/ + + return rv; +} + +kern_return_t +vm_remap( + mach_port_name_t target, + vm_address_t *address, + vm_size_t size, + vm_offset_t mask, + int flags, + mach_port_name_t src_task, + vm_address_t src_address, + boolean_t copy, + vm_prot_t *cur_protection, + vm_prot_t *max_protection, + vm_inherit_t inheritance) +{ + kern_return_t rv; + + rv = _kernelrpc_vm_remap(target, address, size, mask, flags, + src_task, src_address, copy, cur_protection, max_protection, + inheritance); + + /*if (__syscall_logger) { + int eventTypeFlags = stack_logging_type_vm_allocate | stack_logging_type_mapped_file_or_shared_mem; + int userTagFlags = flags & VM_FLAGS_ALIAS_MASK; + __syscall_logger(eventTypeFlags | userTagFlags, (uintptr_t)target, (uintptr_t)size, 0, (uintptr_t)*address, 0); + }*/ + + return rv; +} + +kern_return_t +vm_read( + mach_port_name_t target, + vm_address_t address, + vm_size_t size, + vm_offset_t *data, + mach_msg_type_number_t *dataCnt) +{ + kern_return_t rv; + + rv = _kernelrpc_vm_read(target, address, size, data, dataCnt); + + /*if (__syscall_logger && rv == KERN_SUCCESS) { + int eventTypeFlags = stack_logging_type_vm_allocate | stack_logging_type_mapped_file_or_shared_mem; + // The target argument is the remote task from which data is being read, + // so pass mach_task_self() as the destination task receiving the allocation. + __syscall_logger(eventTypeFlags, (uintptr_t)mach_task_self(), (uintptr_t)*dataCnt, 0, *data, 0); + }*/ + + return rv; +} + +kern_return_t +mach_vm_purgable_control( + mach_port_name_t target, + mach_vm_offset_t address, + vm_purgable_t control, + int *state) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_vm_purgable_control_trap(target, address, control, state); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_vm_purgable_control(target, address, control, state); + } + + return rv; +} + +kern_return_t +vm_purgable_control( + mach_port_name_t task, + vm_offset_t address, + vm_purgable_t control, + int *state) +{ + return mach_vm_purgable_control(task, + (mach_vm_offset_t) address, + control, + state); +} + + +kern_return_t +mach_port_allocate( + ipc_space_t task, + mach_port_right_t right, + mach_port_name_t *name) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_allocate_trap(task, right, name); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_port_allocate(task, right, name); + } + + return rv; +} + +kern_return_t +mach_port_destroy( + ipc_space_t task, + mach_port_name_t name) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_destroy(task, name); + + return rv; +} + +kern_return_t +mach_port_deallocate( + ipc_space_t task, + mach_port_name_t name) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_deallocate_trap(task, name); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_port_deallocate(task, name); + } + + return rv; +} + +kern_return_t +mach_port_get_refs( + ipc_space_t task, + mach_port_name_t name, + mach_port_right_t right, + mach_port_urefs_t *refs) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_get_refs(task, name, right, refs); + + return rv; +} + +kern_return_t +mach_port_mod_refs( + ipc_space_t task, + mach_port_name_t name, + mach_port_right_t right, + mach_port_delta_t delta) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_mod_refs_trap(task, name, right, delta); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_port_mod_refs(task, name, right, delta); + } + + return rv; +} + + +kern_return_t +mach_port_insert_right( + ipc_space_t task, + mach_port_name_t name, + mach_port_t poly, + mach_msg_type_name_t polyPoly) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_insert_right_trap(task, name, poly, polyPoly); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_port_insert_right(task, name, poly, + polyPoly); + } + + return rv; +} + +kern_return_t +mach_port_extract_right( + ipc_space_t task, + mach_port_name_t name, + mach_msg_type_name_t msgt_name, + mach_port_t *poly, + mach_msg_type_name_t *polyPoly) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_extract_right(task, name, msgt_name, + poly, polyPoly); + + return rv; +} + + +kern_return_t +mach_port_guard( + ipc_space_t task, + mach_port_name_t name, + mach_port_context_t guard, + boolean_t strict) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_guard_trap(task, name, (uint64_t) guard, strict); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_port_guard(task, name, (uint64_t) guard, strict); + } + + return rv; +} + +kern_return_t +mach_port_unguard( + ipc_space_t task, + mach_port_name_t name, + mach_port_context_t guard) +{ + kern_return_t rv; + + rv = _kernelrpc_mach_port_unguard_trap(task, name, (uint64_t) guard); + + if (rv == MACH_SEND_INVALID_DEST) { + rv = _kernelrpc_mach_port_unguard(task, name, (uint64_t) guard); + } + + return rv; +} + +#define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \ + mach_msg_trap((msg), (opt), (ssize), (rsize), (rname), (to), (not)) + +#define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT) + +extern mach_msg_return_t mach_msg_trap( + mach_msg_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_port_name_t notify); + +/* + * Routine: mach_msg + * Purpose: + * Send and/or receive a message. If the message operation + * is interrupted, and the user did not request an indication + * of that fact, then restart the appropriate parts of the + * operation. + */ +mach_msg_return_t +mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify) +mach_msg_header_t *msg; +mach_msg_option_t option; +mach_msg_size_t send_size; +mach_msg_size_t rcv_size; +mach_port_t rcv_name; +mach_msg_timeout_t timeout; +mach_port_t notify; +{ + mach_msg_return_t mr; + + /* + * Consider the following cases: + * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED + * plus special bits). + * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options. + * 3) RPC calls with interruptions in one/both halves. + * + * We refrain from passing the option bits that we implement + * to the kernel. This prevents their presence from inhibiting + * the kernel's fast paths (when it checks the option value). + */ + + mr = MACH_MSG_TRAP(msg, option & ~LIBMACH_OPTIONS, + send_size, rcv_size, rcv_name, + timeout, notify); + if (mr == MACH_MSG_SUCCESS) { + return MACH_MSG_SUCCESS; + } + + if ((option & MACH_SEND_INTERRUPT) == 0) { + while (mr == MACH_SEND_INTERRUPTED) { + mr = MACH_MSG_TRAP(msg, + option & ~LIBMACH_OPTIONS, + send_size, rcv_size, rcv_name, + timeout, notify); + } + } + + if ((option & MACH_RCV_INTERRUPT) == 0) { + while (mr == MACH_RCV_INTERRUPTED) { + mr = MACH_MSG_TRAP(msg, + option & ~(LIBMACH_OPTIONS | MACH_SEND_MSG), + 0, rcv_size, rcv_name, + timeout, notify); + } + } + + return mr; +} + +static void +mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type) +{ + if (MACH_PORT_VALID(port)) { + switch (type) { + case MACH_MSG_TYPE_MOVE_SEND: + case MACH_MSG_TYPE_MOVE_SEND_ONCE: + /* destroy the send/send-once right */ + (void) mach_port_deallocate(mach_task_self_, port); + break; + + case MACH_MSG_TYPE_MOVE_RECEIVE: + /* destroy the receive right */ + (void) mach_port_mod_refs(mach_task_self_, port, + MACH_PORT_RIGHT_RECEIVE, -1); + break; + + case MACH_MSG_TYPE_MAKE_SEND: + /* create a send right and then destroy it */ + (void) mach_port_insert_right(mach_task_self_, port, + port, MACH_MSG_TYPE_MAKE_SEND); + (void) mach_port_deallocate(mach_task_self_, port); + break; + + case MACH_MSG_TYPE_MAKE_SEND_ONCE: + /* create a send-once right and then destroy it */ + (void) mach_port_extract_right(mach_task_self_, port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &port, &type); + (void) mach_port_deallocate(mach_task_self_, port); + break; + } + } +} + +static void +mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size) +{ + if (size != 0) { + (void) vm_deallocate(mach_task_self_, addr, size); + } +} + + +/* + * Routine: mach_msg_destroy + * Purpose: + * mach_msg_destroy is useful in two contexts. + * + * First, it can deallocate all port rights and + * out-of-line memory in a received message. + * When a server receives a request it doesn't want, + * it needs this functionality. + * + * Second, it can mimic the side-effects of a msg-send + * operation. The effect is as if the message were sent + * and then destroyed inside the kernel. When a server + * can't send a reply (because the client died), + * it needs this functionality. + */ +void +mach_msg_destroy(mach_msg_header_t *msg) +{ + mach_msg_bits_t mbits = msg->msgh_bits; + + /* + * The msgh_local_port field doesn't hold a port right. + * The receive operation consumes the destination port right. + */ + + mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits)); + mach_msg_destroy_port(msg->msgh_voucher_port, MACH_MSGH_BITS_VOUCHER(mbits)); + + if (mbits & MACH_MSGH_BITS_COMPLEX) { + mach_msg_base_t *base; + mach_msg_type_number_t count, i; + mach_msg_descriptor_t *daddr; + + base = (mach_msg_base_t *) msg; + count = base->body.msgh_descriptor_count; + + daddr = (mach_msg_descriptor_t *) (base + 1); + for (i = 0; i < count; i++) { + switch (daddr->type.type) { + case MACH_MSG_PORT_DESCRIPTOR: { + mach_msg_port_descriptor_t *dsc; + + /* + * Destroy port rights carried in the message + */ + dsc = &daddr->port; + mach_msg_destroy_port(dsc->name, dsc->disposition); + daddr = (mach_msg_descriptor_t *)(dsc + 1); + break; + } + + case MACH_MSG_OOL_DESCRIPTOR: { + mach_msg_ool_descriptor_t *dsc; + + /* + * Destroy memory carried in the message + */ + dsc = &daddr->out_of_line; + if (dsc->deallocate) { + mach_msg_destroy_memory((vm_offset_t)dsc->address, + dsc->size); + } + daddr = (mach_msg_descriptor_t *)(dsc + 1); + break; + } + + case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: { + mach_msg_ool_descriptor_t *dsc; + + /* + * Just skip it. + */ + dsc = &daddr->out_of_line; + daddr = (mach_msg_descriptor_t *)(dsc + 1); + break; + } + + case MACH_MSG_OOL_PORTS_DESCRIPTOR: { + mach_port_t *ports; + mach_msg_ool_ports_descriptor_t *dsc; + mach_msg_type_number_t j; + + /* + * Destroy port rights carried in the message + */ + dsc = &daddr->ool_ports; + ports = (mach_port_t *) dsc->address; + for (j = 0; j < dsc->count; j++, ports++) { + mach_msg_destroy_port(*ports, dsc->disposition); + } + + /* + * Destroy memory carried in the message + */ + if (dsc->deallocate) { + mach_msg_destroy_memory((vm_offset_t)dsc->address, + dsc->count * sizeof(mach_port_t)); + } + daddr = (mach_msg_descriptor_t *)(dsc + 1); + break; + } + + case MACH_MSG_GUARDED_PORT_DESCRIPTOR: { + mach_msg_guarded_port_descriptor_t *dsc; + mach_msg_guard_flags_t flags; + /* + * Destroy port right carried in the message + */ + dsc = &daddr->guarded_port; + flags = dsc->flags; + if ((flags & MACH_MSG_GUARD_FLAGS_UNGUARDED_ON_SEND) == 0) { + /* Need to unguard before destroying the port */ + mach_port_unguard(mach_task_self_, dsc->name, (uint64_t)dsc->context); + } + mach_msg_destroy_port(dsc->name, dsc->disposition); + daddr = (mach_msg_descriptor_t *)(dsc + 1); + break; + } + } + } + } +} + +kern_return_t +host_page_size(__unused host_t host, vm_size_t *out_page_size) { + *out_page_size = vm_kernel_page_size; + return KERN_SUCCESS; +} + +extern mach_port_name_t host_self_trap(void); + +mach_port_t +mach_host_self(void) { + return host_self_trap(); +} + +int +mig_strncpy( + char *dest, + const char *src, + int len) +{ + int i; + + if (len <= 0) { + return 0; + } + + for (i = 1; i < len; i++) { + if (!(*dest++ = *src++)) { + return i; + } + } + + *dest = '\0'; + return i; +} + +int +mig_strncpy_zerofill( + char *dest, + const char *src, + int len) +{ + int i; + boolean_t terminated = FALSE; + int retval = 0; + + if (len <= 0 || dest == 0) { + return 0; + } + + if (src == 0) { + terminated = TRUE; + } + + for (i = 1; i < len; i++) { + if (!terminated) { + if (!(*dest++ = *src++)) { + retval = i; + terminated = TRUE; + } + } else { + *dest++ = '\0'; + } + } + + *dest = '\0'; + if (!terminated) { + retval = i; + } + + return retval; +} + +#define __TSD_MIG_REPLY 2 + +extern mach_port_name_t mach_reply_port(void); + +static void** +_os_tsd_get_base(void); +//#define _os_tsd_get_base() _os_tsd_get_base() + +__attribute__((always_inline)) +static __inline__ void* +_os_tsd_get_direct(unsigned long slot) { + return _os_tsd_get_base()[slot]; +} + +__attribute__((always_inline)) +static __inline__ int +_os_tsd_set_direct(unsigned long slot, void *val) { + _os_tsd_get_base()[slot] = val; + return 0; +} + + +__XNU_PRIVATE_EXTERN mach_port_t _task_reply_port = MACH_PORT_NULL; + +static inline mach_port_t +_mig_get_reply_port() +{ + return (mach_port_t)(uintptr_t)_os_tsd_get_direct(__TSD_MIG_REPLY); +} + +static inline void +_mig_set_reply_port(mach_port_t port) +{ + _os_tsd_set_direct(__TSD_MIG_REPLY, (void *)(uintptr_t)port); +} + +/* + * Called by mig interface code whenever a reply port is needed. + * Tracing is masked during this call; otherwise, a call to printf() + * can result in a call to malloc() which eventually reenters + * mig_get_reply_port() and deadlocks. + */ +mach_port_t +mig_get_reply_port(void) +{ + mach_port_t port = _mig_get_reply_port(); + if (port == MACH_PORT_NULL) { + port = mach_reply_port(); + _mig_set_reply_port(port); + } + return port; +} + +/* + * Called by mig interface code after a timeout on the reply port. + * May also be called by user. The new mig calls with port passed in. + */ +void +mig_dealloc_reply_port(mach_port_t migport) +{ + mach_port_t port = _mig_get_reply_port(); + if (port != MACH_PORT_NULL && port != _task_reply_port) { + _mig_set_reply_port(_task_reply_port); + (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); + if (migport != port) { + (void) mach_port_deallocate(mach_task_self(), migport); + } + _mig_set_reply_port(MACH_PORT_NULL); + } +} + +/************************************************************* + * Called by mig interfaces after each RPC. + * Could be called by user. + ***********************************************************/ + +void +mig_put_reply_port(mach_port_t reply_port __unused) +{ +} + + +static kern_return_t +mach_msg_server_mig_return_code(mig_reply_error_t *reply) +{ + /* + * If the message is complex, it is assumed that the reply was successful, + * as the RetCode is where the count of out of line descriptors is. + * + * If not, we read RetCode. + */ + if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { + return KERN_SUCCESS; + } + return reply->RetCode; +} + +static inline boolean_t +mach_msg_server_is_recoverable_send_error(kern_return_t kr) +{ + switch (kr) { + case MACH_SEND_INVALID_DEST: + case MACH_SEND_TIMED_OUT: + case MACH_SEND_INTERRUPTED: + return TRUE; + default: + /* + * Other errors mean that the message may have been partially destroyed + * by the kernel, and these can't be recovered and may leak resources. + */ + return FALSE; + } +} + +static void +mach_msg_server_consume_unsent_message(mach_msg_header_t *hdr) +{ + /* mach_msg_destroy doesn't handle the local port */ + mach_port_t port = hdr->msgh_local_port; + if (MACH_PORT_VALID(port)) { + switch (MACH_MSGH_BITS_LOCAL(hdr->msgh_bits)) { + case MACH_MSG_TYPE_MOVE_SEND: + case MACH_MSG_TYPE_MOVE_SEND_ONCE: + /* destroy the send/send-once right */ + (void) mach_port_deallocate(mach_task_self_, port); + hdr->msgh_local_port = MACH_PORT_NULL; + break; + } + } + mach_msg_destroy(hdr); +} + +/* + * Routine: mach_msg_overwrite + * Purpose: + * Send and/or receive a message. If the message operation + * is interrupted, and the user did not request an indication + * of that fact, then restart the appropriate parts of the + * operation. + * + * Distinct send and receive buffers may be specified. If + * no separate receive buffer is specified, the msg parameter + * will be used for both send and receive operations. + * + * In addition to a distinct receive buffer, that buffer may + * already contain scatter control information to direct the + * receiving of the message. + */ + +extern mach_msg_return_t mach_msg_overwrite_trap( + mach_msg_header_t *msg, + mach_msg_option_t option, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_msg_priority_t priority, + mach_msg_header_t *rcv_msg, + mach_msg_size_t rcv_limit); + +mach_msg_return_t +mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout, + notify, rcv_msg, rcv_scatter_size) +mach_msg_header_t *msg; +mach_msg_option_t option; +mach_msg_size_t send_size; +mach_msg_size_t rcv_limit; +mach_port_t rcv_name; +mach_msg_timeout_t timeout; +mach_port_t notify; +mach_msg_header_t *rcv_msg; +mach_msg_size_t rcv_scatter_size; +{ + mach_msg_return_t mr; + + /* + * Consider the following cases: + * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED + * plus special bits). + * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options. + * 3) RPC calls with interruptions in one/both halves. + * + * We refrain from passing the option bits that we implement + * to the kernel. This prevents their presence from inhibiting + * the kernel's fast paths (when it checks the option value). + */ + + mr = mach_msg_overwrite_trap(msg, option & ~LIBMACH_OPTIONS, + send_size, rcv_limit, rcv_name, + timeout, notify, rcv_msg, rcv_scatter_size); + if (mr == MACH_MSG_SUCCESS) { + return MACH_MSG_SUCCESS; + } + + if ((option & MACH_SEND_INTERRUPT) == 0) { + while (mr == MACH_SEND_INTERRUPTED) { + mr = mach_msg_overwrite_trap(msg, + option & ~LIBMACH_OPTIONS, + send_size, rcv_limit, rcv_name, + timeout, notify, rcv_msg, rcv_scatter_size); + } + } + + if ((option & MACH_RCV_INTERRUPT) == 0) { + while (mr == MACH_RCV_INTERRUPTED) { + mr = mach_msg_overwrite_trap(msg, + option & ~(LIBMACH_OPTIONS | MACH_SEND_MSG), + 0, rcv_limit, rcv_name, + timeout, notify, rcv_msg, rcv_scatter_size); + } + } + + return mr; +} + +/* + * Routine: mach_msg_server + * Purpose: + * A simple generic server function. Note that changes here + * should be considered for duplication above. + */ +mach_msg_return_t +mach_msg_server( + boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), + mach_msg_size_t max_size, + mach_port_t rcv_name, + mach_msg_options_t options) +{ + mig_reply_error_t *bufRequest, *bufReply; + mach_msg_size_t request_size; + mach_msg_size_t new_request_alloc; + mach_msg_size_t request_alloc; + mach_msg_size_t trailer_alloc; + mach_msg_size_t reply_alloc; + mach_msg_return_t mr; + kern_return_t kr; + mach_port_t self = mach_task_self_; + voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; + boolean_t buffers_swapped = FALSE; + + options &= ~(MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_VOUCHER); + + reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ? + (max_size + MAX_TRAILER_SIZE) : max_size); + + kr = vm_allocate(self, + (vm_address_t *)&bufReply, + reply_alloc, + VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE); + if (kr != KERN_SUCCESS) { + return kr; + } + + request_alloc = 0; + trailer_alloc = REQUESTED_TRAILER_SIZE(options); + new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc); + + request_size = (options & MACH_RCV_LARGE) ? + new_request_alloc : max_size + trailer_alloc; + + for (;;) { + if (request_alloc < new_request_alloc) { + request_alloc = new_request_alloc; + kr = vm_allocate(self, + (vm_address_t *)&bufRequest, + request_alloc, + VM_MAKE_TAG(VM_MEMORY_MACH_MSG) | TRUE); + if (kr != KERN_SUCCESS) { + vm_deallocate(self, + (vm_address_t)bufReply, + reply_alloc); + return kr; + } + } + + mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options, + 0, request_size, rcv_name, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + while (mr == MACH_MSG_SUCCESS) { + /* we have another request message */ + + buffers_swapped = FALSE; + old_state = voucher_mach_msg_adopt(&bufRequest->Head); + bufReply->Head = (mach_msg_header_t){}; + + (void) (*demux)(&bufRequest->Head, &bufReply->Head); + + switch (mach_msg_server_mig_return_code(bufReply)) { + case KERN_SUCCESS: + break; + case MIG_NO_REPLY: + bufReply->Head.msgh_remote_port = MACH_PORT_NULL; + break; + default: + /* + * destroy the request - but not the reply port + * (MIG moved it into the bufReply). + */ + bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; + mach_msg_destroy(&bufRequest->Head); + } + + /* + * We don't want to block indefinitely because the client + * isn't receiving messages from the reply port. + * If we have a send-once right for the reply port, then + * this isn't a concern because the send won't block. + * If we have a send right, we need to use MACH_SEND_TIMEOUT. + * To avoid falling off the kernel's fast RPC path, + * we only supply MACH_SEND_TIMEOUT when absolutely necessary. + */ + if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { + if (request_alloc == reply_alloc) { + mig_reply_error_t *bufTemp; + + mr = mach_msg( + &bufReply->Head, + (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == + MACH_MSG_TYPE_MOVE_SEND_ONCE) ? + MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options : + MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options, + bufReply->Head.msgh_size, request_size, rcv_name, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + /* swap request and reply */ + bufTemp = bufRequest; + bufRequest = bufReply; + bufReply = bufTemp; + buffers_swapped = TRUE; + } else { + mr = mach_msg_overwrite( + &bufReply->Head, + (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == + MACH_MSG_TYPE_MOVE_SEND_ONCE) ? + MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options : + MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT | MACH_RCV_VOUCHER | options, + bufReply->Head.msgh_size, request_size, rcv_name, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, + &bufRequest->Head, 0); + } + + /* + * Need to destroy the reply msg in case if there was a send timeout or + * invalid destination. The reply msg would be swapped with request msg + * if buffers_swapped is true, thus destroy request msg instead of + * reply msg in such cases. + */ + if (mach_msg_server_is_recoverable_send_error(mr)) { + if (buffers_swapped) { + mach_msg_server_consume_unsent_message(&bufRequest->Head); + } else { + mach_msg_server_consume_unsent_message(&bufReply->Head); + } + } else if (mr != MACH_RCV_TIMED_OUT) { + voucher_mach_msg_revert(old_state); + old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; + + continue; + } + } + voucher_mach_msg_revert(old_state); + old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; + + mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG | MACH_RCV_VOUCHER | options, + 0, request_size, rcv_name, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + } /* while (mr == MACH_MSG_SUCCESS) */ + + if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) { + new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size + + trailer_alloc); + request_size = new_request_alloc; + vm_deallocate(self, + (vm_address_t) bufRequest, + request_alloc); + continue; + } + + break; + } /* for(;;) */ + + (void)vm_deallocate(self, + (vm_address_t) bufRequest, + request_alloc); + (void)vm_deallocate(self, + (vm_address_t) bufReply, + reply_alloc); + return mr; +} + +typedef const struct _libkernel_voucher_functions { + /* The following functions are included in version 1 of this structure */ + unsigned long version; + boolean_t (*voucher_mach_msg_set)(mach_msg_header_t*); + void (*voucher_mach_msg_clear)(mach_msg_header_t*); + voucher_mach_msg_state_t (*voucher_mach_msg_adopt)(mach_msg_header_t*); + void (*voucher_mach_msg_revert)(voucher_mach_msg_state_t); + + /* Subsequent versions must only add pointers! */ +} *_libkernel_voucher_functions_t; + +static const struct _libkernel_voucher_functions + _libkernel_voucher_functions_empty; +static _libkernel_voucher_functions_t _libkernel_voucher_functions = + &_libkernel_voucher_functions_empty; + +kern_return_t +__libkernel_voucher_init(_libkernel_voucher_functions_t fns) +{ + _libkernel_voucher_functions = fns; + return KERN_SUCCESS; +} + +boolean_t +voucher_mach_msg_set(mach_msg_header_t *msg) +{ + if (_libkernel_voucher_functions->voucher_mach_msg_set) { + return _libkernel_voucher_functions->voucher_mach_msg_set(msg); + } + return 0; +} + +void +voucher_mach_msg_clear(mach_msg_header_t *msg) +{ + if (_libkernel_voucher_functions->voucher_mach_msg_clear) { + return _libkernel_voucher_functions->voucher_mach_msg_clear(msg); + } +} + +voucher_mach_msg_state_t +voucher_mach_msg_adopt(mach_msg_header_t *msg) +{ + if (_libkernel_voucher_functions->voucher_mach_msg_adopt) { + return _libkernel_voucher_functions->voucher_mach_msg_adopt(msg); + } + return VOUCHER_MACH_MSG_STATE_UNCHANGED; +} + +void +voucher_mach_msg_revert(voucher_mach_msg_state_t state) +{ + if (_libkernel_voucher_functions->voucher_mach_msg_revert) { + return _libkernel_voucher_functions->voucher_mach_msg_revert(state); + } +} + +__attribute__((always_inline, pure)) +static __inline__ void** +_os_tsd_get_base(void) +{ +#if defined(__arm__) + uintptr_t tsd; + __asm__("mrc p15, 0, %0, c13, c0, 3\n" + "bic %0, %0, #0x3\n" : "=r" (tsd)); +#elif defined(__arm64__) + uint64_t tsd; + __asm__("mrs %0, TPIDRRO_EL0\n" + "bic %0, %0, #0x7\n" : "=r" (tsd)); +#endif + + return (void**)(uintptr_t)tsd; +} + diff --git a/libSystem/syscall.s b/libSystem/syscall.s new file mode 100644 index 0000000..51ba506 --- /dev/null +++ b/libSystem/syscall.s @@ -0,0 +1,163 @@ + +#define SWI_SYSCALL 0x80 + +#define kernel_trap(trap_name, trap_number, num_args) \ +.globl _##trap_name %% \ +.text %% \ +.align 2 %% \ +_##trap_name: %% \ + mov x16, #(trap_number) %% \ + svc #SWI_SYSCALL %% \ + ret + +kernel_trap(__proc_info, 0x150, 6) +kernel_trap(getpid, 0x14, 0) + +kernel_trap(_kernelrpc_mach_vm_allocate_trap,-10,5) /* 4 args, +1 for mach_vm_size_t */ +kernel_trap(_kernelrpc_mach_vm_purgable_control_trap,-11,5) /* 4 args, +1 for mach_vm_offset_t */ +kernel_trap(_kernelrpc_mach_vm_deallocate_trap,-12,5) /* 3 args, +2 for mach_vm_size_t and mach_vm_address_t */ +kernel_trap(_kernelrpc_mach_vm_protect_trap,-14,7) /* 5 args, +2 for mach_vm_address_t and mach_vm_size_t */ +kernel_trap(_kernelrpc_mach_vm_map_trap,-15,9) +kernel_trap(_kernelrpc_mach_port_allocate_trap,-16,3) +/* mach_port_destroy */ +kernel_trap(_kernelrpc_mach_port_deallocate_trap,-18,2) +kernel_trap(_kernelrpc_mach_port_mod_refs_trap,-19,4) +kernel_trap(_kernelrpc_mach_port_move_member_trap,-20,3) +kernel_trap(_kernelrpc_mach_port_insert_right_trap,-21,4) +kernel_trap(_kernelrpc_mach_port_insert_member_trap,-22,3) +kernel_trap(_kernelrpc_mach_port_extract_member_trap,-23,3) +kernel_trap(_kernelrpc_mach_port_construct_trap,-24,5) +kernel_trap(_kernelrpc_mach_port_destruct_trap,-25,5) + +kernel_trap(mach_reply_port,-26,0) +kernel_trap(thread_self_trap,-27,0) +kernel_trap(task_self_trap,-28,0) +kernel_trap(host_self_trap,-29,0) + +kernel_trap(mach_msg_trap,-31,7) +kernel_trap(mach_msg_overwrite_trap,-32,9) +kernel_trap(semaphore_signal_trap, -33, 1) +kernel_trap(semaphore_signal_all_trap, -34, 1) +kernel_trap(semaphore_signal_thread_trap, -35, 2) +kernel_trap(semaphore_wait_trap,-36,1) +kernel_trap(semaphore_wait_signal_trap,-37,2) +kernel_trap(semaphore_timedwait_trap,-38,3) +kernel_trap(semaphore_timedwait_signal_trap,-39,4) + +kernel_trap(_kernelrpc_mach_port_get_attributes_trap,-40,5) +kernel_trap(_kernelrpc_mach_port_guard_trap,-41,5) +kernel_trap(_kernelrpc_mach_port_unguard_trap,-42,4) +kernel_trap(mach_generate_activity_id, -43, 3) + +kernel_trap(task_name_for_pid,-44,3) +kernel_trap(task_for_pid,-45,3) +kernel_trap(pid_for_task,-46,2) + +#if defined(__LP64__) +kernel_trap(macx_swapon,-48, 4) +kernel_trap(macx_swapoff,-49, 2) +#else /* __LP64__ */ +kernel_trap(macx_swapon,-48, 5) +kernel_trap(macx_swapoff,-49, 3) +#endif /* __LP64__ */ +kernel_trap(thread_get_special_reply_port,-50,0) +kernel_trap(macx_triggers,-51, 4) +kernel_trap(macx_backing_store_suspend,-52, 1) +kernel_trap(macx_backing_store_recovery,-53, 1) + +/* These are currently used by pthreads even on LP64 */ +/* But as soon as that is fixed - they will go away there */ +kernel_trap(swtch_pri,-59,1) +kernel_trap(swtch,-60,0) + +kernel_trap(syscall_thread_switch,-61,3) +kernel_trap(clock_sleep_trap,-62,5) + +/* voucher traps */ +kernel_trap(host_create_mach_voucher_trap,-70,4) +/* mach_voucher_extract_attr_content */ +kernel_trap(mach_voucher_extract_attr_recipe_trap,-72,4) +/* mach_voucher_extract_all_attr_recipes */ +/* mach_voucher_attr_command */ +/* mach_voucher_debug_info */ + +/* more mach_port traps */ +kernel_trap(_kernelrpc_mach_port_type_trap,-76,3) +kernel_trap(_kernelrpc_mach_port_request_notification_trap,-77,7) + +kernel_trap(mach_timebase_info_trap,-89,1) + +#if defined(__LP64__) +/* unit64_t arguments passed in one register in LP64 */ +kernel_trap(mach_wait_until,-90,1) +#else /* __LP64__ */ +kernel_trap(mach_wait_until,-90,2) +#endif /* __LP64__ */ + +kernel_trap(mk_timer_create,-91,0) +kernel_trap(mk_timer_destroy,-92,1) + +#if defined(__LP64__) +/* unit64_t arguments passed in one register in LP64 */ +kernel_trap(mk_timer_arm,-93,2) +#else /* __LP64__ */ +kernel_trap(mk_timer_arm,-93,3) +#endif /* __LP64__ */ + +kernel_trap(mk_timer_cancel,-94,2) +#if defined(__LP64__) +kernel_trap(mk_timer_arm_leeway,-95,4) +#else +kernel_trap(mk_timer_arm_leeway,-95,7) +#endif +kernel_trap(debug_control_port_for_pid,-96,3) + +#define _COMM_PAGE64_BASE_ADDRESS (0x0000000FFFFFC000ULL) /* In TTBR0 */ +#define _COMM_PAGE_START_ADDRESS (_COMM_PAGE64_BASE_ADDRESS) +#define _COMM_PAGE_CONT_HW_TIMEBASE (_COMM_PAGE_START_ADDRESS+0x0A8) // uint64_t base for mach_continuous_time() relative to CNT[PV]CT +#define _COMM_PAGE_CONT_TIMEBASE (_COMM_PAGE_START_ADDRESS+0x098) // uint64_t base for mach_continuous_time() relative to mach_absolute_time() +#define _COMM_PAGE_CONT_HWCLOCK (_COMM_PAGE_START_ADDRESS+0x091) // uint8_t is always-on hardware clock present for mach_continuous_time() +#define _COMM_PAGE_USER_TIMEBASE (_COMM_PAGE_START_ADDRESS+0x090) // uint8_t is userspace mach_absolute_time supported (can read the timebase) +#define _COMM_PAGE_TIMEBASE_OFFSET (_COMM_PAGE_START_ADDRESS+0x088) // uint64_t timebase offset for constructing mach_absolute_time() + +#define USER_TIMEBASE_NONE 0 +#define USER_TIMEBASE_SPEC 1 + .text + .align 2 + .globl _mach_absolute_time +_mach_absolute_time: + movk x3, #(((_COMM_PAGE_TIMEBASE_OFFSET) >> 48) & 0x000000000000FFFF), lsl #48 + movk x3, #(((_COMM_PAGE_TIMEBASE_OFFSET) >> 32) & 0x000000000000FFFF), lsl #32 + movk x3, #(((_COMM_PAGE_TIMEBASE_OFFSET) >> 16) & 0x000000000000FFFF), lsl #16 + movk x3, #((_COMM_PAGE_TIMEBASE_OFFSET) & 0x000000000000FFFF) + ldrb w2, [x3, #((_COMM_PAGE_USER_TIMEBASE) - (_COMM_PAGE_TIMEBASE_OFFSET))] + cmp x2, #USER_TIMEBASE_NONE // Are userspace reads supported? + b.eq _mach_absolute_time_kernel // If not, go to the kernel + isb // Prevent speculation on CNTPCT across calls + // (see ARMV7C.b section B8.1.2, ARMv8 section D6.1.2) +L_mach_absolute_time_user: + ldr x1, [x3] // Load the offset + mrs x0, CNTPCT_EL0 // Read the timebase + ldr x2, [x3] // Load the offset + cmp x1, x2 // Compare our offset values... + b.ne L_mach_absolute_time_user // If they changed, try again + add x0, x0, x1 // Construct mach_absolute_time + ret + + + + .text + .align 2 + .globl _mach_absolute_time_kernel +_mach_absolute_time_kernel: + mov w16, #-3 // Load the magic MAT number + svc #SWI_SYSCALL + ret + + .text + .align 2 + .globl _mach_continuous_time_kernel +_mach_continuous_time_kernel: + mov w16, #-4 // Load the magic MCT number + svc #SWI_SYSCALL + ret \ No newline at end of file diff --git a/littlespawn.c b/littlespawn.c new file mode 100644 index 0000000..78726ce --- /dev/null +++ b/littlespawn.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "reductant.h" + +extern char** environ; + +extern int map_dyld(int target_pid, const char* dyld_path); + +pid_t +littlespawn(char *const argv[], short posix_spawn_flag) { + // https://github.com/coolstar/electra/issues/53#issuecomment-359287851 + posix_spawnattr_t attr; + if (getenv("RT_DYLD_SHARED_CACHE_DIR")) { + setenv("DYLD_SHARED_CACHE_DIR", getenv("RT_DYLD_SHARED_CACHE_DIR"), 1); + } else { + if (access("/System/macOSSupport/dyld/dyld_shared_cache_arm64e", F_OK) == 0) { + setenv("DYLD_SHARED_REGION", "private", 1); + setenv("DYLD_SHARED_CACHE_DIR", "/System/macOSSupport/dyld", 1); + } + if (access("/System/macOSSupport/", F_OK) == 0) { + setenv("DYLD_ROOT_PATH", "/System/macOSSupport/", 1); + } + } + if (getenv("RT_DYLD_INSERT_LIBRARIES")) { + setenv("DYLD_INSERT_LIBRARIES", getenv("RT_DYLD_INSERT_LIBRARIES"), 1); + } else { + if (access("/System/macOSSupport/usr/lib/rt_hooker.dylib", F_OK) == 0) { + setenv("DYLD_INSERT_LIBRARIES", "/System/macOSSupport/usr/lib/rt_hooker.dylib", 1); + } + } + if (getenv("RT_DYLD_ROOT_PATH")) { + setenv("DYLD_ROOT_PATH", getenv("RT_DYLD_ROOT_PATH"), 1); + } + char *buffer; + const char *path = argv[0]; + int fd = open(path, O_RDONLY); + struct stat exec_stat; + + if (fd >= 0) { + if (fstat(fd, &exec_stat)) { + goto exec; + } + buffer = mmap(NULL, exec_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + goto exec; + } + + struct mach_header_64 *header = (struct mach_header_64 *)buffer; + if ((exec_stat.st_size >= sizeof(*header) && header->magic == MH_MAGIC_64 && header->cputype == CPU_TYPE_ARM64 && (header->cpusubtype & CPU_SUBTYPE_ARM64E) == CPU_SUBTYPE_ARM64E && header->filetype == MH_EXECUTE) || getenv("RT_FORCE_ARM64_RUNNER")) { +arm64e: + path = getenv("RT_ARM64_RUNNER_PATH"); + if (path == NULL) { + path = "/System/macOSSupport/usr/libexec/arm64_runner"; + } + } else { + struct fat_header *fat_header = (struct fat_header *)buffer; + if (fat_header->magic == FAT_CIGAM) { + struct fat_arch* fat_arches = (void*)buffer + sizeof(struct fat_header); + bool has_arm64e = false; + for (unsigned int index = 0; index < ntohl(fat_header->nfat_arch); index++) { + struct fat_arch* fat_arch = &fat_arches[index]; + if (ntohl(fat_arch->cputype) == CPU_TYPE_ARM64) { + if ((ntohl(fat_arch->cpusubtype) & CPU_SUBTYPE_ARM64E) == CPU_SUBTYPE_ARM64E) { + has_arm64e = true; + } else { + goto has_arm64; + } + } + } + if (has_arm64e) { + goto arm64e; + } + } +has_arm64: + close(fd); + fd = -1; + } + } +exec: + posix_spawnattr_init(&attr); + posix_spawnattr_setflags(&attr, posix_spawn_flag); + pid_t child; + int ret = posix_spawnp(&child, path, NULL, &attr, &argv[0], environ); + posix_spawnattr_destroy(&attr); + if (ret) { + fprintf(stderr, "failed to exec %s: %s\n", argv[0], strerror(ret)); + if (fd >= 0) { + close(fd); + } + return -1; + } + if (fd >= 0) { + map_dyld_and_main_executable(child, getenv("RT_DYLD_PATH"), argv[0], fd, buffer, exec_stat.st_size); + munmap(buffer, exec_stat.st_size); + close(fd); + } else { + map_dyld(child, getenv("RT_DYLD_PATH")); + } + return child; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..e481663 --- /dev/null +++ b/main.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __arm64e__ +#define __RT_arm64e__ true +#elif __arm64__ +#define __RT_arm64e__ ({const NXArchInfo *info = NXGetLocalArchInfo(); info->cputype == CPU_TYPE_ARM64 && (info->cpusubtype & CPU_SUBTYPE_ARM64E) == CPU_SUBTYPE_ARM64E;}) +#endif + +extern int map_dyld(int target_pid, const char* dyld_path); +extern int map_dyld_and_main_executable(int target_pid, const char *dyld_path, int fd); +extern pid_t littlespawn(char *const argv[], short posix_spawn_flag); +extern int translator(task_t p); +extern void translator_loop(mach_port_t port); + +static void * +translator_subthread(void *arg) { + pid_t p = *(pid_t*)arg; + task_t child; + kern_return_t err; + err = task_for_pid(mach_task_self(), p, &child); + if (err != KERN_SUCCESS) { + fprintf(stderr, "translator: task_for_pid: %s\n", mach_error_string(err)); + return MACH_PORT_NULL; + } + mach_port_t port = translator(child); + kill(*(pid_t*)arg, SIGCONT); + if (port != MACH_PORT_NULL) { + translator_loop(port); + } + return NULL; +} + +static pid_t pid; + +static void +signal_handle(int signo) { + kill(pid, signo); +} + +int inject(pid_t pid, const char *lib); + +int main(int argc, char *const argv[]) { + if (argc == 1) { + fprintf(stderr, "bad arguments\n"); + return -1; + } + pid = littlespawn(&argv[1], POSIX_SPAWN_START_SUSPENDED); + if (pid < 0) { + return -1; + } + + signal(SIGINT, signal_handle); + signal(SIGTERM, signal_handle); + signal(SIGHUP, signal_handle); + signal(SIGQUIT, signal_handle); + signal(SIGSTOP, signal_handle); + signal(SIGCONT, signal_handle); + signal(SIGTSTP, signal_handle); + signal(SIGTTIN, signal_handle); +#if __arm64__ + if (!__RT_arm64e__) { + pthread_t pth; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, 1); + pthread_create(&pth, &attr, translator_subthread, (void*)&pid); + } else { + kill(pid, SIGCONT); + } +#endif + int status; + if (waitpid(pid, &status, 0) != -1) { + return status; + } else { + fprintf(stderr, "waitpid: %s\n", strerror(errno)); + } + return 0; +} \ No newline at end of file diff --git a/reductant.h b/reductant.h new file mode 100644 index 0000000..fad5ccc --- /dev/null +++ b/reductant.h @@ -0,0 +1,44 @@ +#ifndef __REDUCTANT_H__ +#define __REDUCTANT_H__ + +#include +#include +#include + +#define FD_VALID(fd) ((fd) >= 0) +#define FD_INVALID(fd) (fd < 0) + +__BEGIN_DECLS + +int map_dyld_and_main_executable(int target_pid, const char *dyld_path, const char *argv0, int fd, void *exe_map, size_t map_size); + +kern_return_t mach_vm_read_overwrite( + vm_map_t target_task, + mach_vm_address_t address, + mach_vm_size_t size, + mach_vm_address_t data, + mach_vm_size_t *outsize +); + +kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); + +kern_return_t +mach_vm_remap( + mach_port_name_t target, + mach_vm_address_t *address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + mach_port_name_t src_task, + mach_vm_address_t src_address, + boolean_t copy, + vm_prot_t *cur_protection, + vm_prot_t *max_protection, + vm_inherit_t inheritance); + +kern_return_t +mach_vm_region(vm_map_t, mach_vm_address_t *, mach_vm_size_t *, vm_region_flavor_t, vm_region_info_t, mach_msg_type_number_t *, mach_port_t *); + +__END_DECLS + +#endif // __REDUCTANT_H__ \ No newline at end of file diff --git a/translator.c b/translator.c new file mode 100644 index 0000000..525e215 --- /dev/null +++ b/translator.c @@ -0,0 +1,646 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "mach/mach_exc.h" +#include +#include +#include +#include +#include +#include "reductant.h" +#include "arm64.h" + +#if ENABLE_DOBBY_HOOK +#include "dobby.h" +#endif + +mach_msg_return_t +mach_msg_server( + boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), + mach_msg_size_t max_size, + mach_port_t rcv_name, + mach_msg_options_t options); + +#ifndef IN_PROCESS +#define IN_PROCESS 0 +#endif + +boolean_t mach_exc_server( + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + +static vm_size_t _page_size = 0; + +static const char * +mach_exc_string(exception_type_t exc) { + switch (exc) { + case EXC_BAD_ACCESS: + return "EXC_BAD_ACCESS"; + case EXC_BAD_INSTRUCTION: + return "EXC_BAD_INSTRUCTION"; + case EXC_ARITHMETIC: + return "EXC_ARITHMETIC"; + case EXC_EMULATION: + return "EXC_EMULATION"; + case EXC_SOFTWARE: + return "EXC_SOFTWARE"; + case EXC_BREAKPOINT: + return "EXC_BREAKPOINT"; + case EXC_SYSCALL: + return "EXC_SYSCALL"; + case EXC_MACH_SYSCALL: + return "EXC_MACH_SYSCALL"; + case EXC_RPC_ALERT: + return "EXC_RPC_ALERT"; + default: + return "EXC_UNKNOWN"; + } +} + +const uint16_t catch_mach_exception_raise; +const uint16_t catch_mach_exception_raise_state; + +static inline bool +_patch_current_code_as(task_t task, arm_thread_state64_t *state, uint32_t code) { + vm_address_t addr = 0; + mach_vm_address_t inst_page = state->__pc & ~(_page_size - 1); + const vm_address_t inst_offset = state->__pc & (_page_size - 1); + mach_vm_size_t sz; + + kern_return_t rc = vm_allocate(mach_task_self(), &addr, _page_size, VM_FLAGS_ANYWHERE); + bool ret = false; + if (rc != KERN_SUCCESS) { + fprintf(stderr, "vm_allocate: %s\n", mach_error_string(rc)); + goto failed; + } + rc = mach_vm_read_overwrite(task, inst_page, _page_size, addr, &sz); + if (rc != KERN_SUCCESS || sz != _page_size) { + fprintf(stderr, "mach_vm_read_overwrite: %s\n", mach_error_string(rc)); + goto failed; + } + + *(uint32_t*)(addr + inst_offset) = code; + + rc = vm_protect(mach_task_self(), addr, _page_size, 0, VM_PROT_READ | VM_PROT_EXECUTE); + if (rc != KERN_SUCCESS) { + fprintf(stderr, "vm_protect: %s\n", mach_error_string(rc)); + goto failed; + } + vm_prot_t cur, max; + rc = mach_vm_remap(task, &inst_page, _page_size, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, mach_task_self(), addr, false, &cur, &max, VM_INHERIT_SHARE); + if (rc != KERN_SUCCESS) { + fprintf(stderr, "mach_vm_remap: %s\n", mach_error_string(rc)); + goto failed; + } + ret = true; +failed: + if (addr != 0) { + vm_deallocate(mach_task_self(), addr, _page_size); + } + return ret; +} + +#define _SLE_simulate_order(A, R, data, atomic_func, ptr, value) \ + if (A) { \ + if (R) { \ + data = atomic_func(ptr, value, __ATOMIC_ACQ_REL); \ + } else { \ + data = atomic_func(ptr, value, __ATOMIC_ACQUIRE); \ + } \ + } else { \ + if (R) { \ + data = atomic_func(ptr, value, __ATOMIC_RELEASE); \ + } else { \ + data = atomic_func(ptr, value, __ATOMIC_RELAXED); \ + } \ + } +#if IN_PROCESS +#define _SLE_simulate_remap(result, target) (result) = (target) +#define _SLE_simulate_unmap(addr, size) +#else +#define _SLE_simulate_remap(result, target) { \ + vm_prot_t cur, max; \ + kern_return_t rc = vm_remap(mach_task_self(), &tmp_buf, _page_size, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_OVERWRITE, task, target_page, false, &cur, &max, VM_INHERIT_SHARE); \ + if (rc != KERN_SUCCESS) { \ + fprintf(stderr, "vm_remap: %s\n", mach_error_string(rc)); \ + goto failed; \ + } \ +} +#define _SLE_simulate_unmap(addr, size) vm_deallocate(mach_task_self(), addr, size) +#endif + +#define SLE_simulate(task, state, instr, atomic_func) ({ \ + uint32_t size = (instr >> 30) & 0b11; \ + uint32_t Rs = (instr >> 16) & ARMv8_REG_MASK; \ + uint32_t Rn = (instr >> 5) & ARMv8_REG_MASK; \ + uint32_t Rt = instr & ARMv8_REG_MASK; \ + uint32_t A = (instr >> 23) & 0b1; \ + uint32_t R = (instr >> 22) & 0b1; \ + \ + vm_address_t target_page = state->__x[Rn] & ~(_page_size - 1); \ + vm_address_t target_offset = state->__x[Rn] & (_page_size - 1); \ + vm_address_t tmp_buf = 0; \ + _SLE_simulate_remap(tmp_buf, target_page); \ + switch (size) { \ + case 0b00: { \ + uint8_t value = (Rs != 31) ? state->__x[Rs] : 0; \ + uint8_t *ptr = (uint8_t *)(tmp_buf + target_offset); \ + uint8_t data; \ + _SLE_simulate_order(A, R, data, atomic_func, ptr, value); \ + if (Rt != 31) { \ + state->__x[Rt] = data; \ + } \ + break; \ + } \ + case 0b01: { \ + uint16_t value = (Rs != 31) ? state->__x[Rs] : 0; \ + uint16_t *ptr = (uint16_t *)(tmp_buf + target_offset); \ + uint16_t data; \ + _SLE_simulate_order(A, R, data, atomic_func, ptr, value); \ + if (Rt != 31) { \ + state->__x[Rt] = data; \ + } \ + break; \ + } \ + case 0b10: { \ + uint32_t value = (Rs != 31) ? state->__x[Rs] : 0; \ + uint32_t *ptr = (uint32_t *)(tmp_buf + target_offset); \ + uint32_t data; \ + _SLE_simulate_order(A, R, data, atomic_func, ptr, value); \ + if (Rt != 31) { \ + state->__x[Rt] = data; \ + } \ + break; \ + } \ + case 0b11: { \ + uint64_t value = (Rs != 31) ? state->__x[Rs] : 0; \ + uint64_t *ptr = (uint64_t *)(tmp_buf + target_offset); \ + uint64_t data; \ + _SLE_simulate_order(A, R, data, atomic_func, ptr, value); \ + if (Rt != 31) { \ + state->__x[Rt] = data; \ + } \ + break; \ + } \ + } \ + _SLE_simulate_unmap(tmp_buf, _page_size); \ +}) + +#if ENABLE_DOBBY_HOOK + +#define IMPLEMENT_HOOK_CAS_FAMILY(func, int_type_t) \ +static uintptr_t \ +func(RegisterContext *reg_ctx, const HookEntryInfo *info) { \ + uint32_t instr = *(uint32_t*)info->relocated_origin_instructions; \ + uint32_t Rs = (instr >> 16) & ARMv8_REG_MASK; \ + uint32_t Rn = (instr >> 5) & ARMv8_REG_MASK; \ + uint32_t Rt = instr & ARMv8_REG_MASK; \ + uint64_t result; \ + int_type_t *ptr = (Rn != 31) ? (int_type_t *)(reg_ctx->general.x[Rn]) : (int_type_t *)(reg_ctx->sp); \ + int_type_t expected = (Rs != 31) ? reg_ctx->general.x[Rs] : 0; \ + int_type_t desired = (Rt != 31) ? reg_ctx->general.x[Rt] : 0; \ + __atomic_compare_exchange_n(ptr, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + result = expected; \ + if (Rs != 31) { \ + reg_ctx->general.x[Rs] = result; \ + } \ + return 4; \ +} + +IMPLEMENT_HOOK_CAS_FAMILY(_hook_cas_family64, uint64_t) +IMPLEMENT_HOOK_CAS_FAMILY(_hook_cas_family32, uint32_t) +IMPLEMENT_HOOK_CAS_FAMILY(_hook_cas_family16, uint16_t) +IMPLEMENT_HOOK_CAS_FAMILY(_hook_cas_family8, uint8_t) +#undef IMPLEMENT_HOOK_CAS_FAMILY + +static thread_t translator_thread; + +static int _DobbyInstructionHookCASCMP(void *address, int inst_size) { + static const DBICallTy _hooker_by_inst_size[] = { + _hook_cas_family8, + _hook_cas_family16, + _hook_cas_family32, + _hook_cas_family64, + }; + DBICallTy func = _hooker_by_inst_size[inst_size]; + + return DobbyInstructionHookCASCMP(address, func); +} + +static bool disable_dobby_hook = false; + +#endif + +kern_return_t catch_mach_exception_raise_state_identity(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count, int *flavor, thread_state_t in_state, mach_msg_type_number_t in_state_count, thread_state_t out_state, mach_msg_type_number_t *out_state_count) { + arm_thread_state64_t * const state = (arm_thread_state64_t *)out_state; +#define patch_current_code_as(code) if (!_patch_current_code_as(task, state, (code))) {goto failed;} + memcpy(state, in_state, sizeof(*state)); + *out_state_count = in_state_count; + uint32_t instr = (uint32_t)*((uint64_t*)code + 1); +#if !IN_PROCESS + mach_vm_size_t sz; + + kern_return_t rc; +#else + if (instr != *(uint32_t*)(state->__pc)) { + // instruction has been patched. + goto end; + } +#endif + if (ARMv8_is_BLR(instr) || ARMv8_is_BR(instr) || ARMv8_is_LDAR(instr) || ARMv8_is_LDRi64_preindex(instr) || ARMv8_is_LDRiu64(instr) || ARMv8_is_LDUR64(instr) || ARMv8_is_NOP(instr) || ARMv8_is_RET(instr)) { + // instruction has been patched. + goto end; + } + + if (exception != EXC_BAD_INSTRUCTION) { + goto failed; + } + + if (ARMv8_is_CAS_family(instr)) { //casa, casa, casal, casl casb casab no offset + uint32_t size = (instr >> 30) & 0b11; + uint32_t Rs = (instr >> 16) & ARMv8_REG_MASK; + uint32_t Rn = (instr >> 5) & ARMv8_REG_MASK; + uint32_t Rt = instr & ARMv8_REG_MASK; + vm_address_t target_page = state->__x[Rn] & ~(_page_size - 1); + vm_address_t target_offset = state->__x[Rn] & (_page_size - 1); + vm_address_t tmp_buf = 0; + _SLE_simulate_remap(tmp_buf, target_page); + +#if ENABLE_DOBBY_HOOK + uint32_t next_inst = *(uint32_t*)(state->__pc + 4); + + bool is_cmp = ARMv8_is_CMP_er(next_inst) || ARMv8_is_CMP_i(next_inst) || ARMv8_is_CMP_sr(next_inst); + if (*(uint32_t*)(state->__pc - 4) == 0x14000002) { + is_cmp = false; + } + if (is_cmp) { + is_cmp = (Rs == ((next_inst >> 5) & ARMv8_REG_MASK)); + } + + if (thread == translator_thread || !is_cmp || disable_dobby_hook || RS_FAILED == _DobbyInstructionHookCASCMP((void*)state->__pc, size)) +#endif + { + uint64_t result; + switch (size) { + case 0b00: { + uint8_t *ptr = (uint8_t *)(tmp_buf + target_offset); + uint8_t expected = (Rs != 31) ? state->__x[Rs] : 0; + uint8_t desired = (Rt != 31) ? state->__x[Rt] : 0; + result = __sync_val_compare_and_swap(ptr, expected, desired); + break; + } + case 0b01: { + uint16_t *ptr = (uint16_t *)(tmp_buf + target_offset); + uint16_t expected = (Rs != 31) ? state->__x[Rs] : 0; + uint16_t desired = (Rt != 31) ? state->__x[Rt] : 0; + result = __sync_val_compare_and_swap(ptr, expected, desired); + break; + } + case 0b10: { + uint32_t *ptr = (uint32_t *)(tmp_buf + target_offset); + uint32_t expected = (Rs != 31) ? state->__x[Rs] : 0; + uint32_t desired = (Rt != 31) ? (uint32_t)state->__x[Rt] : 0; + result = __sync_val_compare_and_swap(ptr, expected, desired); + break; + } + case 0b11: { + uint64_t *ptr = (uint64_t *)(tmp_buf + target_offset); + uint64_t expected = (Rs != 31) ? state->__x[Rs] : 0; + uint64_t desired = (Rt != 31) ? state->__x[Rt] : 0; + result = __sync_val_compare_and_swap(ptr, expected, desired); + break; + } + } + if (Rs != 31) { + state->__x[Rs] = result; + } + _SLE_simulate_unmap(tmp_buf, _page_size); + goto next_pc; + } + } else if (ARMv8_is_CASP_family(instr)) { //casp, caspa, caspal, caspl: LSE + uint32_t size = (instr >> 30) & 0b1; + uint32_t Rs = (instr >> 16) & ARMv8_REG_MASK; + uint32_t Rn = (instr >> 5) & ARMv8_REG_MASK; + uint32_t Rt = instr & ARMv8_REG_MASK; + vm_address_t target_page = state->__x[Rn] & ~(_page_size - 1); + vm_address_t target_offset = state->__x[Rn] & (_page_size - 1); + vm_address_t tmp_buf = 0; + _SLE_simulate_remap(tmp_buf, target_page); + if (size) { + //64bit + __uint128_t *ptr = (__uint128_t *)(tmp_buf + target_offset); + __uint128_t expected = ((__uint128_t)state->__x[Rs + 1] << 64) | (__uint128_t)state->__x[Rs]; + __uint128_t desired = ((__uint128_t)state->__x[Rt + 1] << 64) | (__uint128_t)state->__x[Rt]; + __uint128_t ret = __sync_val_compare_and_swap(ptr, expected, desired); + state->__x[Rs] = ret; + state->__x[Rs + 1] = ret >> 64; + } else { + //32bit + uint64_t *ptr = (uint64_t *)(tmp_buf + target_offset); + uint64_t expected = ((uint64_t)(uint32_t)state->__x[Rs + 1] << 32) | (uint64_t)(uint32_t)state->__x[Rs]; + uint64_t desired = ((uint64_t)(uint32_t)state->__x[Rt + 1] << 32) | (uint64_t)(uint32_t)state->__x[Rt]; + uint64_t ret = __sync_val_compare_and_swap(ptr, expected, desired); + state->__x[Rs] = (uint32_t)ret; + state->__x[Rs + 1] = (uint32_t)(ret >> 32); + } + _SLE_simulate_unmap(tmp_buf, _page_size); + goto next_pc; + } else if (ARMv8_is_SWP_family(instr)) { // swp, swpa, swpal, swpl,swpb, swpab, swpalb swplb LSE + SLE_simulate(task, state, instr, __atomic_exchange_n); + goto next_pc; + } else if (ARMv8_is_ldadd_family(instr)) { //ldadd, ldadda, ldaddal, ldaddl LSE + SLE_simulate(task, state, instr, __atomic_fetch_add); + goto next_pc; + } else if (ARMv8_is_LDSET_family(instr)) { //ldsetb, ldsetab, ldsetalb, ldsetlb ldseth, ldsetah, ldsetalh, ldsetlh ldset ldseta ldsetal ldsetl //LSE + SLE_simulate(task, state, instr, __atomic_fetch_or); + goto next_pc; + } else if (ARMv8_is_LDCLR_family(instr)) { //ldclrb, ldclrab, ldclralb, ldclrlb ldclrh, ldclrah, ldclralh, ldclrlh ldclr ldclra ldclral ldclrl //LSE +#define __SLE_atomic_fetch_clr(ptr, value, order) __atomic_fetch_and(ptr, ~value, order) + SLE_simulate(task, state, instr, __SLE_atomic_fetch_clr); +#undef __SLE_atomic_fetch_clr + goto next_pc; + } else if (ARMv8_is_LDEOR_family(instr)) { //ldeor ldeora ldeoral ldeorl ldeor*b ldeor*h LSE + SLE_simulate(task, state, instr, __atomic_fetch_xor); + goto next_pc; + } else if ((instr & ~ARMv8_REG_MASK) == 0xd53be040) { //mrs xt, CNTVCT_EL0 + // pre-iOS 13 do not support this privileged instruction. Simulate it with mach time. + uint32_t Rt = instr & ARMv8_REG_MASK; + if (Rt != 31) { + if (__builtin_available(iOS 10, *)) { +#if IN_PROCESS + uint64_t mach_continuous_time_iOS13(void); + state->__x[Rt] = mach_continuous_time_iOS13(); +#else + state->__x[Rt] = mach_continuous_time(); +#endif + } else { + state->__x[Rt] = mach_absolute_time(); + } + } + goto next_pc; + } else if (instr == 0xd65f0bff || //retaa + instr == 0xd65f0fff) { //retab + patch_current_code_as(ARMv8_RET()); + } else if ((instr & 0xfffffc00) == 0xdac10800 || //pacda + (instr & 0xfffffc00) == 0xdac10000 || //pacia + (instr & 0xfffffc00) == 0xdac10400 || //pacib + (instr & 0xfffffc00) == 0xdac10c00 || //pacdb + (instr & 0xfffffc00) == 0xdac11000 || //autia + (instr & 0xfffffc00) == 0xdac11400 || //autib + (instr & 0xfffffc00) == 0xdac11800 || //autda + (instr & 0xfffffc00) == 0xdac11c00 || //autdb + (instr & 0xfffffc00) == 0xdac11000 || //autia + (instr & 0xffffffe0) == 0xdac123e0 || //paciza + (instr & 0xffffffe0) == 0xdac127e0 || //pacizb + (instr & 0xffffffe0) == 0xdac12be0 || //pacdza + (instr & 0xffffffe0) == 0xdac12fe0 || //pacdzb + (instr & 0xffffffe0) == 0xdac133e0 || //autiza + (instr & 0xffffffe0) == 0xdac137e0 || //autizb + (instr & 0xffffffe0) == 0xdac13be0 || //autdza + (instr & 0xffffffe0) == 0xdac13fe0 || //autdzb + (instr & 0xffffffe0) == 0xdac143e0 || //xpaci + (instr & 0xffffffe0) == 0xdac147e0 || //xpacd + instr == 0xd503233f || //paciasp + instr == 0xd503237f || //pacibsp + instr == 0xd50323bf || //autiasp + instr == 0xd50323ff //autibsp + ) { + patch_current_code_as(ARMv8_NOP()); + } else if ((instr & 0xfffffc00) == 0xd73f0800 || //blraa + (instr & 0xfffffc00) == 0xd73f0c00 || //blrab + (instr & 0xfffffc1f) == 0xd63f081f || //blraaz + (instr & 0xfffffc1f) == 0xd63f0c1f) { //blrabz + patch_current_code_as(ARMv8_BLR((instr >> ARMv8_REG_BITS) & ARMv8_REG_MASK)); + } else if ((instr & 0xfffffc00) == 0xd71f0800 || //braa + (instr & 0xfffffc00) == 0xd71f0c00 || //brab + (instr & 0xfffffc1f) == 0xd61f081f || //braaz + (instr & 0xfffffc1f) == 0xd61f0c1f) { //brabz + patch_current_code_as(ARMv8_BR((instr >> ARMv8_REG_BITS) & ARMv8_REG_MASK)); + } else if ((instr & 0xffa00400) == 0xf8200400 || //ldraa + (instr & 0xffa00400) == 0xf8a00400) { //ldrab + uint32_t sign = (instr & (0b1 << 22)) >> 22; + uint32_t imm = (instr & (0b111111111 << 12)) >> 12; + uint32_t rs_rd = (instr & 0b1111111111); + uint32_t rd = rs_rd & 0b11111; + uint32_t rs = (rs_rd >> 5) & 0b11111; + uint32_t is_pre = (instr & (0x800)) >> 11; + if (is_pre) { //ldra* xd, [xc, #xxx]! + imm <<= 3; + uint32_t real_imm = (sign == 0) ? imm : ((0xffffffff << 12) | imm); + if ((real_imm >> 8) == 0 || ((int32_t)real_imm >> 8) == 0xffffffff) { + real_imm = real_imm & 0b111111111; + patch_current_code_as(ARMv8_LDRi64_preindex(rs, rd, real_imm)); + } else { + uint32_t rd = rs_rd & 0b11111; + uint32_t rs = (rs_rd >> 5) & 0b11111; + state->__x[rs] += (int64_t)(int32_t)real_imm; +#if IN_PROCESS + state->__x[rd] = *(uint64_t *)state->__x[rs]; +#else + rc = mach_vm_read_overwrite(task, state->__x[rs], sizeof(uint64_t), (vm_address_t)&state->__x[rd], &sz); + if (rc != KERN_SUCCESS || sz != sizeof(uint64_t)) { + fprintf(stderr, "mach_vm_read_overwrite: %s\n", mach_error_string(rc)); + goto failed; + } +#endif + goto next_pc; + } + } else { //ldra* xd, [xc, #xxx] + uint32_t rd = rs_rd & 0b11111; + uint32_t rs = (rs_rd >> 5) & 0b11111; + if (sign == 0) { + uint32_t imm12 = ((instr & ~0xffa00400) >> 10) & BIT_MASK(12); + patch_current_code_as(ARMv8_LDRiu64(rd, rs, imm12)); + } else { + imm <<= 3; + uint32_t real_imm = ((0xffffffff << 12) | imm); + if (((int32_t)real_imm >> 8) == 0xffffffff) { + real_imm = real_imm & 0b111111111; + patch_current_code_as(ARMv8_LDUR64(rd, rs, real_imm)); + } else { +#if IN_PROCESS + state->__x[rd] = *(uint64_t *)(state->__x[rs] + (int64_t)(int32_t)real_imm); +#else + rc = mach_vm_read_overwrite(task, state->__x[rs] + (int64_t)(int32_t)real_imm, sizeof(uint64_t), (vm_address_t)&state->__x[rd], &sz); + if (rc != KERN_SUCCESS || sz != sizeof(uint64_t)) { + fprintf(stderr, "mach_vm_read_overwrite: %s\n", mach_error_string(rc)); + goto failed; + } +#endif + goto next_pc; + } + } + + } + //} else if ((instr & 0b00111111111111111111110000000000) == 0b00111000101111111100000000000000) { //ldapr ldaprb ldaprh + } else if (ARMv8_is_LDAPR_family(instr)) { //ldapr ldaprb ldaprh + uint32_t size = (instr >> 30) & 0b11; + uint32_t Rn = (instr >> 5) & 0b11111; + uint32_t Rt = instr & 0b11111; + patch_current_code_as(ARMv8_LDAR(size, Rn, Rt)); + } else { + goto failed; + } + + + goto end; +next_pc: + //sys_icache_invalidate(hhh, 8); + state->__pc += 4; +end: + + return KERN_SUCCESS; +failed: + if (exception == EXC_BAD_INSTRUCTION) { + printf("bad instruction: %x\n", instr); + } + printf( "%s: \n" + "codes %llx\t" + "%llx\n" + "x0 %llx\t" "x1 %llx\n" + "x2 %llx\t" "x3 %llx\n" + "x4 %llx\t" "x5 %llx\n" + "x6 %llx\t" "x7 %llx\n" + "x8 %llx\t" "x9 %llx\n" + "x10 %llx\t" "x11 %llx\n" + "x12 %llx\t" "x13 %llx\n" + "x14 %llx\t" "x15 %llx\n" + "x16 %llx\t" "x17 %llx\n" + "x18 %llx\t" "x19 %llx\n" + "x20 %llx\t" "x21 %llx\n" + "x22 %llx\t" "x23 %llx\n" + "x24 %llx\t" "x25 %llx\n" + "x26 %llx\t" "x27 %llx\n" + "x28 %llx\t" "fp %llx\n" + "lr %llx\t" "sp %llx\n" + "pc %llx\n", + mach_exc_string(exception), + *(uint64_t*)code, + *((uint64_t*)code + 1), + state->__x[0], state->__x[1], + state->__x[2], state->__x[3], + state->__x[4], state->__x[5], + state->__x[6], state->__x[7], + state->__x[8], state->__x[9], + state->__x[10], state->__x[11], + state->__x[12], state->__x[13], + state->__x[14], state->__x[15], + state->__x[16], state->__x[17], + state->__x[18], state->__x[19], + state->__x[20], state->__x[21], + state->__x[22], state->__x[23], + state->__x[24], state->__x[25], + state->__x[26], state->__x[27], + state->__x[28], state->__fp, + state->__lr, state->__sp, + state->__pc); + return KERN_FAILURE; +} + +static mach_port_t +_translator_prepare_exc_port(void) { + kern_return_t err; + mach_port_t server_port; + err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port); + if (err != KERN_SUCCESS) { + fprintf(stderr, "translator: mach_port_allocate: %s\n", mach_error_string(err)); + return MACH_PORT_NULL; + } + err = mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND); + if (err != KERN_SUCCESS) { + fprintf(stderr, "translator: mach_port_insert_right: %s\n", mach_error_string(err)); + mach_port_deallocate(mach_task_self(), server_port); + return MACH_PORT_NULL; + } + return server_port; +} + +mach_port_t translator(task_t t) { + task_t child = t; + kern_return_t err; + //host_page_size(mach_host_self(), &_page_size); + _page_size = vm_page_size; + + mach_port_t server_port = _translator_prepare_exc_port(); + if (server_port == MACH_PORT_NULL) { + fprintf(stderr, "translator: failed to prepare mach_port\n"); + return MACH_PORT_NULL; + } + + err = task_set_exception_ports(child, EXC_MASK_BAD_INSTRUCTION, server_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, ARM_THREAD_STATE64); + if (err != MACH_PORT_NULL) { + fprintf(stderr, "translator: task_set_exception_ports %s\n", mach_error_string(err)); + } + + return server_port; +} + +#if ENABLE_DOBBY_HOOK + +static void * +translator_thread_internal(void *ctx) { + static const char thread_name[] = "com.sxx.reductant-translator0"; + //pthread_setname_np(thread_name); + #define PROC_INFO_CALL_SETCONTROL 0x5 + #define PROC_SELFSET_THREADNAME 2 + int __proc_info(int callnum, int pid, int flavor, uint64_t arg, void * buffer, int buffersize) ; + __proc_info(PROC_INFO_CALL_SETCONTROL, getpid(), PROC_SELFSET_THREADNAME, (uint64_t)0, (void*)thread_name, sizeof(thread_name)); + thread_t translator_thread = *(thread_t *)ctx; + mach_port_t server_port = _translator_prepare_exc_port(); + if (server_port == MACH_PORT_NULL) { + fprintf(stderr, "translator_thread_internal: failed to prepare mach_port\n"); + return NULL; + } + kern_return_t err; + err = thread_set_exception_ports(translator_thread, EXC_MASK_BAD_INSTRUCTION, server_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, ARM_THREAD_STATE64); + if (err != KERN_SUCCESS) { + fprintf(stderr, "translator_thread_internal: thread_set_exception_ports %s\n", mach_error_string(err)); + return NULL; + } + if ((err = mach_msg_server(mach_exc_server, 4096, server_port, 0)) != KERN_SUCCESS) { + fprintf(stderr, "translator_thread_internal: mach_msg_server %d\n", err); + } + return NULL; +} + +#endif + +void +translator_loop(mach_port_t port) { + kern_return_t err; + + if ((err = mach_msg_server(mach_exc_server, 4096, port, 0)) != KERN_SUCCESS) { + fprintf(stderr, "translator_loop: mach_msg_server %d\n", err); + } +} + +void * +translator_subthread(void *arg) { + pthread_setname_np("com.sxx.reductant-translator"); +#if ENABLE_DOBBY_HOOK + if (getenv("RT_DISABLE_DOBBY_HOOK")) { + disable_dobby_hook = true; + } else { + //host_page_size(mach_host_self(), &_page_size); + _page_size = vm_page_size; + dobby_enable_near_branch_hook(); + pthread_t pth; + pthread_attr_t attr; + pthread_attr_init(&attr); + thread_t current_thread = mach_thread_self(); + translator_thread = current_thread; + pthread_attr_setdetachstate(&attr, 1); + pthread_create(&pth, &attr, translator_thread_internal, ¤t_thread); + } +#endif + mach_port_t port = translator(mach_task_self()); + if (port != MACH_PORT_NULL) { + translator_loop(port); + } + return NULL; +} \ No newline at end of file