[
  {
    "path": ".gitignore",
    "content": ".theos\npackages\n.DS_Store\n.scrapped"
  },
  {
    "path": "CoreSymbolication.h",
    "content": "//\n//  CoreSymbolication.h\n//\n//  Created by R J Cooper on 05/06/2012.\n//  This file: Copyright (c) 2012 Mountainstorm\n//  API: Copyright (c) 2008 Apple Inc. All rights reserved.\n//  \n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of this software and associated documentation files (the \"Software\"), to deal\n//  in the Software without restriction, including without limitation the rights\n//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n//  copies of the Software, and to permit persons to whom the Software is\n//  furnished to do so, subject to the following conditions:\n//  \n//  The above copyright notice and this permission notice shall be included in all\n//  copies or substantial portions of the Software.\n//  \n//  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n//  SOFTWARE.\n//\n\n//\n// Derived by looking at use within the dtrace source and a little bit of IDA work\n//\n// See the unit testcases for examples of how to use the API; its a really nice symbol\n// api, a real shame Apple dont make it a public framework. \n//\n// Things you might want to know;\n//  - a Symbolicator is a top level object representing the kernel/process etc\n//  - a Symbolicator contains multiple SymbolOwners\n// \n//  - a SymbolOwner represents a blob which owns symbols e.g. executable, library\n//  - a SymbolOwner contains multiple regions and contains multiple symbols\n//\n//  - a Region represents a continuous block of memory within a symbol owner e.g. the  __TEXT __objc_classname section\n//  - a Region contains multiple symbols ... not it doesn't own them, just contains them\n//\n//  - a Symbol represents a symbol e.g. function, variable\n//\n\n#if !defined(__CORESYMBOLICATION_CORESYMBOLICATION__)\n#define __CORESYMBOLICATION_CORESYMBOLICATION__ 1\n#define __CORESYMBOLICATION__ 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <mach/mach.h>\n\n\n/*\n * Types\n */\n// Under the hood the framework basically just calls through to a set of C++ libraries\nstruct sCSTypeRef {\n\tvoid* csCppData;\t// typically retrieved using CSCppSymbol...::data(csData & 0xFFFFFFF8)\n\tvoid* csCppObj;\t\t// a pointer to the actual CSCppObject\n};\ntypedef struct sCSTypeRef CSTypeRef;\n\n\ntypedef CSTypeRef CSSymbolicatorRef;\ntypedef CSTypeRef CSSourceInfoRef;\ntypedef CSTypeRef CSSymbolOwnerRef;\ntypedef CSTypeRef CSSectionRef;\ntypedef CSTypeRef CSSegmentRef;\ntypedef CSTypeRef CSSymbolRef;\ntypedef CSTypeRef CSRegionRef;\ntypedef CSTypeRef CSUUIDRef;\n\n\nstruct sCSRange {\n   unsigned long long location;\n   unsigned long long length;\n};\ntypedef struct sCSRange CSRange;\n\n\n// Note: this structure may well be wrong\ntypedef struct sCSNotificationData {\n\tCSSymbolicatorRef symbolicator;\n\tunion {\n\t\tstruct {\n\t\t\tlong value;\n\t\t} ping;\n\t\t\n\t\tstruct {\n\t\t\tCSSymbolOwnerRef symbolOwner;\n\t\t} dyldLoad;\n\t} u;\n} CSNotificationData;\n\n\ntypedef void* CSDictionaryKeyCallBacks;\ntypedef void* CSDictionaryValueCallBacks;\ntypedef void* CSSetCallBacks;\n\n\ntypedef int (^CSNotification)(uint32_t notification_type, CSNotificationData data);\ntypedef int (^CSRegionIterator)(CSRegionRef region);\ntypedef int (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner);\ntypedef int (^CSSectionIterator)(CSSectionRef section);\ntypedef int (^CSSourceInfoIterator)(CSSourceInfoRef sourceInfo);\ntypedef int (^CSSymbolIterator)(CSSymbolRef symbol);\ntypedef int (^CSSegmentIterator)(CSSegmentRef segment);\n\n\n/*\n * Defines\n */\n#define kCSNull\t\t\t\t\t\t\t\t((CSTypeRef) {NULL, NULL})\n#define kCSNow\t\t\t\t\t\t\t\t0x8000000000000000ull\n// we've no idea what value kCSSymbolOwnerDataFoundDsym has; its only use in dtrace has been optimised out\n#define kCSSymbolOwnerDataFoundDsym\t\t\t0\n#define kCSSymbolOwnerIsAOut\t\t\t\t0\n#define kCSSymbolicatorTrackDyldActivity\t1\n\n#define kCSNotificationPing\t\t\t\t\t1\n#define kCSNotificationInitialized\t\t\t0x0010\n#define kCSNotificationDyldLoad\t\t\t\t0x0100\n#define kCSNotificationDyldUnload\t\t\t0x0101\n// kCSNotificationTimeout must be a value greater than 0x1001\n#define kCSNotificationTimeout\t\t\t\t0x1002\n#define kCSNotificationTaskExit\t\t\t\t0x1000\n#define kCSNotificationFini\t\t\t\t\t0x80000000\n\n\n/*\n * External symbols\n */\n\nextern const char* kCSRegionMachHeaderName;\nextern const CSDictionaryKeyCallBacks kCSTypeDictionaryKeyCallBacks;\nextern const CSDictionaryValueCallBacks kCSTypeDictionaryValueCallBacks;\nextern const CSDictionaryKeyCallBacks kCSTypeDictionaryWeakKeyCallBacks;\nextern const CSDictionaryValueCallBacks kCSTypeDictionaryWeakValueCallBacks;\nextern const CSSetCallBacks kCSTypeSetCallBacks;\nextern const CSSetCallBacks kCSTypeSetWeakCallBacks;\n\n\n/*\n * Architecture functions\n */\n// Valid names: i386, x86_64, arm, armv4t, armv5tej, armv6, armv7, armv7f, armv7k, ppc, ppc64\ncpu_type_t CSArchitectureGetArchitectureForName(const char* arch);\ncpu_type_t CSArchitectureGetCurrent();\ncpu_type_t CSArchitectureGetFamily(cpu_type_t type);\nconst char* CSArchitectureGetFamilyName(cpu_type_t type);\n\nBoolean CSArchitectureIs32Bit(cpu_type_t type);\nBoolean CSArchitectureIs64Bit(cpu_type_t type);\nBoolean CSArchitectureIsArm(cpu_type_t type);\nBoolean CSArchitectureIsBigEndian(cpu_type_t type);\nBoolean CSArchitectureIsI386(cpu_type_t type);\nBoolean CSArchitectureIsLittleEndian(cpu_type_t type);\nBoolean CSArchitectureIsPPC(cpu_type_t type);\nBoolean CSArchitectureIsPPC64(cpu_type_t type);\nBoolean CSArchitectureIsX86_64(cpu_type_t type);\n\nBoolean CSArchitectureMatchesArchitecture(cpu_type_t a, cpu_type_t b);\n\n\n/*\n * Description functions\n */\nCFStringRef CSCopyDescription(CSTypeRef cs);\nCFStringRef CSCopyDescriptionWithIndent(CSTypeRef cs, unsigned int indent);\n\n\n/*\n * General utility functions\n */\nBoolean CSEqual(CSTypeRef cs1, CSTypeRef cs2);\n//XXX: CSExceptionSafeThreadRunBlock\nCFIndex CSGetRetainCount(CSTypeRef cs);\nBoolean CSIsNull(CSTypeRef cs);\nCSTypeRef CSRetain(CSTypeRef cs);\nvoid CSRelease(CSTypeRef cs);\nvoid CSShow(CSTypeRef cs);\n\n\n/*\n * Dyld functions\n */\nvm_address_t CSGetDyldSharedCacheSlide(mach_port_t port);\nCSUUIDRef CSGetDyldSharedCacheUUID(mach_port_t port);\n\n\n/*\n * XXX: Map functions\n */\n//CSMMapArchiveCacheCopyMMapArchive\n//CSMMapArchiveCacheReleaseMMapArchive\n//CSMMapArchiveCacheSetShouldStoreToDaemon\n\n\n/*\n * Range functions\n */\nBoolean CSRangeContainsRange(CSRange r1, CSRange r2);\nBoolean CSRangeIntersectsRange(CSRange r1, CSRange r2);\n\n\n/*\n * Region functions\n */\nCFStringRef CSRegionCopyDescriptionWithIndent(CSRegionRef region, unsigned int indent);\nint CSRegionForeachSourceInfo(CSRegionRef region, CSSourceInfoIterator each);\nint CSRegionForeachSymbol(CSRegionRef region, CSSymbolIterator each);\nconst char* CSRegionGetName(CSRegionRef region);\nCSRange CSRegionGetRange(CSRegionRef region);\nCSSymbolOwnerRef CSRegionGetSymbolOwner(CSRegionRef region);\nCSSymbolicatorRef CSRegionGetSymbolicator(CSRegionRef region);\n\n\n/*\n * XXX: Section/Segment functions\n */\n/*\nCSSectionGetSegment\nCSSegmentForeachSection\n*/\n\n\n/*\n * XXX: Signature functions\n */\n/*\nCSSignatureAddSegment\nCSSignatureAllocateSegments\nCSSignatureCopy\nCSSignatureEncodeSymbolOwner\nCSSignatureEncodeSymbolicator\nCSSignatureFreeSegments\n*/\n\n\n/*\n * Source Info functions\n */\nCFStringRef CSSourceInfoCopyDescriptionWithIndent(CSSourceInfoRef info, unsigned int indent);\nint CSSourceInfoGetColumn(CSSourceInfoRef info);\nconst char* CSSourceInfoGetFilename(CSSourceInfoRef info);\nint CSSourceInfoGetLineNumber(CSSourceInfoRef info);\nconst char* CSSourceInfoGetPath(CSSourceInfoRef info);\nCSRange CSSourceInfoGetRange(CSSourceInfoRef info);\nCSRegionRef CSSourceInfoGetRegion(CSSourceInfoRef info);\nCSSymbolRef CSSourceInfoGetSymbol(CSSourceInfoRef info);\nCSSymbolOwnerRef CSSourceInfoGetSymbolOwner(CSSourceInfoRef info);\nCSSymbolicatorRef CSSourceInfoGetSymbolicator(CSSourceInfoRef info);\n\n\n/*\n * Symbol functions\n */\n\nCFStringRef CSSymbolCopyDescriptionWithIndent(CSSymbolRef sym, unsigned int indent);\nint CSSymbolForeachSourceInfo(CSSymbolRef sym, CSSourceInfoIterator);\nlong CSSymbolGetFlags(CSSymbolRef sym);\nCSTypeRef CSSymbolGetInstructionData(CSSymbolRef sym);\nconst char* CSSymbolGetMangledName(CSSymbolRef sym);\nconst char* CSSymbolGetName(CSSymbolRef sym);\nCSRange CSSymbolGetRange(CSSymbolRef sym);\nCSRegionRef CSSymbolGetRegion(CSSymbolRef sym);\nCSSectionRef CSSymbolGetSection(CSSymbolRef sym);\nCSSegmentRef CSSymbolGetSegment(CSSymbolRef sym);\nCSSymbolOwnerRef CSSymbolGetSymbolOwner(CSSymbolRef sym);\nCSSymbolicatorRef CSSymbolGetSymbolicator(CSSymbolRef sym);\nBoolean CSSymbolIsArm(CSSymbolRef sym);\nBoolean CSSymbolIsDebugMap(CSSymbolRef sym);\nBoolean CSSymbolIsDwarf(CSSymbolRef sym);\nBoolean CSSymbolIsDyldStub(CSSymbolRef sym);\nBoolean CSSymbolIsExternal(CSSymbolRef sym);\nBoolean CSSymbolIsFunction(CSSymbolRef sym);\nBoolean CSSymbolIsFunctionStarts(CSSymbolRef sym);\nBoolean CSSymbolIsKnownLength(CSSymbolRef sym);\nBoolean CSSymbolIsMangledNameSourceDwarf(CSSymbolRef sym);\nBoolean CSSymbolIsMangledNameSourceDwarfMIPSLinkage(CSSymbolRef sym);\nBoolean CSSymbolIsMangledNameSourceNList(CSSymbolRef sym);\nBoolean CSSymbolIsMerged(CSSymbolRef sym);\nBoolean CSSymbolIsNList(CSSymbolRef sym);\nBoolean CSSymbolIsNameSourceDwarf(CSSymbolRef sym);\nBoolean CSSymbolIsNameSourceDwarfMIPSLinkage(CSSymbolRef sym);\nBoolean CSSymbolIsNameSourceNList(CSSymbolRef sym);\nBoolean CSSymbolIsObjcMethod(CSSymbolRef sym);\nBoolean CSSymbolIsOmitFramePointer(CSSymbolRef sym);\nBoolean CSSymbolIsPrivateExternal(CSSymbolRef sym);\nBoolean CSSymbolIsThumb(CSSymbolRef sym);\nBoolean CSSymbolIsUnnamed(CSSymbolRef sym);\n\n\n/*\n * XXX: SymbolOwner functions\n */\n/*\nCSSymbolOwnerAddInContext\nCSSymbolOwnerCacheFlush\nCSSymbolOwnerCacheGetEntryCount\nCSSymbolOwnerCacheGetFlags\nCSSymbolOwnerCacheGetMemoryLimit\nCSSymbolOwnerCacheGetMemoryUsed\nCSSymbolOwnerCachePrintEntries\nCSSymbolOwnerCachePrintStats\nCSSymbolOwnerCacheResetStats\nCSSymbolOwnerCacheSetFlags\nCSSymbolOwnerCacheSetMemoryLimit\nCSSymbolOwnerCopyDescriptionWithIndent\nCSSymbolOwnerCreateSignature\nCSSymbolOwnerEditRelocations\nCSSymbolOwnerForeachRegion\nCSSymbolOwnerForeachRegionWithName\nCSSymbolOwnerForeachSection\nCSSymbolOwnerForeachSegment\nCSSymbolOwnerForeachSourceInfo\nCSSymbolOwnerForeachSymbol\nCSSymbolOwnerForeachSymbolWithMangledName\nCSSymbolOwnerForeachSymbolWithName\nCSSymbolOwnerGetArchitecture\nCSSymbolOwnerGetBaseAddress\nCSSymbolOwnerGetCompatibilityVersion\nCSSymbolOwnerGetCurrentVersion\nCSSymbolOwnerGetDataFlags\nCSSymbolOwnerGetDataTypeID\nCSSymbolOwnerGetDsymPath\nCSSymbolOwnerGetDsymVersion\nCSSymbolOwnerGetFlags\nCSSymbolOwnerGetLastModifiedTimestamp\nCSSymbolOwnerGetLoadTimestamp\nCSSymbolOwnerGetName\nCSSymbolOwnerGetPath\nCSSymbolOwnerGetRegionCount\nCSSymbolOwnerGetRegionWithAddress\nCSSymbolOwnerGetRegionWithName\nCSSymbolOwnerGetSectionWithAddress\nCSSymbolOwnerGetSectionWithName\nCSSymbolOwnerGetSegmentWithAddress\nCSSymbolOwnerGetSourceInfoCount\nCSSymbolOwnerGetSourceInfoWithAddress\nCSSymbolOwnerGetSymbolCount\nCSSymbolOwnerGetSymbolWithAddress\nCSSymbolOwnerGetSymbolWithMangledName\nCSSymbolOwnerGetSymbolWithName\nCSSymbolOwnerGetSymbolicator\nCSSymbolOwnerGetTransientUserData\nCSSymbolOwnerGetUUID\nCSSymbolOwnerGetUnloadTimestamp\nCSSymbolOwnerGetVersion\nCSSymbolOwnerIsAOut\nCSSymbolOwnerIsBundle\nCSSymbolOwnerIsCommpage\nCSSymbolOwnerIsDsym\nCSSymbolOwnerIsDyld\nCSSymbolOwnerIsDyldSharedCache\nCSSymbolOwnerIsDylib\nCSSymbolOwnerIsDylibStub\nCSSymbolOwnerIsKextBundle\nCSSymbolOwnerIsMachO\nCSSymbolOwnerIsMutable\nCSSymbolOwnerIsObjCGCSupported\nCSSymbolOwnerIsObjCRetainReleaseSupported\nCSSymbolOwnerIsObject\nCSSymbolOwnerIsObsolete\nCSSymbolOwnerIsPIE\nCSSymbolOwnerIsProtected\nCSSymbolOwnerIsRestricted\nCSSymbolOwnerIsSlid\nCSSymbolOwnerIsStaticLibraryArchiveEntry\nCSSymbolOwnerMakeMutableInContext\nCSSymbolOwnerRemoveInContext\nCSSymbolOwnerSetLoadTimestamp\nCSSymbolOwnerSetPath\nCSSymbolOwnerSetRelocationCount\nCSSymbolOwnerSetTransientUserData\nCSSymbolOwnerSetUnloadTimestamp\n*/\n\nconst char *CSSymbolOwnerGetPath(CSSymbolOwnerRef owner);\n\n/*\n * XXX: Symbolicator functions\n */\n// XXX: CSSymbolicatorAddSymbolOwner\n// XXX: CSSymbolicatorApplyMutableContextBlock\nCFStringRef CSSymbolicatorCopyDescriptionWithIndent(CSSymbolicatorRef cs, unsigned int indent);\nCFDataRef CSSymbolicatorCreateSignature(CSSymbolicatorRef cs);\n\nCSSymbolicatorRef CSSymbolicatorCreateWithMachKernel(void);\nCSSymbolicatorRef CSSymbolicatorCreateWithMachKernelFlagsAndNotification(long flags, CSNotification notification);\nCSSymbolicatorRef CSSymbolicatorCreateWithPathAndArchitecture(const char* path, cpu_type_t type);\nCSSymbolicatorRef CSSymbolicatorCreateWithPathArchitectureFlagsAndNotification(const char* path, cpu_type_t type, long flags, CSNotification notification);\nCSSymbolicatorRef CSSymbolicatorCreateWithPid(pid_t pid);\nCSSymbolicatorRef CSSymbolicatorCreateWithPidFlagsAndNotification(pid_t pid, long flags, CSNotification notification);\nCSSymbolicatorRef CSSymbolicatorCreateWithSignature(CFDataRef sig);\nCSSymbolicatorRef CSSymbolicatorCreateWithSignatureAndNotification(CFDataRef sig, CSNotification notification);\nCSSymbolicatorRef CSSymbolicatorCreateWithTask(task_t task);\nCSSymbolicatorRef CSSymbolicatorCreateWithTaskFlagsAndNotification(task_t task, long flags, CSNotification notification);\nCSSymbolicatorRef CSSymbolicatorCreateWithURLAndArchitecture(CFURLRef url, cpu_type_t type);\nCSSymbolicatorRef CSSymbolicatorCreateWithURLArchitectureFlagsAndNotification(CFURLRef url, cpu_type_t type, long flags, CSNotification notification);\n\nint CSSymbolicatorForceFullSymbolExtraction(CSSymbolicatorRef cs);\nint CSSymbolicatorForeachRegionAtTime(CSSymbolicatorRef cs, uint64_t time, CSRegionIterator it);\nint CSSymbolicatorForeachRegionWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSRegionIterator it);\nint CSSymbolicatorForeachSectionAtTime(CSSymbolicatorRef cs, uint64_t time, CSSectionIterator it);\nint CSSymbolicatorForeachSegmentAtTime(CSSymbolicatorRef cs, uint64_t time, CSSegmentIterator it);\n// XXX: CSSymbolicatorForeachSharedCache\n// XXX: CSSymbolicatorForeachSharedCacheSymbolicatorWithFlagsAndNotification\nint CSSymbolicatorForeachSourceInfoAtTime(CSSymbolicatorRef cs, uint64_t time, CSSourceInfoIterator it);\nint CSSymbolicatorForeachSymbolAtTime(CSSymbolicatorRef cs, uint64_t time, CSSymbolIterator it);\nint CSSymbolicatorForeachSymbolOwnerAtTime(CSSymbolicatorRef cs, uint64_t time, CSSymbolOwnerIterator it);\n\n// XXX: CSSymbolicatorForeachSymbolOwnerWithCFUUIDBytesAtTime\nint CSSymbolicatorForeachSymbolOwnerWithFlagsAtTime(CSSymbolicatorRef symbolicator, long flags, uint64_t time, CSSymbolOwnerIterator it);\nint CSSymbolicatorForeachSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolOwnerIterator it);\nint CSSymbolicatorForeachSymbolOwnerWithPathAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolOwnerIterator it);\n// XXX: CSSymbolicatorForeachSymbolOwnerWithUUIDAtTime\nint CSSymbolicatorForeachSymbolWithMangledNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolIterator it);\nint CSSymbolicatorForeachSymbolWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolIterator it);\n// XXX: CSSymbolicatorForeachSymbolicatorWithPath\n// XXX: CSSymbolicatorForeachSymbolicatorWithPathFlagsAndNotification\n// XXX: CSSymbolicatorForeachSymbolicatorWithURL\n// XXX: CSSymbolicatorForeachSymbolicatorWithURLFlagsAndNotification\n\nCSSymbolOwnerRef CSSymbolicatorGetAOutSymbolOwner(CSSymbolicatorRef cs);\ncpu_type_t CSSymbolicatorGetArchitecture(CSSymbolicatorRef cs);\nvm_address_t CSSymbolicatorGetDyldAllImageInfosAddress(CSSymbolicatorRef cs);\n\nlong CSSymbolicatorGetFlagsForDebugMapOnlyData(void);\nlong CSSymbolicatorGetFlagsForDsymOnlyData(void);\nlong CSSymbolicatorGetFlagsForDwarfOnlyData(void);\nlong CSSymbolicatorGetFlagsForFunctionStartsOnlyData(void);\nlong CSSymbolicatorGetFlagsForNListOnlyData(void);\nlong CSSymbolicatorGetFlagsForNoSymbolOrSourceInfoData(void);\n\npid_t CSSymbolicatorGetPid(CSSymbolicatorRef cs);\nint CSSymbolicatorGetRegionCountAtTime(CSSymbolicatorRef cs, uint64_t time);\nCSRegionRef CSSymbolicatorGetRegionWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);\nCSRegionRef CSSymbolicatorGetRegionWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);\nCSSectionRef CSSymbolicatorGetSectionWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);\nCSSegmentRef CSSymbolicatorGetSegmentWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);\nvm_address_t CSSymbolicatorGetSharedCacheSlide(CSSymbolicatorRef cs);\nCSUUIDRef CSSymbolicatorGetSharedCacheUUID(CSSymbolicatorRef cs);\nint CSSymbolicatorGetSourceInfoCountAtTime(CSSymbolicatorRef cs, uint64_t time);\nCSSourceInfoRef CSSymbolicatorGetSourceInfoWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);\nint CSSymbolicatorGetSymbolCountAtTime(CSSymbolicatorRef cs, uint64_t time);\nCSSymbolOwnerRef CSSymbolicatorGetSymbolOwner(CSSymbolicatorRef cs);\nint CSSymbolicatorGetSymbolOwnerCountAtTime(CSSymbolicatorRef cs, uint64_t time);\nCSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);\n// XXX: CSSymbolicatorGetSymbolOwnerWithCFUUIDBytesAtTime\nCSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);\nCSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithUUIDAtTime(CSSymbolicatorRef symbolicator, CFUUIDRef uuid, uint64_t time);\nCSSymbolRef CSSymbolicatorGetSymbolWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);\nCSSymbolRef CSSymbolicatorGetSymbolWithMangledNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);\nCSSymbolRef CSSymbolicatorGetSymbolWithMangledNameFromSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, CSSymbolOwnerRef owner, const char* name, uint64_t time);\nCSSymbolRef CSSymbolicatorGetSymbolWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);\nCSSymbolRef CSSymbolicatorGetSymbolWithNameFromSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, CSSymbolOwnerRef owner, const char* name, uint64_t time);\nmach_port_t CSSymbolicatorGetTask(CSSymbolicatorRef cs);\nBoolean CSSymbolicatorIsKernelSymbolicator(CSSymbolicatorRef cs);\nBoolean CSSymbolicatorIsTaskTranslated(CSSymbolicatorRef cs);\nBoolean CSSymbolicatorIsTaskValid(CSSymbolicatorRef cs);\nvoid CSSymbolicatorResymbolicate(CSSymbolicatorRef cs);\nvoid CSSymbolicatorResymbolicateFail(CSSymbolicatorRef cs);\nint CSSymbolicatorSetForceGlobalSafeMachVMReads(CSSymbolicatorRef cs);\n\n\n/*\n * XXX: CSUUID\n */\n /*\nCSUUIDCFUUIDBytesToPath\nCSUUIDCFUUIDBytesToString\nCSUUIDStringToCFUUIDBytes\n*/\n\n\n\n\n/*\n * SymbolOwner functions\n */\nconst char* CSSymbolOwnerGetPath(CSSymbolOwnerRef symbol);\nconst char* CSSymbolOwnerGetName(CSSymbolOwnerRef symbol);\nvm_address_t CSSymbolOwnerGetBaseAddress(CSSymbolOwnerRef owner);\ncpu_type_t CSSymbolOwnerGetArchitecture(CSSymbolOwnerRef owner);\nBoolean CSSymbolOwnerIsObject(CSSymbolOwnerRef owner);\nlong CSSymbolOwnerGetDataFlags(CSSymbolOwnerRef owner);\nCSRegionRef CSSymbolOwnerGetRegionWithName(CSSymbolOwnerRef owner, const char* name);\nCSSymbolRef CSSymbolOwnerGetSymbolWithName(CSSymbolOwnerRef owner, const char* name);\nCSSymbolRef CSSymbolOwnerGetSymbolWithAddress(CSSymbolOwnerRef owner, mach_vm_address_t addr);\n\nlong CSSymbolOwnerForeachSymbol(CSSymbolOwnerRef owner, CSSymbolIterator each);\n\nCFUUIDBytes *CSSymbolOwnerGetCFUUIDBytes(CSSymbolOwnerRef owner);\n\n/* Other exports\n\n__crashreporter_info__\nclear_mapped_memory\ncreate_mapped_memory_cache_for_task\ncreate_sampling_context_for_task\ndemangle\ndestroy_mapped_memory_cache\ndestroy_sampling_context\ndispatch_queue_name_for_serial_number\nfind_node\nfixup_frames\nget_remote_thread_dispatch_queue\n\nmap_new_node\nmapped_memory_read\nmapped_memory_read_pointer\nnext_node\nsample_remote_thread\nsample_remote_thread_with_dispatch_queue\nsampling_context_clear_cache\ntask_is_64bit\nthread_name_for_thread_port\n*/\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n#endif /* ! __CORESYMBOLICATION_CORESYMBOLICATION__ */"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Lars Fröder\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "TARGET := iphone:clang:16.5:11.0\nARCHS = arm64 arm64e\n\ninclude $(THEOS)/makefiles/common.mk\n\nTOOL_NAME = opainject\n\nopainject_FILES = main.m dyld.m shellcode_inject.m rop_inject.m thread_utils.m task_utils.m arm64.m\nopainject_CFLAGS = -fobjc-arc -DTHEOS_LEAN_AND_MEAN\nopainject_CODESIGN_FLAGS = -Sentitlements.plist\nopainject_INSTALL_PATH = /usr/local/bin\nopainject_PRIVATE_FRAMEWORKS = CoreSymbolication\n\ninclude $(THEOS_MAKE_PATH)/tool.mk\n"
  },
  {
    "path": "README.md",
    "content": "# opainject\n\niOS tool to inject a dylib into a process using both shellcode and ROP methods. (By default ROP method is used, it's superior to the shellcode method in every way but I started with the shellcode method and decided to leave it in).\n\nTested on iOS 14, 15, 16 and 17. Should theoretically work on 11.0 and up. On arm64e devices the dylib will inject but crash the process if it's not in Trust Cache."
  },
  {
    "path": "arm64.h",
    "content": "#ifndef arm64_h\n#define arm64_h\n\n#include <stdio.h>\n#include <stdbool.h>\n\nuint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl);\nuint32_t generate_br(uint8_t x);\nbool decode_adrp(uint32_t inst, uint8_t *rd_out, int32_t *imm_out);\nbool decode_ldr_imm(uint32_t inst, uint16_t *imm_out, uint8_t *rn_out, uint8_t *rt_out);\nbool decode_adrp_ldr(uint32_t adrp_inst, uint32_t ldr_inst, uint64_t pc, uint64_t *dst_out);\n\n#endif /* arm64_h */\n"
  },
  {
    "path": "arm64.m",
    "content": "#include \"arm64.h\"\n#include <stdbool.h>\n\nuint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl)\n{\n\tuint32_t base = 0b11110010100000000000000000000000;\n\n\tuint32_t hw = 0;\n\tif (lsl == 16) {\n\t\thw = 0b01 << 21;\n\t}\n\telse if (lsl == 32) {\n\t\thw = 0b10 << 21;\n\t}\n\telse if (lsl == 48) {\n\t\thw = 0b11 << 21;\n\t}\n\n\tuint32_t imm16 = (uint32_t)val << 5;\n\tuint32_t rd = x & 0x1F;\n\n\treturn base | hw | imm16 | rd;\n}\n\nuint32_t generate_br(uint8_t x)\n{\n\tuint32_t base = 0b11010110000111110000000000000000;\n\tuint32_t rn = ((uint32_t)x & 0x1F) << 5;\n\treturn base | rn;\n}\n\nbool decode_adrp(uint32_t inst, uint8_t *rd_out, int32_t *imm_out)\n{\n\tif ((inst & 0x9F000000) != 0x90000000) return false;\n\t\n\tuint32_t mask_immlo = 0b01100000000000000000000000000000;\n\tuint32_t mask_immhi = 0b00000000111111111111111111100000;\n\tuint32_t mask_rd    = 0b00000000000000000000000000011111;\n\t\n\tint32_t imm = (((inst & mask_immlo) >> 29) | ((inst & mask_immhi) >> 3)) << 12;\n\tuint8_t rd = inst & mask_rd;\n\t\n\tif (rd_out) *rd_out = rd;\n\tif (imm_out) *imm_out = imm;\n\t\n\treturn true;\n}\n\nbool decode_ldr_imm(uint32_t inst, uint16_t *imm_out, uint8_t *rn_out, uint8_t *rt_out)\n{\n\tif ((inst & 0xBFC00000) != 0xB9400000) return false;\n\t// TODO: Support non unsigned instructions\n\t\n\tuint32_t mask_imm12 = 0b00000000001111111111110000000000;\n\tuint32_t mask_rn    = 0b00000000000000000000001111100000;\n\tuint32_t mask_rt    = 0b00000000000000000000000000011111;\n\t\n\tuint8_t  rt    = (inst & mask_rt);\n\tuint8_t  rn    = (inst & mask_rn) >> 5;\n\tuint16_t imm12 = (inst & mask_imm12) >> 10;\n\t\n\tuint32_t bit_is_64_bit = 0b01000000000000000000000000000000;\n\tif (inst & bit_is_64_bit) {\n\t\timm12 *= 8;\n\t}\n\telse {\n\t\timm12 *= 4;\n\t}\n\t\n\tif (imm_out) *imm_out = imm12;\n\tif (rn_out) *rn_out = rn;\n\tif (rt_out) *rt_out = rt;\n\t\n\treturn true;\n}\n\nbool decode_adrp_ldr(uint32_t adrp_inst, uint32_t ldr_inst, uint64_t pc, uint64_t *dst_out)\n{\n\tint32_t adrp_imm = 0;\n\tif (!decode_adrp(adrp_inst, NULL, &adrp_imm)) return false;\n\t\n\tuint16_t ldr_imm = 0;\n\tif (!decode_ldr_imm(ldr_inst, &ldr_imm, NULL, NULL)) return false;\n\t\n\tuint64_t pc_page = pc - (pc % 0x1000);\n\tuint64_t dst = (pc_page + adrp_imm) + ldr_imm;\n\tif (dst_out) *dst_out = dst;\n\treturn true;\n}\n\n/*bool decode_ldr(uint32_t inst, uint8_t *rt_out, uint8_t *rn_out, uint8_t *rm_out)\n{\n\tif ((inst & 0xFFE00C00) != 0xF8600800) return false;\n\t\n\tuint32_t mask_rm     = 0b00000000000111110000000000000000;\n\tuint32_t mask_option = 0b00000000000000001110000000000000;\n\tuint32_t mask_s      = 0b00000000000000000001000000000000;\n\tuint32_t mask_rn     = 0b00000000000000000000001111100000;\n\tuint32_t mask_rt     = 0b00000000000000000000000000011111;\n\t\n\tuint8_t rt     = inst & mask_rt;\n\tuint8_t rn     = inst & mask_rn >> 5;\n\tuint8_t rm     = inst & mask_rm >> 16;\n\tbool    s      = inst & mask_s >> 12;\n\tuint8_t option = inst & mask_option >> 13;\n\t\n\tprintf(\"rt=%d, rn=%d, rm=%d, s=%d, option=%X\\n\", rt, rn, rm, s, option);\n\t\n\treturn true;\n}*/\n"
  },
  {
    "path": "control",
    "content": "Package: com.opa334.opainject\nName: opainject\nVersion: 1.0.6\nArchitecture: iphoneos-arm\nDescription: Injection tool!\nMaintainer: opa334\nAuthor: opa334\nSection: System\nTag: role::hacker\n"
  },
  {
    "path": "dyld.h",
    "content": "extern vm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imageName);\nextern vm_address_t remoteDlSym(task_t task, vm_address_t imageStartPtr, const char* symbolName);\nextern vm_address_t remoteDlSymFindImage(task_t task, vm_address_t allImageInfoAddr, const char* symbolName, char** imageOut);\n\nextern void printImages(task_t task, vm_address_t imageStartPtr);\nextern void printSymbols(task_t task, vm_address_t imageAddress);\nextern vm_address_t scanLibrariesForMemory(task_t task, vm_address_t imageStartPtr, char* memory, size_t memorySize, int alignment);"
  },
  {
    "path": "dyld.m",
    "content": "#import <stdio.h>\n#import <stdlib.h>\n#import <unistd.h>\n#import <dlfcn.h>\n#import <mach-o/getsect.h>\n#import <mach-o/dyld.h>\n#import <mach/mach.h>\n#import <mach-o/loader.h>\n#import <mach-o/nlist.h>\n#import <mach-o/reloc.h>\n#import <mach-o/dyld_images.h>\n#import <sys/utsname.h>\n#import <string.h>\n#import <limits.h>\n#import \"task_utils.h\"\n\n#import <CoreFoundation/CoreFoundation.h>\n\nvoid iterateImages(task_t task, vm_address_t imageStartPtr, void (^iterateBlock)(char*, struct dyld_image_info*, BOOL*))\n{\n\tstruct dyld_all_image_infos imageInfos;\n\ttask_read(task, imageStartPtr, &imageInfos, sizeof(imageInfos));\n\n\tsize_t infoArraySize = sizeof(struct dyld_image_info) * imageInfos.infoArrayCount;\n\tstruct dyld_image_info *infoArray = malloc(infoArraySize);\n\ttask_read(task, (vm_address_t)imageInfos.infoArray, &infoArray[0], infoArraySize);\n\n\tfor(int i = 0; i < imageInfos.infoArrayCount; i++)\n\t{\n\t\t@autoreleasepool\n\t\t{\n\t\t\tchar currentImagePath[PATH_MAX];\n\t\t\tif(task_read(task, (vm_address_t)infoArray[i].imageFilePath, &currentImagePath[0], sizeof(currentImagePath)) == KERN_SUCCESS)\n\t\t\t{\n\t\t\t\tBOOL stop = NO;\n\t\t\t\titerateBlock(currentImagePath, &infoArray[i], &stop);\n\t\t\t\tif(stop) break;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imagePath)\n{\n\t__block vm_address_t outAddr = 0;\n\n\titerateImages(task, imageStartPtr, ^(char* iterImagePath, struct dyld_image_info* imageInfo, BOOL* stop)\n\t{\n\t\tif(strcmp(iterImagePath, imagePath) == 0)\n\t\t{\n\t\t\toutAddr = (vm_address_t)imageInfo->imageLoadAddress;\n\t\t}\n\t});\n\n\treturn outAddr;\n}\n\nvoid iterateLoadCommands(task_t task, vm_address_t imageAddress, void (^iterateBlock)(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stop))\n{\n\tstruct mach_header_64 mh;\n\ttask_read(task, imageAddress, &mh, sizeof(mh));\n\n\tvm_address_t lcStart = (imageAddress + sizeof(struct mach_header_64));\n\tvm_address_t lcEnd = lcStart + mh.sizeofcmds;\n\n\tvm_address_t lcCur = lcStart;\n\tfor(int ci = 0; ci < mh.ncmds && lcCur <= lcEnd; ci++)\n\t{\n\t\tstruct load_command loadCommand;\n\t\ttask_read(task, lcCur, &loadCommand, sizeof(loadCommand));\n\t\tBOOL stop = NO;\n\t\titerateBlock(&loadCommand, lcCur, &stop);\n\t\tlcCur = lcCur + loadCommand.cmdsize;\n\t\tif(stop) break;\n\t}\n}\n\nvoid iterateSections(task_t task, vm_address_t commandAddress, const struct segment_command_64* segmentCommand, void (^iterateBlock)(const struct section_64*, BOOL*))\n{\n\tif(segmentCommand->nsects == 0) return;\n\n\tvm_address_t sectionStart = commandAddress + sizeof(struct segment_command_64);\n\tfor(int i = 0; i < segmentCommand->nsects; i++)\n\t{\n\t\tstruct section_64 section = { 0 };\n\t\tif (task_read(task, sectionStart + i * sizeof(section), &section, sizeof(section)) == KERN_SUCCESS) {\n\t\t\tBOOL stop;\n\t\t\titerateBlock(&section, &stop);\n\t\t\tif(stop) break;\n\t\t}\n\t}\n}\n\nvoid iterateSymbols(task_t task, vm_address_t imageAddress, void (^iterateBlock)(const char*, const char*, vm_address_t, BOOL*))\n{\n\t__block struct segment_command_64 __linkedit = { 0 };\n\t__block struct symtab_command symtabCommand = { 0 };\n\n\t__block vm_address_t slide;\n\t__block BOOL firstSegmentCommand = YES;\n\n\titerateLoadCommands(task, imageAddress, ^(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stop)\n\t{\n\t\tswitch(cmd->cmd)\n\t\t{\n\t\t\tcase LC_SYMTAB:\n\t\t\t{\n\t\t\t\ttask_read(task, cmdAddress, &symtabCommand, sizeof(symtabCommand));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase LC_SEGMENT_64:\n\t\t\t{\n\t\t\t\tstruct segment_command_64 segmentCommand;\n\t\t\t\ttask_read(task, cmdAddress, &segmentCommand, sizeof(segmentCommand));\n\t\t\t\tif(firstSegmentCommand) {\n\t\t\t\t\tslide = imageAddress - segmentCommand.vmaddr;\n\t\t\t\t\tfirstSegmentCommand = NO;\n\t\t\t\t}\n\t\t\t\tif (strncmp(\"__LINKEDIT\", segmentCommand.segname, 16) == 0) {\n\t\t\t\t\t__linkedit = segmentCommand;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tif(__linkedit.cmd != LC_SEGMENT_64)\n\t{\n\t\tprintf(\"ERROR: __LINKEDIT not found\\n\");\n\t\treturn;\n\t}\n\tif(symtabCommand.cmd != LC_SYMTAB)\n\t{\n\t\tprintf(\"ERROR: symtab command not found\\n\");\n\t\treturn;\n\t}\n\n\tuint64_t fileoff = __linkedit.fileoff;\n\tuint64_t vmaddr = __linkedit.vmaddr;\n\n\tvm_address_t baseAddr = vmaddr + slide - fileoff;\n\n\tvm_address_t strtblAddr = baseAddr + symtabCommand.stroff;\n\tsize_t strtblSize = symtabCommand.strsize;\n\tchar *strtbl = malloc(strtblSize);\n\ttask_read(task, strtblAddr, &strtbl[0], strtblSize);\n\tvm_address_t lAddr = baseAddr + symtabCommand.symoff;\n\tfor (uint32_t s = 0; s < symtabCommand.nsyms; s++)\n\t{\n\t\tvm_address_t entryAddr = lAddr + sizeof(struct nlist_64) * s;\n\n\t\tstruct nlist_64 entry = { 0 };\n\t\ttask_read(task, entryAddr, &entry, sizeof(entry));\n\n\t\tuint32_t off = entry.n_un.n_strx;\n\t\tif (off >= strtblSize || off == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst char* sym = &strtbl[off];\n\t\tif (sym[0] == '\\x00')\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst char* type = NULL;\n\t\tswitch(entry.n_type & N_TYPE) {\n\t\t\tcase N_UNDF: type = \"N_UNDF\"; break;\n\t\t\tcase N_ABS:  type = \"N_ABS\"; break;\n\t\t\tcase N_SECT: type = \"N_SECT\"; break;\n\t\t\tcase N_PBUD: type = \"N_PBUD\"; break;\n\t\t\tcase N_INDR: type = \"N_INDR\"; break;\n\t\t}\n\n\t\tBOOL stop = NO;\n\t\titerateBlock(sym, type, entry.n_value + slide, &stop);\n\t\tif(stop)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tfree(strtbl);\n}\n\nvm_address_t remoteDlSym(task_t task, vm_address_t imageAddress, const char* symbolName)\n{\n\t__block vm_address_t outAddr = 0;\n\n\titerateSymbols(task, imageAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop)\n\t{\n\t\tif(strcmp(type, \"N_SECT\") == 0)\n\t\t{\n\t\t\tif(strcmp(iterSymbolName, symbolName) == 0)\n\t\t\t{\n\t\t\t\toutAddr = value;\n\t\t\t}\n\t\t}\n\t});\n\n\treturn outAddr;\n}\n\nvm_address_t remoteDlSymFindImage(task_t task, vm_address_t allImageInfoAddr, const char* symbolName, char** imageOut)\n{\n\t__block vm_address_t outAddr = 0;\n\t__block BOOL* stop1_b;\n\n\titerateImages(task, allImageInfoAddr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stop1)\n\t{\n\t\tstop1_b = stop1;\n\t\titerateSymbols(task, (vm_address_t)imageInfo->imageLoadAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop2)\n\t\t{\n\t\t\tif(strcmp(type, \"N_SECT\") == 0)\n\t\t\t{\n\t\t\t\tif(strcmp(iterSymbolName, symbolName) == 0)\n\t\t\t\t{\n\t\t\t\t\toutAddr = value;\n\t\t\t\t\tif(imageOut)\n\t\t\t\t\t{\n\t\t\t\t\t\t*imageOut = strdup(imageFilePath);\n\t\t\t\t\t}\n\t\t\t\t\t*stop2 = YES;\n\t\t\t\t\t*stop1_b = YES;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t});\n\treturn outAddr;\n}\n\nvoid printImages(task_t task, vm_address_t imageStartPtr)\n{\n\titerateImages(task, imageStartPtr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stop)\n\t{\n\t\tprintf(\"Image %s - %llX\\n\", imageFilePath, (uint64_t)imageInfo->imageLoadAddress);\n\t});\n}\n\nvoid printSymbols(task_t task, vm_address_t imageAddress)\n{\n\titerateSymbols(task, imageAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop)\n\t{\n\t\tif(strcmp(type, \"N_SECT\") == 0)\n\t\t{\n\t\t\tprintf(\"Symbol %s - %llX\\n\", iterSymbolName, (uint64_t)value);\n\t\t}\n\t});\n}\n\nvm_address_t scanMemory(task_t task, vm_address_t begin, size_t size, char* memory, size_t memorySize, int alignment)\n{\n\t//printf(\"scanMemory(%llX, %ld)\\n\", (uint64_t)begin, size);\n\n\tif(alignment == 0) alignment = 1;\n\n\tunsigned char *buf = malloc(size);\n\tif(task_read(task, begin, &buf[0], size) != KERN_SUCCESS)\n\t{\n\t\tprintf(\"[scanMemory] WARNING: Failed to read process memory (%llX, size:%llX)\\n\", (uint64_t)begin, (uint64_t)size);\n\t\tif(buf) free(buf);\n\t\treturn 0;\n\t}\n\n\tvm_address_t foundMemoryAbsoluteFinal = 0;\n\tvm_address_t lastFoundMemory = 0;\n\twhile(1)\n\t{\n\t\tunsigned char* foundMemory = memmem(buf + lastFoundMemory, size - lastFoundMemory, memory, memorySize);\n\t\tif(foundMemory != NULL)\n\t\t{\n\t\t\tlastFoundMemory = foundMemory - buf + memorySize;\n\n\t\t\tvm_address_t foundMemoryAbsolute = (vm_address_t)(begin + (vm_address_t)(foundMemory - buf));\n\t\t\t//printf(\"foundMemory absolute: %llX\\n\", (uint64_t)foundMemoryAbsolute);\n\n\t\t\tint rest = foundMemoryAbsolute % alignment;\n\t\t\t//printf(\"rest: %d\\n\", rest);\n\t\t\tif(rest == 0)\n\t\t\t{\n\t\t\t\tfoundMemoryAbsoluteFinal = foundMemoryAbsolute;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\n\tfree(buf);\n\treturn foundMemoryAbsoluteFinal;\n}\n\nvm_address_t scanTextSegmentForMemory(task_t task, vm_address_t commandAddress, const struct segment_command_64* textCmd, vm_address_t slide, char* memory, size_t memorySize, int alignment)\n{\n\tuint64_t begin = textCmd->vmaddr + slide;\n\t//printf(\"- TEXT: %llX -> %llX, %d -\\n\", begin, begin + textCmd->vmsize, textCmd->nsects);\n\tvm_address_t mainTextScan = scanMemory(task, begin, textCmd->vmsize, memory, memorySize, alignment);\n\tif(mainTextScan != 0) return mainTextScan;\n\n\t__block vm_address_t sectFound = 0;\n\titerateSections(task, commandAddress, textCmd, ^(const struct section_64* sect, BOOL* stop)\n\t{\n\t\tuint64_t sectBegin = sect->addr + slide;\n\t\t//printf(\"-- %s %llX -> %llX --\\n\", sect->sectname, sectBegin, sectBegin + sect->size);\n\t\tif(strcmp(sect->sectname, \"__text\") == 0)\n\t\t{\n\t\t\tvm_address_t subSectScan = scanMemory(task, sectBegin, sect->size, memory, memorySize, alignment);\n\t\t\tif(subSectScan != 0)\n\t\t\t{\n\t\t\t\tsectFound = subSectScan;\n\t\t\t\t*stop = YES;\n\t\t\t}\n\t\t}\n\t});\n\treturn sectFound;\n}\n\nvm_address_t scanLibrariesForMemory(task_t task, vm_address_t imageStartPtr, char* memory, size_t memorySize, int alignment)\n{\n\t__block vm_address_t foundAddr = 0;\n\n\titerateImages(task, imageStartPtr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stopImages)\n\t{\n\t\t__block vm_address_t slide;\n\t\t__block BOOL firstSegmentCommand = YES;\n\t\t//printf(\"- iterating %s -\\n\", imageFilePath);\n\t\titerateLoadCommands(task, (vm_address_t)imageInfo->imageLoadAddress, ^(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stopCommands)\n\t\t{\n\t\t\tif(cmd->cmd == LC_SEGMENT_64)\n\t\t\t{\n\t\t\t\tstruct segment_command_64 segmentCommand = { 0 };\n\t\t\t\ttask_read(task, cmdAddress, &segmentCommand, sizeof(segmentCommand));\n\t\t\t\tif(firstSegmentCommand)\n\t\t\t\t{\n\t\t\t\t\tslide = (vm_address_t)imageInfo->imageLoadAddress - segmentCommand.vmaddr;\n\t\t\t\t\tfirstSegmentCommand = NO;\n\t\t\t\t}\n\t\t\t\tif (strncmp(\"__TEXT\", segmentCommand.segname, 16) == 0)\n\t\t\t\t{\n\t\t\t\t\tvm_address_t addrIfFound = scanTextSegmentForMemory(task, cmdAddress, &segmentCommand, slide, memory, memorySize, alignment);\n\t\t\t\t\tif(addrIfFound != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tfoundAddr = addrIfFound;\n\t\t\t\t\t\t*stopCommands = YES;\n\t\t\t\t\t\t*stopImages = YES;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t});\n\n\treturn foundAddr;\n}\n"
  },
  {
    "path": "entitlements.plist",
    "content": "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>get-task-allow</key>\n\t<true/>\n\t<key>platform-application</key>\n\t<true/>\n\t<key>task_for_pid-allow</key>\n\t<true/>\n\t<key>com.apple.private.security.container-required</key>\n\t<false/>\n\t<key>com.apple.system-task-ports</key>\n\t<true/>\n\t<key>com.apple.system-task-ports.control</key>\n\t<true/>\n\t<key>com.apple.private.thread-set-state</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "main.m",
    "content": "#import <stdio.h>\n#import <stdlib.h>\n#import <unistd.h>\n#import <dlfcn.h>\n#import <mach-o/getsect.h>\n#import <mach-o/dyld.h>\n#import <mach/mach.h>\n#import <mach-o/loader.h>\n#import <mach-o/nlist.h>\n#import <mach-o/reloc.h>\n#import <sys/utsname.h>\n#import <string.h>\n#import <limits.h>\n#import <spawn.h>\n#import \"dyld.h\"\n#import \"sandbox.h\"\n#import <CoreFoundation/CoreFoundation.h>\n#import \"shellcode_inject.h\"\n#import \"rop_inject.h\"\n\n\nchar* resolvePath(char* pathToResolve)\n{\n\tif(strlen(pathToResolve) == 0) return NULL;\n\tif(pathToResolve[0] == '/')\n\t{\n\t\treturn strdup(pathToResolve);\n\t}\n\telse\n\t{\n\t\tchar absolutePath[PATH_MAX];\n\t\tif (realpath(pathToResolve, absolutePath) == NULL) {\n\t\t\tperror(\"[resolvePath] realpath\");\n\t\t\treturn NULL;\n\t\t}\n\t\treturn strdup(absolutePath);\n\t}\n}\n\nextern int posix_spawnattr_set_ptrauth_task_port_np(posix_spawnattr_t * __restrict attr, mach_port_t port);\nvoid spawnPacChild(int argc, char *argv[])\n{\n\tchar** argsToPass = malloc(sizeof(char*) * (argc + 2));\n\tfor(int i = 0; i < argc; i++)\n\t{\n\t\targsToPass[i] = argv[i];\n\t}\n\targsToPass[argc] = \"pac\";\n\targsToPass[argc+1] = NULL;\n\n\tpid_t targetPid = atoi(argv[1]);\n\tmach_port_t task;\n\tkern_return_t kr = KERN_SUCCESS;\n\tkr = task_for_pid(mach_task_self(), targetPid, &task);\n\tif(kr != KERN_SUCCESS) {\n\t\tprintf(\"[spawnPacChild] Failed to obtain task port.\\n\");\n\t\treturn;\n\t}\n\tprintf(\"[spawnPacChild] Got task port %d for pid %d\\n\", task, targetPid);\n\n\tposix_spawnattr_t attr;\n    posix_spawnattr_init(&attr);\n\tposix_spawnattr_set_ptrauth_task_port_np(&attr, task);\n\n\tuint32_t executablePathSize = 0;\n\t_NSGetExecutablePath(NULL, &executablePathSize);\n\tchar *executablePath = malloc(executablePathSize);\n\t_NSGetExecutablePath(executablePath, &executablePathSize);\n\n\tint status = -200;\n\tpid_t pid;\n\tint rc = posix_spawn(&pid, executablePath, NULL, &attr, argsToPass, NULL);\n\n\tposix_spawnattr_destroy(&attr);\n\tfree(argsToPass);\n\tfree(executablePath);\n\n\tif(rc != KERN_SUCCESS)\n\t{\n\t\tprintf(\"[spawnPacChild] posix_spawn failed: %d (%s)\\n\", rc, mach_error_string(rc));\n\t\treturn;\n\t}\n\n\tdo\n\t{\n\t\tif (waitpid(pid, &status, 0) != -1) {\n\t\t\tprintf(\"[spawnPacChild] Child returned %d\\n\", WEXITSTATUS(status));\n\t\t}\n\t} while (!WIFEXITED(status) && !WIFSIGNALED(status));\n\n\treturn;\n}\n\nint main(int argc, char *argv[], char *envp[]) {\n\t@autoreleasepool\n\t{\n\t\tsetlinebuf(stdout);\n\t\tsetlinebuf(stderr);\n\t\tif (argc < 3 || argc > 4)\n\t\t{\n\t\t\tprintf(\"Usage: opainject <pid> <path/to/dylib>\\n\");\n\t\t\treturn -1;\n\t\t}\n\n#ifdef __arm64e__\n\t\tchar* pacArg = NULL;\n\t\tif(argc >= 4)\n\t\t{\n\t\t\tpacArg = argv[3];\n\t\t}\n\t\tif (!pacArg || (strcmp(\"pac\", pacArg) != 0))\n\t\t{\n\t\t\tspawnPacChild(argc, argv);\n\t\t\treturn 0;\n\t\t}\n#endif\n\n\t\tprintf(\"OPAINJECT HERE WE ARE\\n\");\n\t\tprintf(\"RUNNING AS %d\\n\", getuid());\n\n\t\tpid_t targetPid = atoi(argv[1]);\n\t\tkern_return_t kret = 0;\n\t\ttask_t procTask = MACH_PORT_NULL;\n\t\tchar* dylibPath = resolvePath(argv[2]);\n\t\tif(!dylibPath) return -3;\n\t\tif(access(dylibPath, R_OK) < 0)\n\t\t{\n\t\t\tprintf(\"ERROR: Can't access passed dylib at %s\\n\", dylibPath);\n\t\t\treturn -4;\n\t\t}\n\n\t\t// get task port\n\t\tkret = task_for_pid(mach_task_self(), targetPid, &procTask);\n\t\tif(kret != KERN_SUCCESS)\n\t\t{\n\t\t\tprintf(\"ERROR: task_for_pid failed with error code %d (%s)\\n\", kret, mach_error_string(kret));\n\t\t\treturn -2;\n\t\t}\n\t\tif(!MACH_PORT_VALID(procTask))\n\t\t{\n\t\t\tprintf(\"ERROR: Got invalid task port (%d)\\n\", procTask);\n\t\t\treturn -3;\n\t\t}\n\n\t\tprintf(\"Got task port %d for pid %d!\\n\", procTask, targetPid);\n\n\t\t// get aslr slide\n\t\ttask_dyld_info_data_t dyldInfo;\n\t\tuint32_t count = TASK_DYLD_INFO_COUNT;\n\t\ttask_info(procTask, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count);\n\n\t\tinjectDylibViaRop(procTask, targetPid, dylibPath, dyldInfo.all_image_info_addr);\n\n\t\tmach_port_deallocate(mach_task_self(), procTask);\n\t\t\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "pac.h",
    "content": "#ifndef PTRAUTH_HELPERS_H\n#define PTRAUTH_HELPERS_H\n// Helpers for PAC archs.\n\n// If the compiler understands __arm64e__, assume it's paired with an SDK that has\n// ptrauth.h. Otherwise, it'll probably error if we try to include it so don't.\n#if __arm64e__\n#include <ptrauth.h>\n#endif\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-function\"\n\n// Given a pointer to instructions, sign it so you can call it like a normal fptr.\nstatic void *make_sym_callable(void *ptr) {\n#if __arm64e__\n    ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0);\n#endif\n    return ptr;\n}\n\nstatic void *make_sym_callable_data(void *ptr) {\n#if __arm64e__\n    ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_process_independent_data), ptrauth_key_process_independent_data, 0);\n#endif\n    return ptr;\n}\n\n// Given a function pointer, strip the PAC so you can read the instructions.\nstatic void *make_sym_readable(void *ptr) {\n#if __arm64e__\n    ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer);\n#endif\n    return ptr;\n}\n\nstatic void *make_sym_readable_data(void *ptr) {\n#if __arm64e__\n    ptr = ptrauth_strip(ptr, ptrauth_key_process_independent_data);\n#endif\n    return ptr;\n}\n\n#pragma clang diagnostic pop\n#endif"
  },
  {
    "path": "rop_inject.h",
    "content": "extern void injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr);"
  },
  {
    "path": "rop_inject.m",
    "content": "#import <stdio.h>\n#import <unistd.h>\n#import <stdlib.h>\n#import <dlfcn.h>\n#import <errno.h>\n#import <string.h>\n#import <limits.h>\n#import <pthread.h>\n#import <pthread_spis.h>\n#import <mach/mach.h>\n#import <mach/error.h>\n#import <mach-o/getsect.h>\n#import <mach-o/dyld.h>\n#import <mach-o/loader.h>\n#import <mach-o/nlist.h>\n#import <mach-o/reloc.h>\n#import <mach-o/dyld_images.h>\n#import <sys/utsname.h>\n#import <sys/types.h>\n#import <sys/sysctl.h>\n#import <sys/mman.h>\n#import <sys/stat.h>\n#import <sys/wait.h>\n#import <CoreFoundation/CoreFoundation.h>\n\n#import \"pac.h\"\n#import \"dyld.h\"\n#import \"sandbox.h\"\n#import \"CoreSymbolication.h\"\n#import \"task_utils.h\"\n#import \"thread_utils.h\"\n#import \"arm64.h\"\n\nvm_address_t writeStringToTask(task_t task, const char* string, size_t* lengthOut)\n{\n\tkern_return_t kr = KERN_SUCCESS;\n\tvm_address_t remoteString = (vm_address_t)NULL;\n\tsize_t stringLen = strlen(string)+1;\n\n\tkr = vm_allocate(task, &remoteString, stringLen, VM_FLAGS_ANYWHERE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"ERROR: Unable to memory for string %s: %s\\n\", string, mach_error_string(kr));\n\t\treturn 0;\n\t}\n\n\tkr = vm_protect(task, remoteString, stringLen, TRUE, VM_PROT_READ | VM_PROT_WRITE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tvm_deallocate(task, remoteString, stringLen);\n\t\tprintf(\"ERROR: Failed to make string %s read/write: %s.\\n\", string, mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_write(task, remoteString, (vm_address_t)string, stringLen);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tvm_deallocate(task, remoteString, stringLen);\n\t\tprintf(\"ERROR: Failed to write string %s to memory: %s\\n\", string, mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tif(lengthOut)\n\t{\n\t\t*lengthOut = stringLen;\n\t}\n\n\treturn remoteString;\n}\n\nvoid findRopLoop(task_t task, vm_address_t allImageInfoAddr)\n{\n\tuint32_t inst = CFSwapInt32(0x00000014);\n\tropLoop = (uint64_t)scanLibrariesForMemory(task, allImageInfoAddr, (char*)&inst, sizeof(inst), 4);\n}\n\n// Create an infinitely spinning pthread in target process\nkern_return_t createRemotePthread(task_t task, vm_address_t allImageInfoAddr, thread_act_t* remotePthreadOut)\n{\n\tkern_return_t kr = KERN_SUCCESS;\n\n#if __arm64e__\n\t// GET ANY VALID THREAD STATE\n\tmach_msg_type_number_t validThreadStateCount = ARM_THREAD_STATE64_COUNT;\n\tstruct arm_unified_thread_state validThreadState;\n\tthread_act_array_t allThreadsForFindingValid;\n\tmach_msg_type_number_t threadCountForFindingValid;\n\tkr = task_threads(task, &allThreadsForFindingValid, &threadCountForFindingValid);\n\tif(kr != KERN_SUCCESS || threadCountForFindingValid == 0)\n\t{\n\t\tprintf(\"[createRemotePthread] ERROR: failed to get threads in task: %s\\n\", mach_error_string(kr));\n\t\tif (kr == KERN_SUCCESS) return 1;\n\t\treturn kr;\n\t}\n\tkr = thread_get_state(allThreadsForFindingValid[0], ARM_THREAD_STATE64, (thread_state_t)&validThreadState.ts_64, &validThreadStateCount);\n\tif(kr != KERN_SUCCESS )\n\t{\n\t\tprintf(\"[createRemotePthread] ERROR: failed to get valid thread state: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\tvm_deallocate(mach_task_self(), (vm_offset_t)allThreadsForFindingValid, sizeof(thread_act_array_t) * threadCountForFindingValid);\n#endif\n\n\t// GATHER OFFSETS\n\t__unused vm_address_t libSystemPthreadAddr = getRemoteImageAddress(task, allImageInfoAddr, \"/usr/lib/system/libsystem_pthread.dylib\");\n\n\tuint64_t mainThread = 0;\n\tif (@available(iOS 12, *)) {\n\t\t// TODO: maybe instead of this, allocate our own pthread object?\n\t\t// kinda worried about side effects here, but as long our thread doesn't\n\t\t// somehow trigger pthread_main_thread modifications, it should be fine\n\t\tuint64_t pthread_main_thread_np = remoteDlSym(task, libSystemPthreadAddr, \"_pthread_main_thread_np\");\n\n\t\tuint32_t instructions[2];\n\t\tkr = task_read(task, pthread_main_thread_np, &instructions[0], sizeof(instructions));\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"ERROR: Failed to find main thread (1/3)\\n\");\n\t\t\treturn kr;\n\t\t}\n\n\t\tuint64_t _main_thread_ptr = 0;\n\t\tif (!decode_adrp_ldr(instructions[0], instructions[1], pthread_main_thread_np, &_main_thread_ptr)) {\n\t\t\tprintf(\"ERROR: Failed to find main thread (2/3)\\n\");\n\t\t\treturn 1;\n\t\t}\n\n\t\tkr = task_read(task, _main_thread_ptr, &mainThread, sizeof(mainThread));\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"ERROR: Failed to find main thread (3/3)\\n\");\n\t\t\treturn kr;\n\t\t}\n\t}\n\tuint64_t _pthread_set_self = remoteDlSym(task, libSystemPthreadAddr, \"__pthread_set_self\");\n\n\t// ALLOCATE STACK\n\tvm_address_t remoteStack64 = (vm_address_t)NULL;\n\tkr = vm_allocate(task, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"[createRemotePthread] ERROR: Unable to allocate stack memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_protect(task, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tprintf(\"[createRemotePthread] ERROR: Failed to make remote stack writable: %s.\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tthread_act_t bootstrapThread = 0;\n\tstruct arm_unified_thread_state bootstrapThreadState;\n\tmemset(&bootstrapThreadState, 0, sizeof(struct arm_unified_thread_state));\n\n\t// spawn pthread to infinite loop\n\tbootstrapThreadState.ash.flavor = ARM_THREAD_STATE64;\n\tbootstrapThreadState.ash.count = ARM_THREAD_STATE64_COUNT;\n#if __arm64e__\n\tbootstrapThreadState.ts_64.__opaque_flags = validThreadState.ts_64.__opaque_flags;\n#endif\n\tuint64_t sp = (remoteStack64 + (STACK_SIZE / 2));\n\t__unused uint64_t x2 = ropLoop;\n#if __arm64e__\n\tif (!(bootstrapThreadState.ts_64.__opaque_flags & __DARWIN_ARM_THREAD_STATE64_FLAGS_NO_PTRAUTH)) {\n\t\tx2 = (uint64_t)make_sym_callable((void*)x2);\n\t}\n#endif\n\t__darwin_arm_thread_state64_set_sp(bootstrapThreadState.ts_64, (void*)sp);\n\t__darwin_arm_thread_state64_set_pc_fptr(bootstrapThreadState.ts_64, make_sym_callable((void*)_pthread_set_self));\n\t__darwin_arm_thread_state64_set_lr_fptr(bootstrapThreadState.ts_64, make_sym_callable((void*)ropLoop)); //when done, go to infinite loop\n\tbootstrapThreadState.ts_64.__x[0] = mainThread;\n\n\t//printThreadState_state(bootstrapThreadState);\n\n\tkr = thread_create_running(task, ARM_THREAD_STATE64, (thread_state_t)&bootstrapThreadState.ts_64, ARM_THREAD_STATE64_COUNT, &bootstrapThread);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"[createRemotePthread] ERROR: Failed to create running thread: %s.\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tprintf(\"[createRemotePthread] Created bootstrap thread... now waiting on finish\\n\");\n\n\tstruct arm_unified_thread_state outState;\n\tkr = wait_for_thread(bootstrapThread, ropLoop, &outState);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"[createRemotePthread] ERROR: failed to wait for bootstrap thread: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tprintf(\"[createRemotePthread] Bootstrap done!\\n\");\n\n\tif(remotePthreadOut) *remotePthreadOut = bootstrapThread;\n\n\treturn kr;\n}\n\nkern_return_t arbCall(task_t task, thread_act_t targetThread, uint64_t* retOut, bool willReturn, vm_address_t funcPtr, int numArgs, ...)\n{\n\tkern_return_t kr = KERN_SUCCESS;\n\tif(numArgs > 8)\n\t{\n\t\tprintf(\"[arbCall] ERROR: Only 8 arguments are supported by arbCall\\n\");\n\t\treturn -2;\n\t}\n\tif(!targetThread)\n\t{\n\t\tprintf(\"[arbCall] ERROR: targetThread == null\\n\");\n\t\treturn -3;\n\t}\n\n\tva_list ap;\n\tva_start(ap, numArgs);\n\n\t// suspend target thread\n\tthread_suspend(targetThread);\n\n\t// backup states of target thread\n\n\tmach_msg_type_number_t origThreadStateCount = ARM_THREAD_STATE64_COUNT;\n\tstruct arm_unified_thread_state origThreadState;\n\tkr = thread_get_state(targetThread, ARM_THREAD_STATE64, (thread_state_t)&origThreadState.ts_64, &origThreadStateCount);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tthread_resume(targetThread);\n\t\tprintf(\"[arbCall] ERROR: failed to save original state of target thread: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tstruct arm64_thread_full_state* origThreadFullState = thread_save_state_arm64(targetThread);\n\tif(!origThreadFullState)\n\t{\n\t\tthread_resume(targetThread);\n\t\tprintf(\"[arbCall] ERROR: failed to backup original state of target thread\\n\");\n\t\treturn kr;\n\t}\n\n\t// prepare target thread for arbitary call\n\n\t// allocate stack\n\tvm_address_t remoteStack = (vm_address_t)NULL;\n\tkr = vm_allocate(task, &remoteStack, STACK_SIZE, VM_FLAGS_ANYWHERE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(origThreadFullState);\n\t\tthread_resume(targetThread);\n\t\tprintf(\"[arbCall] ERROR: Unable to allocate stack memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\t// make stack read / write\n\tkr = vm_protect(task, remoteStack, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(origThreadFullState);\n\t\tvm_deallocate(task, remoteStack, STACK_SIZE);\n\t\tthread_resume(targetThread);\n\t\tprintf(\"[arbCall] ERROR: Failed to make remote stack writable: %s.\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\t// abort any existing syscalls by target thread, thanks to Linus Henze for this suggestion :P\n\tthread_abort(targetThread);\n\n\t// set state for arb call\n\tstruct arm_unified_thread_state newState = origThreadState;\n\tuint64_t sp = remoteStack + (STACK_SIZE / 2);\n\t__darwin_arm_thread_state64_set_sp(newState.ts_64, (void*)sp);\n\t__darwin_arm_thread_state64_set_pc_fptr(newState.ts_64, make_sym_callable((void*)funcPtr));\n\t__darwin_arm_thread_state64_set_lr_fptr(newState.ts_64, make_sym_callable((void*)ropLoop));\n\n\t// write arguments into registers\n\tfor (int i = 0; i < numArgs; i++)\n\t{\n\t\tnewState.ts_64.__x[i] = va_arg(ap, uint64_t);\n\t}\n\n\tkr = thread_set_state(targetThread, ARM_THREAD_STATE64, (thread_state_t)&newState.ts_64, ARM_THREAD_STATE64_COUNT);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(origThreadFullState);\n\t\tvm_deallocate(task, remoteStack, STACK_SIZE);\n\t\tthread_resume(targetThread);\n\t\tprintf(\"[arbCall] ERROR: failed to set state for thread: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tprintf(\"[arbCall] Set thread state for arbitary call\\n\");\n\t//printThreadState(targetThread);\n\n\tthread_act_array_t cachedThreads;\n\tmach_msg_type_number_t cachedThreadCount;\n\tkr = task_threads(task, &cachedThreads, &cachedThreadCount);\n\tif (kr != KERN_SUCCESS) return kr;\n\n\tsuspend_threads_except_for(cachedThreads, cachedThreadCount, targetThread);\n\n\t// perform arbitary call\n\tthread_resume(targetThread);\n\tprintf(\"[arbCall] Started thread, waiting for it to finish...\\n\");\n\n\t// wait for arbitary call to finish (or not)\n\tstruct arm_unified_thread_state outState;\n\tif (willReturn)\n\t{\n\t\tkr = wait_for_thread(targetThread, ropLoop, &outState);\n\t\tif(kr != KERN_SUCCESS)\n\t\t{\n\t\t\tfree(origThreadFullState);\n\t\t\tprintf(\"[arbCall] ERROR: failed to wait for thread to finish: %s\\n\", mach_error_string(kr));\n\t\t\treturn kr;\n\t\t}\n\n\t\t// extract return value from state if needed\n\t\tif(retOut)\n\t\t{\n\t\t\t*retOut = outState.ts_64.__x[0];\n\t\t}\n\t}\n\telse\n\t{\n\t\tkr = wait_for_thread(targetThread, 0, &outState);\n\t\tprintf(\"[arbCall] pthread successfully did not return with code %d (%s)\\n\", kr, mach_error_string(kr));\n\t}\n\n\tresume_threads_except_for(cachedThreads, cachedThreadCount, targetThread);\n\n\tvm_deallocate(mach_task_self(), (vm_offset_t)cachedThreads, sizeof(thread_act_array_t) * cachedThreadCount);\n\n\t// release fake stack as it's no longer needed\n\tvm_deallocate(task, remoteStack, STACK_SIZE);\n\n\tif (willReturn)\n\t{\n\t\t// suspend target thread\n\t\tthread_suspend(targetThread);\n\t\tthread_abort(targetThread);\n\n\t\t// restore states of target thread to what they were before the arbitary call\n\t\tbool restoreSuccess = thread_restore_state_arm64(targetThread, origThreadFullState);\n\t\tif(!restoreSuccess)\n\t\t{\n\t\t\tprintf(\"[arbCall] ERROR: failed to revert to old thread state\\n\");\n\t\t\treturn kr;\n\t\t}\n\n\t\t// resume thread again, process should continue executing as before\n\t\t//printThreadState(targetThread);\n\t\tthread_resume(targetThread);\n\t}\n\n\treturn kr;\n}\n\nvoid prepareForMagic(task_t task, vm_address_t allImageInfoAddr)\n{\n\t// FIND INFINITE LOOP ROP GADGET\n\tstatic dispatch_once_t onceToken;\n\tdispatch_once (&onceToken, ^{\n\t\tfindRopLoop(task, allImageInfoAddr);\n\t});\n\tprintf(\"[prepareForMagic] done, ropLoop: 0x%llX\\n\", ropLoop);\n}\n\nbool sandboxFixup(task_t task, thread_act_t pthread, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr)\n{\n\tint readExtensionNeeded = sandbox_check(pid, \"file-read-data\", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath);\n\tint executableExtensionNeeded = sandbox_check(pid, \"file-map-executable\", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath);\n\n\tint retval = 0;\n\tvm_address_t libSystemSandboxAddr = 0;\n\tuint64_t sandbox_extension_consumeAddr = 0;\n\tif (readExtensionNeeded || executableExtensionNeeded) {\n\t\tlibSystemSandboxAddr = getRemoteImageAddress(task, allImageInfoAddr, \"/usr/lib/system/libsystem_sandbox.dylib\");\n\t\tsandbox_extension_consumeAddr = remoteDlSym(task, libSystemSandboxAddr, \"_sandbox_extension_consume\");\n\t\tprintf(\"[sandboxFixup] applying sandbox extension(s)! sandbox_extension_consume: 0x%llX\\n\", sandbox_extension_consumeAddr);\n\t}\n\n\tif (readExtensionNeeded) {\n\t\tchar* extString = sandbox_extension_issue_file(APP_SANDBOX_READ, dylibPath, 0);\n\t\tsize_t remoteExtStringSize = 0;\n\t\tvm_address_t remoteExtString = writeStringToTask(task, (const char*)extString, &remoteExtStringSize);\n\t\tif(remoteExtString)\n\t\t{\n\t\t\tint64_t readExtensionRet = 0;\n\t\t\tarbCall(task, pthread, (uint64_t*)&readExtensionRet, true, sandbox_extension_consumeAddr, 1, remoteExtString);\n\t\t\tvm_deallocate(task, remoteExtString, remoteExtStringSize);\n\n\t\t\tprintf(\"[sandboxFixup] sandbox_extension_consume returned %lld for read extension\\n\", (int64_t)readExtensionRet);\n\t\t\tretval |= (readExtensionRet <= 0);\n\t\t}\n\t}\n\telse {\n\t\tprintf(\"[sandboxFixup] read extension not needed, skipping...\\n\");\n\t}\n\n\tif (executableExtensionNeeded) {\n\t\tchar* extString = sandbox_extension_issue_file(\"com.apple.sandbox.executable\", dylibPath, 0);\n\t\tsize_t remoteExtStringSize = 0;\n\t\tvm_address_t remoteExtString = writeStringToTask(task, (const char*)extString, &remoteExtStringSize);\n\t\tif(remoteExtString)\n\t\t{\n\t\t\tint64_t executableExtensionRet = 0;\n\t\t\tarbCall(task, pthread, (uint64_t*)&executableExtensionRet, true, sandbox_extension_consumeAddr, 1, remoteExtString);\n\t\t\tvm_deallocate(task, remoteExtString, remoteExtStringSize);\n\n\t\t\tprintf(\"[sandboxFixup] sandbox_extension_consume returned %lld for executable extension\\n\", (int64_t)executableExtensionRet);\n\t\t\tretval |= (executableExtensionRet <= 0);\n\t\t}\n\t}\n\telse {\n\t\tprintf(\"[sandboxFixup] executable extension not needed, skipping...\\n\");\n\t}\n\n\treturn retval == 0;\n}\n\nvoid injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr)\n{\n\tprepareForMagic(task, allImageInfoAddr);\n\n\tthread_act_t pthread = 0;\n\tkern_return_t kr = createRemotePthread(task, allImageInfoAddr, &pthread);\n\tif(kr != KERN_SUCCESS) return;\n\n\tsandboxFixup(task, pthread, pid, dylibPath, allImageInfoAddr);\n\n\tprintf(\"[injectDylibViaRop] Preparation done, now injecting!\\n\");\n\n\t// FIND OFFSETS\n\tvm_address_t libDyldAddr = getRemoteImageAddress(task, allImageInfoAddr, \"/usr/lib/system/libdyld.dylib\");\n\tuint64_t dlopenAddr = remoteDlSym(task, libDyldAddr, \"_dlopen\");\n\tuint64_t dlerrorAddr = remoteDlSym(task, libDyldAddr, \"_dlerror\");\n\n\tprintf(\"[injectDylibViaRop] dlopen: 0x%llX, dlerror: 0x%llX\\n\", (unsigned long long)dlopenAddr, (unsigned long long)dlerrorAddr);\n\n\t// CALL DLOPEN\n\tsize_t remoteDylibPathSize = 0;\n\tvm_address_t remoteDylibPath = writeStringToTask(task, (const char*)dylibPath, &remoteDylibPathSize);\n\tif(remoteDylibPath)\n\t{\n\t\tvoid* dlopenRet;\n\t\tarbCall(task, pthread, (uint64_t*)&dlopenRet, true, dlopenAddr, 2, remoteDylibPath, RTLD_NOW);\n\t\tvm_deallocate(task, remoteDylibPath, remoteDylibPathSize);\n\n\t\tif (dlopenRet) {\n\t\t\tprintf(\"[injectDylibViaRop] dlopen succeeded, library handle: %p\\n\", dlopenRet);\n\t\t}\n\t\telse {\n\t\t\tuint64_t remoteErrorString = 0;\n\t\t\tarbCall(task, pthread, (uint64_t*)&remoteErrorString, true, dlerrorAddr, 0);\n\t\t\tchar *errorString = task_copy_string(task, remoteErrorString);\n\t\t\tprintf(\"[injectDylibViaRop] dlopen failed, error:\\n%s\\n\", errorString);\n\t\t\tfree(errorString);\n\t\t}\n\t}\n\n\tthread_terminate(pthread);\n}"
  },
  {
    "path": "sandbox.h",
    "content": "enum sandbox_filter_type {\n\tSANDBOX_FILTER_NONE,\n\tSANDBOX_FILTER_PATH,\n\tSANDBOX_FILTER_GLOBAL_NAME,\n\tSANDBOX_FILTER_LOCAL_NAME,\n\tSANDBOX_FILTER_APPLEEVENT_DESTINATION,\n\tSANDBOX_FILTER_RIGHT_NAME,\n\tSANDBOX_FILTER_PREFERENCE_DOMAIN,\n\tSANDBOX_FILTER_KEXT_BUNDLE_ID,\n\tSANDBOX_FILTER_INFO_TYPE,\n\tSANDBOX_FILTER_NOTIFICATION,\n\t// ?\n\t// ?\n\tSANDBOX_FILTER_XPC_SERVICE_NAME = 12,\n\tSANDBOX_FILTER_IOKIT_CONNECTION,\n\t// ?\n\t// ?\n\t// ?\n\t// ?\n};\n\n\nenum sandbox_extension_flags {\n\tFS_EXT_DEFAULTS =              0,\n\tFS_EXT_FOR_PATH =       (1 << 0),\n\tFS_EXT_FOR_FILE =       (1 << 1),\n\tFS_EXT_READ =           (1 << 2),\n\tFS_EXT_WRITE =          (1 << 3),\n\tFS_EXT_PREFER_FILEID =  (1 << 4),\n};\n\nextern const char * APP_SANDBOX_IOKIT_CLIENT;\nextern const char * APP_SANDBOX_MACH;\nextern const char * APP_SANDBOX_READ;\nextern const char * APP_SANDBOX_READ_WRITE;\n\nextern const char * IOS_SANDBOX_APPLICATION_GROUP;\nextern const char * IOS_SANDBOX_CONTAINER;\n\nextern const enum sandbox_filter_type SANDBOX_CHECK_ALLOW_APPROVAL;\nextern const enum sandbox_filter_type SANDBOX_CHECK_CANONICAL;\nextern const enum sandbox_filter_type SANDBOX_CHECK_NOFOLLOW;\nextern const enum sandbox_filter_type SANDBOX_CHECK_NO_APPROVAL;\nextern const enum sandbox_filter_type SANDBOX_CHECK_NO_REPORT;\n\nextern const uint32_t SANDBOX_EXTENSION_CANONICAL;\nextern const uint32_t SANDBOX_EXTENSION_DEFAULT;\nextern const uint32_t SANDBOX_EXTENSION_MAGIC;\nextern const uint32_t SANDBOX_EXTENSION_NOFOLLOW;\nextern const uint32_t SANDBOX_EXTENSION_NO_REPORT;\nextern const uint32_t SANDBOX_EXTENSION_NO_STORAGE_CLASS;\nextern const uint32_t SANDBOX_EXTENSION_PREFIXMATCH;\nextern const uint32_t SANDBOX_EXTENSION_UNRESOLVED;\n\nint sandbox_check(pid_t, const char *operation, enum sandbox_filter_type, ...);\nint sandbox_check_by_audit_token(audit_token_t, const char *operation, enum sandbox_filter_type, ...);\nint sandbox_check_by_uniqueid(uid_t, pid_t, const char *operation, enum sandbox_filter_type, ...);\n\nint64_t sandbox_extension_consume(const char *extension_token);\nchar *sandbox_extension_issue_file(const char *extension_class, const char *path, uint32_t flags);\nchar *sandbox_extension_issue_file_to_process(const char *extension_class, const char *path, uint32_t flags, audit_token_t);\nchar *sandbox_extension_issue_file_to_process_by_pid(const char *extension_class, const char *path, uint32_t flags, pid_t);\nchar *sandbox_extension_issue_file_to_self(const char *extension_class, const char *path, uint32_t flags);\nchar *sandbox_extension_issue_generic(const char *extension_class, uint32_t flags);\nchar *sandbox_extension_issue_generic_to_process(const char *extension_class, uint32_t flags, audit_token_t);\nchar *sandbox_extension_issue_generic_to_process_by_pid(const char *extension_class, uint32_t flags, pid_t);\nchar *sandbox_extension_issue_iokit_registry_entry_class(const char *extension_class, const char *registry_entry_class, uint32_t flags);\nchar *sandbox_extension_issue_iokit_registry_entry_class_to_process(const char *extension_class, const char *registry_entry_class, uint32_t flags, audit_token_t);\nchar *sandbox_extension_issue_iokit_registry_entry_class_to_process_by_pid(const char *extension_class, const char *registry_entry_class, uint32_t flags, pid_t);\nchar *sandbox_extension_issue_iokit_user_client_class(const char *extension_class, const char *registry_entry_class, uint32_t flags);\nchar *sandbox_extension_issue_mach(const char *extension_class, const char *name, uint32_t flags);\nchar *sandbox_extension_issue_mach_to_process(const char *extension_class, const char *name, uint32_t flags, audit_token_t);\nchar *sandbox_extension_issue_mach_to_process_by_pid(const char *extension_class, const char *name, uint32_t flags, pid_t);\nchar *sandbox_extension_issue_posix_ipc(const char *extension_class, const char *name, uint32_t flags);\nvoid sandbox_extension_reap(void);\nint sandbox_extension_release(int64_t extension_handle);\nint sandbox_extension_release_file(int64_t extension_handle, const char *path);\nint sandbox_extension_update_file(int64_t extension_handle, const char *path);"
  },
  {
    "path": "shellcode_inject.h",
    "content": "int injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr);"
  },
  {
    "path": "shellcode_inject.m",
    "content": "#include <stdlib.h>\n#include <sys/wait.h>\n#include <stdio.h>\n#import <unistd.h>\n#import <dlfcn.h>\n#import <mach-o/getsect.h>\n#import <mach-o/dyld.h>\n#import <mach/mach.h>\n#import <mach-o/loader.h>\n#import <mach-o/nlist.h>\n#import <mach-o/reloc.h>\n#import <mach-o/dyld_images.h>\n#import <sys/utsname.h>\n#import <string.h>\n#import <limits.h>\n\n#include <sys/types.h>\n#include <mach/error.h>\n#include <errno.h>\n#include <sys/sysctl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <pthread.h>\n#include <pthread_spis.h>\n\n#include <mach/arm/thread_status.h>\n#import \"dyld.h\"\n#import \"sandbox.h\"\n#import <CoreFoundation/CoreFoundation.h>\n\n#define STACK_SIZE 65536\n\n#define\tPT_TRACE_ME\t0\t/* child declares it's being traced */\n#define\tPT_READ_I\t1\t/* read word in child's I space */\n#define\tPT_READ_D\t2\t/* read word in child's D space */\n#define\tPT_READ_U\t3\t/* read word in child's user structure */\n#define\tPT_WRITE_I\t4\t/* write word in child's I space */\n#define\tPT_WRITE_D\t5\t/* write word in child's D space */\n#define\tPT_WRITE_U\t6\t/* write word in child's user structure */\n#define\tPT_CONTINUE\t7\t/* continue the child */\n#define\tPT_KILL\t\t8\t/* kill the child process */\n#define\tPT_STEP\t\t9\t/* single step the child */\n#define\tPT_ATTACH\t10\t/* trace some running process */\n#define\tPT_DETACH\t11\t/* stop tracing a process */\n#define\tPT_SIGEXC\t12\t/* signals as exceptions for current_proc */\n#define PT_THUPDATE\t13\t/* signal for thread# */\n#define PT_ATTACHEXC\t14\t/* attach to running process with signal exception */\nextern int ptrace(int request, pid_t pid, caddr_t addr, int data);\n\nstatic kern_return_t runPayload(task_t task, uint8_t* payload, size_t payloadSize, uint64_t codeStart, vm_address_t allImageInfoAddr)\n{\n\t// GATHER OFFSETS\n\n\tvm_address_t libSystemPthreadAddr = getRemoteImageAddress(task, allImageInfoAddr, \"/usr/lib/system/libsystem_pthread.dylib\");\n\tuint64_t pthread_create_from_mach_threadAddr = remoteDlSym(task, libSystemPthreadAddr, \"_pthread_create_from_mach_thread\");\n\tuint64_t pthread_exitAddr = remoteDlSym(task, libSystemPthreadAddr, \"_pthread_exit\");\n\n\n\t// ALLOCATE STACK\n\n\tvm_address_t remoteStack64 = (vm_address_t)NULL;\n\tkern_return_t kr = KERN_SUCCESS;\n\tkr = vm_allocate(task, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"ERROR: Unable to allocate stack memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_protect(task, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tprintf(\"ERROR: Failed to make remote stack writable: %s.\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\n\t// THREAD BOOTSTRAP PAYLOAD (x2: startFunc argument passed to pthread_create_from_mach_thread)\n\n\tuint32_t bootstrapCode[7] = {\n\t\t// pthread_create_from_mach_thread call\n\t\t// arg1: x0, output pthread_t = pointer to stack\n\t\t// arg2: x1, attributes = NULL\n\t\t// arg3: x2, start_routine, pointer to real payload, passed via thread state\n\t\t// arg4: x3, arg, pointer to arguments array = NULL\n\t\tCFSwapInt32(0xE0230091), // add x0, sp, #8 ; x0 = SP+8\n\t\tCFSwapInt32(0x010080D2), // mov x1, #0x0\n\t\tCFSwapInt32(0x030080D2), // mov x3, #0x0\n\t\tCFSwapInt32(0x68FFFF58), // ldr x8, -0x14 ; pthread_create_from_mach_threadAddr\n\t\tCFSwapInt32(0x00013FD6), // blr x8\n\t\tCFSwapInt32(0x490880D2), // mov x9, 0x42 (end code)\n\t\tCFSwapInt32(0x00000014), // b #0 (infinite loop)\n\t};\n\n\tuint64_t bootstrapCodeVarCount = 1; // count of variables before code\n\tsize_t bootstrapCodeVarSize = bootstrapCodeVarCount * sizeof(uint64_t);\n\n\tsize_t bootstrapPayloadSize = bootstrapCodeVarCount * sizeof(uint32_t) + sizeof(bootstrapCode); // allocate needed memory space\n\tchar* bootstrapPayload = malloc(bootstrapPayloadSize);\n\tbzero(&bootstrapPayload[0], bootstrapPayloadSize);\n\n\tintptr_t bootstrapPayloadPtr = (intptr_t)bootstrapPayload; // insert variables\n\tmemcpy((void*)(bootstrapPayloadPtr), (const void*)&pthread_create_from_mach_threadAddr, sizeof(uint64_t));\n\n\tmemcpy((void*)(bootstrapPayloadPtr+bootstrapCodeVarSize), &bootstrapCode[0], sizeof(bootstrapCode)); //insert code\n\n\tvm_address_t remoteBootstrapPayload = (vm_address_t)NULL;\n\tkr = vm_allocate(task, &remoteBootstrapPayload, bootstrapPayloadSize, VM_FLAGS_ANYWHERE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(bootstrapPayload);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tprintf(\"ERROR: Unable to allocate memory for bootstrap code: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_write(task, remoteBootstrapPayload, (vm_address_t)bootstrapPayload, bootstrapPayloadSize);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(bootstrapPayload);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Failed to write payload to code memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_protect(task, remoteBootstrapPayload + bootstrapCodeVarSize, sizeof(bootstrapCode), FALSE, VM_PROT_READ | VM_PROT_EXECUTE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(bootstrapPayload);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Failed to make bootstrap payload executable: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\t/*kr = vm_protect(task, remoteBootstrapPayload, bootstrapCodeVarSize, FALSE, VM_PROT_READ | VM_PROT_WRITE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Failed to make bootstrap payload variables read/write: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}*/\n\n\tfree(bootstrapPayload);\n\n\n\t// SETUP PAYLOAD SUFFIX (pthread_exit(0))\n\n\tuint32_t payloadSuffixCode[3] = {\n\t\tCFSwapInt32(0x000080D2), // mov x0, #0\n\t\tCFSwapInt32(0x48000058), // ldr x8, 0x8 ; pthread_exit\n\t\tCFSwapInt32(0x00013FD6), // blr x8\n\t};\n\n\tuint64_t payloadSuffixVarCount = 1; // count of variables after suffix code\n\tuint64_t payloadSuffixVarSize = payloadSuffixVarCount * sizeof(uint64_t);\n\tuint64_t payloadSuffixCodeSize = sizeof(payloadSuffixCode);\n\tuint64_t payloadSuffixSize = payloadSuffixVarSize + payloadSuffixCodeSize;\n\n\tchar* payloadSuffix = malloc(payloadSuffixSize); // allocate buffer\n\tintptr_t payloadSuffixPtr = (intptr_t)payloadSuffix;\n\n\tmemcpy((void*)(payloadSuffixPtr), &payloadSuffixCode[0], payloadSuffixCodeSize); // insert code\n\tmemcpy((void*)(payloadSuffixPtr+payloadSuffixCodeSize), (const void*)&pthread_exitAddr, sizeof(uint64_t)); // insert variables\n\n\n\t// WRITE PASSED PAYLOAD\n\n\tuint64_t fullPayloadSize = payloadSize + payloadSuffixSize;\n\n\tvm_address_t remotePayload = (vm_address_t)NULL;\n\tkr = vm_allocate(task, &remotePayload, fullPayloadSize, VM_FLAGS_ANYWHERE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(payloadSuffix);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Unable to allocate payload code memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_write(task, remotePayload, (vm_address_t)payload, payloadSize);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(payloadSuffix);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remotePayload, fullPayloadSize);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Failed to write payload to code memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_write(task, remotePayload+payloadSize, (vm_address_t)payloadSuffix, payloadSuffixSize);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(payloadSuffix);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remotePayload, fullPayloadSize);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Failed to write payload suffix to code memory: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tkr = vm_protect(task, remotePayload + codeStart, fullPayloadSize - codeStart - payloadSuffixVarSize, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tfree(payloadSuffix);\n\t\tvm_deallocate(task, remoteStack64, STACK_SIZE);\n\t\tvm_deallocate(task, remotePayload, fullPayloadSize);\n\t\tvm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);\n\t\tprintf(\"ERROR: Failed to make code payload executable: %s\\n\", mach_error_string(kr));\n\t\treturn kr;\n\t}\n\n\tprintf(\"marked %llX - %llX as rx\\n\", (uint64_t)remotePayload + codeStart, (uint64_t)remotePayload + (fullPayloadSize - codeStart - payloadSuffixVarSize));\n\n\n\t// ALLOCATE THREAD AND START IT\n\n\tthread_act_t remoteThread;\n\n\tstruct arm_unified_thread_state remoteThreadState64;\n\tbzero(&remoteThreadState64, sizeof(struct arm_unified_thread_state));\n\n\tremoteThreadState64.ash.flavor = ARM_THREAD_STATE64;\n\tremoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;\n\t__darwin_arm_thread_state64_set_sp(remoteThreadState64.ts_64, (void*)(remoteStack64 + (STACK_SIZE / 2)));\n\t__darwin_arm_thread_state64_set_pc_fptr(remoteThreadState64.ts_64, (void*)(remoteBootstrapPayload + bootstrapCodeVarSize));\n\tremoteThreadState64.ts_64.__x[2] = remotePayload + codeStart; // <- startFunc argument of pthread_create_from_mach_thread call\n\n\tprintf(\"About to jump to %llX (thread bootstrap)\\n\", (uint64_t)__darwin_arm_thread_state64_get_pc_fptr(remoteThreadState64.ts_64));\n\tprintf(\"Real payload: %llX (code start: %llX)\\n\", (uint64_t)remotePayload,  (uint64_t)(remotePayload + codeStart));\n\n\t// Uncomment to debug issues before the jump to code happens\n\t//getchar();\n\n\tprintf(\"Starting thread in task!\\n\");\n\tkr = thread_create_running(task, ARM_THREAD_STATE64, (thread_state_t)&remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT, &remoteThread);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"ERROR: Failed to create running thread: %s.\\n\", mach_error_string(kr));\n\t}\n\n\n\t// WAIT FOR THREAD TO FINISH\n\t// (Note: The pthread this thread spawns will still be running by the time it has fininshed)\n\t// (So we unfortunately can't release the remote memory again because otherwise the pthread would crash)\n\n\tprintf(\"Started thread, now waiting for it to finish.\\n\");\n\n\tmach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;\n\tfor (;;) {\n\t\tkr = thread_get_state(remoteThread, ARM_THREAD_STATE64, (thread_state_t)&remoteThreadState64.ts_64, &thread_state_count);\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"Error getting stub thread state: error %s\", mach_error_string(kr));\n\t\t\tbreak;\n\t\t}\n\n\t\t// Check whether x9 is 0x42 (exit code)\n\t\tif (remoteThreadState64.ts_64.__x[9] == 0x42) {\n\t\t\tprintf(\"Stub thread finished\\n\");\n\t\t\tkr = thread_terminate(remoteThread);\n\t\t\tif (kr != KERN_SUCCESS) {\n\t\t\t\tprintf(\"Error terminating stub thread: error %s\\n\", mach_error_string(kr));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprintf(\"Thread finished, we done here.\\n\");\n\n\t// Deallocating is very hard to time right, we just leave the memory in there, doesn't matter too much anyways\n\t//vm_deallocate(task, remoteStack64, STACK_SIZE);\n\t//vm_deallocate(task, remoteCode64, payloadSize);\n\tfree(payloadSuffix);\n\treturn kr;\n}\n\nint injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr)\n{\n\t// STEP ONE: gather offsets\n\t\n\tvm_address_t libSystemSandboxAddr = getRemoteImageAddress(task, allImageInfoAddr, \"/usr/lib/system/libsystem_sandbox.dylib\");\n\tvm_address_t libDyldAddr = getRemoteImageAddress(task, allImageInfoAddr, \"/usr/lib/system/libdyld.dylib\");\n\n\t//uint64_t pthread_set_selfAddr = remoteDlSym(task, libSystemPthreadAddr, \"__pthread_set_self\");\n\t\n\tuint64_t sandbox_extension_consumeAddr = remoteDlSym(task, libSystemSandboxAddr, \"_sandbox_extension_consume\");\n\tuint64_t dlopenAddr = remoteDlSym(task, libDyldAddr, \"_dlopen\");\n\n\tprintf(\"sandbox_extension_consumeAddr: %llX\\n\", (unsigned long long)sandbox_extension_consumeAddr);\n\tprintf(\"dlopenAddr: %llX\\n\", (unsigned long long)dlopenAddr);\n\n\t// STEP TWO: set up ptrace\n\n\t/*kern_return_t ret;\n\tret = ptrace(PT_ATTACH, pid, NULL, 0);\n\tif(ret != KERN_SUCCESS)\n\t{\n\t\tprintf(\"Error attaching to process %d: %d\\n\", pid, errno);\n\t\treturn 1;\n\t}\n\tret = ptrace(PT_CONTINUE, pid, NULL, 0);\n\tif(ret != KERN_SUCCESS)\n\t{\n\t\tprintf(\"Error continuing execution of process %d\\n\", pid);\n\t\treturn 1;\n\t}*/\n\n\t// STEP THREE: apply sandbox extension if needed\n\n\t// Check whether sandbox extension is needed\n\tint sandboxExtensionNeeded = sandbox_check(pid, \"file-read-data\", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath);\n\tif(sandboxExtensionNeeded)\n\t{\n\t\tprintf(\"Sandbox extension needed, performing magic...\\n\");\n\n\t\tchar* extString = sandbox_extension_issue_file(APP_SANDBOX_READ, dylibPath, 0);\n\t\tsize_t stringAllocSize = 300;\n\n\t\tuint64_t codeVarCount = 1;\n\t\tuint64_t codeVarSize = codeVarCount * sizeof(uint64_t);\n\n\t\tuint32_t code[3] = {\n\t\t\t// sandbox_extension_consume call\n\t\t\t// arg1: x0, pointer to extension string\n\t\t\tCFSwapInt32(0x60F6FF10), // adr x0, -0x134 ; reference to extString\n\t\t\tCFSwapInt32(0xA8FFFF58), // ldr x8, -0x0C ; sandbox_extension_consumeAddr\n\t\t\tCFSwapInt32(0x00013FD6), // blr x8\n\t\t};\n\n\t\tsize_t payloadSize = stringAllocSize + codeVarSize + sizeof(code);\n\t\tchar* payload = malloc(payloadSize);\n\t\tbzero(&payload[0], payloadSize);\n\t\tstrlcpy(&payload[0], extString, stringAllocSize); // insert extString\n\n\t\tintptr_t payloadIntPtr = (intptr_t)payload;\n\t\tmemcpy((void*)(payloadIntPtr+stringAllocSize), (const void*)&sandbox_extension_consumeAddr, sizeof(uint64_t)); // insert vars\n\n\t\tuint64_t codeStart = stringAllocSize + codeVarSize;\n\t\tmemcpy((void*)(payloadIntPtr+codeStart), &code[0], sizeof(code)); // insert code\n\n\t\tprintf(\"constructed sandbox_extension_consume payload!\\n\");\n\n\t\trunPayload(task, (uint8_t*)payload, payloadSize, codeStart, allImageInfoAddr);\n\t\tfree(payload);\n\t}\n\telse\n\t{\n\t\tprintf(\"No Sandbox extension needed, skipping straight to dylib injection...\\n\");\n\t}\n\n\t// STEP FOUR: load dylib\n\tsize_t stringAllocSize = 256;\n\tuint64_t codeVarCount = 1;\n\tuint64_t codeVarSize = codeVarCount * sizeof(uint64_t);\n\n\tuint32_t code[3] = {\n\t\t// sandbox_extension_consume call\n\t\t// arg1: x0, pointer to extension string\n\t\tCFSwapInt32(0xC0F7FF10), // adr x0, -0x108 ; reference to dylibPath\n\t\tCFSwapInt32(0xA8FFFF58), // ldr x8, -0x0C ; dlopenAddr\n\t\tCFSwapInt32(0x00013FD6), // blr x8\n\t};\n\n\tsize_t payloadSize = stringAllocSize + codeVarSize + sizeof(code);\n\tchar* payload = malloc(payloadSize);\n\tbzero(&payload[0], payloadSize);\n\tstrlcpy(&payload[0], dylibPath, stringAllocSize); // insert extString\n\n\tintptr_t payloadIntPtr = (intptr_t)payload;\n\tmemcpy((void*)(payloadIntPtr+stringAllocSize), (const void*)&dlopenAddr, sizeof(uint64_t)); // insert vars\n\n\tuint64_t codeStart = stringAllocSize + codeVarSize;\n\tmemcpy((void*)(payloadIntPtr+codeStart), &code[0], sizeof(code)); // insert code\n\n\tprintf(\"constructed dlopen payload!\\n\");\n\n\trunPayload(task, (uint8_t*)payload, payloadSize, codeStart, allImageInfoAddr);\n\tfree(payload);\n\n\t// STEP FIVE: detach from process\n\n\t/*ret = ptrace(PT_DETACH, pid, NULL, 0);\n\tif(ret != KERN_SUCCESS)\n\t{\n\t\tprintf(\"Error detaching from process %d\\n\", pid);\n\t\treturn 1;\n\t}*/\n\n\treturn 0;\n}"
  },
  {
    "path": "task_utils.h",
    "content": "kern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size);\nchar *task_copy_string(task_t task, vm_address_t address);\nkern_return_t task_write(task_t task, vm_address_t address, void* inBuf, vm_size_t size);"
  },
  {
    "path": "task_utils.m",
    "content": "#import <mach/mach.h>\n#import <stdlib.h>\n\nkern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size)\n{\n\tsize_t maxSize = size;\n\tkern_return_t kr = vm_read_overwrite(task, address, size, (vm_address_t)outBuf, &maxSize);\n\tif (kr == KERN_SUCCESS) {\n\t\tif (maxSize < size) {\n\t\t\tuint8_t *outBufU = outBuf;\n\t\t\tmemset(&outBufU[maxSize-1], 0, size - maxSize);\n\t\t}\n\t}\n\treturn kr;\n}\n\nchar *task_copy_string(task_t task, vm_address_t address)\n{\n\t// find end of string\n\tsize_t len = 0;\n\tchar buf = 0;\n\tdo {\n\t\tif (task_read(task, address + (len++), &buf, sizeof(buf)) != KERN_SUCCESS) return NULL;\n\t} while (buf != '\\0');\n\n\t// copy string\n\tchar *strBuf = malloc(len);\n\tif (task_read(task, address, &strBuf[0], len) != KERN_SUCCESS) return NULL;\n\treturn strBuf;\n}\n\nkern_return_t task_write(task_t task, vm_address_t address, void* inBuf, vm_size_t size)\n{\n\treturn vm_write(task, address, (vm_offset_t)inBuf, size);\n}"
  },
  {
    "path": "thread_utils.h",
    "content": "#import <mach/arm/thread_status.h>\n#import <mach/thread_status.h>\n#import <mach/mach.h>\n#import <mach/error.h>\n#import <CoreFoundation/CoreFoundation.h>\n\n\n#define STACK_SIZE 65536\nstatic uint64_t ropLoop;\n\n#if __DARWIN_OPAQUE_ARM_THREAD_STATE64\n#define FP_UNIFIED __opaque_fp\n#define LR_UNIFIED __opaque_lr\n#define SP_UNIFIED __opaque_sp\n#define PC_UNIFIED __opaque_pc\n#define FLAGS_UNIFIED __opaque_flags\n#define REGISTER_TYPE void*\n#else\n#define FP_UNIFIED __fp\n#define LR_UNIFIED __lr\n#define SP_UNIFIED __sp\n#define PC_UNIFIED __pc\n#define FLAGS_UNIFIED __pad\n#define REGISTER_TYPE uint64_t\n#endif\n\nstruct arm64_thread_full_state {\n\tarm_thread_state64_t    thread;\n\tarm_exception_state64_t exception;\n\tarm_neon_state64_t      neon;\n\tarm_debug_state64_t     debug;\n\tuint32_t                thread_valid:1,\n\t\t\t\t\t\t\texception_valid:1,\n\t\t\t\t\t\t\tneon_valid:1,\n\t\t\t\t\t\t\tdebug_valid:1,\n\t\t\t\t\t\t\tcpmu_valid:1;\n};\n\nextern struct arm64_thread_full_state* thread_save_state_arm64(thread_act_t thread);\nextern bool thread_restore_state_arm64(thread_act_t thread, struct arm64_thread_full_state* state);\nextern kern_return_t wait_for_thread(thread_act_t thread, uint64_t pcToWait, struct arm_unified_thread_state* stateOut);\nextern void printThreadState_state(struct arm_unified_thread_state threadState);\nextern void printThreadState(thread_act_t thread);\nextern void printThreadInfo(thread_act_t thread);\nkern_return_t suspend_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread);\nkern_return_t resume_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread);\n"
  },
  {
    "path": "thread_utils.m",
    "content": "#import \"thread_utils.h\"\n\n#import <stdio.h>\n#import <unistd.h>\n#import <stdlib.h>\n\n#import <mach-o/getsect.h>\n#import <mach-o/dyld.h>\n#import <mach-o/loader.h>\n#import <mach-o/nlist.h>\n#import <mach-o/reloc.h>\n#import <mach-o/dyld_images.h>\n\nstruct arm64_thread_full_state* thread_save_state_arm64(thread_act_t thread)\n{\n\tstruct arm64_thread_full_state* s = malloc(sizeof(struct arm64_thread_full_state));\n\tmach_msg_type_number_t count;\n\tkern_return_t kr;\n\n\t// ARM_THREAD_STATE64\n\tcount = ARM_THREAD_STATE64_COUNT;\n\tkr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t) &s->thread, &count);\n\ts->thread_valid = (kr == KERN_SUCCESS);\n\tif (kr != KERN_SUCCESS) {\n\t\tprintf(\"ERROR: Failed to save ARM_THREAD_STATE64 state: %s\", mach_error_string(kr));\n\t\tfree(s);\n\t\treturn NULL;\n\t}\n\t// ARM_EXCEPTION_STATE64\n\tcount = ARM_EXCEPTION_STATE64_COUNT;\n\tkr = thread_get_state(thread, ARM_EXCEPTION_STATE64,\n\t\t\t(thread_state_t) &s->exception, &count);\n\ts->exception_valid = (kr == KERN_SUCCESS);\n\tif (kr != KERN_SUCCESS) {\n\t\tprintf(\"WARNING: Failed to save ARM_EXCEPTION_STATE64 state: %s\", mach_error_string(kr));\n\t}\n\t// ARM_NEON_STATE64\n\tcount = ARM_NEON_STATE64_COUNT;\n\tkr = thread_get_state(thread, ARM_NEON_STATE64, (thread_state_t) &s->neon, &count);\n\ts->neon_valid = (kr == KERN_SUCCESS);\n\tif (kr != KERN_SUCCESS) {\n\t\tprintf(\"WARNING: Failed to save ARM_NEON_STATE64 state: %s\", mach_error_string(kr));\n\t}\n\t// ARM_DEBUG_STATE64\n\tcount = ARM_DEBUG_STATE64_COUNT;\n\tkr = thread_get_state(thread, ARM_DEBUG_STATE64, (thread_state_t) &s->debug, &count);\n\ts->debug_valid = (kr == KERN_SUCCESS);\n\tif (kr != KERN_SUCCESS) {\n\t\tprintf(\"WARNING: Failed to save ARM_DEBUG_STATE64 state: %s\", mach_error_string(kr));\n\t}\n\n\treturn s;\n}\n\nbool thread_restore_state_arm64(thread_act_t thread, struct arm64_thread_full_state* state)\n{\n\tstruct arm64_thread_full_state *s = (void *) state;\n\tkern_return_t kr;\n\tbool success = true;\n\t// ARM_THREAD_STATE64\n\tif (s->thread_valid) {\n\t\tkr = thread_set_state(thread, ARM_THREAD_STATE64, (thread_state_t) &s->thread, ARM_THREAD_STATE64_COUNT);\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"ERROR: Failed to restore ARM_THREAD_STATE64 state: %s\", mach_error_string(kr));\n\t\t\tsuccess = false;\n\t\t}\n\t}\n\t// ARM_EXCEPTION_STATE64\n\tif (s->exception_valid) {\n\t\tkr = thread_set_state(thread, ARM_EXCEPTION_STATE64, (thread_state_t) &s->exception, ARM_EXCEPTION_STATE64_COUNT);\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"ERROR: Failed to restore ARM_EXCEPTION_STATE64 state: %s\", mach_error_string(kr));\n\t\t\tsuccess = false;\n\t\t}\n\t}\n\t// ARM_NEON_STATE64\n\tif (s->neon_valid) {\n\t\tkr = thread_set_state(thread, ARM_NEON_STATE64, (thread_state_t) &s->neon, ARM_NEON_STATE64_COUNT);\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"ERROR: Failed to restore ARM_NEON_STATE64 state: %s\", mach_error_string(kr));\n\t\t\tsuccess = false;\n\t\t}\n\t}\n\t// ARM_DEBUG_STATE64\n\tif (s->debug_valid) {\n\t\tkr = thread_set_state(thread, ARM_DEBUG_STATE64, (thread_state_t) &s->debug, ARM_DEBUG_STATE64_COUNT);\n\t\tif (kr != KERN_SUCCESS) {\n\t\t\tprintf(\"ERROR: Failed to restore ARM_DEBUG_STATE64 state: %s\", mach_error_string(kr));\n\t\t\tsuccess = false;\n\t\t}\n\t}\n\t// Now free the struct.\n\tfree(s);\n\treturn success;\n}\n\nkern_return_t wait_for_thread(thread_act_t thread, uint64_t pcToWait, struct arm_unified_thread_state* stateOut)\n{\n\tmach_msg_type_number_t stateToObserveCount = ARM_THREAD_STATE64_COUNT;\n\tstruct arm_unified_thread_state stateToObserve;\n\n\tint errCount = 0;\n\twhile(1)\n\t{\n\t\tkern_return_t kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&stateToObserve.ts_64, &stateToObserveCount);\n\t\tif(kr != KERN_SUCCESS)\n\t\t{\n\t\t\tif (pcToWait == 0) return kr;\n\n\t\t\terrCount++;\n\t\t\tif(errCount >= 5)\n\t\t\t{\n\t\t\t\treturn kr;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\terrCount = 0;\n\n\t\t// wait until pc matches with infinite loop rop gadget\n\t\tuint64_t pc = (uint64_t)__darwin_arm_thread_state64_get_pc(stateToObserve.ts_64);\n\t\tif(pc == pcToWait) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(stateOut)\n\t{\n\t\t*stateOut = stateToObserve;\n\t}\n\n\treturn KERN_SUCCESS;\n}\n\nkern_return_t suspend_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread)\n{\n\tfor (int i = 0; i < threadCount; i++) {\n\t\tthread_act_t thread = allThreads[i];\n\t\tif (thread != exceptForThread) {\n\t\t\tthread_suspend(thread);\n\t\t}\n\t}\n\treturn KERN_SUCCESS;\n}\n\nkern_return_t resume_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread)\n{\n\tfor (int i = 0; i < threadCount; i++) {\n\t\tthread_act_t thread = allThreads[i];\n\t\tif (thread != exceptForThread) {\n\t\t\tthread_resume(thread);\n\t\t}\n\t}\n\treturn KERN_SUCCESS;\n}\n\nvoid printThreadState_state(struct arm_unified_thread_state threadState)\n{\n\tfor(int i = 0; i <= 28; i++)\n\t{\n\t\tprintf(\"x%d = 0x%llX\\n\", i, threadState.ts_64.__x[i]);\n\t}\n\t//printf(\"fp: 0x%llX\\n\", (uint64_t)__darwin_arm_thread_state64_get_fp(threadState.ts_64));\n\t//printf(\"lr: 0x%llX\\n\", (uint64_t)__darwin_arm_thread_state64_get_lr(threadState.ts_64));\n\t//printf(\"sp: 0x%llX\\n\", (uint64_t)__darwin_arm_thread_state64_get_sp(threadState.ts_64));\n\t//printf(\"pc: 0x%llX\\n\", (uint64_t)__darwin_arm_thread_state64_get_pc(threadState.ts_64));\n\tprintf(\"pc: 0x%llX\\n\", (uint64_t)threadState.ts_64.PC_UNIFIED);\n\tprintf(\"sp: 0x%llX\\n\", (uint64_t)threadState.ts_64.SP_UNIFIED);\n\tprintf(\"fp: 0x%llX\\n\", (uint64_t)threadState.ts_64.FP_UNIFIED);\n\tprintf(\"lr: 0x%llX\\n\", (uint64_t)threadState.ts_64.LR_UNIFIED);\n\tprintf(\"cpsr: 0x%X\\n\", threadState.ts_64.__cpsr);\n\t#if __arm64e__\n\tprintf(\"flags: 0x%X\\n\", threadState.ts_64.__opaque_flags);\n\t#endif\n}\n\nvoid printThreadState(thread_act_t thread)\n{\n\tprintf(\"- THREAD STATE -\\n\");\n\n\tmach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;\n\tstruct arm_unified_thread_state threadState;\n\tkern_return_t kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&threadState.ts_64, &thread_state_count);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"<Failed to read thread state>\\n\");\n\t\treturn;\n\t}\n\n\tprintThreadState_state(threadState);\n}\n\nvoid printThreadInfo(thread_act_t thread)\n{\n\tprintf(\"- INFO OF THREAD %d -\\n\", thread);\n\n\tthread_basic_info_data_t basicInfo;\n\tmach_msg_type_number_t biCount = THREAD_BASIC_INFO_COUNT;\n\tkern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basicInfo, &biCount);\n\tif(kr != KERN_SUCCESS)\n\t{\n\t\tprintf(\"<Failed to fetch info>\\n\");\n\t\treturn;\n\t}\n\n\tprintf(\"cpu_usage: %d\\n\", basicInfo.cpu_usage);\n\tprintf(\"flags: %d\\n\", basicInfo.flags);\n\tprintf(\"policy: %d\\n\", basicInfo.policy);\n\tprintf(\"run_state: %d\\n\", basicInfo.run_state);\n\tprintf(\"sleep_time: %d\\n\", basicInfo.sleep_time);\n\tprintf(\"suspend_count: %d\\n\", basicInfo.suspend_count);\n\n\tprintf(\"system_time: %d.%d\\n\", basicInfo.system_time.seconds, basicInfo.system_time.microseconds);\n\tprintf(\"user_time: %d.%d\\n\", basicInfo.user_time.seconds, basicInfo.user_time.microseconds);\n}"
  }
]