├── .gitignore ├── CoreSymbolication.h ├── LICENSE ├── Makefile ├── README.md ├── arm64.h ├── arm64.m ├── control ├── dyld.h ├── dyld.m ├── entitlements.plist ├── main.m ├── pac.h ├── rop_inject.h ├── rop_inject.m ├── sandbox.h ├── shellcode_inject.h ├── shellcode_inject.m ├── task_utils.h ├── task_utils.m ├── thread_utils.h └── thread_utils.m /.gitignore: -------------------------------------------------------------------------------- 1 | .theos 2 | packages 3 | .DS_Store 4 | .scrapped -------------------------------------------------------------------------------- /CoreSymbolication.h: -------------------------------------------------------------------------------- 1 | // 2 | // CoreSymbolication.h 3 | // 4 | // Created by R J Cooper on 05/06/2012. 5 | // This file: Copyright (c) 2012 Mountainstorm 6 | // API: Copyright (c) 2008 Apple Inc. All rights reserved. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | // 28 | // Derived by looking at use within the dtrace source and a little bit of IDA work 29 | // 30 | // See the unit testcases for examples of how to use the API; its a really nice symbol 31 | // api, a real shame Apple dont make it a public framework. 32 | // 33 | // Things you might want to know; 34 | // - a Symbolicator is a top level object representing the kernel/process etc 35 | // - a Symbolicator contains multiple SymbolOwners 36 | // 37 | // - a SymbolOwner represents a blob which owns symbols e.g. executable, library 38 | // - a SymbolOwner contains multiple regions and contains multiple symbols 39 | // 40 | // - a Region represents a continuous block of memory within a symbol owner e.g. the __TEXT __objc_classname section 41 | // - a Region contains multiple symbols ... not it doesn't own them, just contains them 42 | // 43 | // - a Symbol represents a symbol e.g. function, variable 44 | // 45 | 46 | #if !defined(__CORESYMBOLICATION_CORESYMBOLICATION__) 47 | #define __CORESYMBOLICATION_CORESYMBOLICATION__ 1 48 | #define __CORESYMBOLICATION__ 1 49 | 50 | #ifdef __cplusplus 51 | extern "C" { 52 | #endif 53 | 54 | #include 55 | #include 56 | 57 | 58 | /* 59 | * Types 60 | */ 61 | // Under the hood the framework basically just calls through to a set of C++ libraries 62 | struct sCSTypeRef { 63 | void* csCppData; // typically retrieved using CSCppSymbol...::data(csData & 0xFFFFFFF8) 64 | void* csCppObj; // a pointer to the actual CSCppObject 65 | }; 66 | typedef struct sCSTypeRef CSTypeRef; 67 | 68 | 69 | typedef CSTypeRef CSSymbolicatorRef; 70 | typedef CSTypeRef CSSourceInfoRef; 71 | typedef CSTypeRef CSSymbolOwnerRef; 72 | typedef CSTypeRef CSSectionRef; 73 | typedef CSTypeRef CSSegmentRef; 74 | typedef CSTypeRef CSSymbolRef; 75 | typedef CSTypeRef CSRegionRef; 76 | typedef CSTypeRef CSUUIDRef; 77 | 78 | 79 | struct sCSRange { 80 | unsigned long long location; 81 | unsigned long long length; 82 | }; 83 | typedef struct sCSRange CSRange; 84 | 85 | 86 | // Note: this structure may well be wrong 87 | typedef struct sCSNotificationData { 88 | CSSymbolicatorRef symbolicator; 89 | union { 90 | struct { 91 | long value; 92 | } ping; 93 | 94 | struct { 95 | CSSymbolOwnerRef symbolOwner; 96 | } dyldLoad; 97 | } u; 98 | } CSNotificationData; 99 | 100 | 101 | typedef void* CSDictionaryKeyCallBacks; 102 | typedef void* CSDictionaryValueCallBacks; 103 | typedef void* CSSetCallBacks; 104 | 105 | 106 | typedef int (^CSNotification)(uint32_t notification_type, CSNotificationData data); 107 | typedef int (^CSRegionIterator)(CSRegionRef region); 108 | typedef int (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); 109 | typedef int (^CSSectionIterator)(CSSectionRef section); 110 | typedef int (^CSSourceInfoIterator)(CSSourceInfoRef sourceInfo); 111 | typedef int (^CSSymbolIterator)(CSSymbolRef symbol); 112 | typedef int (^CSSegmentIterator)(CSSegmentRef segment); 113 | 114 | 115 | /* 116 | * Defines 117 | */ 118 | #define kCSNull ((CSTypeRef) {NULL, NULL}) 119 | #define kCSNow 0x8000000000000000ull 120 | // we've no idea what value kCSSymbolOwnerDataFoundDsym has; its only use in dtrace has been optimised out 121 | #define kCSSymbolOwnerDataFoundDsym 0 122 | #define kCSSymbolOwnerIsAOut 0 123 | #define kCSSymbolicatorTrackDyldActivity 1 124 | 125 | #define kCSNotificationPing 1 126 | #define kCSNotificationInitialized 0x0010 127 | #define kCSNotificationDyldLoad 0x0100 128 | #define kCSNotificationDyldUnload 0x0101 129 | // kCSNotificationTimeout must be a value greater than 0x1001 130 | #define kCSNotificationTimeout 0x1002 131 | #define kCSNotificationTaskExit 0x1000 132 | #define kCSNotificationFini 0x80000000 133 | 134 | 135 | /* 136 | * External symbols 137 | */ 138 | 139 | extern const char* kCSRegionMachHeaderName; 140 | extern const CSDictionaryKeyCallBacks kCSTypeDictionaryKeyCallBacks; 141 | extern const CSDictionaryValueCallBacks kCSTypeDictionaryValueCallBacks; 142 | extern const CSDictionaryKeyCallBacks kCSTypeDictionaryWeakKeyCallBacks; 143 | extern const CSDictionaryValueCallBacks kCSTypeDictionaryWeakValueCallBacks; 144 | extern const CSSetCallBacks kCSTypeSetCallBacks; 145 | extern const CSSetCallBacks kCSTypeSetWeakCallBacks; 146 | 147 | 148 | /* 149 | * Architecture functions 150 | */ 151 | // Valid names: i386, x86_64, arm, armv4t, armv5tej, armv6, armv7, armv7f, armv7k, ppc, ppc64 152 | cpu_type_t CSArchitectureGetArchitectureForName(const char* arch); 153 | cpu_type_t CSArchitectureGetCurrent(); 154 | cpu_type_t CSArchitectureGetFamily(cpu_type_t type); 155 | const char* CSArchitectureGetFamilyName(cpu_type_t type); 156 | 157 | Boolean CSArchitectureIs32Bit(cpu_type_t type); 158 | Boolean CSArchitectureIs64Bit(cpu_type_t type); 159 | Boolean CSArchitectureIsArm(cpu_type_t type); 160 | Boolean CSArchitectureIsBigEndian(cpu_type_t type); 161 | Boolean CSArchitectureIsI386(cpu_type_t type); 162 | Boolean CSArchitectureIsLittleEndian(cpu_type_t type); 163 | Boolean CSArchitectureIsPPC(cpu_type_t type); 164 | Boolean CSArchitectureIsPPC64(cpu_type_t type); 165 | Boolean CSArchitectureIsX86_64(cpu_type_t type); 166 | 167 | Boolean CSArchitectureMatchesArchitecture(cpu_type_t a, cpu_type_t b); 168 | 169 | 170 | /* 171 | * Description functions 172 | */ 173 | CFStringRef CSCopyDescription(CSTypeRef cs); 174 | CFStringRef CSCopyDescriptionWithIndent(CSTypeRef cs, unsigned int indent); 175 | 176 | 177 | /* 178 | * General utility functions 179 | */ 180 | Boolean CSEqual(CSTypeRef cs1, CSTypeRef cs2); 181 | //XXX: CSExceptionSafeThreadRunBlock 182 | CFIndex CSGetRetainCount(CSTypeRef cs); 183 | Boolean CSIsNull(CSTypeRef cs); 184 | CSTypeRef CSRetain(CSTypeRef cs); 185 | void CSRelease(CSTypeRef cs); 186 | void CSShow(CSTypeRef cs); 187 | 188 | 189 | /* 190 | * Dyld functions 191 | */ 192 | vm_address_t CSGetDyldSharedCacheSlide(mach_port_t port); 193 | CSUUIDRef CSGetDyldSharedCacheUUID(mach_port_t port); 194 | 195 | 196 | /* 197 | * XXX: Map functions 198 | */ 199 | //CSMMapArchiveCacheCopyMMapArchive 200 | //CSMMapArchiveCacheReleaseMMapArchive 201 | //CSMMapArchiveCacheSetShouldStoreToDaemon 202 | 203 | 204 | /* 205 | * Range functions 206 | */ 207 | Boolean CSRangeContainsRange(CSRange r1, CSRange r2); 208 | Boolean CSRangeIntersectsRange(CSRange r1, CSRange r2); 209 | 210 | 211 | /* 212 | * Region functions 213 | */ 214 | CFStringRef CSRegionCopyDescriptionWithIndent(CSRegionRef region, unsigned int indent); 215 | int CSRegionForeachSourceInfo(CSRegionRef region, CSSourceInfoIterator each); 216 | int CSRegionForeachSymbol(CSRegionRef region, CSSymbolIterator each); 217 | const char* CSRegionGetName(CSRegionRef region); 218 | CSRange CSRegionGetRange(CSRegionRef region); 219 | CSSymbolOwnerRef CSRegionGetSymbolOwner(CSRegionRef region); 220 | CSSymbolicatorRef CSRegionGetSymbolicator(CSRegionRef region); 221 | 222 | 223 | /* 224 | * XXX: Section/Segment functions 225 | */ 226 | /* 227 | CSSectionGetSegment 228 | CSSegmentForeachSection 229 | */ 230 | 231 | 232 | /* 233 | * XXX: Signature functions 234 | */ 235 | /* 236 | CSSignatureAddSegment 237 | CSSignatureAllocateSegments 238 | CSSignatureCopy 239 | CSSignatureEncodeSymbolOwner 240 | CSSignatureEncodeSymbolicator 241 | CSSignatureFreeSegments 242 | */ 243 | 244 | 245 | /* 246 | * Source Info functions 247 | */ 248 | CFStringRef CSSourceInfoCopyDescriptionWithIndent(CSSourceInfoRef info, unsigned int indent); 249 | int CSSourceInfoGetColumn(CSSourceInfoRef info); 250 | const char* CSSourceInfoGetFilename(CSSourceInfoRef info); 251 | int CSSourceInfoGetLineNumber(CSSourceInfoRef info); 252 | const char* CSSourceInfoGetPath(CSSourceInfoRef info); 253 | CSRange CSSourceInfoGetRange(CSSourceInfoRef info); 254 | CSRegionRef CSSourceInfoGetRegion(CSSourceInfoRef info); 255 | CSSymbolRef CSSourceInfoGetSymbol(CSSourceInfoRef info); 256 | CSSymbolOwnerRef CSSourceInfoGetSymbolOwner(CSSourceInfoRef info); 257 | CSSymbolicatorRef CSSourceInfoGetSymbolicator(CSSourceInfoRef info); 258 | 259 | 260 | /* 261 | * Symbol functions 262 | */ 263 | 264 | CFStringRef CSSymbolCopyDescriptionWithIndent(CSSymbolRef sym, unsigned int indent); 265 | int CSSymbolForeachSourceInfo(CSSymbolRef sym, CSSourceInfoIterator); 266 | long CSSymbolGetFlags(CSSymbolRef sym); 267 | CSTypeRef CSSymbolGetInstructionData(CSSymbolRef sym); 268 | const char* CSSymbolGetMangledName(CSSymbolRef sym); 269 | const char* CSSymbolGetName(CSSymbolRef sym); 270 | CSRange CSSymbolGetRange(CSSymbolRef sym); 271 | CSRegionRef CSSymbolGetRegion(CSSymbolRef sym); 272 | CSSectionRef CSSymbolGetSection(CSSymbolRef sym); 273 | CSSegmentRef CSSymbolGetSegment(CSSymbolRef sym); 274 | CSSymbolOwnerRef CSSymbolGetSymbolOwner(CSSymbolRef sym); 275 | CSSymbolicatorRef CSSymbolGetSymbolicator(CSSymbolRef sym); 276 | Boolean CSSymbolIsArm(CSSymbolRef sym); 277 | Boolean CSSymbolIsDebugMap(CSSymbolRef sym); 278 | Boolean CSSymbolIsDwarf(CSSymbolRef sym); 279 | Boolean CSSymbolIsDyldStub(CSSymbolRef sym); 280 | Boolean CSSymbolIsExternal(CSSymbolRef sym); 281 | Boolean CSSymbolIsFunction(CSSymbolRef sym); 282 | Boolean CSSymbolIsFunctionStarts(CSSymbolRef sym); 283 | Boolean CSSymbolIsKnownLength(CSSymbolRef sym); 284 | Boolean CSSymbolIsMangledNameSourceDwarf(CSSymbolRef sym); 285 | Boolean CSSymbolIsMangledNameSourceDwarfMIPSLinkage(CSSymbolRef sym); 286 | Boolean CSSymbolIsMangledNameSourceNList(CSSymbolRef sym); 287 | Boolean CSSymbolIsMerged(CSSymbolRef sym); 288 | Boolean CSSymbolIsNList(CSSymbolRef sym); 289 | Boolean CSSymbolIsNameSourceDwarf(CSSymbolRef sym); 290 | Boolean CSSymbolIsNameSourceDwarfMIPSLinkage(CSSymbolRef sym); 291 | Boolean CSSymbolIsNameSourceNList(CSSymbolRef sym); 292 | Boolean CSSymbolIsObjcMethod(CSSymbolRef sym); 293 | Boolean CSSymbolIsOmitFramePointer(CSSymbolRef sym); 294 | Boolean CSSymbolIsPrivateExternal(CSSymbolRef sym); 295 | Boolean CSSymbolIsThumb(CSSymbolRef sym); 296 | Boolean CSSymbolIsUnnamed(CSSymbolRef sym); 297 | 298 | 299 | /* 300 | * XXX: SymbolOwner functions 301 | */ 302 | /* 303 | CSSymbolOwnerAddInContext 304 | CSSymbolOwnerCacheFlush 305 | CSSymbolOwnerCacheGetEntryCount 306 | CSSymbolOwnerCacheGetFlags 307 | CSSymbolOwnerCacheGetMemoryLimit 308 | CSSymbolOwnerCacheGetMemoryUsed 309 | CSSymbolOwnerCachePrintEntries 310 | CSSymbolOwnerCachePrintStats 311 | CSSymbolOwnerCacheResetStats 312 | CSSymbolOwnerCacheSetFlags 313 | CSSymbolOwnerCacheSetMemoryLimit 314 | CSSymbolOwnerCopyDescriptionWithIndent 315 | CSSymbolOwnerCreateSignature 316 | CSSymbolOwnerEditRelocations 317 | CSSymbolOwnerForeachRegion 318 | CSSymbolOwnerForeachRegionWithName 319 | CSSymbolOwnerForeachSection 320 | CSSymbolOwnerForeachSegment 321 | CSSymbolOwnerForeachSourceInfo 322 | CSSymbolOwnerForeachSymbol 323 | CSSymbolOwnerForeachSymbolWithMangledName 324 | CSSymbolOwnerForeachSymbolWithName 325 | CSSymbolOwnerGetArchitecture 326 | CSSymbolOwnerGetBaseAddress 327 | CSSymbolOwnerGetCompatibilityVersion 328 | CSSymbolOwnerGetCurrentVersion 329 | CSSymbolOwnerGetDataFlags 330 | CSSymbolOwnerGetDataTypeID 331 | CSSymbolOwnerGetDsymPath 332 | CSSymbolOwnerGetDsymVersion 333 | CSSymbolOwnerGetFlags 334 | CSSymbolOwnerGetLastModifiedTimestamp 335 | CSSymbolOwnerGetLoadTimestamp 336 | CSSymbolOwnerGetName 337 | CSSymbolOwnerGetPath 338 | CSSymbolOwnerGetRegionCount 339 | CSSymbolOwnerGetRegionWithAddress 340 | CSSymbolOwnerGetRegionWithName 341 | CSSymbolOwnerGetSectionWithAddress 342 | CSSymbolOwnerGetSectionWithName 343 | CSSymbolOwnerGetSegmentWithAddress 344 | CSSymbolOwnerGetSourceInfoCount 345 | CSSymbolOwnerGetSourceInfoWithAddress 346 | CSSymbolOwnerGetSymbolCount 347 | CSSymbolOwnerGetSymbolWithAddress 348 | CSSymbolOwnerGetSymbolWithMangledName 349 | CSSymbolOwnerGetSymbolWithName 350 | CSSymbolOwnerGetSymbolicator 351 | CSSymbolOwnerGetTransientUserData 352 | CSSymbolOwnerGetUUID 353 | CSSymbolOwnerGetUnloadTimestamp 354 | CSSymbolOwnerGetVersion 355 | CSSymbolOwnerIsAOut 356 | CSSymbolOwnerIsBundle 357 | CSSymbolOwnerIsCommpage 358 | CSSymbolOwnerIsDsym 359 | CSSymbolOwnerIsDyld 360 | CSSymbolOwnerIsDyldSharedCache 361 | CSSymbolOwnerIsDylib 362 | CSSymbolOwnerIsDylibStub 363 | CSSymbolOwnerIsKextBundle 364 | CSSymbolOwnerIsMachO 365 | CSSymbolOwnerIsMutable 366 | CSSymbolOwnerIsObjCGCSupported 367 | CSSymbolOwnerIsObjCRetainReleaseSupported 368 | CSSymbolOwnerIsObject 369 | CSSymbolOwnerIsObsolete 370 | CSSymbolOwnerIsPIE 371 | CSSymbolOwnerIsProtected 372 | CSSymbolOwnerIsRestricted 373 | CSSymbolOwnerIsSlid 374 | CSSymbolOwnerIsStaticLibraryArchiveEntry 375 | CSSymbolOwnerMakeMutableInContext 376 | CSSymbolOwnerRemoveInContext 377 | CSSymbolOwnerSetLoadTimestamp 378 | CSSymbolOwnerSetPath 379 | CSSymbolOwnerSetRelocationCount 380 | CSSymbolOwnerSetTransientUserData 381 | CSSymbolOwnerSetUnloadTimestamp 382 | */ 383 | 384 | const char *CSSymbolOwnerGetPath(CSSymbolOwnerRef owner); 385 | 386 | /* 387 | * XXX: Symbolicator functions 388 | */ 389 | // XXX: CSSymbolicatorAddSymbolOwner 390 | // XXX: CSSymbolicatorApplyMutableContextBlock 391 | CFStringRef CSSymbolicatorCopyDescriptionWithIndent(CSSymbolicatorRef cs, unsigned int indent); 392 | CFDataRef CSSymbolicatorCreateSignature(CSSymbolicatorRef cs); 393 | 394 | CSSymbolicatorRef CSSymbolicatorCreateWithMachKernel(void); 395 | CSSymbolicatorRef CSSymbolicatorCreateWithMachKernelFlagsAndNotification(long flags, CSNotification notification); 396 | CSSymbolicatorRef CSSymbolicatorCreateWithPathAndArchitecture(const char* path, cpu_type_t type); 397 | CSSymbolicatorRef CSSymbolicatorCreateWithPathArchitectureFlagsAndNotification(const char* path, cpu_type_t type, long flags, CSNotification notification); 398 | CSSymbolicatorRef CSSymbolicatorCreateWithPid(pid_t pid); 399 | CSSymbolicatorRef CSSymbolicatorCreateWithPidFlagsAndNotification(pid_t pid, long flags, CSNotification notification); 400 | CSSymbolicatorRef CSSymbolicatorCreateWithSignature(CFDataRef sig); 401 | CSSymbolicatorRef CSSymbolicatorCreateWithSignatureAndNotification(CFDataRef sig, CSNotification notification); 402 | CSSymbolicatorRef CSSymbolicatorCreateWithTask(task_t task); 403 | CSSymbolicatorRef CSSymbolicatorCreateWithTaskFlagsAndNotification(task_t task, long flags, CSNotification notification); 404 | CSSymbolicatorRef CSSymbolicatorCreateWithURLAndArchitecture(CFURLRef url, cpu_type_t type); 405 | CSSymbolicatorRef CSSymbolicatorCreateWithURLArchitectureFlagsAndNotification(CFURLRef url, cpu_type_t type, long flags, CSNotification notification); 406 | 407 | int CSSymbolicatorForceFullSymbolExtraction(CSSymbolicatorRef cs); 408 | int CSSymbolicatorForeachRegionAtTime(CSSymbolicatorRef cs, uint64_t time, CSRegionIterator it); 409 | int CSSymbolicatorForeachRegionWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSRegionIterator it); 410 | int CSSymbolicatorForeachSectionAtTime(CSSymbolicatorRef cs, uint64_t time, CSSectionIterator it); 411 | int CSSymbolicatorForeachSegmentAtTime(CSSymbolicatorRef cs, uint64_t time, CSSegmentIterator it); 412 | // XXX: CSSymbolicatorForeachSharedCache 413 | // XXX: CSSymbolicatorForeachSharedCacheSymbolicatorWithFlagsAndNotification 414 | int CSSymbolicatorForeachSourceInfoAtTime(CSSymbolicatorRef cs, uint64_t time, CSSourceInfoIterator it); 415 | int CSSymbolicatorForeachSymbolAtTime(CSSymbolicatorRef cs, uint64_t time, CSSymbolIterator it); 416 | int CSSymbolicatorForeachSymbolOwnerAtTime(CSSymbolicatorRef cs, uint64_t time, CSSymbolOwnerIterator it); 417 | 418 | // XXX: CSSymbolicatorForeachSymbolOwnerWithCFUUIDBytesAtTime 419 | int CSSymbolicatorForeachSymbolOwnerWithFlagsAtTime(CSSymbolicatorRef symbolicator, long flags, uint64_t time, CSSymbolOwnerIterator it); 420 | int CSSymbolicatorForeachSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolOwnerIterator it); 421 | int CSSymbolicatorForeachSymbolOwnerWithPathAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolOwnerIterator it); 422 | // XXX: CSSymbolicatorForeachSymbolOwnerWithUUIDAtTime 423 | int CSSymbolicatorForeachSymbolWithMangledNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolIterator it); 424 | int CSSymbolicatorForeachSymbolWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolIterator it); 425 | // XXX: CSSymbolicatorForeachSymbolicatorWithPath 426 | // XXX: CSSymbolicatorForeachSymbolicatorWithPathFlagsAndNotification 427 | // XXX: CSSymbolicatorForeachSymbolicatorWithURL 428 | // XXX: CSSymbolicatorForeachSymbolicatorWithURLFlagsAndNotification 429 | 430 | CSSymbolOwnerRef CSSymbolicatorGetAOutSymbolOwner(CSSymbolicatorRef cs); 431 | cpu_type_t CSSymbolicatorGetArchitecture(CSSymbolicatorRef cs); 432 | vm_address_t CSSymbolicatorGetDyldAllImageInfosAddress(CSSymbolicatorRef cs); 433 | 434 | long CSSymbolicatorGetFlagsForDebugMapOnlyData(void); 435 | long CSSymbolicatorGetFlagsForDsymOnlyData(void); 436 | long CSSymbolicatorGetFlagsForDwarfOnlyData(void); 437 | long CSSymbolicatorGetFlagsForFunctionStartsOnlyData(void); 438 | long CSSymbolicatorGetFlagsForNListOnlyData(void); 439 | long CSSymbolicatorGetFlagsForNoSymbolOrSourceInfoData(void); 440 | 441 | pid_t CSSymbolicatorGetPid(CSSymbolicatorRef cs); 442 | int CSSymbolicatorGetRegionCountAtTime(CSSymbolicatorRef cs, uint64_t time); 443 | CSRegionRef CSSymbolicatorGetRegionWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time); 444 | CSRegionRef CSSymbolicatorGetRegionWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time); 445 | CSSectionRef CSSymbolicatorGetSectionWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time); 446 | CSSegmentRef CSSymbolicatorGetSegmentWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time); 447 | vm_address_t CSSymbolicatorGetSharedCacheSlide(CSSymbolicatorRef cs); 448 | CSUUIDRef CSSymbolicatorGetSharedCacheUUID(CSSymbolicatorRef cs); 449 | int CSSymbolicatorGetSourceInfoCountAtTime(CSSymbolicatorRef cs, uint64_t time); 450 | CSSourceInfoRef CSSymbolicatorGetSourceInfoWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time); 451 | int CSSymbolicatorGetSymbolCountAtTime(CSSymbolicatorRef cs, uint64_t time); 452 | CSSymbolOwnerRef CSSymbolicatorGetSymbolOwner(CSSymbolicatorRef cs); 453 | int CSSymbolicatorGetSymbolOwnerCountAtTime(CSSymbolicatorRef cs, uint64_t time); 454 | CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time); 455 | // XXX: CSSymbolicatorGetSymbolOwnerWithCFUUIDBytesAtTime 456 | CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time); 457 | CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithUUIDAtTime(CSSymbolicatorRef symbolicator, CFUUIDRef uuid, uint64_t time); 458 | CSSymbolRef CSSymbolicatorGetSymbolWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time); 459 | CSSymbolRef CSSymbolicatorGetSymbolWithMangledNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time); 460 | CSSymbolRef CSSymbolicatorGetSymbolWithMangledNameFromSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, CSSymbolOwnerRef owner, const char* name, uint64_t time); 461 | CSSymbolRef CSSymbolicatorGetSymbolWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time); 462 | CSSymbolRef CSSymbolicatorGetSymbolWithNameFromSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, CSSymbolOwnerRef owner, const char* name, uint64_t time); 463 | mach_port_t CSSymbolicatorGetTask(CSSymbolicatorRef cs); 464 | Boolean CSSymbolicatorIsKernelSymbolicator(CSSymbolicatorRef cs); 465 | Boolean CSSymbolicatorIsTaskTranslated(CSSymbolicatorRef cs); 466 | Boolean CSSymbolicatorIsTaskValid(CSSymbolicatorRef cs); 467 | void CSSymbolicatorResymbolicate(CSSymbolicatorRef cs); 468 | void CSSymbolicatorResymbolicateFail(CSSymbolicatorRef cs); 469 | int CSSymbolicatorSetForceGlobalSafeMachVMReads(CSSymbolicatorRef cs); 470 | 471 | 472 | /* 473 | * XXX: CSUUID 474 | */ 475 | /* 476 | CSUUIDCFUUIDBytesToPath 477 | CSUUIDCFUUIDBytesToString 478 | CSUUIDStringToCFUUIDBytes 479 | */ 480 | 481 | 482 | 483 | 484 | /* 485 | * SymbolOwner functions 486 | */ 487 | const char* CSSymbolOwnerGetPath(CSSymbolOwnerRef symbol); 488 | const char* CSSymbolOwnerGetName(CSSymbolOwnerRef symbol); 489 | vm_address_t CSSymbolOwnerGetBaseAddress(CSSymbolOwnerRef owner); 490 | cpu_type_t CSSymbolOwnerGetArchitecture(CSSymbolOwnerRef owner); 491 | Boolean CSSymbolOwnerIsObject(CSSymbolOwnerRef owner); 492 | long CSSymbolOwnerGetDataFlags(CSSymbolOwnerRef owner); 493 | CSRegionRef CSSymbolOwnerGetRegionWithName(CSSymbolOwnerRef owner, const char* name); 494 | CSSymbolRef CSSymbolOwnerGetSymbolWithName(CSSymbolOwnerRef owner, const char* name); 495 | CSSymbolRef CSSymbolOwnerGetSymbolWithAddress(CSSymbolOwnerRef owner, mach_vm_address_t addr); 496 | 497 | long CSSymbolOwnerForeachSymbol(CSSymbolOwnerRef owner, CSSymbolIterator each); 498 | 499 | CFUUIDBytes *CSSymbolOwnerGetCFUUIDBytes(CSSymbolOwnerRef owner); 500 | 501 | /* Other exports 502 | 503 | __crashreporter_info__ 504 | clear_mapped_memory 505 | create_mapped_memory_cache_for_task 506 | create_sampling_context_for_task 507 | demangle 508 | destroy_mapped_memory_cache 509 | destroy_sampling_context 510 | dispatch_queue_name_for_serial_number 511 | find_node 512 | fixup_frames 513 | get_remote_thread_dispatch_queue 514 | 515 | map_new_node 516 | mapped_memory_read 517 | mapped_memory_read_pointer 518 | next_node 519 | sample_remote_thread 520 | sample_remote_thread_with_dispatch_queue 521 | sampling_context_clear_cache 522 | task_is_64bit 523 | thread_name_for_thread_port 524 | */ 525 | 526 | #ifdef __cplusplus 527 | } // extern "C" 528 | #endif 529 | 530 | #endif /* ! __CORESYMBOLICATION_CORESYMBOLICATION__ */ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lars Fröder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:16.5:11.0 2 | ARCHS = arm64 arm64e 3 | 4 | include $(THEOS)/makefiles/common.mk 5 | 6 | TOOL_NAME = opainject 7 | 8 | opainject_FILES = main.m dyld.m shellcode_inject.m rop_inject.m thread_utils.m task_utils.m arm64.m 9 | opainject_CFLAGS = -fobjc-arc -DTHEOS_LEAN_AND_MEAN 10 | opainject_CODESIGN_FLAGS = -Sentitlements.plist 11 | opainject_INSTALL_PATH = /usr/local/bin 12 | opainject_PRIVATE_FRAMEWORKS = CoreSymbolication 13 | 14 | include $(THEOS_MAKE_PATH)/tool.mk 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opainject 2 | 3 | iOS 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). 4 | 5 | Tested 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. -------------------------------------------------------------------------------- /arm64.h: -------------------------------------------------------------------------------- 1 | #ifndef arm64_h 2 | #define arm64_h 3 | 4 | #include 5 | #include 6 | 7 | uint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl); 8 | uint32_t generate_br(uint8_t x); 9 | bool decode_adrp(uint32_t inst, uint8_t *rd_out, int32_t *imm_out); 10 | bool decode_ldr_imm(uint32_t inst, uint16_t *imm_out, uint8_t *rn_out, uint8_t *rt_out); 11 | bool decode_adrp_ldr(uint32_t adrp_inst, uint32_t ldr_inst, uint64_t pc, uint64_t *dst_out); 12 | 13 | #endif /* arm64_h */ 14 | -------------------------------------------------------------------------------- /arm64.m: -------------------------------------------------------------------------------- 1 | #include "arm64.h" 2 | #include 3 | 4 | uint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl) 5 | { 6 | uint32_t base = 0b11110010100000000000000000000000; 7 | 8 | uint32_t hw = 0; 9 | if (lsl == 16) { 10 | hw = 0b01 << 21; 11 | } 12 | else if (lsl == 32) { 13 | hw = 0b10 << 21; 14 | } 15 | else if (lsl == 48) { 16 | hw = 0b11 << 21; 17 | } 18 | 19 | uint32_t imm16 = (uint32_t)val << 5; 20 | uint32_t rd = x & 0x1F; 21 | 22 | return base | hw | imm16 | rd; 23 | } 24 | 25 | uint32_t generate_br(uint8_t x) 26 | { 27 | uint32_t base = 0b11010110000111110000000000000000; 28 | uint32_t rn = ((uint32_t)x & 0x1F) << 5; 29 | return base | rn; 30 | } 31 | 32 | bool decode_adrp(uint32_t inst, uint8_t *rd_out, int32_t *imm_out) 33 | { 34 | if ((inst & 0x9F000000) != 0x90000000) return false; 35 | 36 | uint32_t mask_immlo = 0b01100000000000000000000000000000; 37 | uint32_t mask_immhi = 0b00000000111111111111111111100000; 38 | uint32_t mask_rd = 0b00000000000000000000000000011111; 39 | 40 | int32_t imm = (((inst & mask_immlo) >> 29) | ((inst & mask_immhi) >> 3)) << 12; 41 | uint8_t rd = inst & mask_rd; 42 | 43 | if (rd_out) *rd_out = rd; 44 | if (imm_out) *imm_out = imm; 45 | 46 | return true; 47 | } 48 | 49 | bool decode_ldr_imm(uint32_t inst, uint16_t *imm_out, uint8_t *rn_out, uint8_t *rt_out) 50 | { 51 | if ((inst & 0xBFC00000) != 0xB9400000) return false; 52 | // TODO: Support non unsigned instructions 53 | 54 | uint32_t mask_imm12 = 0b00000000001111111111110000000000; 55 | uint32_t mask_rn = 0b00000000000000000000001111100000; 56 | uint32_t mask_rt = 0b00000000000000000000000000011111; 57 | 58 | uint8_t rt = (inst & mask_rt); 59 | uint8_t rn = (inst & mask_rn) >> 5; 60 | uint16_t imm12 = (inst & mask_imm12) >> 10; 61 | 62 | uint32_t bit_is_64_bit = 0b01000000000000000000000000000000; 63 | if (inst & bit_is_64_bit) { 64 | imm12 *= 8; 65 | } 66 | else { 67 | imm12 *= 4; 68 | } 69 | 70 | if (imm_out) *imm_out = imm12; 71 | if (rn_out) *rn_out = rn; 72 | if (rt_out) *rt_out = rt; 73 | 74 | return true; 75 | } 76 | 77 | bool decode_adrp_ldr(uint32_t adrp_inst, uint32_t ldr_inst, uint64_t pc, uint64_t *dst_out) 78 | { 79 | int32_t adrp_imm = 0; 80 | if (!decode_adrp(adrp_inst, NULL, &adrp_imm)) return false; 81 | 82 | uint16_t ldr_imm = 0; 83 | if (!decode_ldr_imm(ldr_inst, &ldr_imm, NULL, NULL)) return false; 84 | 85 | uint64_t pc_page = pc - (pc % 0x1000); 86 | uint64_t dst = (pc_page + adrp_imm) + ldr_imm; 87 | if (dst_out) *dst_out = dst; 88 | return true; 89 | } 90 | 91 | /*bool decode_ldr(uint32_t inst, uint8_t *rt_out, uint8_t *rn_out, uint8_t *rm_out) 92 | { 93 | if ((inst & 0xFFE00C00) != 0xF8600800) return false; 94 | 95 | uint32_t mask_rm = 0b00000000000111110000000000000000; 96 | uint32_t mask_option = 0b00000000000000001110000000000000; 97 | uint32_t mask_s = 0b00000000000000000001000000000000; 98 | uint32_t mask_rn = 0b00000000000000000000001111100000; 99 | uint32_t mask_rt = 0b00000000000000000000000000011111; 100 | 101 | uint8_t rt = inst & mask_rt; 102 | uint8_t rn = inst & mask_rn >> 5; 103 | uint8_t rm = inst & mask_rm >> 16; 104 | bool s = inst & mask_s >> 12; 105 | uint8_t option = inst & mask_option >> 13; 106 | 107 | printf("rt=%d, rn=%d, rm=%d, s=%d, option=%X\n", rt, rn, rm, s, option); 108 | 109 | return true; 110 | }*/ 111 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.opa334.opainject 2 | Name: opainject 3 | Version: 1.0.6 4 | Architecture: iphoneos-arm 5 | Description: Injection tool! 6 | Maintainer: opa334 7 | Author: opa334 8 | Section: System 9 | Tag: role::hacker 10 | -------------------------------------------------------------------------------- /dyld.h: -------------------------------------------------------------------------------- 1 | extern vm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imageName); 2 | extern vm_address_t remoteDlSym(task_t task, vm_address_t imageStartPtr, const char* symbolName); 3 | extern vm_address_t remoteDlSymFindImage(task_t task, vm_address_t allImageInfoAddr, const char* symbolName, char** imageOut); 4 | 5 | extern void printImages(task_t task, vm_address_t imageStartPtr); 6 | extern void printSymbols(task_t task, vm_address_t imageAddress); 7 | extern vm_address_t scanLibrariesForMemory(task_t task, vm_address_t imageStartPtr, char* memory, size_t memorySize, int alignment); -------------------------------------------------------------------------------- /dyld.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import "task_utils.h" 16 | 17 | #import 18 | 19 | void iterateImages(task_t task, vm_address_t imageStartPtr, void (^iterateBlock)(char*, struct dyld_image_info*, BOOL*)) 20 | { 21 | struct dyld_all_image_infos imageInfos; 22 | task_read(task, imageStartPtr, &imageInfos, sizeof(imageInfos)); 23 | 24 | size_t infoArraySize = sizeof(struct dyld_image_info) * imageInfos.infoArrayCount; 25 | struct dyld_image_info *infoArray = malloc(infoArraySize); 26 | task_read(task, (vm_address_t)imageInfos.infoArray, &infoArray[0], infoArraySize); 27 | 28 | for(int i = 0; i < imageInfos.infoArrayCount; i++) 29 | { 30 | @autoreleasepool 31 | { 32 | char currentImagePath[PATH_MAX]; 33 | if(task_read(task, (vm_address_t)infoArray[i].imageFilePath, ¤tImagePath[0], sizeof(currentImagePath)) == KERN_SUCCESS) 34 | { 35 | BOOL stop = NO; 36 | iterateBlock(currentImagePath, &infoArray[i], &stop); 37 | if(stop) break; 38 | } 39 | } 40 | } 41 | } 42 | 43 | vm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imagePath) 44 | { 45 | __block vm_address_t outAddr = 0; 46 | 47 | iterateImages(task, imageStartPtr, ^(char* iterImagePath, struct dyld_image_info* imageInfo, BOOL* stop) 48 | { 49 | if(strcmp(iterImagePath, imagePath) == 0) 50 | { 51 | outAddr = (vm_address_t)imageInfo->imageLoadAddress; 52 | } 53 | }); 54 | 55 | return outAddr; 56 | } 57 | 58 | void iterateLoadCommands(task_t task, vm_address_t imageAddress, void (^iterateBlock)(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stop)) 59 | { 60 | struct mach_header_64 mh; 61 | task_read(task, imageAddress, &mh, sizeof(mh)); 62 | 63 | vm_address_t lcStart = (imageAddress + sizeof(struct mach_header_64)); 64 | vm_address_t lcEnd = lcStart + mh.sizeofcmds; 65 | 66 | vm_address_t lcCur = lcStart; 67 | for(int ci = 0; ci < mh.ncmds && lcCur <= lcEnd; ci++) 68 | { 69 | struct load_command loadCommand; 70 | task_read(task, lcCur, &loadCommand, sizeof(loadCommand)); 71 | BOOL stop = NO; 72 | iterateBlock(&loadCommand, lcCur, &stop); 73 | lcCur = lcCur + loadCommand.cmdsize; 74 | if(stop) break; 75 | } 76 | } 77 | 78 | void iterateSections(task_t task, vm_address_t commandAddress, const struct segment_command_64* segmentCommand, void (^iterateBlock)(const struct section_64*, BOOL*)) 79 | { 80 | if(segmentCommand->nsects == 0) return; 81 | 82 | vm_address_t sectionStart = commandAddress + sizeof(struct segment_command_64); 83 | for(int i = 0; i < segmentCommand->nsects; i++) 84 | { 85 | struct section_64 section = { 0 }; 86 | if (task_read(task, sectionStart + i * sizeof(section), §ion, sizeof(section)) == KERN_SUCCESS) { 87 | BOOL stop; 88 | iterateBlock(§ion, &stop); 89 | if(stop) break; 90 | } 91 | } 92 | } 93 | 94 | void iterateSymbols(task_t task, vm_address_t imageAddress, void (^iterateBlock)(const char*, const char*, vm_address_t, BOOL*)) 95 | { 96 | __block struct segment_command_64 __linkedit = { 0 }; 97 | __block struct symtab_command symtabCommand = { 0 }; 98 | 99 | __block vm_address_t slide; 100 | __block BOOL firstSegmentCommand = YES; 101 | 102 | iterateLoadCommands(task, imageAddress, ^(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stop) 103 | { 104 | switch(cmd->cmd) 105 | { 106 | case LC_SYMTAB: 107 | { 108 | task_read(task, cmdAddress, &symtabCommand, sizeof(symtabCommand)); 109 | break; 110 | } 111 | 112 | case LC_SEGMENT_64: 113 | { 114 | struct segment_command_64 segmentCommand; 115 | task_read(task, cmdAddress, &segmentCommand, sizeof(segmentCommand)); 116 | if(firstSegmentCommand) { 117 | slide = imageAddress - segmentCommand.vmaddr; 118 | firstSegmentCommand = NO; 119 | } 120 | if (strncmp("__LINKEDIT", segmentCommand.segname, 16) == 0) { 121 | __linkedit = segmentCommand; 122 | } 123 | break; 124 | } 125 | } 126 | }); 127 | 128 | if(__linkedit.cmd != LC_SEGMENT_64) 129 | { 130 | printf("ERROR: __LINKEDIT not found\n"); 131 | return; 132 | } 133 | if(symtabCommand.cmd != LC_SYMTAB) 134 | { 135 | printf("ERROR: symtab command not found\n"); 136 | return; 137 | } 138 | 139 | uint64_t fileoff = __linkedit.fileoff; 140 | uint64_t vmaddr = __linkedit.vmaddr; 141 | 142 | vm_address_t baseAddr = vmaddr + slide - fileoff; 143 | 144 | vm_address_t strtblAddr = baseAddr + symtabCommand.stroff; 145 | size_t strtblSize = symtabCommand.strsize; 146 | char *strtbl = malloc(strtblSize); 147 | task_read(task, strtblAddr, &strtbl[0], strtblSize); 148 | vm_address_t lAddr = baseAddr + symtabCommand.symoff; 149 | for (uint32_t s = 0; s < symtabCommand.nsyms; s++) 150 | { 151 | vm_address_t entryAddr = lAddr + sizeof(struct nlist_64) * s; 152 | 153 | struct nlist_64 entry = { 0 }; 154 | task_read(task, entryAddr, &entry, sizeof(entry)); 155 | 156 | uint32_t off = entry.n_un.n_strx; 157 | if (off >= strtblSize || off == 0) { 158 | continue; 159 | } 160 | 161 | const char* sym = &strtbl[off]; 162 | if (sym[0] == '\x00') 163 | { 164 | continue; 165 | } 166 | 167 | const char* type = NULL; 168 | switch(entry.n_type & N_TYPE) { 169 | case N_UNDF: type = "N_UNDF"; break; 170 | case N_ABS: type = "N_ABS"; break; 171 | case N_SECT: type = "N_SECT"; break; 172 | case N_PBUD: type = "N_PBUD"; break; 173 | case N_INDR: type = "N_INDR"; break; 174 | } 175 | 176 | BOOL stop = NO; 177 | iterateBlock(sym, type, entry.n_value + slide, &stop); 178 | if(stop) 179 | { 180 | break; 181 | } 182 | } 183 | 184 | free(strtbl); 185 | } 186 | 187 | vm_address_t remoteDlSym(task_t task, vm_address_t imageAddress, const char* symbolName) 188 | { 189 | __block vm_address_t outAddr = 0; 190 | 191 | iterateSymbols(task, imageAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop) 192 | { 193 | if(strcmp(type, "N_SECT") == 0) 194 | { 195 | if(strcmp(iterSymbolName, symbolName) == 0) 196 | { 197 | outAddr = value; 198 | } 199 | } 200 | }); 201 | 202 | return outAddr; 203 | } 204 | 205 | vm_address_t remoteDlSymFindImage(task_t task, vm_address_t allImageInfoAddr, const char* symbolName, char** imageOut) 206 | { 207 | __block vm_address_t outAddr = 0; 208 | __block BOOL* stop1_b; 209 | 210 | iterateImages(task, allImageInfoAddr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stop1) 211 | { 212 | stop1_b = stop1; 213 | iterateSymbols(task, (vm_address_t)imageInfo->imageLoadAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop2) 214 | { 215 | if(strcmp(type, "N_SECT") == 0) 216 | { 217 | if(strcmp(iterSymbolName, symbolName) == 0) 218 | { 219 | outAddr = value; 220 | if(imageOut) 221 | { 222 | *imageOut = strdup(imageFilePath); 223 | } 224 | *stop2 = YES; 225 | *stop1_b = YES; 226 | } 227 | } 228 | }); 229 | }); 230 | return outAddr; 231 | } 232 | 233 | void printImages(task_t task, vm_address_t imageStartPtr) 234 | { 235 | iterateImages(task, imageStartPtr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stop) 236 | { 237 | printf("Image %s - %llX\n", imageFilePath, (uint64_t)imageInfo->imageLoadAddress); 238 | }); 239 | } 240 | 241 | void printSymbols(task_t task, vm_address_t imageAddress) 242 | { 243 | iterateSymbols(task, imageAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop) 244 | { 245 | if(strcmp(type, "N_SECT") == 0) 246 | { 247 | printf("Symbol %s - %llX\n", iterSymbolName, (uint64_t)value); 248 | } 249 | }); 250 | } 251 | 252 | vm_address_t scanMemory(task_t task, vm_address_t begin, size_t size, char* memory, size_t memorySize, int alignment) 253 | { 254 | //printf("scanMemory(%llX, %ld)\n", (uint64_t)begin, size); 255 | 256 | if(alignment == 0) alignment = 1; 257 | 258 | unsigned char *buf = malloc(size); 259 | if(task_read(task, begin, &buf[0], size) != KERN_SUCCESS) 260 | { 261 | printf("[scanMemory] WARNING: Failed to read process memory (%llX, size:%llX)\n", (uint64_t)begin, (uint64_t)size); 262 | if(buf) free(buf); 263 | return 0; 264 | } 265 | 266 | vm_address_t foundMemoryAbsoluteFinal = 0; 267 | vm_address_t lastFoundMemory = 0; 268 | while(1) 269 | { 270 | unsigned char* foundMemory = memmem(buf + lastFoundMemory, size - lastFoundMemory, memory, memorySize); 271 | if(foundMemory != NULL) 272 | { 273 | lastFoundMemory = foundMemory - buf + memorySize; 274 | 275 | vm_address_t foundMemoryAbsolute = (vm_address_t)(begin + (vm_address_t)(foundMemory - buf)); 276 | //printf("foundMemory absolute: %llX\n", (uint64_t)foundMemoryAbsolute); 277 | 278 | int rest = foundMemoryAbsolute % alignment; 279 | //printf("rest: %d\n", rest); 280 | if(rest == 0) 281 | { 282 | foundMemoryAbsoluteFinal = foundMemoryAbsolute; 283 | break; 284 | } 285 | continue; 286 | } 287 | break; 288 | } 289 | 290 | free(buf); 291 | return foundMemoryAbsoluteFinal; 292 | } 293 | 294 | vm_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) 295 | { 296 | uint64_t begin = textCmd->vmaddr + slide; 297 | //printf("- TEXT: %llX -> %llX, %d -\n", begin, begin + textCmd->vmsize, textCmd->nsects); 298 | vm_address_t mainTextScan = scanMemory(task, begin, textCmd->vmsize, memory, memorySize, alignment); 299 | if(mainTextScan != 0) return mainTextScan; 300 | 301 | __block vm_address_t sectFound = 0; 302 | iterateSections(task, commandAddress, textCmd, ^(const struct section_64* sect, BOOL* stop) 303 | { 304 | uint64_t sectBegin = sect->addr + slide; 305 | //printf("-- %s %llX -> %llX --\n", sect->sectname, sectBegin, sectBegin + sect->size); 306 | if(strcmp(sect->sectname, "__text") == 0) 307 | { 308 | vm_address_t subSectScan = scanMemory(task, sectBegin, sect->size, memory, memorySize, alignment); 309 | if(subSectScan != 0) 310 | { 311 | sectFound = subSectScan; 312 | *stop = YES; 313 | } 314 | } 315 | }); 316 | return sectFound; 317 | } 318 | 319 | vm_address_t scanLibrariesForMemory(task_t task, vm_address_t imageStartPtr, char* memory, size_t memorySize, int alignment) 320 | { 321 | __block vm_address_t foundAddr = 0; 322 | 323 | iterateImages(task, imageStartPtr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stopImages) 324 | { 325 | __block vm_address_t slide; 326 | __block BOOL firstSegmentCommand = YES; 327 | //printf("- iterating %s -\n", imageFilePath); 328 | iterateLoadCommands(task, (vm_address_t)imageInfo->imageLoadAddress, ^(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stopCommands) 329 | { 330 | if(cmd->cmd == LC_SEGMENT_64) 331 | { 332 | struct segment_command_64 segmentCommand = { 0 }; 333 | task_read(task, cmdAddress, &segmentCommand, sizeof(segmentCommand)); 334 | if(firstSegmentCommand) 335 | { 336 | slide = (vm_address_t)imageInfo->imageLoadAddress - segmentCommand.vmaddr; 337 | firstSegmentCommand = NO; 338 | } 339 | if (strncmp("__TEXT", segmentCommand.segname, 16) == 0) 340 | { 341 | vm_address_t addrIfFound = scanTextSegmentForMemory(task, cmdAddress, &segmentCommand, slide, memory, memorySize, alignment); 342 | if(addrIfFound != 0) 343 | { 344 | foundAddr = addrIfFound; 345 | *stopCommands = YES; 346 | *stopImages = YES; 347 | } 348 | } 349 | } 350 | }); 351 | }); 352 | 353 | return foundAddr; 354 | } 355 | -------------------------------------------------------------------------------- /entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | get-task-allow 5 | 6 | platform-application 7 | 8 | task_for_pid-allow 9 | 10 | com.apple.private.security.container-required 11 | 12 | com.apple.system-task-ports 13 | 14 | com.apple.system-task-ports.control 15 | 16 | com.apple.private.thread-set-state 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import "dyld.h" 16 | #import "sandbox.h" 17 | #import 18 | #import "shellcode_inject.h" 19 | #import "rop_inject.h" 20 | 21 | 22 | char* resolvePath(char* pathToResolve) 23 | { 24 | if(strlen(pathToResolve) == 0) return NULL; 25 | if(pathToResolve[0] == '/') 26 | { 27 | return strdup(pathToResolve); 28 | } 29 | else 30 | { 31 | char absolutePath[PATH_MAX]; 32 | if (realpath(pathToResolve, absolutePath) == NULL) { 33 | perror("[resolvePath] realpath"); 34 | return NULL; 35 | } 36 | return strdup(absolutePath); 37 | } 38 | } 39 | 40 | extern int posix_spawnattr_set_ptrauth_task_port_np(posix_spawnattr_t * __restrict attr, mach_port_t port); 41 | void spawnPacChild(int argc, char *argv[]) 42 | { 43 | char** argsToPass = malloc(sizeof(char*) * (argc + 2)); 44 | for(int i = 0; i < argc; i++) 45 | { 46 | argsToPass[i] = argv[i]; 47 | } 48 | argsToPass[argc] = "pac"; 49 | argsToPass[argc+1] = NULL; 50 | 51 | pid_t targetPid = atoi(argv[1]); 52 | mach_port_t task; 53 | kern_return_t kr = KERN_SUCCESS; 54 | kr = task_for_pid(mach_task_self(), targetPid, &task); 55 | if(kr != KERN_SUCCESS) { 56 | printf("[spawnPacChild] Failed to obtain task port.\n"); 57 | return; 58 | } 59 | printf("[spawnPacChild] Got task port %d for pid %d\n", task, targetPid); 60 | 61 | posix_spawnattr_t attr; 62 | posix_spawnattr_init(&attr); 63 | posix_spawnattr_set_ptrauth_task_port_np(&attr, task); 64 | 65 | uint32_t executablePathSize = 0; 66 | _NSGetExecutablePath(NULL, &executablePathSize); 67 | char *executablePath = malloc(executablePathSize); 68 | _NSGetExecutablePath(executablePath, &executablePathSize); 69 | 70 | int status = -200; 71 | pid_t pid; 72 | int rc = posix_spawn(&pid, executablePath, NULL, &attr, argsToPass, NULL); 73 | 74 | posix_spawnattr_destroy(&attr); 75 | free(argsToPass); 76 | free(executablePath); 77 | 78 | if(rc != KERN_SUCCESS) 79 | { 80 | printf("[spawnPacChild] posix_spawn failed: %d (%s)\n", rc, mach_error_string(rc)); 81 | return; 82 | } 83 | 84 | do 85 | { 86 | if (waitpid(pid, &status, 0) != -1) { 87 | printf("[spawnPacChild] Child returned %d\n", WEXITSTATUS(status)); 88 | } 89 | } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 90 | 91 | return; 92 | } 93 | 94 | int main(int argc, char *argv[], char *envp[]) { 95 | @autoreleasepool 96 | { 97 | setlinebuf(stdout); 98 | setlinebuf(stderr); 99 | if (argc < 3 || argc > 4) 100 | { 101 | printf("Usage: opainject \n"); 102 | return -1; 103 | } 104 | 105 | #ifdef __arm64e__ 106 | char* pacArg = NULL; 107 | if(argc >= 4) 108 | { 109 | pacArg = argv[3]; 110 | } 111 | if (!pacArg || (strcmp("pac", pacArg) != 0)) 112 | { 113 | spawnPacChild(argc, argv); 114 | return 0; 115 | } 116 | #endif 117 | 118 | printf("OPAINJECT HERE WE ARE\n"); 119 | printf("RUNNING AS %d\n", getuid()); 120 | 121 | pid_t targetPid = atoi(argv[1]); 122 | kern_return_t kret = 0; 123 | task_t procTask = MACH_PORT_NULL; 124 | char* dylibPath = resolvePath(argv[2]); 125 | if(!dylibPath) return -3; 126 | if(access(dylibPath, R_OK) < 0) 127 | { 128 | printf("ERROR: Can't access passed dylib at %s\n", dylibPath); 129 | return -4; 130 | } 131 | 132 | // get task port 133 | kret = task_for_pid(mach_task_self(), targetPid, &procTask); 134 | if(kret != KERN_SUCCESS) 135 | { 136 | printf("ERROR: task_for_pid failed with error code %d (%s)\n", kret, mach_error_string(kret)); 137 | return -2; 138 | } 139 | if(!MACH_PORT_VALID(procTask)) 140 | { 141 | printf("ERROR: Got invalid task port (%d)\n", procTask); 142 | return -3; 143 | } 144 | 145 | printf("Got task port %d for pid %d!\n", procTask, targetPid); 146 | 147 | // get aslr slide 148 | task_dyld_info_data_t dyldInfo; 149 | uint32_t count = TASK_DYLD_INFO_COUNT; 150 | task_info(procTask, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count); 151 | 152 | injectDylibViaRop(procTask, targetPid, dylibPath, dyldInfo.all_image_info_addr); 153 | 154 | mach_port_deallocate(mach_task_self(), procTask); 155 | 156 | return 0; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /pac.h: -------------------------------------------------------------------------------- 1 | #ifndef PTRAUTH_HELPERS_H 2 | #define PTRAUTH_HELPERS_H 3 | // Helpers for PAC archs. 4 | 5 | // If the compiler understands __arm64e__, assume it's paired with an SDK that has 6 | // ptrauth.h. Otherwise, it'll probably error if we try to include it so don't. 7 | #if __arm64e__ 8 | #include 9 | #endif 10 | 11 | #pragma clang diagnostic push 12 | #pragma clang diagnostic ignored "-Wunused-function" 13 | 14 | // Given a pointer to instructions, sign it so you can call it like a normal fptr. 15 | static void *make_sym_callable(void *ptr) { 16 | #if __arm64e__ 17 | ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0); 18 | #endif 19 | return ptr; 20 | } 21 | 22 | static void *make_sym_callable_data(void *ptr) { 23 | #if __arm64e__ 24 | ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_process_independent_data), ptrauth_key_process_independent_data, 0); 25 | #endif 26 | return ptr; 27 | } 28 | 29 | // Given a function pointer, strip the PAC so you can read the instructions. 30 | static void *make_sym_readable(void *ptr) { 31 | #if __arm64e__ 32 | ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer); 33 | #endif 34 | return ptr; 35 | } 36 | 37 | static void *make_sym_readable_data(void *ptr) { 38 | #if __arm64e__ 39 | ptr = ptrauth_strip(ptr, ptrauth_key_process_independent_data); 40 | #endif 41 | return ptr; 42 | } 43 | 44 | #pragma clang diagnostic pop 45 | #endif -------------------------------------------------------------------------------- /rop_inject.h: -------------------------------------------------------------------------------- 1 | extern void injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr); -------------------------------------------------------------------------------- /rop_inject.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | 26 | #import "pac.h" 27 | #import "dyld.h" 28 | #import "sandbox.h" 29 | #import "CoreSymbolication.h" 30 | #import "task_utils.h" 31 | #import "thread_utils.h" 32 | #import "arm64.h" 33 | 34 | vm_address_t writeStringToTask(task_t task, const char* string, size_t* lengthOut) 35 | { 36 | kern_return_t kr = KERN_SUCCESS; 37 | vm_address_t remoteString = (vm_address_t)NULL; 38 | size_t stringLen = strlen(string)+1; 39 | 40 | kr = vm_allocate(task, &remoteString, stringLen, VM_FLAGS_ANYWHERE); 41 | if(kr != KERN_SUCCESS) 42 | { 43 | printf("ERROR: Unable to memory for string %s: %s\n", string, mach_error_string(kr)); 44 | return 0; 45 | } 46 | 47 | kr = vm_protect(task, remoteString, stringLen, TRUE, VM_PROT_READ | VM_PROT_WRITE); 48 | if(kr != KERN_SUCCESS) 49 | { 50 | vm_deallocate(task, remoteString, stringLen); 51 | printf("ERROR: Failed to make string %s read/write: %s.\n", string, mach_error_string(kr)); 52 | return kr; 53 | } 54 | 55 | kr = vm_write(task, remoteString, (vm_address_t)string, stringLen); 56 | if(kr != KERN_SUCCESS) 57 | { 58 | vm_deallocate(task, remoteString, stringLen); 59 | printf("ERROR: Failed to write string %s to memory: %s\n", string, mach_error_string(kr)); 60 | return kr; 61 | } 62 | 63 | if(lengthOut) 64 | { 65 | *lengthOut = stringLen; 66 | } 67 | 68 | return remoteString; 69 | } 70 | 71 | void findRopLoop(task_t task, vm_address_t allImageInfoAddr) 72 | { 73 | uint32_t inst = CFSwapInt32(0x00000014); 74 | ropLoop = (uint64_t)scanLibrariesForMemory(task, allImageInfoAddr, (char*)&inst, sizeof(inst), 4); 75 | } 76 | 77 | // Create an infinitely spinning pthread in target process 78 | kern_return_t createRemotePthread(task_t task, vm_address_t allImageInfoAddr, thread_act_t* remotePthreadOut) 79 | { 80 | kern_return_t kr = KERN_SUCCESS; 81 | 82 | #if __arm64e__ 83 | // GET ANY VALID THREAD STATE 84 | mach_msg_type_number_t validThreadStateCount = ARM_THREAD_STATE64_COUNT; 85 | struct arm_unified_thread_state validThreadState; 86 | thread_act_array_t allThreadsForFindingValid; 87 | mach_msg_type_number_t threadCountForFindingValid; 88 | kr = task_threads(task, &allThreadsForFindingValid, &threadCountForFindingValid); 89 | if(kr != KERN_SUCCESS || threadCountForFindingValid == 0) 90 | { 91 | printf("[createRemotePthread] ERROR: failed to get threads in task: %s\n", mach_error_string(kr)); 92 | if (kr == KERN_SUCCESS) return 1; 93 | return kr; 94 | } 95 | kr = thread_get_state(allThreadsForFindingValid[0], ARM_THREAD_STATE64, (thread_state_t)&validThreadState.ts_64, &validThreadStateCount); 96 | if(kr != KERN_SUCCESS ) 97 | { 98 | printf("[createRemotePthread] ERROR: failed to get valid thread state: %s\n", mach_error_string(kr)); 99 | return kr; 100 | } 101 | vm_deallocate(mach_task_self(), (vm_offset_t)allThreadsForFindingValid, sizeof(thread_act_array_t) * threadCountForFindingValid); 102 | #endif 103 | 104 | // GATHER OFFSETS 105 | __unused vm_address_t libSystemPthreadAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_pthread.dylib"); 106 | 107 | uint64_t mainThread = 0; 108 | if (@available(iOS 12, *)) { 109 | // TODO: maybe instead of this, allocate our own pthread object? 110 | // kinda worried about side effects here, but as long our thread doesn't 111 | // somehow trigger pthread_main_thread modifications, it should be fine 112 | uint64_t pthread_main_thread_np = remoteDlSym(task, libSystemPthreadAddr, "_pthread_main_thread_np"); 113 | 114 | uint32_t instructions[2]; 115 | kr = task_read(task, pthread_main_thread_np, &instructions[0], sizeof(instructions)); 116 | if (kr != KERN_SUCCESS) { 117 | printf("ERROR: Failed to find main thread (1/3)\n"); 118 | return kr; 119 | } 120 | 121 | uint64_t _main_thread_ptr = 0; 122 | if (!decode_adrp_ldr(instructions[0], instructions[1], pthread_main_thread_np, &_main_thread_ptr)) { 123 | printf("ERROR: Failed to find main thread (2/3)\n"); 124 | return 1; 125 | } 126 | 127 | kr = task_read(task, _main_thread_ptr, &mainThread, sizeof(mainThread)); 128 | if (kr != KERN_SUCCESS) { 129 | printf("ERROR: Failed to find main thread (3/3)\n"); 130 | return kr; 131 | } 132 | } 133 | uint64_t _pthread_set_self = remoteDlSym(task, libSystemPthreadAddr, "__pthread_set_self"); 134 | 135 | // ALLOCATE STACK 136 | vm_address_t remoteStack64 = (vm_address_t)NULL; 137 | kr = vm_allocate(task, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); 138 | if(kr != KERN_SUCCESS) 139 | { 140 | printf("[createRemotePthread] ERROR: Unable to allocate stack memory: %s\n", mach_error_string(kr)); 141 | return kr; 142 | } 143 | 144 | kr = vm_protect(task, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); 145 | if(kr != KERN_SUCCESS) 146 | { 147 | vm_deallocate(task, remoteStack64, STACK_SIZE); 148 | printf("[createRemotePthread] ERROR: Failed to make remote stack writable: %s.\n", mach_error_string(kr)); 149 | return kr; 150 | } 151 | 152 | thread_act_t bootstrapThread = 0; 153 | struct arm_unified_thread_state bootstrapThreadState; 154 | memset(&bootstrapThreadState, 0, sizeof(struct arm_unified_thread_state)); 155 | 156 | // spawn pthread to infinite loop 157 | bootstrapThreadState.ash.flavor = ARM_THREAD_STATE64; 158 | bootstrapThreadState.ash.count = ARM_THREAD_STATE64_COUNT; 159 | #if __arm64e__ 160 | bootstrapThreadState.ts_64.__opaque_flags = validThreadState.ts_64.__opaque_flags; 161 | #endif 162 | uint64_t sp = (remoteStack64 + (STACK_SIZE / 2)); 163 | __unused uint64_t x2 = ropLoop; 164 | #if __arm64e__ 165 | if (!(bootstrapThreadState.ts_64.__opaque_flags & __DARWIN_ARM_THREAD_STATE64_FLAGS_NO_PTRAUTH)) { 166 | x2 = (uint64_t)make_sym_callable((void*)x2); 167 | } 168 | #endif 169 | __darwin_arm_thread_state64_set_sp(bootstrapThreadState.ts_64, (void*)sp); 170 | __darwin_arm_thread_state64_set_pc_fptr(bootstrapThreadState.ts_64, make_sym_callable((void*)_pthread_set_self)); 171 | __darwin_arm_thread_state64_set_lr_fptr(bootstrapThreadState.ts_64, make_sym_callable((void*)ropLoop)); //when done, go to infinite loop 172 | bootstrapThreadState.ts_64.__x[0] = mainThread; 173 | 174 | //printThreadState_state(bootstrapThreadState); 175 | 176 | kr = thread_create_running(task, ARM_THREAD_STATE64, (thread_state_t)&bootstrapThreadState.ts_64, ARM_THREAD_STATE64_COUNT, &bootstrapThread); 177 | if(kr != KERN_SUCCESS) 178 | { 179 | printf("[createRemotePthread] ERROR: Failed to create running thread: %s.\n", mach_error_string(kr)); 180 | return kr; 181 | } 182 | 183 | printf("[createRemotePthread] Created bootstrap thread... now waiting on finish\n"); 184 | 185 | struct arm_unified_thread_state outState; 186 | kr = wait_for_thread(bootstrapThread, ropLoop, &outState); 187 | if(kr != KERN_SUCCESS) 188 | { 189 | printf("[createRemotePthread] ERROR: failed to wait for bootstrap thread: %s\n", mach_error_string(kr)); 190 | return kr; 191 | } 192 | 193 | printf("[createRemotePthread] Bootstrap done!\n"); 194 | 195 | if(remotePthreadOut) *remotePthreadOut = bootstrapThread; 196 | 197 | return kr; 198 | } 199 | 200 | kern_return_t arbCall(task_t task, thread_act_t targetThread, uint64_t* retOut, bool willReturn, vm_address_t funcPtr, int numArgs, ...) 201 | { 202 | kern_return_t kr = KERN_SUCCESS; 203 | if(numArgs > 8) 204 | { 205 | printf("[arbCall] ERROR: Only 8 arguments are supported by arbCall\n"); 206 | return -2; 207 | } 208 | if(!targetThread) 209 | { 210 | printf("[arbCall] ERROR: targetThread == null\n"); 211 | return -3; 212 | } 213 | 214 | va_list ap; 215 | va_start(ap, numArgs); 216 | 217 | // suspend target thread 218 | thread_suspend(targetThread); 219 | 220 | // backup states of target thread 221 | 222 | mach_msg_type_number_t origThreadStateCount = ARM_THREAD_STATE64_COUNT; 223 | struct arm_unified_thread_state origThreadState; 224 | kr = thread_get_state(targetThread, ARM_THREAD_STATE64, (thread_state_t)&origThreadState.ts_64, &origThreadStateCount); 225 | if(kr != KERN_SUCCESS) 226 | { 227 | thread_resume(targetThread); 228 | printf("[arbCall] ERROR: failed to save original state of target thread: %s\n", mach_error_string(kr)); 229 | return kr; 230 | } 231 | 232 | struct arm64_thread_full_state* origThreadFullState = thread_save_state_arm64(targetThread); 233 | if(!origThreadFullState) 234 | { 235 | thread_resume(targetThread); 236 | printf("[arbCall] ERROR: failed to backup original state of target thread\n"); 237 | return kr; 238 | } 239 | 240 | // prepare target thread for arbitary call 241 | 242 | // allocate stack 243 | vm_address_t remoteStack = (vm_address_t)NULL; 244 | kr = vm_allocate(task, &remoteStack, STACK_SIZE, VM_FLAGS_ANYWHERE); 245 | if(kr != KERN_SUCCESS) 246 | { 247 | free(origThreadFullState); 248 | thread_resume(targetThread); 249 | printf("[arbCall] ERROR: Unable to allocate stack memory: %s\n", mach_error_string(kr)); 250 | return kr; 251 | } 252 | 253 | // make stack read / write 254 | kr = vm_protect(task, remoteStack, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); 255 | if(kr != KERN_SUCCESS) 256 | { 257 | free(origThreadFullState); 258 | vm_deallocate(task, remoteStack, STACK_SIZE); 259 | thread_resume(targetThread); 260 | printf("[arbCall] ERROR: Failed to make remote stack writable: %s.\n", mach_error_string(kr)); 261 | return kr; 262 | } 263 | 264 | // abort any existing syscalls by target thread, thanks to Linus Henze for this suggestion :P 265 | thread_abort(targetThread); 266 | 267 | // set state for arb call 268 | struct arm_unified_thread_state newState = origThreadState; 269 | uint64_t sp = remoteStack + (STACK_SIZE / 2); 270 | __darwin_arm_thread_state64_set_sp(newState.ts_64, (void*)sp); 271 | __darwin_arm_thread_state64_set_pc_fptr(newState.ts_64, make_sym_callable((void*)funcPtr)); 272 | __darwin_arm_thread_state64_set_lr_fptr(newState.ts_64, make_sym_callable((void*)ropLoop)); 273 | 274 | // write arguments into registers 275 | for (int i = 0; i < numArgs; i++) 276 | { 277 | newState.ts_64.__x[i] = va_arg(ap, uint64_t); 278 | } 279 | 280 | kr = thread_set_state(targetThread, ARM_THREAD_STATE64, (thread_state_t)&newState.ts_64, ARM_THREAD_STATE64_COUNT); 281 | if(kr != KERN_SUCCESS) 282 | { 283 | free(origThreadFullState); 284 | vm_deallocate(task, remoteStack, STACK_SIZE); 285 | thread_resume(targetThread); 286 | printf("[arbCall] ERROR: failed to set state for thread: %s\n", mach_error_string(kr)); 287 | return kr; 288 | } 289 | 290 | printf("[arbCall] Set thread state for arbitary call\n"); 291 | //printThreadState(targetThread); 292 | 293 | thread_act_array_t cachedThreads; 294 | mach_msg_type_number_t cachedThreadCount; 295 | kr = task_threads(task, &cachedThreads, &cachedThreadCount); 296 | if (kr != KERN_SUCCESS) return kr; 297 | 298 | suspend_threads_except_for(cachedThreads, cachedThreadCount, targetThread); 299 | 300 | // perform arbitary call 301 | thread_resume(targetThread); 302 | printf("[arbCall] Started thread, waiting for it to finish...\n"); 303 | 304 | // wait for arbitary call to finish (or not) 305 | struct arm_unified_thread_state outState; 306 | if (willReturn) 307 | { 308 | kr = wait_for_thread(targetThread, ropLoop, &outState); 309 | if(kr != KERN_SUCCESS) 310 | { 311 | free(origThreadFullState); 312 | printf("[arbCall] ERROR: failed to wait for thread to finish: %s\n", mach_error_string(kr)); 313 | return kr; 314 | } 315 | 316 | // extract return value from state if needed 317 | if(retOut) 318 | { 319 | *retOut = outState.ts_64.__x[0]; 320 | } 321 | } 322 | else 323 | { 324 | kr = wait_for_thread(targetThread, 0, &outState); 325 | printf("[arbCall] pthread successfully did not return with code %d (%s)\n", kr, mach_error_string(kr)); 326 | } 327 | 328 | resume_threads_except_for(cachedThreads, cachedThreadCount, targetThread); 329 | 330 | vm_deallocate(mach_task_self(), (vm_offset_t)cachedThreads, sizeof(thread_act_array_t) * cachedThreadCount); 331 | 332 | // release fake stack as it's no longer needed 333 | vm_deallocate(task, remoteStack, STACK_SIZE); 334 | 335 | if (willReturn) 336 | { 337 | // suspend target thread 338 | thread_suspend(targetThread); 339 | thread_abort(targetThread); 340 | 341 | // restore states of target thread to what they were before the arbitary call 342 | bool restoreSuccess = thread_restore_state_arm64(targetThread, origThreadFullState); 343 | if(!restoreSuccess) 344 | { 345 | printf("[arbCall] ERROR: failed to revert to old thread state\n"); 346 | return kr; 347 | } 348 | 349 | // resume thread again, process should continue executing as before 350 | //printThreadState(targetThread); 351 | thread_resume(targetThread); 352 | } 353 | 354 | return kr; 355 | } 356 | 357 | void prepareForMagic(task_t task, vm_address_t allImageInfoAddr) 358 | { 359 | // FIND INFINITE LOOP ROP GADGET 360 | static dispatch_once_t onceToken; 361 | dispatch_once (&onceToken, ^{ 362 | findRopLoop(task, allImageInfoAddr); 363 | }); 364 | printf("[prepareForMagic] done, ropLoop: 0x%llX\n", ropLoop); 365 | } 366 | 367 | bool sandboxFixup(task_t task, thread_act_t pthread, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr) 368 | { 369 | int readExtensionNeeded = sandbox_check(pid, "file-read-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath); 370 | int executableExtensionNeeded = sandbox_check(pid, "file-map-executable", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath); 371 | 372 | int retval = 0; 373 | vm_address_t libSystemSandboxAddr = 0; 374 | uint64_t sandbox_extension_consumeAddr = 0; 375 | if (readExtensionNeeded || executableExtensionNeeded) { 376 | libSystemSandboxAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_sandbox.dylib"); 377 | sandbox_extension_consumeAddr = remoteDlSym(task, libSystemSandboxAddr, "_sandbox_extension_consume"); 378 | printf("[sandboxFixup] applying sandbox extension(s)! sandbox_extension_consume: 0x%llX\n", sandbox_extension_consumeAddr); 379 | } 380 | 381 | if (readExtensionNeeded) { 382 | char* extString = sandbox_extension_issue_file(APP_SANDBOX_READ, dylibPath, 0); 383 | size_t remoteExtStringSize = 0; 384 | vm_address_t remoteExtString = writeStringToTask(task, (const char*)extString, &remoteExtStringSize); 385 | if(remoteExtString) 386 | { 387 | int64_t readExtensionRet = 0; 388 | arbCall(task, pthread, (uint64_t*)&readExtensionRet, true, sandbox_extension_consumeAddr, 1, remoteExtString); 389 | vm_deallocate(task, remoteExtString, remoteExtStringSize); 390 | 391 | printf("[sandboxFixup] sandbox_extension_consume returned %lld for read extension\n", (int64_t)readExtensionRet); 392 | retval |= (readExtensionRet <= 0); 393 | } 394 | } 395 | else { 396 | printf("[sandboxFixup] read extension not needed, skipping...\n"); 397 | } 398 | 399 | if (executableExtensionNeeded) { 400 | char* extString = sandbox_extension_issue_file("com.apple.sandbox.executable", dylibPath, 0); 401 | size_t remoteExtStringSize = 0; 402 | vm_address_t remoteExtString = writeStringToTask(task, (const char*)extString, &remoteExtStringSize); 403 | if(remoteExtString) 404 | { 405 | int64_t executableExtensionRet = 0; 406 | arbCall(task, pthread, (uint64_t*)&executableExtensionRet, true, sandbox_extension_consumeAddr, 1, remoteExtString); 407 | vm_deallocate(task, remoteExtString, remoteExtStringSize); 408 | 409 | printf("[sandboxFixup] sandbox_extension_consume returned %lld for executable extension\n", (int64_t)executableExtensionRet); 410 | retval |= (executableExtensionRet <= 0); 411 | } 412 | } 413 | else { 414 | printf("[sandboxFixup] executable extension not needed, skipping...\n"); 415 | } 416 | 417 | return retval == 0; 418 | } 419 | 420 | void injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr) 421 | { 422 | prepareForMagic(task, allImageInfoAddr); 423 | 424 | thread_act_t pthread = 0; 425 | kern_return_t kr = createRemotePthread(task, allImageInfoAddr, &pthread); 426 | if(kr != KERN_SUCCESS) return; 427 | 428 | sandboxFixup(task, pthread, pid, dylibPath, allImageInfoAddr); 429 | 430 | printf("[injectDylibViaRop] Preparation done, now injecting!\n"); 431 | 432 | // FIND OFFSETS 433 | vm_address_t libDyldAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libdyld.dylib"); 434 | uint64_t dlopenAddr = remoteDlSym(task, libDyldAddr, "_dlopen"); 435 | uint64_t dlerrorAddr = remoteDlSym(task, libDyldAddr, "_dlerror"); 436 | 437 | printf("[injectDylibViaRop] dlopen: 0x%llX, dlerror: 0x%llX\n", (unsigned long long)dlopenAddr, (unsigned long long)dlerrorAddr); 438 | 439 | // CALL DLOPEN 440 | size_t remoteDylibPathSize = 0; 441 | vm_address_t remoteDylibPath = writeStringToTask(task, (const char*)dylibPath, &remoteDylibPathSize); 442 | if(remoteDylibPath) 443 | { 444 | void* dlopenRet; 445 | arbCall(task, pthread, (uint64_t*)&dlopenRet, true, dlopenAddr, 2, remoteDylibPath, RTLD_NOW); 446 | vm_deallocate(task, remoteDylibPath, remoteDylibPathSize); 447 | 448 | if (dlopenRet) { 449 | printf("[injectDylibViaRop] dlopen succeeded, library handle: %p\n", dlopenRet); 450 | } 451 | else { 452 | uint64_t remoteErrorString = 0; 453 | arbCall(task, pthread, (uint64_t*)&remoteErrorString, true, dlerrorAddr, 0); 454 | char *errorString = task_copy_string(task, remoteErrorString); 455 | printf("[injectDylibViaRop] dlopen failed, error:\n%s\n", errorString); 456 | free(errorString); 457 | } 458 | } 459 | 460 | thread_terminate(pthread); 461 | } -------------------------------------------------------------------------------- /sandbox.h: -------------------------------------------------------------------------------- 1 | enum sandbox_filter_type { 2 | SANDBOX_FILTER_NONE, 3 | SANDBOX_FILTER_PATH, 4 | SANDBOX_FILTER_GLOBAL_NAME, 5 | SANDBOX_FILTER_LOCAL_NAME, 6 | SANDBOX_FILTER_APPLEEVENT_DESTINATION, 7 | SANDBOX_FILTER_RIGHT_NAME, 8 | SANDBOX_FILTER_PREFERENCE_DOMAIN, 9 | SANDBOX_FILTER_KEXT_BUNDLE_ID, 10 | SANDBOX_FILTER_INFO_TYPE, 11 | SANDBOX_FILTER_NOTIFICATION, 12 | // ? 13 | // ? 14 | SANDBOX_FILTER_XPC_SERVICE_NAME = 12, 15 | SANDBOX_FILTER_IOKIT_CONNECTION, 16 | // ? 17 | // ? 18 | // ? 19 | // ? 20 | }; 21 | 22 | 23 | enum sandbox_extension_flags { 24 | FS_EXT_DEFAULTS = 0, 25 | FS_EXT_FOR_PATH = (1 << 0), 26 | FS_EXT_FOR_FILE = (1 << 1), 27 | FS_EXT_READ = (1 << 2), 28 | FS_EXT_WRITE = (1 << 3), 29 | FS_EXT_PREFER_FILEID = (1 << 4), 30 | }; 31 | 32 | extern const char * APP_SANDBOX_IOKIT_CLIENT; 33 | extern const char * APP_SANDBOX_MACH; 34 | extern const char * APP_SANDBOX_READ; 35 | extern const char * APP_SANDBOX_READ_WRITE; 36 | 37 | extern const char * IOS_SANDBOX_APPLICATION_GROUP; 38 | extern const char * IOS_SANDBOX_CONTAINER; 39 | 40 | extern const enum sandbox_filter_type SANDBOX_CHECK_ALLOW_APPROVAL; 41 | extern const enum sandbox_filter_type SANDBOX_CHECK_CANONICAL; 42 | extern const enum sandbox_filter_type SANDBOX_CHECK_NOFOLLOW; 43 | extern const enum sandbox_filter_type SANDBOX_CHECK_NO_APPROVAL; 44 | extern const enum sandbox_filter_type SANDBOX_CHECK_NO_REPORT; 45 | 46 | extern const uint32_t SANDBOX_EXTENSION_CANONICAL; 47 | extern const uint32_t SANDBOX_EXTENSION_DEFAULT; 48 | extern const uint32_t SANDBOX_EXTENSION_MAGIC; 49 | extern const uint32_t SANDBOX_EXTENSION_NOFOLLOW; 50 | extern const uint32_t SANDBOX_EXTENSION_NO_REPORT; 51 | extern const uint32_t SANDBOX_EXTENSION_NO_STORAGE_CLASS; 52 | extern const uint32_t SANDBOX_EXTENSION_PREFIXMATCH; 53 | extern const uint32_t SANDBOX_EXTENSION_UNRESOLVED; 54 | 55 | int sandbox_check(pid_t, const char *operation, enum sandbox_filter_type, ...); 56 | int sandbox_check_by_audit_token(audit_token_t, const char *operation, enum sandbox_filter_type, ...); 57 | int sandbox_check_by_uniqueid(uid_t, pid_t, const char *operation, enum sandbox_filter_type, ...); 58 | 59 | int64_t sandbox_extension_consume(const char *extension_token); 60 | char *sandbox_extension_issue_file(const char *extension_class, const char *path, uint32_t flags); 61 | char *sandbox_extension_issue_file_to_process(const char *extension_class, const char *path, uint32_t flags, audit_token_t); 62 | char *sandbox_extension_issue_file_to_process_by_pid(const char *extension_class, const char *path, uint32_t flags, pid_t); 63 | char *sandbox_extension_issue_file_to_self(const char *extension_class, const char *path, uint32_t flags); 64 | char *sandbox_extension_issue_generic(const char *extension_class, uint32_t flags); 65 | char *sandbox_extension_issue_generic_to_process(const char *extension_class, uint32_t flags, audit_token_t); 66 | char *sandbox_extension_issue_generic_to_process_by_pid(const char *extension_class, uint32_t flags, pid_t); 67 | char *sandbox_extension_issue_iokit_registry_entry_class(const char *extension_class, const char *registry_entry_class, uint32_t flags); 68 | char *sandbox_extension_issue_iokit_registry_entry_class_to_process(const char *extension_class, const char *registry_entry_class, uint32_t flags, audit_token_t); 69 | char *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); 70 | char *sandbox_extension_issue_iokit_user_client_class(const char *extension_class, const char *registry_entry_class, uint32_t flags); 71 | char *sandbox_extension_issue_mach(const char *extension_class, const char *name, uint32_t flags); 72 | char *sandbox_extension_issue_mach_to_process(const char *extension_class, const char *name, uint32_t flags, audit_token_t); 73 | char *sandbox_extension_issue_mach_to_process_by_pid(const char *extension_class, const char *name, uint32_t flags, pid_t); 74 | char *sandbox_extension_issue_posix_ipc(const char *extension_class, const char *name, uint32_t flags); 75 | void sandbox_extension_reap(void); 76 | int sandbox_extension_release(int64_t extension_handle); 77 | int sandbox_extension_release_file(int64_t extension_handle, const char *path); 78 | int sandbox_extension_update_file(int64_t extension_handle, const char *path); -------------------------------------------------------------------------------- /shellcode_inject.h: -------------------------------------------------------------------------------- 1 | int injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr); -------------------------------------------------------------------------------- /shellcode_inject.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #import "dyld.h" 28 | #import "sandbox.h" 29 | #import 30 | 31 | #define STACK_SIZE 65536 32 | 33 | #define PT_TRACE_ME 0 /* child declares it's being traced */ 34 | #define PT_READ_I 1 /* read word in child's I space */ 35 | #define PT_READ_D 2 /* read word in child's D space */ 36 | #define PT_READ_U 3 /* read word in child's user structure */ 37 | #define PT_WRITE_I 4 /* write word in child's I space */ 38 | #define PT_WRITE_D 5 /* write word in child's D space */ 39 | #define PT_WRITE_U 6 /* write word in child's user structure */ 40 | #define PT_CONTINUE 7 /* continue the child */ 41 | #define PT_KILL 8 /* kill the child process */ 42 | #define PT_STEP 9 /* single step the child */ 43 | #define PT_ATTACH 10 /* trace some running process */ 44 | #define PT_DETACH 11 /* stop tracing a process */ 45 | #define PT_SIGEXC 12 /* signals as exceptions for current_proc */ 46 | #define PT_THUPDATE 13 /* signal for thread# */ 47 | #define PT_ATTACHEXC 14 /* attach to running process with signal exception */ 48 | extern int ptrace(int request, pid_t pid, caddr_t addr, int data); 49 | 50 | static kern_return_t runPayload(task_t task, uint8_t* payload, size_t payloadSize, uint64_t codeStart, vm_address_t allImageInfoAddr) 51 | { 52 | // GATHER OFFSETS 53 | 54 | vm_address_t libSystemPthreadAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_pthread.dylib"); 55 | uint64_t pthread_create_from_mach_threadAddr = remoteDlSym(task, libSystemPthreadAddr, "_pthread_create_from_mach_thread"); 56 | uint64_t pthread_exitAddr = remoteDlSym(task, libSystemPthreadAddr, "_pthread_exit"); 57 | 58 | 59 | // ALLOCATE STACK 60 | 61 | vm_address_t remoteStack64 = (vm_address_t)NULL; 62 | kern_return_t kr = KERN_SUCCESS; 63 | kr = vm_allocate(task, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); 64 | if(kr != KERN_SUCCESS) 65 | { 66 | printf("ERROR: Unable to allocate stack memory: %s\n", mach_error_string(kr)); 67 | return kr; 68 | } 69 | 70 | kr = vm_protect(task, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); 71 | if(kr != KERN_SUCCESS) 72 | { 73 | vm_deallocate(task, remoteStack64, STACK_SIZE); 74 | printf("ERROR: Failed to make remote stack writable: %s.\n", mach_error_string(kr)); 75 | return kr; 76 | } 77 | 78 | 79 | // THREAD BOOTSTRAP PAYLOAD (x2: startFunc argument passed to pthread_create_from_mach_thread) 80 | 81 | uint32_t bootstrapCode[7] = { 82 | // pthread_create_from_mach_thread call 83 | // arg1: x0, output pthread_t = pointer to stack 84 | // arg2: x1, attributes = NULL 85 | // arg3: x2, start_routine, pointer to real payload, passed via thread state 86 | // arg4: x3, arg, pointer to arguments array = NULL 87 | CFSwapInt32(0xE0230091), // add x0, sp, #8 ; x0 = SP+8 88 | CFSwapInt32(0x010080D2), // mov x1, #0x0 89 | CFSwapInt32(0x030080D2), // mov x3, #0x0 90 | CFSwapInt32(0x68FFFF58), // ldr x8, -0x14 ; pthread_create_from_mach_threadAddr 91 | CFSwapInt32(0x00013FD6), // blr x8 92 | CFSwapInt32(0x490880D2), // mov x9, 0x42 (end code) 93 | CFSwapInt32(0x00000014), // b #0 (infinite loop) 94 | }; 95 | 96 | uint64_t bootstrapCodeVarCount = 1; // count of variables before code 97 | size_t bootstrapCodeVarSize = bootstrapCodeVarCount * sizeof(uint64_t); 98 | 99 | size_t bootstrapPayloadSize = bootstrapCodeVarCount * sizeof(uint32_t) + sizeof(bootstrapCode); // allocate needed memory space 100 | char* bootstrapPayload = malloc(bootstrapPayloadSize); 101 | bzero(&bootstrapPayload[0], bootstrapPayloadSize); 102 | 103 | intptr_t bootstrapPayloadPtr = (intptr_t)bootstrapPayload; // insert variables 104 | memcpy((void*)(bootstrapPayloadPtr), (const void*)&pthread_create_from_mach_threadAddr, sizeof(uint64_t)); 105 | 106 | memcpy((void*)(bootstrapPayloadPtr+bootstrapCodeVarSize), &bootstrapCode[0], sizeof(bootstrapCode)); //insert code 107 | 108 | vm_address_t remoteBootstrapPayload = (vm_address_t)NULL; 109 | kr = vm_allocate(task, &remoteBootstrapPayload, bootstrapPayloadSize, VM_FLAGS_ANYWHERE); 110 | if(kr != KERN_SUCCESS) 111 | { 112 | free(bootstrapPayload); 113 | vm_deallocate(task, remoteStack64, STACK_SIZE); 114 | printf("ERROR: Unable to allocate memory for bootstrap code: %s\n", mach_error_string(kr)); 115 | return kr; 116 | } 117 | 118 | kr = vm_write(task, remoteBootstrapPayload, (vm_address_t)bootstrapPayload, bootstrapPayloadSize); 119 | if(kr != KERN_SUCCESS) 120 | { 121 | free(bootstrapPayload); 122 | vm_deallocate(task, remoteStack64, STACK_SIZE); 123 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 124 | printf("ERROR: Failed to write payload to code memory: %s\n", mach_error_string(kr)); 125 | return kr; 126 | } 127 | 128 | kr = vm_protect(task, remoteBootstrapPayload + bootstrapCodeVarSize, sizeof(bootstrapCode), FALSE, VM_PROT_READ | VM_PROT_EXECUTE); 129 | if(kr != KERN_SUCCESS) 130 | { 131 | free(bootstrapPayload); 132 | vm_deallocate(task, remoteStack64, STACK_SIZE); 133 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 134 | printf("ERROR: Failed to make bootstrap payload executable: %s\n", mach_error_string(kr)); 135 | return kr; 136 | } 137 | 138 | /*kr = vm_protect(task, remoteBootstrapPayload, bootstrapCodeVarSize, FALSE, VM_PROT_READ | VM_PROT_WRITE); 139 | if(kr != KERN_SUCCESS) 140 | { 141 | vm_deallocate(task, remoteStack64, STACK_SIZE); 142 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 143 | printf("ERROR: Failed to make bootstrap payload variables read/write: %s\n", mach_error_string(kr)); 144 | return kr; 145 | }*/ 146 | 147 | free(bootstrapPayload); 148 | 149 | 150 | // SETUP PAYLOAD SUFFIX (pthread_exit(0)) 151 | 152 | uint32_t payloadSuffixCode[3] = { 153 | CFSwapInt32(0x000080D2), // mov x0, #0 154 | CFSwapInt32(0x48000058), // ldr x8, 0x8 ; pthread_exit 155 | CFSwapInt32(0x00013FD6), // blr x8 156 | }; 157 | 158 | uint64_t payloadSuffixVarCount = 1; // count of variables after suffix code 159 | uint64_t payloadSuffixVarSize = payloadSuffixVarCount * sizeof(uint64_t); 160 | uint64_t payloadSuffixCodeSize = sizeof(payloadSuffixCode); 161 | uint64_t payloadSuffixSize = payloadSuffixVarSize + payloadSuffixCodeSize; 162 | 163 | char* payloadSuffix = malloc(payloadSuffixSize); // allocate buffer 164 | intptr_t payloadSuffixPtr = (intptr_t)payloadSuffix; 165 | 166 | memcpy((void*)(payloadSuffixPtr), &payloadSuffixCode[0], payloadSuffixCodeSize); // insert code 167 | memcpy((void*)(payloadSuffixPtr+payloadSuffixCodeSize), (const void*)&pthread_exitAddr, sizeof(uint64_t)); // insert variables 168 | 169 | 170 | // WRITE PASSED PAYLOAD 171 | 172 | uint64_t fullPayloadSize = payloadSize + payloadSuffixSize; 173 | 174 | vm_address_t remotePayload = (vm_address_t)NULL; 175 | kr = vm_allocate(task, &remotePayload, fullPayloadSize, VM_FLAGS_ANYWHERE); 176 | if(kr != KERN_SUCCESS) 177 | { 178 | free(payloadSuffix); 179 | vm_deallocate(task, remoteStack64, STACK_SIZE); 180 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 181 | printf("ERROR: Unable to allocate payload code memory: %s\n", mach_error_string(kr)); 182 | return kr; 183 | } 184 | 185 | kr = vm_write(task, remotePayload, (vm_address_t)payload, payloadSize); 186 | if(kr != KERN_SUCCESS) 187 | { 188 | free(payloadSuffix); 189 | vm_deallocate(task, remoteStack64, STACK_SIZE); 190 | vm_deallocate(task, remotePayload, fullPayloadSize); 191 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 192 | printf("ERROR: Failed to write payload to code memory: %s\n", mach_error_string(kr)); 193 | return kr; 194 | } 195 | 196 | kr = vm_write(task, remotePayload+payloadSize, (vm_address_t)payloadSuffix, payloadSuffixSize); 197 | if(kr != KERN_SUCCESS) 198 | { 199 | free(payloadSuffix); 200 | vm_deallocate(task, remoteStack64, STACK_SIZE); 201 | vm_deallocate(task, remotePayload, fullPayloadSize); 202 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 203 | printf("ERROR: Failed to write payload suffix to code memory: %s\n", mach_error_string(kr)); 204 | return kr; 205 | } 206 | 207 | kr = vm_protect(task, remotePayload + codeStart, fullPayloadSize - codeStart - payloadSuffixVarSize, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); 208 | if(kr != KERN_SUCCESS) 209 | { 210 | free(payloadSuffix); 211 | vm_deallocate(task, remoteStack64, STACK_SIZE); 212 | vm_deallocate(task, remotePayload, fullPayloadSize); 213 | vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize); 214 | printf("ERROR: Failed to make code payload executable: %s\n", mach_error_string(kr)); 215 | return kr; 216 | } 217 | 218 | printf("marked %llX - %llX as rx\n", (uint64_t)remotePayload + codeStart, (uint64_t)remotePayload + (fullPayloadSize - codeStart - payloadSuffixVarSize)); 219 | 220 | 221 | // ALLOCATE THREAD AND START IT 222 | 223 | thread_act_t remoteThread; 224 | 225 | struct arm_unified_thread_state remoteThreadState64; 226 | bzero(&remoteThreadState64, sizeof(struct arm_unified_thread_state)); 227 | 228 | remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; 229 | remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; 230 | __darwin_arm_thread_state64_set_sp(remoteThreadState64.ts_64, (void*)(remoteStack64 + (STACK_SIZE / 2))); 231 | __darwin_arm_thread_state64_set_pc_fptr(remoteThreadState64.ts_64, (void*)(remoteBootstrapPayload + bootstrapCodeVarSize)); 232 | remoteThreadState64.ts_64.__x[2] = remotePayload + codeStart; // <- startFunc argument of pthread_create_from_mach_thread call 233 | 234 | printf("About to jump to %llX (thread bootstrap)\n", (uint64_t)__darwin_arm_thread_state64_get_pc_fptr(remoteThreadState64.ts_64)); 235 | printf("Real payload: %llX (code start: %llX)\n", (uint64_t)remotePayload, (uint64_t)(remotePayload + codeStart)); 236 | 237 | // Uncomment to debug issues before the jump to code happens 238 | //getchar(); 239 | 240 | printf("Starting thread in task!\n"); 241 | kr = thread_create_running(task, ARM_THREAD_STATE64, (thread_state_t)&remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT, &remoteThread); 242 | if(kr != KERN_SUCCESS) 243 | { 244 | printf("ERROR: Failed to create running thread: %s.\n", mach_error_string(kr)); 245 | } 246 | 247 | 248 | // WAIT FOR THREAD TO FINISH 249 | // (Note: The pthread this thread spawns will still be running by the time it has fininshed) 250 | // (So we unfortunately can't release the remote memory again because otherwise the pthread would crash) 251 | 252 | printf("Started thread, now waiting for it to finish.\n"); 253 | 254 | mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; 255 | for (;;) { 256 | kr = thread_get_state(remoteThread, ARM_THREAD_STATE64, (thread_state_t)&remoteThreadState64.ts_64, &thread_state_count); 257 | if (kr != KERN_SUCCESS) { 258 | printf("Error getting stub thread state: error %s", mach_error_string(kr)); 259 | break; 260 | } 261 | 262 | // Check whether x9 is 0x42 (exit code) 263 | if (remoteThreadState64.ts_64.__x[9] == 0x42) { 264 | printf("Stub thread finished\n"); 265 | kr = thread_terminate(remoteThread); 266 | if (kr != KERN_SUCCESS) { 267 | printf("Error terminating stub thread: error %s\n", mach_error_string(kr)); 268 | } 269 | break; 270 | } 271 | } 272 | 273 | printf("Thread finished, we done here.\n"); 274 | 275 | // Deallocating is very hard to time right, we just leave the memory in there, doesn't matter too much anyways 276 | //vm_deallocate(task, remoteStack64, STACK_SIZE); 277 | //vm_deallocate(task, remoteCode64, payloadSize); 278 | free(payloadSuffix); 279 | return kr; 280 | } 281 | 282 | int injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr) 283 | { 284 | // STEP ONE: gather offsets 285 | 286 | vm_address_t libSystemSandboxAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_sandbox.dylib"); 287 | vm_address_t libDyldAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libdyld.dylib"); 288 | 289 | //uint64_t pthread_set_selfAddr = remoteDlSym(task, libSystemPthreadAddr, "__pthread_set_self"); 290 | 291 | uint64_t sandbox_extension_consumeAddr = remoteDlSym(task, libSystemSandboxAddr, "_sandbox_extension_consume"); 292 | uint64_t dlopenAddr = remoteDlSym(task, libDyldAddr, "_dlopen"); 293 | 294 | printf("sandbox_extension_consumeAddr: %llX\n", (unsigned long long)sandbox_extension_consumeAddr); 295 | printf("dlopenAddr: %llX\n", (unsigned long long)dlopenAddr); 296 | 297 | // STEP TWO: set up ptrace 298 | 299 | /*kern_return_t ret; 300 | ret = ptrace(PT_ATTACH, pid, NULL, 0); 301 | if(ret != KERN_SUCCESS) 302 | { 303 | printf("Error attaching to process %d: %d\n", pid, errno); 304 | return 1; 305 | } 306 | ret = ptrace(PT_CONTINUE, pid, NULL, 0); 307 | if(ret != KERN_SUCCESS) 308 | { 309 | printf("Error continuing execution of process %d\n", pid); 310 | return 1; 311 | }*/ 312 | 313 | // STEP THREE: apply sandbox extension if needed 314 | 315 | // Check whether sandbox extension is needed 316 | int sandboxExtensionNeeded = sandbox_check(pid, "file-read-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath); 317 | if(sandboxExtensionNeeded) 318 | { 319 | printf("Sandbox extension needed, performing magic...\n"); 320 | 321 | char* extString = sandbox_extension_issue_file(APP_SANDBOX_READ, dylibPath, 0); 322 | size_t stringAllocSize = 300; 323 | 324 | uint64_t codeVarCount = 1; 325 | uint64_t codeVarSize = codeVarCount * sizeof(uint64_t); 326 | 327 | uint32_t code[3] = { 328 | // sandbox_extension_consume call 329 | // arg1: x0, pointer to extension string 330 | CFSwapInt32(0x60F6FF10), // adr x0, -0x134 ; reference to extString 331 | CFSwapInt32(0xA8FFFF58), // ldr x8, -0x0C ; sandbox_extension_consumeAddr 332 | CFSwapInt32(0x00013FD6), // blr x8 333 | }; 334 | 335 | size_t payloadSize = stringAllocSize + codeVarSize + sizeof(code); 336 | char* payload = malloc(payloadSize); 337 | bzero(&payload[0], payloadSize); 338 | strlcpy(&payload[0], extString, stringAllocSize); // insert extString 339 | 340 | intptr_t payloadIntPtr = (intptr_t)payload; 341 | memcpy((void*)(payloadIntPtr+stringAllocSize), (const void*)&sandbox_extension_consumeAddr, sizeof(uint64_t)); // insert vars 342 | 343 | uint64_t codeStart = stringAllocSize + codeVarSize; 344 | memcpy((void*)(payloadIntPtr+codeStart), &code[0], sizeof(code)); // insert code 345 | 346 | printf("constructed sandbox_extension_consume payload!\n"); 347 | 348 | runPayload(task, (uint8_t*)payload, payloadSize, codeStart, allImageInfoAddr); 349 | free(payload); 350 | } 351 | else 352 | { 353 | printf("No Sandbox extension needed, skipping straight to dylib injection...\n"); 354 | } 355 | 356 | // STEP FOUR: load dylib 357 | size_t stringAllocSize = 256; 358 | uint64_t codeVarCount = 1; 359 | uint64_t codeVarSize = codeVarCount * sizeof(uint64_t); 360 | 361 | uint32_t code[3] = { 362 | // sandbox_extension_consume call 363 | // arg1: x0, pointer to extension string 364 | CFSwapInt32(0xC0F7FF10), // adr x0, -0x108 ; reference to dylibPath 365 | CFSwapInt32(0xA8FFFF58), // ldr x8, -0x0C ; dlopenAddr 366 | CFSwapInt32(0x00013FD6), // blr x8 367 | }; 368 | 369 | size_t payloadSize = stringAllocSize + codeVarSize + sizeof(code); 370 | char* payload = malloc(payloadSize); 371 | bzero(&payload[0], payloadSize); 372 | strlcpy(&payload[0], dylibPath, stringAllocSize); // insert extString 373 | 374 | intptr_t payloadIntPtr = (intptr_t)payload; 375 | memcpy((void*)(payloadIntPtr+stringAllocSize), (const void*)&dlopenAddr, sizeof(uint64_t)); // insert vars 376 | 377 | uint64_t codeStart = stringAllocSize + codeVarSize; 378 | memcpy((void*)(payloadIntPtr+codeStart), &code[0], sizeof(code)); // insert code 379 | 380 | printf("constructed dlopen payload!\n"); 381 | 382 | runPayload(task, (uint8_t*)payload, payloadSize, codeStart, allImageInfoAddr); 383 | free(payload); 384 | 385 | // STEP FIVE: detach from process 386 | 387 | /*ret = ptrace(PT_DETACH, pid, NULL, 0); 388 | if(ret != KERN_SUCCESS) 389 | { 390 | printf("Error detaching from process %d\n", pid); 391 | return 1; 392 | }*/ 393 | 394 | return 0; 395 | } -------------------------------------------------------------------------------- /task_utils.h: -------------------------------------------------------------------------------- 1 | kern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size); 2 | char *task_copy_string(task_t task, vm_address_t address); 3 | kern_return_t task_write(task_t task, vm_address_t address, void* inBuf, vm_size_t size); -------------------------------------------------------------------------------- /task_utils.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | kern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size) 5 | { 6 | size_t maxSize = size; 7 | kern_return_t kr = vm_read_overwrite(task, address, size, (vm_address_t)outBuf, &maxSize); 8 | if (kr == KERN_SUCCESS) { 9 | if (maxSize < size) { 10 | uint8_t *outBufU = outBuf; 11 | memset(&outBufU[maxSize-1], 0, size - maxSize); 12 | } 13 | } 14 | return kr; 15 | } 16 | 17 | char *task_copy_string(task_t task, vm_address_t address) 18 | { 19 | // find end of string 20 | size_t len = 0; 21 | char buf = 0; 22 | do { 23 | if (task_read(task, address + (len++), &buf, sizeof(buf)) != KERN_SUCCESS) return NULL; 24 | } while (buf != '\0'); 25 | 26 | // copy string 27 | char *strBuf = malloc(len); 28 | if (task_read(task, address, &strBuf[0], len) != KERN_SUCCESS) return NULL; 29 | return strBuf; 30 | } 31 | 32 | kern_return_t task_write(task_t task, vm_address_t address, void* inBuf, vm_size_t size) 33 | { 34 | return vm_write(task, address, (vm_offset_t)inBuf, size); 35 | } -------------------------------------------------------------------------------- /thread_utils.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | 8 | #define STACK_SIZE 65536 9 | static uint64_t ropLoop; 10 | 11 | #if __DARWIN_OPAQUE_ARM_THREAD_STATE64 12 | #define FP_UNIFIED __opaque_fp 13 | #define LR_UNIFIED __opaque_lr 14 | #define SP_UNIFIED __opaque_sp 15 | #define PC_UNIFIED __opaque_pc 16 | #define FLAGS_UNIFIED __opaque_flags 17 | #define REGISTER_TYPE void* 18 | #else 19 | #define FP_UNIFIED __fp 20 | #define LR_UNIFIED __lr 21 | #define SP_UNIFIED __sp 22 | #define PC_UNIFIED __pc 23 | #define FLAGS_UNIFIED __pad 24 | #define REGISTER_TYPE uint64_t 25 | #endif 26 | 27 | struct arm64_thread_full_state { 28 | arm_thread_state64_t thread; 29 | arm_exception_state64_t exception; 30 | arm_neon_state64_t neon; 31 | arm_debug_state64_t debug; 32 | uint32_t thread_valid:1, 33 | exception_valid:1, 34 | neon_valid:1, 35 | debug_valid:1, 36 | cpmu_valid:1; 37 | }; 38 | 39 | extern struct arm64_thread_full_state* thread_save_state_arm64(thread_act_t thread); 40 | extern bool thread_restore_state_arm64(thread_act_t thread, struct arm64_thread_full_state* state); 41 | extern kern_return_t wait_for_thread(thread_act_t thread, uint64_t pcToWait, struct arm_unified_thread_state* stateOut); 42 | extern void printThreadState_state(struct arm_unified_thread_state threadState); 43 | extern void printThreadState(thread_act_t thread); 44 | extern void printThreadInfo(thread_act_t thread); 45 | kern_return_t suspend_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread); 46 | kern_return_t resume_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread); 47 | -------------------------------------------------------------------------------- /thread_utils.m: -------------------------------------------------------------------------------- 1 | #import "thread_utils.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | struct arm64_thread_full_state* thread_save_state_arm64(thread_act_t thread) 15 | { 16 | struct arm64_thread_full_state* s = malloc(sizeof(struct arm64_thread_full_state)); 17 | mach_msg_type_number_t count; 18 | kern_return_t kr; 19 | 20 | // ARM_THREAD_STATE64 21 | count = ARM_THREAD_STATE64_COUNT; 22 | kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t) &s->thread, &count); 23 | s->thread_valid = (kr == KERN_SUCCESS); 24 | if (kr != KERN_SUCCESS) { 25 | printf("ERROR: Failed to save ARM_THREAD_STATE64 state: %s", mach_error_string(kr)); 26 | free(s); 27 | return NULL; 28 | } 29 | // ARM_EXCEPTION_STATE64 30 | count = ARM_EXCEPTION_STATE64_COUNT; 31 | kr = thread_get_state(thread, ARM_EXCEPTION_STATE64, 32 | (thread_state_t) &s->exception, &count); 33 | s->exception_valid = (kr == KERN_SUCCESS); 34 | if (kr != KERN_SUCCESS) { 35 | printf("WARNING: Failed to save ARM_EXCEPTION_STATE64 state: %s", mach_error_string(kr)); 36 | } 37 | // ARM_NEON_STATE64 38 | count = ARM_NEON_STATE64_COUNT; 39 | kr = thread_get_state(thread, ARM_NEON_STATE64, (thread_state_t) &s->neon, &count); 40 | s->neon_valid = (kr == KERN_SUCCESS); 41 | if (kr != KERN_SUCCESS) { 42 | printf("WARNING: Failed to save ARM_NEON_STATE64 state: %s", mach_error_string(kr)); 43 | } 44 | // ARM_DEBUG_STATE64 45 | count = ARM_DEBUG_STATE64_COUNT; 46 | kr = thread_get_state(thread, ARM_DEBUG_STATE64, (thread_state_t) &s->debug, &count); 47 | s->debug_valid = (kr == KERN_SUCCESS); 48 | if (kr != KERN_SUCCESS) { 49 | printf("WARNING: Failed to save ARM_DEBUG_STATE64 state: %s", mach_error_string(kr)); 50 | } 51 | 52 | return s; 53 | } 54 | 55 | bool thread_restore_state_arm64(thread_act_t thread, struct arm64_thread_full_state* state) 56 | { 57 | struct arm64_thread_full_state *s = (void *) state; 58 | kern_return_t kr; 59 | bool success = true; 60 | // ARM_THREAD_STATE64 61 | if (s->thread_valid) { 62 | kr = thread_set_state(thread, ARM_THREAD_STATE64, (thread_state_t) &s->thread, ARM_THREAD_STATE64_COUNT); 63 | if (kr != KERN_SUCCESS) { 64 | printf("ERROR: Failed to restore ARM_THREAD_STATE64 state: %s", mach_error_string(kr)); 65 | success = false; 66 | } 67 | } 68 | // ARM_EXCEPTION_STATE64 69 | if (s->exception_valid) { 70 | kr = thread_set_state(thread, ARM_EXCEPTION_STATE64, (thread_state_t) &s->exception, ARM_EXCEPTION_STATE64_COUNT); 71 | if (kr != KERN_SUCCESS) { 72 | printf("ERROR: Failed to restore ARM_EXCEPTION_STATE64 state: %s", mach_error_string(kr)); 73 | success = false; 74 | } 75 | } 76 | // ARM_NEON_STATE64 77 | if (s->neon_valid) { 78 | kr = thread_set_state(thread, ARM_NEON_STATE64, (thread_state_t) &s->neon, ARM_NEON_STATE64_COUNT); 79 | if (kr != KERN_SUCCESS) { 80 | printf("ERROR: Failed to restore ARM_NEON_STATE64 state: %s", mach_error_string(kr)); 81 | success = false; 82 | } 83 | } 84 | // ARM_DEBUG_STATE64 85 | if (s->debug_valid) { 86 | kr = thread_set_state(thread, ARM_DEBUG_STATE64, (thread_state_t) &s->debug, ARM_DEBUG_STATE64_COUNT); 87 | if (kr != KERN_SUCCESS) { 88 | printf("ERROR: Failed to restore ARM_DEBUG_STATE64 state: %s", mach_error_string(kr)); 89 | success = false; 90 | } 91 | } 92 | // Now free the struct. 93 | free(s); 94 | return success; 95 | } 96 | 97 | kern_return_t wait_for_thread(thread_act_t thread, uint64_t pcToWait, struct arm_unified_thread_state* stateOut) 98 | { 99 | mach_msg_type_number_t stateToObserveCount = ARM_THREAD_STATE64_COUNT; 100 | struct arm_unified_thread_state stateToObserve; 101 | 102 | int errCount = 0; 103 | while(1) 104 | { 105 | kern_return_t kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&stateToObserve.ts_64, &stateToObserveCount); 106 | if(kr != KERN_SUCCESS) 107 | { 108 | if (pcToWait == 0) return kr; 109 | 110 | errCount++; 111 | if(errCount >= 5) 112 | { 113 | return kr; 114 | } 115 | continue; 116 | } 117 | 118 | errCount = 0; 119 | 120 | // wait until pc matches with infinite loop rop gadget 121 | uint64_t pc = (uint64_t)__darwin_arm_thread_state64_get_pc(stateToObserve.ts_64); 122 | if(pc == pcToWait) { 123 | break; 124 | } 125 | } 126 | 127 | if(stateOut) 128 | { 129 | *stateOut = stateToObserve; 130 | } 131 | 132 | return KERN_SUCCESS; 133 | } 134 | 135 | kern_return_t suspend_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread) 136 | { 137 | for (int i = 0; i < threadCount; i++) { 138 | thread_act_t thread = allThreads[i]; 139 | if (thread != exceptForThread) { 140 | thread_suspend(thread); 141 | } 142 | } 143 | return KERN_SUCCESS; 144 | } 145 | 146 | kern_return_t resume_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread) 147 | { 148 | for (int i = 0; i < threadCount; i++) { 149 | thread_act_t thread = allThreads[i]; 150 | if (thread != exceptForThread) { 151 | thread_resume(thread); 152 | } 153 | } 154 | return KERN_SUCCESS; 155 | } 156 | 157 | void printThreadState_state(struct arm_unified_thread_state threadState) 158 | { 159 | for(int i = 0; i <= 28; i++) 160 | { 161 | printf("x%d = 0x%llX\n", i, threadState.ts_64.__x[i]); 162 | } 163 | //printf("fp: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_fp(threadState.ts_64)); 164 | //printf("lr: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_lr(threadState.ts_64)); 165 | //printf("sp: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_sp(threadState.ts_64)); 166 | //printf("pc: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_pc(threadState.ts_64)); 167 | printf("pc: 0x%llX\n", (uint64_t)threadState.ts_64.PC_UNIFIED); 168 | printf("sp: 0x%llX\n", (uint64_t)threadState.ts_64.SP_UNIFIED); 169 | printf("fp: 0x%llX\n", (uint64_t)threadState.ts_64.FP_UNIFIED); 170 | printf("lr: 0x%llX\n", (uint64_t)threadState.ts_64.LR_UNIFIED); 171 | printf("cpsr: 0x%X\n", threadState.ts_64.__cpsr); 172 | #if __arm64e__ 173 | printf("flags: 0x%X\n", threadState.ts_64.__opaque_flags); 174 | #endif 175 | } 176 | 177 | void printThreadState(thread_act_t thread) 178 | { 179 | printf("- THREAD STATE -\n"); 180 | 181 | mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; 182 | struct arm_unified_thread_state threadState; 183 | kern_return_t kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&threadState.ts_64, &thread_state_count); 184 | if(kr != KERN_SUCCESS) 185 | { 186 | printf("\n"); 187 | return; 188 | } 189 | 190 | printThreadState_state(threadState); 191 | } 192 | 193 | void printThreadInfo(thread_act_t thread) 194 | { 195 | printf("- INFO OF THREAD %d -\n", thread); 196 | 197 | thread_basic_info_data_t basicInfo; 198 | mach_msg_type_number_t biCount = THREAD_BASIC_INFO_COUNT; 199 | kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basicInfo, &biCount); 200 | if(kr != KERN_SUCCESS) 201 | { 202 | printf("\n"); 203 | return; 204 | } 205 | 206 | printf("cpu_usage: %d\n", basicInfo.cpu_usage); 207 | printf("flags: %d\n", basicInfo.flags); 208 | printf("policy: %d\n", basicInfo.policy); 209 | printf("run_state: %d\n", basicInfo.run_state); 210 | printf("sleep_time: %d\n", basicInfo.sleep_time); 211 | printf("suspend_count: %d\n", basicInfo.suspend_count); 212 | 213 | printf("system_time: %d.%d\n", basicInfo.system_time.seconds, basicInfo.system_time.microseconds); 214 | printf("user_time: %d.%d\n", basicInfo.user_time.seconds, basicInfo.user_time.microseconds); 215 | } --------------------------------------------------------------------------------