Full Code of opa334/opainject for AI

main 849bb296ea8b cached
22 files
85.1 KB
24.5k tokens
28 symbols
1 requests
Download .txt
Repository: opa334/opainject
Branch: main
Commit: 849bb296ea8b
Files: 22
Total size: 85.1 KB

Directory structure:
gitextract_p0sncp36/

├── .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

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.theos
packages
.DS_Store
.scrapped

================================================
FILE: CoreSymbolication.h
================================================
//
//  CoreSymbolication.h
//
//  Created by R J Cooper on 05/06/2012.
//  This file: Copyright (c) 2012 Mountainstorm
//  API: Copyright (c) 2008 Apple Inc. All rights reserved.
//  
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//  
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//  
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.
//

//
// Derived by looking at use within the dtrace source and a little bit of IDA work
//
// See the unit testcases for examples of how to use the API; its a really nice symbol
// api, a real shame Apple dont make it a public framework. 
//
// Things you might want to know;
//  - a Symbolicator is a top level object representing the kernel/process etc
//  - a Symbolicator contains multiple SymbolOwners
// 
//  - a SymbolOwner represents a blob which owns symbols e.g. executable, library
//  - a SymbolOwner contains multiple regions and contains multiple symbols
//
//  - a Region represents a continuous block of memory within a symbol owner e.g. the  __TEXT __objc_classname section
//  - a Region contains multiple symbols ... not it doesn't own them, just contains them
//
//  - a Symbol represents a symbol e.g. function, variable
//

#if !defined(__CORESYMBOLICATION_CORESYMBOLICATION__)
#define __CORESYMBOLICATION_CORESYMBOLICATION__ 1
#define __CORESYMBOLICATION__ 1

#ifdef __cplusplus
extern "C" {
#endif

#include <CoreFoundation/CoreFoundation.h>
#include <mach/mach.h>


/*
 * Types
 */
// Under the hood the framework basically just calls through to a set of C++ libraries
struct sCSTypeRef {
	void* csCppData;	// typically retrieved using CSCppSymbol...::data(csData & 0xFFFFFFF8)
	void* csCppObj;		// a pointer to the actual CSCppObject
};
typedef struct sCSTypeRef CSTypeRef;


typedef CSTypeRef CSSymbolicatorRef;
typedef CSTypeRef CSSourceInfoRef;
typedef CSTypeRef CSSymbolOwnerRef;
typedef CSTypeRef CSSectionRef;
typedef CSTypeRef CSSegmentRef;
typedef CSTypeRef CSSymbolRef;
typedef CSTypeRef CSRegionRef;
typedef CSTypeRef CSUUIDRef;


struct sCSRange {
   unsigned long long location;
   unsigned long long length;
};
typedef struct sCSRange CSRange;


// Note: this structure may well be wrong
typedef struct sCSNotificationData {
	CSSymbolicatorRef symbolicator;
	union {
		struct {
			long value;
		} ping;
		
		struct {
			CSSymbolOwnerRef symbolOwner;
		} dyldLoad;
	} u;
} CSNotificationData;


typedef void* CSDictionaryKeyCallBacks;
typedef void* CSDictionaryValueCallBacks;
typedef void* CSSetCallBacks;


typedef int (^CSNotification)(uint32_t notification_type, CSNotificationData data);
typedef int (^CSRegionIterator)(CSRegionRef region);
typedef int (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner);
typedef int (^CSSectionIterator)(CSSectionRef section);
typedef int (^CSSourceInfoIterator)(CSSourceInfoRef sourceInfo);
typedef int (^CSSymbolIterator)(CSSymbolRef symbol);
typedef int (^CSSegmentIterator)(CSSegmentRef segment);


/*
 * Defines
 */
#define kCSNull								((CSTypeRef) {NULL, NULL})
#define kCSNow								0x8000000000000000ull
// we've no idea what value kCSSymbolOwnerDataFoundDsym has; its only use in dtrace has been optimised out
#define kCSSymbolOwnerDataFoundDsym			0
#define kCSSymbolOwnerIsAOut				0
#define kCSSymbolicatorTrackDyldActivity	1

#define kCSNotificationPing					1
#define kCSNotificationInitialized			0x0010
#define kCSNotificationDyldLoad				0x0100
#define kCSNotificationDyldUnload			0x0101
// kCSNotificationTimeout must be a value greater than 0x1001
#define kCSNotificationTimeout				0x1002
#define kCSNotificationTaskExit				0x1000
#define kCSNotificationFini					0x80000000


/*
 * External symbols
 */

extern const char* kCSRegionMachHeaderName;
extern const CSDictionaryKeyCallBacks kCSTypeDictionaryKeyCallBacks;
extern const CSDictionaryValueCallBacks kCSTypeDictionaryValueCallBacks;
extern const CSDictionaryKeyCallBacks kCSTypeDictionaryWeakKeyCallBacks;
extern const CSDictionaryValueCallBacks kCSTypeDictionaryWeakValueCallBacks;
extern const CSSetCallBacks kCSTypeSetCallBacks;
extern const CSSetCallBacks kCSTypeSetWeakCallBacks;


/*
 * Architecture functions
 */
// Valid names: i386, x86_64, arm, armv4t, armv5tej, armv6, armv7, armv7f, armv7k, ppc, ppc64
cpu_type_t CSArchitectureGetArchitectureForName(const char* arch);
cpu_type_t CSArchitectureGetCurrent();
cpu_type_t CSArchitectureGetFamily(cpu_type_t type);
const char* CSArchitectureGetFamilyName(cpu_type_t type);

Boolean CSArchitectureIs32Bit(cpu_type_t type);
Boolean CSArchitectureIs64Bit(cpu_type_t type);
Boolean CSArchitectureIsArm(cpu_type_t type);
Boolean CSArchitectureIsBigEndian(cpu_type_t type);
Boolean CSArchitectureIsI386(cpu_type_t type);
Boolean CSArchitectureIsLittleEndian(cpu_type_t type);
Boolean CSArchitectureIsPPC(cpu_type_t type);
Boolean CSArchitectureIsPPC64(cpu_type_t type);
Boolean CSArchitectureIsX86_64(cpu_type_t type);

Boolean CSArchitectureMatchesArchitecture(cpu_type_t a, cpu_type_t b);


/*
 * Description functions
 */
CFStringRef CSCopyDescription(CSTypeRef cs);
CFStringRef CSCopyDescriptionWithIndent(CSTypeRef cs, unsigned int indent);


/*
 * General utility functions
 */
Boolean CSEqual(CSTypeRef cs1, CSTypeRef cs2);
//XXX: CSExceptionSafeThreadRunBlock
CFIndex CSGetRetainCount(CSTypeRef cs);
Boolean CSIsNull(CSTypeRef cs);
CSTypeRef CSRetain(CSTypeRef cs);
void CSRelease(CSTypeRef cs);
void CSShow(CSTypeRef cs);


/*
 * Dyld functions
 */
vm_address_t CSGetDyldSharedCacheSlide(mach_port_t port);
CSUUIDRef CSGetDyldSharedCacheUUID(mach_port_t port);


/*
 * XXX: Map functions
 */
//CSMMapArchiveCacheCopyMMapArchive
//CSMMapArchiveCacheReleaseMMapArchive
//CSMMapArchiveCacheSetShouldStoreToDaemon


/*
 * Range functions
 */
Boolean CSRangeContainsRange(CSRange r1, CSRange r2);
Boolean CSRangeIntersectsRange(CSRange r1, CSRange r2);


/*
 * Region functions
 */
CFStringRef CSRegionCopyDescriptionWithIndent(CSRegionRef region, unsigned int indent);
int CSRegionForeachSourceInfo(CSRegionRef region, CSSourceInfoIterator each);
int CSRegionForeachSymbol(CSRegionRef region, CSSymbolIterator each);
const char* CSRegionGetName(CSRegionRef region);
CSRange CSRegionGetRange(CSRegionRef region);
CSSymbolOwnerRef CSRegionGetSymbolOwner(CSRegionRef region);
CSSymbolicatorRef CSRegionGetSymbolicator(CSRegionRef region);


/*
 * XXX: Section/Segment functions
 */
/*
CSSectionGetSegment
CSSegmentForeachSection
*/


/*
 * XXX: Signature functions
 */
/*
CSSignatureAddSegment
CSSignatureAllocateSegments
CSSignatureCopy
CSSignatureEncodeSymbolOwner
CSSignatureEncodeSymbolicator
CSSignatureFreeSegments
*/


/*
 * Source Info functions
 */
CFStringRef CSSourceInfoCopyDescriptionWithIndent(CSSourceInfoRef info, unsigned int indent);
int CSSourceInfoGetColumn(CSSourceInfoRef info);
const char* CSSourceInfoGetFilename(CSSourceInfoRef info);
int CSSourceInfoGetLineNumber(CSSourceInfoRef info);
const char* CSSourceInfoGetPath(CSSourceInfoRef info);
CSRange CSSourceInfoGetRange(CSSourceInfoRef info);
CSRegionRef CSSourceInfoGetRegion(CSSourceInfoRef info);
CSSymbolRef CSSourceInfoGetSymbol(CSSourceInfoRef info);
CSSymbolOwnerRef CSSourceInfoGetSymbolOwner(CSSourceInfoRef info);
CSSymbolicatorRef CSSourceInfoGetSymbolicator(CSSourceInfoRef info);


/*
 * Symbol functions
 */

CFStringRef CSSymbolCopyDescriptionWithIndent(CSSymbolRef sym, unsigned int indent);
int CSSymbolForeachSourceInfo(CSSymbolRef sym, CSSourceInfoIterator);
long CSSymbolGetFlags(CSSymbolRef sym);
CSTypeRef CSSymbolGetInstructionData(CSSymbolRef sym);
const char* CSSymbolGetMangledName(CSSymbolRef sym);
const char* CSSymbolGetName(CSSymbolRef sym);
CSRange CSSymbolGetRange(CSSymbolRef sym);
CSRegionRef CSSymbolGetRegion(CSSymbolRef sym);
CSSectionRef CSSymbolGetSection(CSSymbolRef sym);
CSSegmentRef CSSymbolGetSegment(CSSymbolRef sym);
CSSymbolOwnerRef CSSymbolGetSymbolOwner(CSSymbolRef sym);
CSSymbolicatorRef CSSymbolGetSymbolicator(CSSymbolRef sym);
Boolean CSSymbolIsArm(CSSymbolRef sym);
Boolean CSSymbolIsDebugMap(CSSymbolRef sym);
Boolean CSSymbolIsDwarf(CSSymbolRef sym);
Boolean CSSymbolIsDyldStub(CSSymbolRef sym);
Boolean CSSymbolIsExternal(CSSymbolRef sym);
Boolean CSSymbolIsFunction(CSSymbolRef sym);
Boolean CSSymbolIsFunctionStarts(CSSymbolRef sym);
Boolean CSSymbolIsKnownLength(CSSymbolRef sym);
Boolean CSSymbolIsMangledNameSourceDwarf(CSSymbolRef sym);
Boolean CSSymbolIsMangledNameSourceDwarfMIPSLinkage(CSSymbolRef sym);
Boolean CSSymbolIsMangledNameSourceNList(CSSymbolRef sym);
Boolean CSSymbolIsMerged(CSSymbolRef sym);
Boolean CSSymbolIsNList(CSSymbolRef sym);
Boolean CSSymbolIsNameSourceDwarf(CSSymbolRef sym);
Boolean CSSymbolIsNameSourceDwarfMIPSLinkage(CSSymbolRef sym);
Boolean CSSymbolIsNameSourceNList(CSSymbolRef sym);
Boolean CSSymbolIsObjcMethod(CSSymbolRef sym);
Boolean CSSymbolIsOmitFramePointer(CSSymbolRef sym);
Boolean CSSymbolIsPrivateExternal(CSSymbolRef sym);
Boolean CSSymbolIsThumb(CSSymbolRef sym);
Boolean CSSymbolIsUnnamed(CSSymbolRef sym);


/*
 * XXX: SymbolOwner functions
 */
/*
CSSymbolOwnerAddInContext
CSSymbolOwnerCacheFlush
CSSymbolOwnerCacheGetEntryCount
CSSymbolOwnerCacheGetFlags
CSSymbolOwnerCacheGetMemoryLimit
CSSymbolOwnerCacheGetMemoryUsed
CSSymbolOwnerCachePrintEntries
CSSymbolOwnerCachePrintStats
CSSymbolOwnerCacheResetStats
CSSymbolOwnerCacheSetFlags
CSSymbolOwnerCacheSetMemoryLimit
CSSymbolOwnerCopyDescriptionWithIndent
CSSymbolOwnerCreateSignature
CSSymbolOwnerEditRelocations
CSSymbolOwnerForeachRegion
CSSymbolOwnerForeachRegionWithName
CSSymbolOwnerForeachSection
CSSymbolOwnerForeachSegment
CSSymbolOwnerForeachSourceInfo
CSSymbolOwnerForeachSymbol
CSSymbolOwnerForeachSymbolWithMangledName
CSSymbolOwnerForeachSymbolWithName
CSSymbolOwnerGetArchitecture
CSSymbolOwnerGetBaseAddress
CSSymbolOwnerGetCompatibilityVersion
CSSymbolOwnerGetCurrentVersion
CSSymbolOwnerGetDataFlags
CSSymbolOwnerGetDataTypeID
CSSymbolOwnerGetDsymPath
CSSymbolOwnerGetDsymVersion
CSSymbolOwnerGetFlags
CSSymbolOwnerGetLastModifiedTimestamp
CSSymbolOwnerGetLoadTimestamp
CSSymbolOwnerGetName
CSSymbolOwnerGetPath
CSSymbolOwnerGetRegionCount
CSSymbolOwnerGetRegionWithAddress
CSSymbolOwnerGetRegionWithName
CSSymbolOwnerGetSectionWithAddress
CSSymbolOwnerGetSectionWithName
CSSymbolOwnerGetSegmentWithAddress
CSSymbolOwnerGetSourceInfoCount
CSSymbolOwnerGetSourceInfoWithAddress
CSSymbolOwnerGetSymbolCount
CSSymbolOwnerGetSymbolWithAddress
CSSymbolOwnerGetSymbolWithMangledName
CSSymbolOwnerGetSymbolWithName
CSSymbolOwnerGetSymbolicator
CSSymbolOwnerGetTransientUserData
CSSymbolOwnerGetUUID
CSSymbolOwnerGetUnloadTimestamp
CSSymbolOwnerGetVersion
CSSymbolOwnerIsAOut
CSSymbolOwnerIsBundle
CSSymbolOwnerIsCommpage
CSSymbolOwnerIsDsym
CSSymbolOwnerIsDyld
CSSymbolOwnerIsDyldSharedCache
CSSymbolOwnerIsDylib
CSSymbolOwnerIsDylibStub
CSSymbolOwnerIsKextBundle
CSSymbolOwnerIsMachO
CSSymbolOwnerIsMutable
CSSymbolOwnerIsObjCGCSupported
CSSymbolOwnerIsObjCRetainReleaseSupported
CSSymbolOwnerIsObject
CSSymbolOwnerIsObsolete
CSSymbolOwnerIsPIE
CSSymbolOwnerIsProtected
CSSymbolOwnerIsRestricted
CSSymbolOwnerIsSlid
CSSymbolOwnerIsStaticLibraryArchiveEntry
CSSymbolOwnerMakeMutableInContext
CSSymbolOwnerRemoveInContext
CSSymbolOwnerSetLoadTimestamp
CSSymbolOwnerSetPath
CSSymbolOwnerSetRelocationCount
CSSymbolOwnerSetTransientUserData
CSSymbolOwnerSetUnloadTimestamp
*/

const char *CSSymbolOwnerGetPath(CSSymbolOwnerRef owner);

/*
 * XXX: Symbolicator functions
 */
// XXX: CSSymbolicatorAddSymbolOwner
// XXX: CSSymbolicatorApplyMutableContextBlock
CFStringRef CSSymbolicatorCopyDescriptionWithIndent(CSSymbolicatorRef cs, unsigned int indent);
CFDataRef CSSymbolicatorCreateSignature(CSSymbolicatorRef cs);

CSSymbolicatorRef CSSymbolicatorCreateWithMachKernel(void);
CSSymbolicatorRef CSSymbolicatorCreateWithMachKernelFlagsAndNotification(long flags, CSNotification notification);
CSSymbolicatorRef CSSymbolicatorCreateWithPathAndArchitecture(const char* path, cpu_type_t type);
CSSymbolicatorRef CSSymbolicatorCreateWithPathArchitectureFlagsAndNotification(const char* path, cpu_type_t type, long flags, CSNotification notification);
CSSymbolicatorRef CSSymbolicatorCreateWithPid(pid_t pid);
CSSymbolicatorRef CSSymbolicatorCreateWithPidFlagsAndNotification(pid_t pid, long flags, CSNotification notification);
CSSymbolicatorRef CSSymbolicatorCreateWithSignature(CFDataRef sig);
CSSymbolicatorRef CSSymbolicatorCreateWithSignatureAndNotification(CFDataRef sig, CSNotification notification);
CSSymbolicatorRef CSSymbolicatorCreateWithTask(task_t task);
CSSymbolicatorRef CSSymbolicatorCreateWithTaskFlagsAndNotification(task_t task, long flags, CSNotification notification);
CSSymbolicatorRef CSSymbolicatorCreateWithURLAndArchitecture(CFURLRef url, cpu_type_t type);
CSSymbolicatorRef CSSymbolicatorCreateWithURLArchitectureFlagsAndNotification(CFURLRef url, cpu_type_t type, long flags, CSNotification notification);

int CSSymbolicatorForceFullSymbolExtraction(CSSymbolicatorRef cs);
int CSSymbolicatorForeachRegionAtTime(CSSymbolicatorRef cs, uint64_t time, CSRegionIterator it);
int CSSymbolicatorForeachRegionWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSRegionIterator it);
int CSSymbolicatorForeachSectionAtTime(CSSymbolicatorRef cs, uint64_t time, CSSectionIterator it);
int CSSymbolicatorForeachSegmentAtTime(CSSymbolicatorRef cs, uint64_t time, CSSegmentIterator it);
// XXX: CSSymbolicatorForeachSharedCache
// XXX: CSSymbolicatorForeachSharedCacheSymbolicatorWithFlagsAndNotification
int CSSymbolicatorForeachSourceInfoAtTime(CSSymbolicatorRef cs, uint64_t time, CSSourceInfoIterator it);
int CSSymbolicatorForeachSymbolAtTime(CSSymbolicatorRef cs, uint64_t time, CSSymbolIterator it);
int CSSymbolicatorForeachSymbolOwnerAtTime(CSSymbolicatorRef cs, uint64_t time, CSSymbolOwnerIterator it);

// XXX: CSSymbolicatorForeachSymbolOwnerWithCFUUIDBytesAtTime
int CSSymbolicatorForeachSymbolOwnerWithFlagsAtTime(CSSymbolicatorRef symbolicator, long flags, uint64_t time, CSSymbolOwnerIterator it);
int CSSymbolicatorForeachSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolOwnerIterator it);
int CSSymbolicatorForeachSymbolOwnerWithPathAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolOwnerIterator it);
// XXX: CSSymbolicatorForeachSymbolOwnerWithUUIDAtTime
int CSSymbolicatorForeachSymbolWithMangledNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolIterator it);
int CSSymbolicatorForeachSymbolWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time, CSSymbolIterator it);
// XXX: CSSymbolicatorForeachSymbolicatorWithPath
// XXX: CSSymbolicatorForeachSymbolicatorWithPathFlagsAndNotification
// XXX: CSSymbolicatorForeachSymbolicatorWithURL
// XXX: CSSymbolicatorForeachSymbolicatorWithURLFlagsAndNotification

CSSymbolOwnerRef CSSymbolicatorGetAOutSymbolOwner(CSSymbolicatorRef cs);
cpu_type_t CSSymbolicatorGetArchitecture(CSSymbolicatorRef cs);
vm_address_t CSSymbolicatorGetDyldAllImageInfosAddress(CSSymbolicatorRef cs);

long CSSymbolicatorGetFlagsForDebugMapOnlyData(void);
long CSSymbolicatorGetFlagsForDsymOnlyData(void);
long CSSymbolicatorGetFlagsForDwarfOnlyData(void);
long CSSymbolicatorGetFlagsForFunctionStartsOnlyData(void);
long CSSymbolicatorGetFlagsForNListOnlyData(void);
long CSSymbolicatorGetFlagsForNoSymbolOrSourceInfoData(void);

pid_t CSSymbolicatorGetPid(CSSymbolicatorRef cs);
int CSSymbolicatorGetRegionCountAtTime(CSSymbolicatorRef cs, uint64_t time);
CSRegionRef CSSymbolicatorGetRegionWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);
CSRegionRef CSSymbolicatorGetRegionWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);
CSSectionRef CSSymbolicatorGetSectionWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);
CSSegmentRef CSSymbolicatorGetSegmentWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);
vm_address_t CSSymbolicatorGetSharedCacheSlide(CSSymbolicatorRef cs);
CSUUIDRef CSSymbolicatorGetSharedCacheUUID(CSSymbolicatorRef cs);
int CSSymbolicatorGetSourceInfoCountAtTime(CSSymbolicatorRef cs, uint64_t time);
CSSourceInfoRef CSSymbolicatorGetSourceInfoWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);
int CSSymbolicatorGetSymbolCountAtTime(CSSymbolicatorRef cs, uint64_t time);
CSSymbolOwnerRef CSSymbolicatorGetSymbolOwner(CSSymbolicatorRef cs);
int CSSymbolicatorGetSymbolOwnerCountAtTime(CSSymbolicatorRef cs, uint64_t time);
CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);
// XXX: CSSymbolicatorGetSymbolOwnerWithCFUUIDBytesAtTime
CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);
CSSymbolOwnerRef CSSymbolicatorGetSymbolOwnerWithUUIDAtTime(CSSymbolicatorRef symbolicator, CFUUIDRef uuid, uint64_t time);
CSSymbolRef CSSymbolicatorGetSymbolWithAddressAtTime(CSSymbolicatorRef cs, vm_address_t addr, uint64_t time);
CSSymbolRef CSSymbolicatorGetSymbolWithMangledNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);
CSSymbolRef CSSymbolicatorGetSymbolWithMangledNameFromSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, CSSymbolOwnerRef owner, const char* name, uint64_t time);
CSSymbolRef CSSymbolicatorGetSymbolWithNameAtTime(CSSymbolicatorRef cs, const char* name, uint64_t time);
CSSymbolRef CSSymbolicatorGetSymbolWithNameFromSymbolOwnerWithNameAtTime(CSSymbolicatorRef cs, CSSymbolOwnerRef owner, const char* name, uint64_t time);
mach_port_t CSSymbolicatorGetTask(CSSymbolicatorRef cs);
Boolean CSSymbolicatorIsKernelSymbolicator(CSSymbolicatorRef cs);
Boolean CSSymbolicatorIsTaskTranslated(CSSymbolicatorRef cs);
Boolean CSSymbolicatorIsTaskValid(CSSymbolicatorRef cs);
void CSSymbolicatorResymbolicate(CSSymbolicatorRef cs);
void CSSymbolicatorResymbolicateFail(CSSymbolicatorRef cs);
int CSSymbolicatorSetForceGlobalSafeMachVMReads(CSSymbolicatorRef cs);


/*
 * XXX: CSUUID
 */
 /*
CSUUIDCFUUIDBytesToPath
CSUUIDCFUUIDBytesToString
CSUUIDStringToCFUUIDBytes
*/




/*
 * SymbolOwner functions
 */
const char* CSSymbolOwnerGetPath(CSSymbolOwnerRef symbol);
const char* CSSymbolOwnerGetName(CSSymbolOwnerRef symbol);
vm_address_t CSSymbolOwnerGetBaseAddress(CSSymbolOwnerRef owner);
cpu_type_t CSSymbolOwnerGetArchitecture(CSSymbolOwnerRef owner);
Boolean CSSymbolOwnerIsObject(CSSymbolOwnerRef owner);
long CSSymbolOwnerGetDataFlags(CSSymbolOwnerRef owner);
CSRegionRef CSSymbolOwnerGetRegionWithName(CSSymbolOwnerRef owner, const char* name);
CSSymbolRef CSSymbolOwnerGetSymbolWithName(CSSymbolOwnerRef owner, const char* name);
CSSymbolRef CSSymbolOwnerGetSymbolWithAddress(CSSymbolOwnerRef owner, mach_vm_address_t addr);

long CSSymbolOwnerForeachSymbol(CSSymbolOwnerRef owner, CSSymbolIterator each);

CFUUIDBytes *CSSymbolOwnerGetCFUUIDBytes(CSSymbolOwnerRef owner);

/* Other exports

__crashreporter_info__
clear_mapped_memory
create_mapped_memory_cache_for_task
create_sampling_context_for_task
demangle
destroy_mapped_memory_cache
destroy_sampling_context
dispatch_queue_name_for_serial_number
find_node
fixup_frames
get_remote_thread_dispatch_queue

map_new_node
mapped_memory_read
mapped_memory_read_pointer
next_node
sample_remote_thread
sample_remote_thread_with_dispatch_queue
sampling_context_clear_cache
task_is_64bit
thread_name_for_thread_port
*/

#ifdef __cplusplus
} // extern "C"
#endif

#endif /* ! __CORESYMBOLICATION_CORESYMBOLICATION__ */

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022 Lars Fröder

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
TARGET := iphone:clang:16.5:11.0
ARCHS = arm64 arm64e

include $(THEOS)/makefiles/common.mk

TOOL_NAME = opainject

opainject_FILES = main.m dyld.m shellcode_inject.m rop_inject.m thread_utils.m task_utils.m arm64.m
opainject_CFLAGS = -fobjc-arc -DTHEOS_LEAN_AND_MEAN
opainject_CODESIGN_FLAGS = -Sentitlements.plist
opainject_INSTALL_PATH = /usr/local/bin
opainject_PRIVATE_FRAMEWORKS = CoreSymbolication

include $(THEOS_MAKE_PATH)/tool.mk


================================================
FILE: README.md
================================================
# opainject

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).

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.

================================================
FILE: arm64.h
================================================
#ifndef arm64_h
#define arm64_h

#include <stdio.h>
#include <stdbool.h>

uint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl);
uint32_t generate_br(uint8_t x);
bool decode_adrp(uint32_t inst, uint8_t *rd_out, int32_t *imm_out);
bool decode_ldr_imm(uint32_t inst, uint16_t *imm_out, uint8_t *rn_out, uint8_t *rt_out);
bool decode_adrp_ldr(uint32_t adrp_inst, uint32_t ldr_inst, uint64_t pc, uint64_t *dst_out);

#endif /* arm64_h */


================================================
FILE: arm64.m
================================================
#include "arm64.h"
#include <stdbool.h>

uint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl)
{
	uint32_t base = 0b11110010100000000000000000000000;

	uint32_t hw = 0;
	if (lsl == 16) {
		hw = 0b01 << 21;
	}
	else if (lsl == 32) {
		hw = 0b10 << 21;
	}
	else if (lsl == 48) {
		hw = 0b11 << 21;
	}

	uint32_t imm16 = (uint32_t)val << 5;
	uint32_t rd = x & 0x1F;

	return base | hw | imm16 | rd;
}

uint32_t generate_br(uint8_t x)
{
	uint32_t base = 0b11010110000111110000000000000000;
	uint32_t rn = ((uint32_t)x & 0x1F) << 5;
	return base | rn;
}

bool decode_adrp(uint32_t inst, uint8_t *rd_out, int32_t *imm_out)
{
	if ((inst & 0x9F000000) != 0x90000000) return false;
	
	uint32_t mask_immlo = 0b01100000000000000000000000000000;
	uint32_t mask_immhi = 0b00000000111111111111111111100000;
	uint32_t mask_rd    = 0b00000000000000000000000000011111;
	
	int32_t imm = (((inst & mask_immlo) >> 29) | ((inst & mask_immhi) >> 3)) << 12;
	uint8_t rd = inst & mask_rd;
	
	if (rd_out) *rd_out = rd;
	if (imm_out) *imm_out = imm;
	
	return true;
}

bool decode_ldr_imm(uint32_t inst, uint16_t *imm_out, uint8_t *rn_out, uint8_t *rt_out)
{
	if ((inst & 0xBFC00000) != 0xB9400000) return false;
	// TODO: Support non unsigned instructions
	
	uint32_t mask_imm12 = 0b00000000001111111111110000000000;
	uint32_t mask_rn    = 0b00000000000000000000001111100000;
	uint32_t mask_rt    = 0b00000000000000000000000000011111;
	
	uint8_t  rt    = (inst & mask_rt);
	uint8_t  rn    = (inst & mask_rn) >> 5;
	uint16_t imm12 = (inst & mask_imm12) >> 10;
	
	uint32_t bit_is_64_bit = 0b01000000000000000000000000000000;
	if (inst & bit_is_64_bit) {
		imm12 *= 8;
	}
	else {
		imm12 *= 4;
	}
	
	if (imm_out) *imm_out = imm12;
	if (rn_out) *rn_out = rn;
	if (rt_out) *rt_out = rt;
	
	return true;
}

bool decode_adrp_ldr(uint32_t adrp_inst, uint32_t ldr_inst, uint64_t pc, uint64_t *dst_out)
{
	int32_t adrp_imm = 0;
	if (!decode_adrp(adrp_inst, NULL, &adrp_imm)) return false;
	
	uint16_t ldr_imm = 0;
	if (!decode_ldr_imm(ldr_inst, &ldr_imm, NULL, NULL)) return false;
	
	uint64_t pc_page = pc - (pc % 0x1000);
	uint64_t dst = (pc_page + adrp_imm) + ldr_imm;
	if (dst_out) *dst_out = dst;
	return true;
}

/*bool decode_ldr(uint32_t inst, uint8_t *rt_out, uint8_t *rn_out, uint8_t *rm_out)
{
	if ((inst & 0xFFE00C00) != 0xF8600800) return false;
	
	uint32_t mask_rm     = 0b00000000000111110000000000000000;
	uint32_t mask_option = 0b00000000000000001110000000000000;
	uint32_t mask_s      = 0b00000000000000000001000000000000;
	uint32_t mask_rn     = 0b00000000000000000000001111100000;
	uint32_t mask_rt     = 0b00000000000000000000000000011111;
	
	uint8_t rt     = inst & mask_rt;
	uint8_t rn     = inst & mask_rn >> 5;
	uint8_t rm     = inst & mask_rm >> 16;
	bool    s      = inst & mask_s >> 12;
	uint8_t option = inst & mask_option >> 13;
	
	printf("rt=%d, rn=%d, rm=%d, s=%d, option=%X\n", rt, rn, rm, s, option);
	
	return true;
}*/


================================================
FILE: control
================================================
Package: com.opa334.opainject
Name: opainject
Version: 1.0.6
Architecture: iphoneos-arm
Description: Injection tool!
Maintainer: opa334
Author: opa334
Section: System
Tag: role::hacker


================================================
FILE: dyld.h
================================================
extern vm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imageName);
extern vm_address_t remoteDlSym(task_t task, vm_address_t imageStartPtr, const char* symbolName);
extern vm_address_t remoteDlSymFindImage(task_t task, vm_address_t allImageInfoAddr, const char* symbolName, char** imageOut);

extern void printImages(task_t task, vm_address_t imageStartPtr);
extern void printSymbols(task_t task, vm_address_t imageAddress);
extern vm_address_t scanLibrariesForMemory(task_t task, vm_address_t imageStartPtr, char* memory, size_t memorySize, int alignment);

================================================
FILE: dyld.m
================================================
#import <stdio.h>
#import <stdlib.h>
#import <unistd.h>
#import <dlfcn.h>
#import <mach-o/getsect.h>
#import <mach-o/dyld.h>
#import <mach/mach.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#import <mach-o/reloc.h>
#import <mach-o/dyld_images.h>
#import <sys/utsname.h>
#import <string.h>
#import <limits.h>
#import "task_utils.h"

#import <CoreFoundation/CoreFoundation.h>

void iterateImages(task_t task, vm_address_t imageStartPtr, void (^iterateBlock)(char*, struct dyld_image_info*, BOOL*))
{
	struct dyld_all_image_infos imageInfos;
	task_read(task, imageStartPtr, &imageInfos, sizeof(imageInfos));

	size_t infoArraySize = sizeof(struct dyld_image_info) * imageInfos.infoArrayCount;
	struct dyld_image_info *infoArray = malloc(infoArraySize);
	task_read(task, (vm_address_t)imageInfos.infoArray, &infoArray[0], infoArraySize);

	for(int i = 0; i < imageInfos.infoArrayCount; i++)
	{
		@autoreleasepool
		{
			char currentImagePath[PATH_MAX];
			if(task_read(task, (vm_address_t)infoArray[i].imageFilePath, &currentImagePath[0], sizeof(currentImagePath)) == KERN_SUCCESS)
			{
				BOOL stop = NO;
				iterateBlock(currentImagePath, &infoArray[i], &stop);
				if(stop) break;
			}
		}
	}
}

vm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imagePath)
{
	__block vm_address_t outAddr = 0;

	iterateImages(task, imageStartPtr, ^(char* iterImagePath, struct dyld_image_info* imageInfo, BOOL* stop)
	{
		if(strcmp(iterImagePath, imagePath) == 0)
		{
			outAddr = (vm_address_t)imageInfo->imageLoadAddress;
		}
	});

	return outAddr;
}

void iterateLoadCommands(task_t task, vm_address_t imageAddress, void (^iterateBlock)(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stop))
{
	struct mach_header_64 mh;
	task_read(task, imageAddress, &mh, sizeof(mh));

	vm_address_t lcStart = (imageAddress + sizeof(struct mach_header_64));
	vm_address_t lcEnd = lcStart + mh.sizeofcmds;

	vm_address_t lcCur = lcStart;
	for(int ci = 0; ci < mh.ncmds && lcCur <= lcEnd; ci++)
	{
		struct load_command loadCommand;
		task_read(task, lcCur, &loadCommand, sizeof(loadCommand));
		BOOL stop = NO;
		iterateBlock(&loadCommand, lcCur, &stop);
		lcCur = lcCur + loadCommand.cmdsize;
		if(stop) break;
	}
}

void iterateSections(task_t task, vm_address_t commandAddress, const struct segment_command_64* segmentCommand, void (^iterateBlock)(const struct section_64*, BOOL*))
{
	if(segmentCommand->nsects == 0) return;

	vm_address_t sectionStart = commandAddress + sizeof(struct segment_command_64);
	for(int i = 0; i < segmentCommand->nsects; i++)
	{
		struct section_64 section = { 0 };
		if (task_read(task, sectionStart + i * sizeof(section), &section, sizeof(section)) == KERN_SUCCESS) {
			BOOL stop;
			iterateBlock(&section, &stop);
			if(stop) break;
		}
	}
}

void iterateSymbols(task_t task, vm_address_t imageAddress, void (^iterateBlock)(const char*, const char*, vm_address_t, BOOL*))
{
	__block struct segment_command_64 __linkedit = { 0 };
	__block struct symtab_command symtabCommand = { 0 };

	__block vm_address_t slide;
	__block BOOL firstSegmentCommand = YES;

	iterateLoadCommands(task, imageAddress, ^(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stop)
	{
		switch(cmd->cmd)
		{
			case LC_SYMTAB:
			{
				task_read(task, cmdAddress, &symtabCommand, sizeof(symtabCommand));
				break;
			}

			case LC_SEGMENT_64:
			{
				struct segment_command_64 segmentCommand;
				task_read(task, cmdAddress, &segmentCommand, sizeof(segmentCommand));
				if(firstSegmentCommand) {
					slide = imageAddress - segmentCommand.vmaddr;
					firstSegmentCommand = NO;
				}
				if (strncmp("__LINKEDIT", segmentCommand.segname, 16) == 0) {
					__linkedit = segmentCommand;
				}
				break;
			}
		}
	});

	if(__linkedit.cmd != LC_SEGMENT_64)
	{
		printf("ERROR: __LINKEDIT not found\n");
		return;
	}
	if(symtabCommand.cmd != LC_SYMTAB)
	{
		printf("ERROR: symtab command not found\n");
		return;
	}

	uint64_t fileoff = __linkedit.fileoff;
	uint64_t vmaddr = __linkedit.vmaddr;

	vm_address_t baseAddr = vmaddr + slide - fileoff;

	vm_address_t strtblAddr = baseAddr + symtabCommand.stroff;
	size_t strtblSize = symtabCommand.strsize;
	char *strtbl = malloc(strtblSize);
	task_read(task, strtblAddr, &strtbl[0], strtblSize);
	vm_address_t lAddr = baseAddr + symtabCommand.symoff;
	for (uint32_t s = 0; s < symtabCommand.nsyms; s++)
	{
		vm_address_t entryAddr = lAddr + sizeof(struct nlist_64) * s;

		struct nlist_64 entry = { 0 };
		task_read(task, entryAddr, &entry, sizeof(entry));

		uint32_t off = entry.n_un.n_strx;
		if (off >= strtblSize || off == 0) {
			continue;
		}

		const char* sym = &strtbl[off];
		if (sym[0] == '\x00')
		{
			continue;
		}

		const char* type = NULL;
		switch(entry.n_type & N_TYPE) {
			case N_UNDF: type = "N_UNDF"; break;
			case N_ABS:  type = "N_ABS"; break;
			case N_SECT: type = "N_SECT"; break;
			case N_PBUD: type = "N_PBUD"; break;
			case N_INDR: type = "N_INDR"; break;
		}

		BOOL stop = NO;
		iterateBlock(sym, type, entry.n_value + slide, &stop);
		if(stop)
		{
			break;
		}
	}

	free(strtbl);
}

vm_address_t remoteDlSym(task_t task, vm_address_t imageAddress, const char* symbolName)
{
	__block vm_address_t outAddr = 0;

	iterateSymbols(task, imageAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop)
	{
		if(strcmp(type, "N_SECT") == 0)
		{
			if(strcmp(iterSymbolName, symbolName) == 0)
			{
				outAddr = value;
			}
		}
	});

	return outAddr;
}

vm_address_t remoteDlSymFindImage(task_t task, vm_address_t allImageInfoAddr, const char* symbolName, char** imageOut)
{
	__block vm_address_t outAddr = 0;
	__block BOOL* stop1_b;

	iterateImages(task, allImageInfoAddr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stop1)
	{
		stop1_b = stop1;
		iterateSymbols(task, (vm_address_t)imageInfo->imageLoadAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop2)
		{
			if(strcmp(type, "N_SECT") == 0)
			{
				if(strcmp(iterSymbolName, symbolName) == 0)
				{
					outAddr = value;
					if(imageOut)
					{
						*imageOut = strdup(imageFilePath);
					}
					*stop2 = YES;
					*stop1_b = YES;
				}
			}
		});
	});
	return outAddr;
}

void printImages(task_t task, vm_address_t imageStartPtr)
{
	iterateImages(task, imageStartPtr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stop)
	{
		printf("Image %s - %llX\n", imageFilePath, (uint64_t)imageInfo->imageLoadAddress);
	});
}

void printSymbols(task_t task, vm_address_t imageAddress)
{
	iterateSymbols(task, imageAddress, ^(const char* iterSymbolName, const char* type, vm_address_t value, BOOL* stop)
	{
		if(strcmp(type, "N_SECT") == 0)
		{
			printf("Symbol %s - %llX\n", iterSymbolName, (uint64_t)value);
		}
	});
}

vm_address_t scanMemory(task_t task, vm_address_t begin, size_t size, char* memory, size_t memorySize, int alignment)
{
	//printf("scanMemory(%llX, %ld)\n", (uint64_t)begin, size);

	if(alignment == 0) alignment = 1;

	unsigned char *buf = malloc(size);
	if(task_read(task, begin, &buf[0], size) != KERN_SUCCESS)
	{
		printf("[scanMemory] WARNING: Failed to read process memory (%llX, size:%llX)\n", (uint64_t)begin, (uint64_t)size);
		if(buf) free(buf);
		return 0;
	}

	vm_address_t foundMemoryAbsoluteFinal = 0;
	vm_address_t lastFoundMemory = 0;
	while(1)
	{
		unsigned char* foundMemory = memmem(buf + lastFoundMemory, size - lastFoundMemory, memory, memorySize);
		if(foundMemory != NULL)
		{
			lastFoundMemory = foundMemory - buf + memorySize;

			vm_address_t foundMemoryAbsolute = (vm_address_t)(begin + (vm_address_t)(foundMemory - buf));
			//printf("foundMemory absolute: %llX\n", (uint64_t)foundMemoryAbsolute);

			int rest = foundMemoryAbsolute % alignment;
			//printf("rest: %d\n", rest);
			if(rest == 0)
			{
				foundMemoryAbsoluteFinal = foundMemoryAbsolute;
				break;
			}
			continue;
		}
		break;
	}

	free(buf);
	return foundMemoryAbsoluteFinal;
}

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)
{
	uint64_t begin = textCmd->vmaddr + slide;
	//printf("- TEXT: %llX -> %llX, %d -\n", begin, begin + textCmd->vmsize, textCmd->nsects);
	vm_address_t mainTextScan = scanMemory(task, begin, textCmd->vmsize, memory, memorySize, alignment);
	if(mainTextScan != 0) return mainTextScan;

	__block vm_address_t sectFound = 0;
	iterateSections(task, commandAddress, textCmd, ^(const struct section_64* sect, BOOL* stop)
	{
		uint64_t sectBegin = sect->addr + slide;
		//printf("-- %s %llX -> %llX --\n", sect->sectname, sectBegin, sectBegin + sect->size);
		if(strcmp(sect->sectname, "__text") == 0)
		{
			vm_address_t subSectScan = scanMemory(task, sectBegin, sect->size, memory, memorySize, alignment);
			if(subSectScan != 0)
			{
				sectFound = subSectScan;
				*stop = YES;
			}
		}
	});
	return sectFound;
}

vm_address_t scanLibrariesForMemory(task_t task, vm_address_t imageStartPtr, char* memory, size_t memorySize, int alignment)
{
	__block vm_address_t foundAddr = 0;

	iterateImages(task, imageStartPtr, ^(char* imageFilePath, struct dyld_image_info* imageInfo, BOOL* stopImages)
	{
		__block vm_address_t slide;
		__block BOOL firstSegmentCommand = YES;
		//printf("- iterating %s -\n", imageFilePath);
		iterateLoadCommands(task, (vm_address_t)imageInfo->imageLoadAddress, ^(const struct load_command* cmd, vm_address_t cmdAddress, BOOL* stopCommands)
		{
			if(cmd->cmd == LC_SEGMENT_64)
			{
				struct segment_command_64 segmentCommand = { 0 };
				task_read(task, cmdAddress, &segmentCommand, sizeof(segmentCommand));
				if(firstSegmentCommand)
				{
					slide = (vm_address_t)imageInfo->imageLoadAddress - segmentCommand.vmaddr;
					firstSegmentCommand = NO;
				}
				if (strncmp("__TEXT", segmentCommand.segname, 16) == 0)
				{
					vm_address_t addrIfFound = scanTextSegmentForMemory(task, cmdAddress, &segmentCommand, slide, memory, memorySize, alignment);
					if(addrIfFound != 0)
					{
						foundAddr = addrIfFound;
						*stopCommands = YES;
						*stopImages = YES;
					}
				}
			}
		});
	});

	return foundAddr;
}


================================================
FILE: entitlements.plist
================================================
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>get-task-allow</key>
	<true/>
	<key>platform-application</key>
	<true/>
	<key>task_for_pid-allow</key>
	<true/>
	<key>com.apple.private.security.container-required</key>
	<false/>
	<key>com.apple.system-task-ports</key>
	<true/>
	<key>com.apple.system-task-ports.control</key>
	<true/>
	<key>com.apple.private.thread-set-state</key>
	<true/>
</dict>
</plist>


================================================
FILE: main.m
================================================
#import <stdio.h>
#import <stdlib.h>
#import <unistd.h>
#import <dlfcn.h>
#import <mach-o/getsect.h>
#import <mach-o/dyld.h>
#import <mach/mach.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#import <mach-o/reloc.h>
#import <sys/utsname.h>
#import <string.h>
#import <limits.h>
#import <spawn.h>
#import "dyld.h"
#import "sandbox.h"
#import <CoreFoundation/CoreFoundation.h>
#import "shellcode_inject.h"
#import "rop_inject.h"


char* resolvePath(char* pathToResolve)
{
	if(strlen(pathToResolve) == 0) return NULL;
	if(pathToResolve[0] == '/')
	{
		return strdup(pathToResolve);
	}
	else
	{
		char absolutePath[PATH_MAX];
		if (realpath(pathToResolve, absolutePath) == NULL) {
			perror("[resolvePath] realpath");
			return NULL;
		}
		return strdup(absolutePath);
	}
}

extern int posix_spawnattr_set_ptrauth_task_port_np(posix_spawnattr_t * __restrict attr, mach_port_t port);
void spawnPacChild(int argc, char *argv[])
{
	char** argsToPass = malloc(sizeof(char*) * (argc + 2));
	for(int i = 0; i < argc; i++)
	{
		argsToPass[i] = argv[i];
	}
	argsToPass[argc] = "pac";
	argsToPass[argc+1] = NULL;

	pid_t targetPid = atoi(argv[1]);
	mach_port_t task;
	kern_return_t kr = KERN_SUCCESS;
	kr = task_for_pid(mach_task_self(), targetPid, &task);
	if(kr != KERN_SUCCESS) {
		printf("[spawnPacChild] Failed to obtain task port.\n");
		return;
	}
	printf("[spawnPacChild] Got task port %d for pid %d\n", task, targetPid);

	posix_spawnattr_t attr;
    posix_spawnattr_init(&attr);
	posix_spawnattr_set_ptrauth_task_port_np(&attr, task);

	uint32_t executablePathSize = 0;
	_NSGetExecutablePath(NULL, &executablePathSize);
	char *executablePath = malloc(executablePathSize);
	_NSGetExecutablePath(executablePath, &executablePathSize);

	int status = -200;
	pid_t pid;
	int rc = posix_spawn(&pid, executablePath, NULL, &attr, argsToPass, NULL);

	posix_spawnattr_destroy(&attr);
	free(argsToPass);
	free(executablePath);

	if(rc != KERN_SUCCESS)
	{
		printf("[spawnPacChild] posix_spawn failed: %d (%s)\n", rc, mach_error_string(rc));
		return;
	}

	do
	{
		if (waitpid(pid, &status, 0) != -1) {
			printf("[spawnPacChild] Child returned %d\n", WEXITSTATUS(status));
		}
	} while (!WIFEXITED(status) && !WIFSIGNALED(status));

	return;
}

int main(int argc, char *argv[], char *envp[]) {
	@autoreleasepool
	{
		setlinebuf(stdout);
		setlinebuf(stderr);
		if (argc < 3 || argc > 4)
		{
			printf("Usage: opainject <pid> <path/to/dylib>\n");
			return -1;
		}

#ifdef __arm64e__
		char* pacArg = NULL;
		if(argc >= 4)
		{
			pacArg = argv[3];
		}
		if (!pacArg || (strcmp("pac", pacArg) != 0))
		{
			spawnPacChild(argc, argv);
			return 0;
		}
#endif

		printf("OPAINJECT HERE WE ARE\n");
		printf("RUNNING AS %d\n", getuid());

		pid_t targetPid = atoi(argv[1]);
		kern_return_t kret = 0;
		task_t procTask = MACH_PORT_NULL;
		char* dylibPath = resolvePath(argv[2]);
		if(!dylibPath) return -3;
		if(access(dylibPath, R_OK) < 0)
		{
			printf("ERROR: Can't access passed dylib at %s\n", dylibPath);
			return -4;
		}

		// get task port
		kret = task_for_pid(mach_task_self(), targetPid, &procTask);
		if(kret != KERN_SUCCESS)
		{
			printf("ERROR: task_for_pid failed with error code %d (%s)\n", kret, mach_error_string(kret));
			return -2;
		}
		if(!MACH_PORT_VALID(procTask))
		{
			printf("ERROR: Got invalid task port (%d)\n", procTask);
			return -3;
		}

		printf("Got task port %d for pid %d!\n", procTask, targetPid);

		// get aslr slide
		task_dyld_info_data_t dyldInfo;
		uint32_t count = TASK_DYLD_INFO_COUNT;
		task_info(procTask, TASK_DYLD_INFO, (task_info_t)&dyldInfo, &count);

		injectDylibViaRop(procTask, targetPid, dylibPath, dyldInfo.all_image_info_addr);

		mach_port_deallocate(mach_task_self(), procTask);
		
		return 0;
	}
}


================================================
FILE: pac.h
================================================
#ifndef PTRAUTH_HELPERS_H
#define PTRAUTH_HELPERS_H
// Helpers for PAC archs.

// If the compiler understands __arm64e__, assume it's paired with an SDK that has
// ptrauth.h. Otherwise, it'll probably error if we try to include it so don't.
#if __arm64e__
#include <ptrauth.h>
#endif

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"

// Given a pointer to instructions, sign it so you can call it like a normal fptr.
static void *make_sym_callable(void *ptr) {
#if __arm64e__
    ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0);
#endif
    return ptr;
}

static void *make_sym_callable_data(void *ptr) {
#if __arm64e__
    ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_process_independent_data), ptrauth_key_process_independent_data, 0);
#endif
    return ptr;
}

// Given a function pointer, strip the PAC so you can read the instructions.
static void *make_sym_readable(void *ptr) {
#if __arm64e__
    ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer);
#endif
    return ptr;
}

static void *make_sym_readable_data(void *ptr) {
#if __arm64e__
    ptr = ptrauth_strip(ptr, ptrauth_key_process_independent_data);
#endif
    return ptr;
}

#pragma clang diagnostic pop
#endif

================================================
FILE: rop_inject.h
================================================
extern void injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr);

================================================
FILE: rop_inject.m
================================================
#import <stdio.h>
#import <unistd.h>
#import <stdlib.h>
#import <dlfcn.h>
#import <errno.h>
#import <string.h>
#import <limits.h>
#import <pthread.h>
#import <pthread_spis.h>
#import <mach/mach.h>
#import <mach/error.h>
#import <mach-o/getsect.h>
#import <mach-o/dyld.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#import <mach-o/reloc.h>
#import <mach-o/dyld_images.h>
#import <sys/utsname.h>
#import <sys/types.h>
#import <sys/sysctl.h>
#import <sys/mman.h>
#import <sys/stat.h>
#import <sys/wait.h>
#import <CoreFoundation/CoreFoundation.h>

#import "pac.h"
#import "dyld.h"
#import "sandbox.h"
#import "CoreSymbolication.h"
#import "task_utils.h"
#import "thread_utils.h"
#import "arm64.h"

vm_address_t writeStringToTask(task_t task, const char* string, size_t* lengthOut)
{
	kern_return_t kr = KERN_SUCCESS;
	vm_address_t remoteString = (vm_address_t)NULL;
	size_t stringLen = strlen(string)+1;

	kr = vm_allocate(task, &remoteString, stringLen, VM_FLAGS_ANYWHERE);
	if(kr != KERN_SUCCESS)
	{
		printf("ERROR: Unable to memory for string %s: %s\n", string, mach_error_string(kr));
		return 0;
	}

	kr = vm_protect(task, remoteString, stringLen, TRUE, VM_PROT_READ | VM_PROT_WRITE);
	if(kr != KERN_SUCCESS)
	{
		vm_deallocate(task, remoteString, stringLen);
		printf("ERROR: Failed to make string %s read/write: %s.\n", string, mach_error_string(kr));
		return kr;
	}

	kr = vm_write(task, remoteString, (vm_address_t)string, stringLen);
	if(kr != KERN_SUCCESS)
	{
		vm_deallocate(task, remoteString, stringLen);
		printf("ERROR: Failed to write string %s to memory: %s\n", string, mach_error_string(kr));
		return kr;
	}

	if(lengthOut)
	{
		*lengthOut = stringLen;
	}

	return remoteString;
}

void findRopLoop(task_t task, vm_address_t allImageInfoAddr)
{
	uint32_t inst = CFSwapInt32(0x00000014);
	ropLoop = (uint64_t)scanLibrariesForMemory(task, allImageInfoAddr, (char*)&inst, sizeof(inst), 4);
}

// Create an infinitely spinning pthread in target process
kern_return_t createRemotePthread(task_t task, vm_address_t allImageInfoAddr, thread_act_t* remotePthreadOut)
{
	kern_return_t kr = KERN_SUCCESS;

#if __arm64e__
	// GET ANY VALID THREAD STATE
	mach_msg_type_number_t validThreadStateCount = ARM_THREAD_STATE64_COUNT;
	struct arm_unified_thread_state validThreadState;
	thread_act_array_t allThreadsForFindingValid;
	mach_msg_type_number_t threadCountForFindingValid;
	kr = task_threads(task, &allThreadsForFindingValid, &threadCountForFindingValid);
	if(kr != KERN_SUCCESS || threadCountForFindingValid == 0)
	{
		printf("[createRemotePthread] ERROR: failed to get threads in task: %s\n", mach_error_string(kr));
		if (kr == KERN_SUCCESS) return 1;
		return kr;
	}
	kr = thread_get_state(allThreadsForFindingValid[0], ARM_THREAD_STATE64, (thread_state_t)&validThreadState.ts_64, &validThreadStateCount);
	if(kr != KERN_SUCCESS )
	{
		printf("[createRemotePthread] ERROR: failed to get valid thread state: %s\n", mach_error_string(kr));
		return kr;
	}
	vm_deallocate(mach_task_self(), (vm_offset_t)allThreadsForFindingValid, sizeof(thread_act_array_t) * threadCountForFindingValid);
#endif

	// GATHER OFFSETS
	__unused vm_address_t libSystemPthreadAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_pthread.dylib");

	uint64_t mainThread = 0;
	if (@available(iOS 12, *)) {
		// TODO: maybe instead of this, allocate our own pthread object?
		// kinda worried about side effects here, but as long our thread doesn't
		// somehow trigger pthread_main_thread modifications, it should be fine
		uint64_t pthread_main_thread_np = remoteDlSym(task, libSystemPthreadAddr, "_pthread_main_thread_np");

		uint32_t instructions[2];
		kr = task_read(task, pthread_main_thread_np, &instructions[0], sizeof(instructions));
		if (kr != KERN_SUCCESS) {
			printf("ERROR: Failed to find main thread (1/3)\n");
			return kr;
		}

		uint64_t _main_thread_ptr = 0;
		if (!decode_adrp_ldr(instructions[0], instructions[1], pthread_main_thread_np, &_main_thread_ptr)) {
			printf("ERROR: Failed to find main thread (2/3)\n");
			return 1;
		}

		kr = task_read(task, _main_thread_ptr, &mainThread, sizeof(mainThread));
		if (kr != KERN_SUCCESS) {
			printf("ERROR: Failed to find main thread (3/3)\n");
			return kr;
		}
	}
	uint64_t _pthread_set_self = remoteDlSym(task, libSystemPthreadAddr, "__pthread_set_self");

	// ALLOCATE STACK
	vm_address_t remoteStack64 = (vm_address_t)NULL;
	kr = vm_allocate(task, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
	if(kr != KERN_SUCCESS)
	{
		printf("[createRemotePthread] ERROR: Unable to allocate stack memory: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_protect(task, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
	if(kr != KERN_SUCCESS)
	{
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		printf("[createRemotePthread] ERROR: Failed to make remote stack writable: %s.\n", mach_error_string(kr));
		return kr;
	}

	thread_act_t bootstrapThread = 0;
	struct arm_unified_thread_state bootstrapThreadState;
	memset(&bootstrapThreadState, 0, sizeof(struct arm_unified_thread_state));

	// spawn pthread to infinite loop
	bootstrapThreadState.ash.flavor = ARM_THREAD_STATE64;
	bootstrapThreadState.ash.count = ARM_THREAD_STATE64_COUNT;
#if __arm64e__
	bootstrapThreadState.ts_64.__opaque_flags = validThreadState.ts_64.__opaque_flags;
#endif
	uint64_t sp = (remoteStack64 + (STACK_SIZE / 2));
	__unused uint64_t x2 = ropLoop;
#if __arm64e__
	if (!(bootstrapThreadState.ts_64.__opaque_flags & __DARWIN_ARM_THREAD_STATE64_FLAGS_NO_PTRAUTH)) {
		x2 = (uint64_t)make_sym_callable((void*)x2);
	}
#endif
	__darwin_arm_thread_state64_set_sp(bootstrapThreadState.ts_64, (void*)sp);
	__darwin_arm_thread_state64_set_pc_fptr(bootstrapThreadState.ts_64, make_sym_callable((void*)_pthread_set_self));
	__darwin_arm_thread_state64_set_lr_fptr(bootstrapThreadState.ts_64, make_sym_callable((void*)ropLoop)); //when done, go to infinite loop
	bootstrapThreadState.ts_64.__x[0] = mainThread;

	//printThreadState_state(bootstrapThreadState);

	kr = thread_create_running(task, ARM_THREAD_STATE64, (thread_state_t)&bootstrapThreadState.ts_64, ARM_THREAD_STATE64_COUNT, &bootstrapThread);
	if(kr != KERN_SUCCESS)
	{
		printf("[createRemotePthread] ERROR: Failed to create running thread: %s.\n", mach_error_string(kr));
		return kr;
	}

	printf("[createRemotePthread] Created bootstrap thread... now waiting on finish\n");

	struct arm_unified_thread_state outState;
	kr = wait_for_thread(bootstrapThread, ropLoop, &outState);
	if(kr != KERN_SUCCESS)
	{
		printf("[createRemotePthread] ERROR: failed to wait for bootstrap thread: %s\n", mach_error_string(kr));
		return kr;
	}

	printf("[createRemotePthread] Bootstrap done!\n");

	if(remotePthreadOut) *remotePthreadOut = bootstrapThread;

	return kr;
}

kern_return_t arbCall(task_t task, thread_act_t targetThread, uint64_t* retOut, bool willReturn, vm_address_t funcPtr, int numArgs, ...)
{
	kern_return_t kr = KERN_SUCCESS;
	if(numArgs > 8)
	{
		printf("[arbCall] ERROR: Only 8 arguments are supported by arbCall\n");
		return -2;
	}
	if(!targetThread)
	{
		printf("[arbCall] ERROR: targetThread == null\n");
		return -3;
	}

	va_list ap;
	va_start(ap, numArgs);

	// suspend target thread
	thread_suspend(targetThread);

	// backup states of target thread

	mach_msg_type_number_t origThreadStateCount = ARM_THREAD_STATE64_COUNT;
	struct arm_unified_thread_state origThreadState;
	kr = thread_get_state(targetThread, ARM_THREAD_STATE64, (thread_state_t)&origThreadState.ts_64, &origThreadStateCount);
	if(kr != KERN_SUCCESS)
	{
		thread_resume(targetThread);
		printf("[arbCall] ERROR: failed to save original state of target thread: %s\n", mach_error_string(kr));
		return kr;
	}

	struct arm64_thread_full_state* origThreadFullState = thread_save_state_arm64(targetThread);
	if(!origThreadFullState)
	{
		thread_resume(targetThread);
		printf("[arbCall] ERROR: failed to backup original state of target thread\n");
		return kr;
	}

	// prepare target thread for arbitary call

	// allocate stack
	vm_address_t remoteStack = (vm_address_t)NULL;
	kr = vm_allocate(task, &remoteStack, STACK_SIZE, VM_FLAGS_ANYWHERE);
	if(kr != KERN_SUCCESS)
	{
		free(origThreadFullState);
		thread_resume(targetThread);
		printf("[arbCall] ERROR: Unable to allocate stack memory: %s\n", mach_error_string(kr));
		return kr;
	}

	// make stack read / write
	kr = vm_protect(task, remoteStack, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
	if(kr != KERN_SUCCESS)
	{
		free(origThreadFullState);
		vm_deallocate(task, remoteStack, STACK_SIZE);
		thread_resume(targetThread);
		printf("[arbCall] ERROR: Failed to make remote stack writable: %s.\n", mach_error_string(kr));
		return kr;
	}

	// abort any existing syscalls by target thread, thanks to Linus Henze for this suggestion :P
	thread_abort(targetThread);

	// set state for arb call
	struct arm_unified_thread_state newState = origThreadState;
	uint64_t sp = remoteStack + (STACK_SIZE / 2);
	__darwin_arm_thread_state64_set_sp(newState.ts_64, (void*)sp);
	__darwin_arm_thread_state64_set_pc_fptr(newState.ts_64, make_sym_callable((void*)funcPtr));
	__darwin_arm_thread_state64_set_lr_fptr(newState.ts_64, make_sym_callable((void*)ropLoop));

	// write arguments into registers
	for (int i = 0; i < numArgs; i++)
	{
		newState.ts_64.__x[i] = va_arg(ap, uint64_t);
	}

	kr = thread_set_state(targetThread, ARM_THREAD_STATE64, (thread_state_t)&newState.ts_64, ARM_THREAD_STATE64_COUNT);
	if(kr != KERN_SUCCESS)
	{
		free(origThreadFullState);
		vm_deallocate(task, remoteStack, STACK_SIZE);
		thread_resume(targetThread);
		printf("[arbCall] ERROR: failed to set state for thread: %s\n", mach_error_string(kr));
		return kr;
	}

	printf("[arbCall] Set thread state for arbitary call\n");
	//printThreadState(targetThread);

	thread_act_array_t cachedThreads;
	mach_msg_type_number_t cachedThreadCount;
	kr = task_threads(task, &cachedThreads, &cachedThreadCount);
	if (kr != KERN_SUCCESS) return kr;

	suspend_threads_except_for(cachedThreads, cachedThreadCount, targetThread);

	// perform arbitary call
	thread_resume(targetThread);
	printf("[arbCall] Started thread, waiting for it to finish...\n");

	// wait for arbitary call to finish (or not)
	struct arm_unified_thread_state outState;
	if (willReturn)
	{
		kr = wait_for_thread(targetThread, ropLoop, &outState);
		if(kr != KERN_SUCCESS)
		{
			free(origThreadFullState);
			printf("[arbCall] ERROR: failed to wait for thread to finish: %s\n", mach_error_string(kr));
			return kr;
		}

		// extract return value from state if needed
		if(retOut)
		{
			*retOut = outState.ts_64.__x[0];
		}
	}
	else
	{
		kr = wait_for_thread(targetThread, 0, &outState);
		printf("[arbCall] pthread successfully did not return with code %d (%s)\n", kr, mach_error_string(kr));
	}

	resume_threads_except_for(cachedThreads, cachedThreadCount, targetThread);

	vm_deallocate(mach_task_self(), (vm_offset_t)cachedThreads, sizeof(thread_act_array_t) * cachedThreadCount);

	// release fake stack as it's no longer needed
	vm_deallocate(task, remoteStack, STACK_SIZE);

	if (willReturn)
	{
		// suspend target thread
		thread_suspend(targetThread);
		thread_abort(targetThread);

		// restore states of target thread to what they were before the arbitary call
		bool restoreSuccess = thread_restore_state_arm64(targetThread, origThreadFullState);
		if(!restoreSuccess)
		{
			printf("[arbCall] ERROR: failed to revert to old thread state\n");
			return kr;
		}

		// resume thread again, process should continue executing as before
		//printThreadState(targetThread);
		thread_resume(targetThread);
	}

	return kr;
}

void prepareForMagic(task_t task, vm_address_t allImageInfoAddr)
{
	// FIND INFINITE LOOP ROP GADGET
	static dispatch_once_t onceToken;
	dispatch_once (&onceToken, ^{
		findRopLoop(task, allImageInfoAddr);
	});
	printf("[prepareForMagic] done, ropLoop: 0x%llX\n", ropLoop);
}

bool sandboxFixup(task_t task, thread_act_t pthread, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr)
{
	int readExtensionNeeded = sandbox_check(pid, "file-read-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath);
	int executableExtensionNeeded = sandbox_check(pid, "file-map-executable", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath);

	int retval = 0;
	vm_address_t libSystemSandboxAddr = 0;
	uint64_t sandbox_extension_consumeAddr = 0;
	if (readExtensionNeeded || executableExtensionNeeded) {
		libSystemSandboxAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_sandbox.dylib");
		sandbox_extension_consumeAddr = remoteDlSym(task, libSystemSandboxAddr, "_sandbox_extension_consume");
		printf("[sandboxFixup] applying sandbox extension(s)! sandbox_extension_consume: 0x%llX\n", sandbox_extension_consumeAddr);
	}

	if (readExtensionNeeded) {
		char* extString = sandbox_extension_issue_file(APP_SANDBOX_READ, dylibPath, 0);
		size_t remoteExtStringSize = 0;
		vm_address_t remoteExtString = writeStringToTask(task, (const char*)extString, &remoteExtStringSize);
		if(remoteExtString)
		{
			int64_t readExtensionRet = 0;
			arbCall(task, pthread, (uint64_t*)&readExtensionRet, true, sandbox_extension_consumeAddr, 1, remoteExtString);
			vm_deallocate(task, remoteExtString, remoteExtStringSize);

			printf("[sandboxFixup] sandbox_extension_consume returned %lld for read extension\n", (int64_t)readExtensionRet);
			retval |= (readExtensionRet <= 0);
		}
	}
	else {
		printf("[sandboxFixup] read extension not needed, skipping...\n");
	}

	if (executableExtensionNeeded) {
		char* extString = sandbox_extension_issue_file("com.apple.sandbox.executable", dylibPath, 0);
		size_t remoteExtStringSize = 0;
		vm_address_t remoteExtString = writeStringToTask(task, (const char*)extString, &remoteExtStringSize);
		if(remoteExtString)
		{
			int64_t executableExtensionRet = 0;
			arbCall(task, pthread, (uint64_t*)&executableExtensionRet, true, sandbox_extension_consumeAddr, 1, remoteExtString);
			vm_deallocate(task, remoteExtString, remoteExtStringSize);

			printf("[sandboxFixup] sandbox_extension_consume returned %lld for executable extension\n", (int64_t)executableExtensionRet);
			retval |= (executableExtensionRet <= 0);
		}
	}
	else {
		printf("[sandboxFixup] executable extension not needed, skipping...\n");
	}

	return retval == 0;
}

void injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr)
{
	prepareForMagic(task, allImageInfoAddr);

	thread_act_t pthread = 0;
	kern_return_t kr = createRemotePthread(task, allImageInfoAddr, &pthread);
	if(kr != KERN_SUCCESS) return;

	sandboxFixup(task, pthread, pid, dylibPath, allImageInfoAddr);

	printf("[injectDylibViaRop] Preparation done, now injecting!\n");

	// FIND OFFSETS
	vm_address_t libDyldAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libdyld.dylib");
	uint64_t dlopenAddr = remoteDlSym(task, libDyldAddr, "_dlopen");
	uint64_t dlerrorAddr = remoteDlSym(task, libDyldAddr, "_dlerror");

	printf("[injectDylibViaRop] dlopen: 0x%llX, dlerror: 0x%llX\n", (unsigned long long)dlopenAddr, (unsigned long long)dlerrorAddr);

	// CALL DLOPEN
	size_t remoteDylibPathSize = 0;
	vm_address_t remoteDylibPath = writeStringToTask(task, (const char*)dylibPath, &remoteDylibPathSize);
	if(remoteDylibPath)
	{
		void* dlopenRet;
		arbCall(task, pthread, (uint64_t*)&dlopenRet, true, dlopenAddr, 2, remoteDylibPath, RTLD_NOW);
		vm_deallocate(task, remoteDylibPath, remoteDylibPathSize);

		if (dlopenRet) {
			printf("[injectDylibViaRop] dlopen succeeded, library handle: %p\n", dlopenRet);
		}
		else {
			uint64_t remoteErrorString = 0;
			arbCall(task, pthread, (uint64_t*)&remoteErrorString, true, dlerrorAddr, 0);
			char *errorString = task_copy_string(task, remoteErrorString);
			printf("[injectDylibViaRop] dlopen failed, error:\n%s\n", errorString);
			free(errorString);
		}
	}

	thread_terminate(pthread);
}

================================================
FILE: sandbox.h
================================================
enum sandbox_filter_type {
	SANDBOX_FILTER_NONE,
	SANDBOX_FILTER_PATH,
	SANDBOX_FILTER_GLOBAL_NAME,
	SANDBOX_FILTER_LOCAL_NAME,
	SANDBOX_FILTER_APPLEEVENT_DESTINATION,
	SANDBOX_FILTER_RIGHT_NAME,
	SANDBOX_FILTER_PREFERENCE_DOMAIN,
	SANDBOX_FILTER_KEXT_BUNDLE_ID,
	SANDBOX_FILTER_INFO_TYPE,
	SANDBOX_FILTER_NOTIFICATION,
	// ?
	// ?
	SANDBOX_FILTER_XPC_SERVICE_NAME = 12,
	SANDBOX_FILTER_IOKIT_CONNECTION,
	// ?
	// ?
	// ?
	// ?
};


enum sandbox_extension_flags {
	FS_EXT_DEFAULTS =              0,
	FS_EXT_FOR_PATH =       (1 << 0),
	FS_EXT_FOR_FILE =       (1 << 1),
	FS_EXT_READ =           (1 << 2),
	FS_EXT_WRITE =          (1 << 3),
	FS_EXT_PREFER_FILEID =  (1 << 4),
};

extern const char * APP_SANDBOX_IOKIT_CLIENT;
extern const char * APP_SANDBOX_MACH;
extern const char * APP_SANDBOX_READ;
extern const char * APP_SANDBOX_READ_WRITE;

extern const char * IOS_SANDBOX_APPLICATION_GROUP;
extern const char * IOS_SANDBOX_CONTAINER;

extern const enum sandbox_filter_type SANDBOX_CHECK_ALLOW_APPROVAL;
extern const enum sandbox_filter_type SANDBOX_CHECK_CANONICAL;
extern const enum sandbox_filter_type SANDBOX_CHECK_NOFOLLOW;
extern const enum sandbox_filter_type SANDBOX_CHECK_NO_APPROVAL;
extern const enum sandbox_filter_type SANDBOX_CHECK_NO_REPORT;

extern const uint32_t SANDBOX_EXTENSION_CANONICAL;
extern const uint32_t SANDBOX_EXTENSION_DEFAULT;
extern const uint32_t SANDBOX_EXTENSION_MAGIC;
extern const uint32_t SANDBOX_EXTENSION_NOFOLLOW;
extern const uint32_t SANDBOX_EXTENSION_NO_REPORT;
extern const uint32_t SANDBOX_EXTENSION_NO_STORAGE_CLASS;
extern const uint32_t SANDBOX_EXTENSION_PREFIXMATCH;
extern const uint32_t SANDBOX_EXTENSION_UNRESOLVED;

int sandbox_check(pid_t, const char *operation, enum sandbox_filter_type, ...);
int sandbox_check_by_audit_token(audit_token_t, const char *operation, enum sandbox_filter_type, ...);
int sandbox_check_by_uniqueid(uid_t, pid_t, const char *operation, enum sandbox_filter_type, ...);

int64_t sandbox_extension_consume(const char *extension_token);
char *sandbox_extension_issue_file(const char *extension_class, const char *path, uint32_t flags);
char *sandbox_extension_issue_file_to_process(const char *extension_class, const char *path, uint32_t flags, audit_token_t);
char *sandbox_extension_issue_file_to_process_by_pid(const char *extension_class, const char *path, uint32_t flags, pid_t);
char *sandbox_extension_issue_file_to_self(const char *extension_class, const char *path, uint32_t flags);
char *sandbox_extension_issue_generic(const char *extension_class, uint32_t flags);
char *sandbox_extension_issue_generic_to_process(const char *extension_class, uint32_t flags, audit_token_t);
char *sandbox_extension_issue_generic_to_process_by_pid(const char *extension_class, uint32_t flags, pid_t);
char *sandbox_extension_issue_iokit_registry_entry_class(const char *extension_class, const char *registry_entry_class, uint32_t flags);
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);
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);
char *sandbox_extension_issue_iokit_user_client_class(const char *extension_class, const char *registry_entry_class, uint32_t flags);
char *sandbox_extension_issue_mach(const char *extension_class, const char *name, uint32_t flags);
char *sandbox_extension_issue_mach_to_process(const char *extension_class, const char *name, uint32_t flags, audit_token_t);
char *sandbox_extension_issue_mach_to_process_by_pid(const char *extension_class, const char *name, uint32_t flags, pid_t);
char *sandbox_extension_issue_posix_ipc(const char *extension_class, const char *name, uint32_t flags);
void sandbox_extension_reap(void);
int sandbox_extension_release(int64_t extension_handle);
int sandbox_extension_release_file(int64_t extension_handle, const char *path);
int sandbox_extension_update_file(int64_t extension_handle, const char *path);

================================================
FILE: shellcode_inject.h
================================================
int injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr);

================================================
FILE: shellcode_inject.m
================================================
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
#import <unistd.h>
#import <dlfcn.h>
#import <mach-o/getsect.h>
#import <mach-o/dyld.h>
#import <mach/mach.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#import <mach-o/reloc.h>
#import <mach-o/dyld_images.h>
#import <sys/utsname.h>
#import <string.h>
#import <limits.h>

#include <sys/types.h>
#include <mach/error.h>
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
#include <pthread_spis.h>

#include <mach/arm/thread_status.h>
#import "dyld.h"
#import "sandbox.h"
#import <CoreFoundation/CoreFoundation.h>

#define STACK_SIZE 65536

#define	PT_TRACE_ME	0	/* child declares it's being traced */
#define	PT_READ_I	1	/* read word in child's I space */
#define	PT_READ_D	2	/* read word in child's D space */
#define	PT_READ_U	3	/* read word in child's user structure */
#define	PT_WRITE_I	4	/* write word in child's I space */
#define	PT_WRITE_D	5	/* write word in child's D space */
#define	PT_WRITE_U	6	/* write word in child's user structure */
#define	PT_CONTINUE	7	/* continue the child */
#define	PT_KILL		8	/* kill the child process */
#define	PT_STEP		9	/* single step the child */
#define	PT_ATTACH	10	/* trace some running process */
#define	PT_DETACH	11	/* stop tracing a process */
#define	PT_SIGEXC	12	/* signals as exceptions for current_proc */
#define PT_THUPDATE	13	/* signal for thread# */
#define PT_ATTACHEXC	14	/* attach to running process with signal exception */
extern int ptrace(int request, pid_t pid, caddr_t addr, int data);

static kern_return_t runPayload(task_t task, uint8_t* payload, size_t payloadSize, uint64_t codeStart, vm_address_t allImageInfoAddr)
{
	// GATHER OFFSETS

	vm_address_t libSystemPthreadAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_pthread.dylib");
	uint64_t pthread_create_from_mach_threadAddr = remoteDlSym(task, libSystemPthreadAddr, "_pthread_create_from_mach_thread");
	uint64_t pthread_exitAddr = remoteDlSym(task, libSystemPthreadAddr, "_pthread_exit");


	// ALLOCATE STACK

	vm_address_t remoteStack64 = (vm_address_t)NULL;
	kern_return_t kr = KERN_SUCCESS;
	kr = vm_allocate(task, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
	if(kr != KERN_SUCCESS)
	{
		printf("ERROR: Unable to allocate stack memory: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_protect(task, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
	if(kr != KERN_SUCCESS)
	{
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		printf("ERROR: Failed to make remote stack writable: %s.\n", mach_error_string(kr));
		return kr;
	}


	// THREAD BOOTSTRAP PAYLOAD (x2: startFunc argument passed to pthread_create_from_mach_thread)

	uint32_t bootstrapCode[7] = {
		// pthread_create_from_mach_thread call
		// arg1: x0, output pthread_t = pointer to stack
		// arg2: x1, attributes = NULL
		// arg3: x2, start_routine, pointer to real payload, passed via thread state
		// arg4: x3, arg, pointer to arguments array = NULL
		CFSwapInt32(0xE0230091), // add x0, sp, #8 ; x0 = SP+8
		CFSwapInt32(0x010080D2), // mov x1, #0x0
		CFSwapInt32(0x030080D2), // mov x3, #0x0
		CFSwapInt32(0x68FFFF58), // ldr x8, -0x14 ; pthread_create_from_mach_threadAddr
		CFSwapInt32(0x00013FD6), // blr x8
		CFSwapInt32(0x490880D2), // mov x9, 0x42 (end code)
		CFSwapInt32(0x00000014), // b #0 (infinite loop)
	};

	uint64_t bootstrapCodeVarCount = 1; // count of variables before code
	size_t bootstrapCodeVarSize = bootstrapCodeVarCount * sizeof(uint64_t);

	size_t bootstrapPayloadSize = bootstrapCodeVarCount * sizeof(uint32_t) + sizeof(bootstrapCode); // allocate needed memory space
	char* bootstrapPayload = malloc(bootstrapPayloadSize);
	bzero(&bootstrapPayload[0], bootstrapPayloadSize);

	intptr_t bootstrapPayloadPtr = (intptr_t)bootstrapPayload; // insert variables
	memcpy((void*)(bootstrapPayloadPtr), (const void*)&pthread_create_from_mach_threadAddr, sizeof(uint64_t));

	memcpy((void*)(bootstrapPayloadPtr+bootstrapCodeVarSize), &bootstrapCode[0], sizeof(bootstrapCode)); //insert code

	vm_address_t remoteBootstrapPayload = (vm_address_t)NULL;
	kr = vm_allocate(task, &remoteBootstrapPayload, bootstrapPayloadSize, VM_FLAGS_ANYWHERE);
	if(kr != KERN_SUCCESS)
	{
		free(bootstrapPayload);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		printf("ERROR: Unable to allocate memory for bootstrap code: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_write(task, remoteBootstrapPayload, (vm_address_t)bootstrapPayload, bootstrapPayloadSize);
	if(kr != KERN_SUCCESS)
	{
		free(bootstrapPayload);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Failed to write payload to code memory: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_protect(task, remoteBootstrapPayload + bootstrapCodeVarSize, sizeof(bootstrapCode), FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
	if(kr != KERN_SUCCESS)
	{
		free(bootstrapPayload);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Failed to make bootstrap payload executable: %s\n", mach_error_string(kr));
		return kr;
	}

	/*kr = vm_protect(task, remoteBootstrapPayload, bootstrapCodeVarSize, FALSE, VM_PROT_READ | VM_PROT_WRITE);
	if(kr != KERN_SUCCESS)
	{
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Failed to make bootstrap payload variables read/write: %s\n", mach_error_string(kr));
		return kr;
	}*/

	free(bootstrapPayload);


	// SETUP PAYLOAD SUFFIX (pthread_exit(0))

	uint32_t payloadSuffixCode[3] = {
		CFSwapInt32(0x000080D2), // mov x0, #0
		CFSwapInt32(0x48000058), // ldr x8, 0x8 ; pthread_exit
		CFSwapInt32(0x00013FD6), // blr x8
	};

	uint64_t payloadSuffixVarCount = 1; // count of variables after suffix code
	uint64_t payloadSuffixVarSize = payloadSuffixVarCount * sizeof(uint64_t);
	uint64_t payloadSuffixCodeSize = sizeof(payloadSuffixCode);
	uint64_t payloadSuffixSize = payloadSuffixVarSize + payloadSuffixCodeSize;

	char* payloadSuffix = malloc(payloadSuffixSize); // allocate buffer
	intptr_t payloadSuffixPtr = (intptr_t)payloadSuffix;

	memcpy((void*)(payloadSuffixPtr), &payloadSuffixCode[0], payloadSuffixCodeSize); // insert code
	memcpy((void*)(payloadSuffixPtr+payloadSuffixCodeSize), (const void*)&pthread_exitAddr, sizeof(uint64_t)); // insert variables


	// WRITE PASSED PAYLOAD

	uint64_t fullPayloadSize = payloadSize + payloadSuffixSize;

	vm_address_t remotePayload = (vm_address_t)NULL;
	kr = vm_allocate(task, &remotePayload, fullPayloadSize, VM_FLAGS_ANYWHERE);
	if(kr != KERN_SUCCESS)
	{
		free(payloadSuffix);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Unable to allocate payload code memory: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_write(task, remotePayload, (vm_address_t)payload, payloadSize);
	if(kr != KERN_SUCCESS)
	{
		free(payloadSuffix);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remotePayload, fullPayloadSize);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Failed to write payload to code memory: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_write(task, remotePayload+payloadSize, (vm_address_t)payloadSuffix, payloadSuffixSize);
	if(kr != KERN_SUCCESS)
	{
		free(payloadSuffix);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remotePayload, fullPayloadSize);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Failed to write payload suffix to code memory: %s\n", mach_error_string(kr));
		return kr;
	}

	kr = vm_protect(task, remotePayload + codeStart, fullPayloadSize - codeStart - payloadSuffixVarSize, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
	if(kr != KERN_SUCCESS)
	{
		free(payloadSuffix);
		vm_deallocate(task, remoteStack64, STACK_SIZE);
		vm_deallocate(task, remotePayload, fullPayloadSize);
		vm_deallocate(task, remoteBootstrapPayload, bootstrapPayloadSize);
		printf("ERROR: Failed to make code payload executable: %s\n", mach_error_string(kr));
		return kr;
	}

	printf("marked %llX - %llX as rx\n", (uint64_t)remotePayload + codeStart, (uint64_t)remotePayload + (fullPayloadSize - codeStart - payloadSuffixVarSize));


	// ALLOCATE THREAD AND START IT

	thread_act_t remoteThread;

	struct arm_unified_thread_state remoteThreadState64;
	bzero(&remoteThreadState64, sizeof(struct arm_unified_thread_state));

	remoteThreadState64.ash.flavor = ARM_THREAD_STATE64;
	remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;
	__darwin_arm_thread_state64_set_sp(remoteThreadState64.ts_64, (void*)(remoteStack64 + (STACK_SIZE / 2)));
	__darwin_arm_thread_state64_set_pc_fptr(remoteThreadState64.ts_64, (void*)(remoteBootstrapPayload + bootstrapCodeVarSize));
	remoteThreadState64.ts_64.__x[2] = remotePayload + codeStart; // <- startFunc argument of pthread_create_from_mach_thread call

	printf("About to jump to %llX (thread bootstrap)\n", (uint64_t)__darwin_arm_thread_state64_get_pc_fptr(remoteThreadState64.ts_64));
	printf("Real payload: %llX (code start: %llX)\n", (uint64_t)remotePayload,  (uint64_t)(remotePayload + codeStart));

	// Uncomment to debug issues before the jump to code happens
	//getchar();

	printf("Starting thread in task!\n");
	kr = thread_create_running(task, ARM_THREAD_STATE64, (thread_state_t)&remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT, &remoteThread);
	if(kr != KERN_SUCCESS)
	{
		printf("ERROR: Failed to create running thread: %s.\n", mach_error_string(kr));
	}


	// WAIT FOR THREAD TO FINISH
	// (Note: The pthread this thread spawns will still be running by the time it has fininshed)
	// (So we unfortunately can't release the remote memory again because otherwise the pthread would crash)

	printf("Started thread, now waiting for it to finish.\n");

	mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
	for (;;) {
		kr = thread_get_state(remoteThread, ARM_THREAD_STATE64, (thread_state_t)&remoteThreadState64.ts_64, &thread_state_count);
		if (kr != KERN_SUCCESS) {
			printf("Error getting stub thread state: error %s", mach_error_string(kr));
			break;
		}

		// Check whether x9 is 0x42 (exit code)
		if (remoteThreadState64.ts_64.__x[9] == 0x42) {
			printf("Stub thread finished\n");
			kr = thread_terminate(remoteThread);
			if (kr != KERN_SUCCESS) {
				printf("Error terminating stub thread: error %s\n", mach_error_string(kr));
			}
			break;
		}
	}

	printf("Thread finished, we done here.\n");

	// Deallocating is very hard to time right, we just leave the memory in there, doesn't matter too much anyways
	//vm_deallocate(task, remoteStack64, STACK_SIZE);
	//vm_deallocate(task, remoteCode64, payloadSize);
	free(payloadSuffix);
	return kr;
}

int injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr)
{
	// STEP ONE: gather offsets
	
	vm_address_t libSystemSandboxAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libsystem_sandbox.dylib");
	vm_address_t libDyldAddr = getRemoteImageAddress(task, allImageInfoAddr, "/usr/lib/system/libdyld.dylib");

	//uint64_t pthread_set_selfAddr = remoteDlSym(task, libSystemPthreadAddr, "__pthread_set_self");
	
	uint64_t sandbox_extension_consumeAddr = remoteDlSym(task, libSystemSandboxAddr, "_sandbox_extension_consume");
	uint64_t dlopenAddr = remoteDlSym(task, libDyldAddr, "_dlopen");

	printf("sandbox_extension_consumeAddr: %llX\n", (unsigned long long)sandbox_extension_consumeAddr);
	printf("dlopenAddr: %llX\n", (unsigned long long)dlopenAddr);

	// STEP TWO: set up ptrace

	/*kern_return_t ret;
	ret = ptrace(PT_ATTACH, pid, NULL, 0);
	if(ret != KERN_SUCCESS)
	{
		printf("Error attaching to process %d: %d\n", pid, errno);
		return 1;
	}
	ret = ptrace(PT_CONTINUE, pid, NULL, 0);
	if(ret != KERN_SUCCESS)
	{
		printf("Error continuing execution of process %d\n", pid);
		return 1;
	}*/

	// STEP THREE: apply sandbox extension if needed

	// Check whether sandbox extension is needed
	int sandboxExtensionNeeded = sandbox_check(pid, "file-read-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, dylibPath);
	if(sandboxExtensionNeeded)
	{
		printf("Sandbox extension needed, performing magic...\n");

		char* extString = sandbox_extension_issue_file(APP_SANDBOX_READ, dylibPath, 0);
		size_t stringAllocSize = 300;

		uint64_t codeVarCount = 1;
		uint64_t codeVarSize = codeVarCount * sizeof(uint64_t);

		uint32_t code[3] = {
			// sandbox_extension_consume call
			// arg1: x0, pointer to extension string
			CFSwapInt32(0x60F6FF10), // adr x0, -0x134 ; reference to extString
			CFSwapInt32(0xA8FFFF58), // ldr x8, -0x0C ; sandbox_extension_consumeAddr
			CFSwapInt32(0x00013FD6), // blr x8
		};

		size_t payloadSize = stringAllocSize + codeVarSize + sizeof(code);
		char* payload = malloc(payloadSize);
		bzero(&payload[0], payloadSize);
		strlcpy(&payload[0], extString, stringAllocSize); // insert extString

		intptr_t payloadIntPtr = (intptr_t)payload;
		memcpy((void*)(payloadIntPtr+stringAllocSize), (const void*)&sandbox_extension_consumeAddr, sizeof(uint64_t)); // insert vars

		uint64_t codeStart = stringAllocSize + codeVarSize;
		memcpy((void*)(payloadIntPtr+codeStart), &code[0], sizeof(code)); // insert code

		printf("constructed sandbox_extension_consume payload!\n");

		runPayload(task, (uint8_t*)payload, payloadSize, codeStart, allImageInfoAddr);
		free(payload);
	}
	else
	{
		printf("No Sandbox extension needed, skipping straight to dylib injection...\n");
	}

	// STEP FOUR: load dylib
	size_t stringAllocSize = 256;
	uint64_t codeVarCount = 1;
	uint64_t codeVarSize = codeVarCount * sizeof(uint64_t);

	uint32_t code[3] = {
		// sandbox_extension_consume call
		// arg1: x0, pointer to extension string
		CFSwapInt32(0xC0F7FF10), // adr x0, -0x108 ; reference to dylibPath
		CFSwapInt32(0xA8FFFF58), // ldr x8, -0x0C ; dlopenAddr
		CFSwapInt32(0x00013FD6), // blr x8
	};

	size_t payloadSize = stringAllocSize + codeVarSize + sizeof(code);
	char* payload = malloc(payloadSize);
	bzero(&payload[0], payloadSize);
	strlcpy(&payload[0], dylibPath, stringAllocSize); // insert extString

	intptr_t payloadIntPtr = (intptr_t)payload;
	memcpy((void*)(payloadIntPtr+stringAllocSize), (const void*)&dlopenAddr, sizeof(uint64_t)); // insert vars

	uint64_t codeStart = stringAllocSize + codeVarSize;
	memcpy((void*)(payloadIntPtr+codeStart), &code[0], sizeof(code)); // insert code

	printf("constructed dlopen payload!\n");

	runPayload(task, (uint8_t*)payload, payloadSize, codeStart, allImageInfoAddr);
	free(payload);

	// STEP FIVE: detach from process

	/*ret = ptrace(PT_DETACH, pid, NULL, 0);
	if(ret != KERN_SUCCESS)
	{
		printf("Error detaching from process %d\n", pid);
		return 1;
	}*/

	return 0;
}

================================================
FILE: task_utils.h
================================================
kern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size);
char *task_copy_string(task_t task, vm_address_t address);
kern_return_t task_write(task_t task, vm_address_t address, void* inBuf, vm_size_t size);

================================================
FILE: task_utils.m
================================================
#import <mach/mach.h>
#import <stdlib.h>

kern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size)
{
	size_t maxSize = size;
	kern_return_t kr = vm_read_overwrite(task, address, size, (vm_address_t)outBuf, &maxSize);
	if (kr == KERN_SUCCESS) {
		if (maxSize < size) {
			uint8_t *outBufU = outBuf;
			memset(&outBufU[maxSize-1], 0, size - maxSize);
		}
	}
	return kr;
}

char *task_copy_string(task_t task, vm_address_t address)
{
	// find end of string
	size_t len = 0;
	char buf = 0;
	do {
		if (task_read(task, address + (len++), &buf, sizeof(buf)) != KERN_SUCCESS) return NULL;
	} while (buf != '\0');

	// copy string
	char *strBuf = malloc(len);
	if (task_read(task, address, &strBuf[0], len) != KERN_SUCCESS) return NULL;
	return strBuf;
}

kern_return_t task_write(task_t task, vm_address_t address, void* inBuf, vm_size_t size)
{
	return vm_write(task, address, (vm_offset_t)inBuf, size);
}

================================================
FILE: thread_utils.h
================================================
#import <mach/arm/thread_status.h>
#import <mach/thread_status.h>
#import <mach/mach.h>
#import <mach/error.h>
#import <CoreFoundation/CoreFoundation.h>


#define STACK_SIZE 65536
static uint64_t ropLoop;

#if __DARWIN_OPAQUE_ARM_THREAD_STATE64
#define FP_UNIFIED __opaque_fp
#define LR_UNIFIED __opaque_lr
#define SP_UNIFIED __opaque_sp
#define PC_UNIFIED __opaque_pc
#define FLAGS_UNIFIED __opaque_flags
#define REGISTER_TYPE void*
#else
#define FP_UNIFIED __fp
#define LR_UNIFIED __lr
#define SP_UNIFIED __sp
#define PC_UNIFIED __pc
#define FLAGS_UNIFIED __pad
#define REGISTER_TYPE uint64_t
#endif

struct arm64_thread_full_state {
	arm_thread_state64_t    thread;
	arm_exception_state64_t exception;
	arm_neon_state64_t      neon;
	arm_debug_state64_t     debug;
	uint32_t                thread_valid:1,
							exception_valid:1,
							neon_valid:1,
							debug_valid:1,
							cpmu_valid:1;
};

extern struct arm64_thread_full_state* thread_save_state_arm64(thread_act_t thread);
extern bool thread_restore_state_arm64(thread_act_t thread, struct arm64_thread_full_state* state);
extern kern_return_t wait_for_thread(thread_act_t thread, uint64_t pcToWait, struct arm_unified_thread_state* stateOut);
extern void printThreadState_state(struct arm_unified_thread_state threadState);
extern void printThreadState(thread_act_t thread);
extern void printThreadInfo(thread_act_t thread);
kern_return_t suspend_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread);
kern_return_t resume_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread);


================================================
FILE: thread_utils.m
================================================
#import "thread_utils.h"

#import <stdio.h>
#import <unistd.h>
#import <stdlib.h>

#import <mach-o/getsect.h>
#import <mach-o/dyld.h>
#import <mach-o/loader.h>
#import <mach-o/nlist.h>
#import <mach-o/reloc.h>
#import <mach-o/dyld_images.h>

struct arm64_thread_full_state* thread_save_state_arm64(thread_act_t thread)
{
	struct arm64_thread_full_state* s = malloc(sizeof(struct arm64_thread_full_state));
	mach_msg_type_number_t count;
	kern_return_t kr;

	// ARM_THREAD_STATE64
	count = ARM_THREAD_STATE64_COUNT;
	kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t) &s->thread, &count);
	s->thread_valid = (kr == KERN_SUCCESS);
	if (kr != KERN_SUCCESS) {
		printf("ERROR: Failed to save ARM_THREAD_STATE64 state: %s", mach_error_string(kr));
		free(s);
		return NULL;
	}
	// ARM_EXCEPTION_STATE64
	count = ARM_EXCEPTION_STATE64_COUNT;
	kr = thread_get_state(thread, ARM_EXCEPTION_STATE64,
			(thread_state_t) &s->exception, &count);
	s->exception_valid = (kr == KERN_SUCCESS);
	if (kr != KERN_SUCCESS) {
		printf("WARNING: Failed to save ARM_EXCEPTION_STATE64 state: %s", mach_error_string(kr));
	}
	// ARM_NEON_STATE64
	count = ARM_NEON_STATE64_COUNT;
	kr = thread_get_state(thread, ARM_NEON_STATE64, (thread_state_t) &s->neon, &count);
	s->neon_valid = (kr == KERN_SUCCESS);
	if (kr != KERN_SUCCESS) {
		printf("WARNING: Failed to save ARM_NEON_STATE64 state: %s", mach_error_string(kr));
	}
	// ARM_DEBUG_STATE64
	count = ARM_DEBUG_STATE64_COUNT;
	kr = thread_get_state(thread, ARM_DEBUG_STATE64, (thread_state_t) &s->debug, &count);
	s->debug_valid = (kr == KERN_SUCCESS);
	if (kr != KERN_SUCCESS) {
		printf("WARNING: Failed to save ARM_DEBUG_STATE64 state: %s", mach_error_string(kr));
	}

	return s;
}

bool thread_restore_state_arm64(thread_act_t thread, struct arm64_thread_full_state* state)
{
	struct arm64_thread_full_state *s = (void *) state;
	kern_return_t kr;
	bool success = true;
	// ARM_THREAD_STATE64
	if (s->thread_valid) {
		kr = thread_set_state(thread, ARM_THREAD_STATE64, (thread_state_t) &s->thread, ARM_THREAD_STATE64_COUNT);
		if (kr != KERN_SUCCESS) {
			printf("ERROR: Failed to restore ARM_THREAD_STATE64 state: %s", mach_error_string(kr));
			success = false;
		}
	}
	// ARM_EXCEPTION_STATE64
	if (s->exception_valid) {
		kr = thread_set_state(thread, ARM_EXCEPTION_STATE64, (thread_state_t) &s->exception, ARM_EXCEPTION_STATE64_COUNT);
		if (kr != KERN_SUCCESS) {
			printf("ERROR: Failed to restore ARM_EXCEPTION_STATE64 state: %s", mach_error_string(kr));
			success = false;
		}
	}
	// ARM_NEON_STATE64
	if (s->neon_valid) {
		kr = thread_set_state(thread, ARM_NEON_STATE64, (thread_state_t) &s->neon, ARM_NEON_STATE64_COUNT);
		if (kr != KERN_SUCCESS) {
			printf("ERROR: Failed to restore ARM_NEON_STATE64 state: %s", mach_error_string(kr));
			success = false;
		}
	}
	// ARM_DEBUG_STATE64
	if (s->debug_valid) {
		kr = thread_set_state(thread, ARM_DEBUG_STATE64, (thread_state_t) &s->debug, ARM_DEBUG_STATE64_COUNT);
		if (kr != KERN_SUCCESS) {
			printf("ERROR: Failed to restore ARM_DEBUG_STATE64 state: %s", mach_error_string(kr));
			success = false;
		}
	}
	// Now free the struct.
	free(s);
	return success;
}

kern_return_t wait_for_thread(thread_act_t thread, uint64_t pcToWait, struct arm_unified_thread_state* stateOut)
{
	mach_msg_type_number_t stateToObserveCount = ARM_THREAD_STATE64_COUNT;
	struct arm_unified_thread_state stateToObserve;

	int errCount = 0;
	while(1)
	{
		kern_return_t kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&stateToObserve.ts_64, &stateToObserveCount);
		if(kr != KERN_SUCCESS)
		{
			if (pcToWait == 0) return kr;

			errCount++;
			if(errCount >= 5)
			{
				return kr;
			}
			continue;
		}

		errCount = 0;

		// wait until pc matches with infinite loop rop gadget
		uint64_t pc = (uint64_t)__darwin_arm_thread_state64_get_pc(stateToObserve.ts_64);
		if(pc == pcToWait) {
			break;
		}
	}

	if(stateOut)
	{
		*stateOut = stateToObserve;
	}

	return KERN_SUCCESS;
}

kern_return_t suspend_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread)
{
	for (int i = 0; i < threadCount; i++) {
		thread_act_t thread = allThreads[i];
		if (thread != exceptForThread) {
			thread_suspend(thread);
		}
	}
	return KERN_SUCCESS;
}

kern_return_t resume_threads_except_for(thread_act_array_t allThreads, mach_msg_type_number_t threadCount, thread_act_t exceptForThread)
{
	for (int i = 0; i < threadCount; i++) {
		thread_act_t thread = allThreads[i];
		if (thread != exceptForThread) {
			thread_resume(thread);
		}
	}
	return KERN_SUCCESS;
}

void printThreadState_state(struct arm_unified_thread_state threadState)
{
	for(int i = 0; i <= 28; i++)
	{
		printf("x%d = 0x%llX\n", i, threadState.ts_64.__x[i]);
	}
	//printf("fp: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_fp(threadState.ts_64));
	//printf("lr: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_lr(threadState.ts_64));
	//printf("sp: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_sp(threadState.ts_64));
	//printf("pc: 0x%llX\n", (uint64_t)__darwin_arm_thread_state64_get_pc(threadState.ts_64));
	printf("pc: 0x%llX\n", (uint64_t)threadState.ts_64.PC_UNIFIED);
	printf("sp: 0x%llX\n", (uint64_t)threadState.ts_64.SP_UNIFIED);
	printf("fp: 0x%llX\n", (uint64_t)threadState.ts_64.FP_UNIFIED);
	printf("lr: 0x%llX\n", (uint64_t)threadState.ts_64.LR_UNIFIED);
	printf("cpsr: 0x%X\n", threadState.ts_64.__cpsr);
	#if __arm64e__
	printf("flags: 0x%X\n", threadState.ts_64.__opaque_flags);
	#endif
}

void printThreadState(thread_act_t thread)
{
	printf("- THREAD STATE -\n");

	mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
	struct arm_unified_thread_state threadState;
	kern_return_t kr = thread_get_state(thread, ARM_THREAD_STATE64, (thread_state_t)&threadState.ts_64, &thread_state_count);
	if(kr != KERN_SUCCESS)
	{
		printf("<Failed to read thread state>\n");
		return;
	}

	printThreadState_state(threadState);
}

void printThreadInfo(thread_act_t thread)
{
	printf("- INFO OF THREAD %d -\n", thread);

	thread_basic_info_data_t basicInfo;
	mach_msg_type_number_t biCount = THREAD_BASIC_INFO_COUNT;
	kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&basicInfo, &biCount);
	if(kr != KERN_SUCCESS)
	{
		printf("<Failed to fetch info>\n");
		return;
	}

	printf("cpu_usage: %d\n", basicInfo.cpu_usage);
	printf("flags: %d\n", basicInfo.flags);
	printf("policy: %d\n", basicInfo.policy);
	printf("run_state: %d\n", basicInfo.run_state);
	printf("sleep_time: %d\n", basicInfo.sleep_time);
	printf("suspend_count: %d\n", basicInfo.suspend_count);

	printf("system_time: %d.%d\n", basicInfo.system_time.seconds, basicInfo.system_time.microseconds);
	printf("user_time: %d.%d\n", basicInfo.user_time.seconds, basicInfo.user_time.microseconds);
}
Download .txt
gitextract_p0sncp36/

├── .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
Download .txt
SYMBOL INDEX (28 symbols across 3 files)

FILE: CoreSymbolication.h
  type sCSTypeRef (line 62) | struct sCSTypeRef {
  type CSTypeRef (line 66) | typedef struct sCSTypeRef CSTypeRef;
  type CSTypeRef (line 69) | typedef CSTypeRef CSSymbolicatorRef;
  type CSTypeRef (line 70) | typedef CSTypeRef CSSourceInfoRef;
  type CSTypeRef (line 71) | typedef CSTypeRef CSSymbolOwnerRef;
  type CSTypeRef (line 72) | typedef CSTypeRef CSSectionRef;
  type CSTypeRef (line 73) | typedef CSTypeRef CSSegmentRef;
  type CSTypeRef (line 74) | typedef CSTypeRef CSSymbolRef;
  type CSTypeRef (line 75) | typedef CSTypeRef CSRegionRef;
  type CSTypeRef (line 76) | typedef CSTypeRef CSUUIDRef;
  type sCSRange (line 79) | struct sCSRange {
  type CSRange (line 83) | typedef struct sCSRange CSRange;
  type CSNotificationData (line 87) | typedef struct sCSNotificationData {

FILE: sandbox.h
  type sandbox_filter_type (line 1) | enum sandbox_filter_type {
  type sandbox_extension_flags (line 23) | enum sandbox_extension_flags {
  type sandbox_filter_type (line 40) | enum sandbox_filter_type
  type sandbox_filter_type (line 41) | enum sandbox_filter_type
  type sandbox_filter_type (line 42) | enum sandbox_filter_type
  type sandbox_filter_type (line 43) | enum sandbox_filter_type
  type sandbox_filter_type (line 44) | enum sandbox_filter_type
  type sandbox_filter_type (line 55) | enum sandbox_filter_type
  type sandbox_filter_type (line 56) | enum sandbox_filter_type
  type sandbox_filter_type (line 57) | enum sandbox_filter_type

FILE: thread_utils.h
  type arm64_thread_full_state (line 27) | struct arm64_thread_full_state {
  type arm64_thread_full_state (line 39) | struct arm64_thread_full_state
  type arm64_thread_full_state (line 40) | struct arm64_thread_full_state
  type arm_unified_thread_state (line 41) | struct arm_unified_thread_state
  type arm_unified_thread_state (line 42) | struct arm_unified_thread_state
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (93K chars).
[
  {
    "path": ".gitignore",
    "chars": 35,
    "preview": ".theos\npackages\n.DS_Store\n.scrapped"
  },
  {
    "path": "CoreSymbolication.h",
    "chars": 20364,
    "preview": "//\n//  CoreSymbolication.h\n//\n//  Created by R J Cooper on 05/06/2012.\n//  This file: Copyright (c) 2012 Mountainstorm\n/"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2022 Lars Fröder\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "Makefile",
    "chars": 441,
    "preview": "TARGET := iphone:clang:16.5:11.0\nARCHS = arm64 arm64e\n\ninclude $(THEOS)/makefiles/common.mk\n\nTOOL_NAME = opainject\n\nopai"
  },
  {
    "path": "README.md",
    "chars": 410,
    "preview": "# opainject\n\niOS tool to inject a dylib into a process using both shellcode and ROP methods. (By default ROP method is u"
  },
  {
    "path": "arm64.h",
    "chars": 442,
    "preview": "#ifndef arm64_h\n#define arm64_h\n\n#include <stdio.h>\n#include <stdbool.h>\n\nuint32_t generate_movk(uint8_t x, uint16_t val"
  },
  {
    "path": "arm64.m",
    "chars": 2928,
    "preview": "#include \"arm64.h\"\n#include <stdbool.h>\n\nuint32_t generate_movk(uint8_t x, uint16_t val, uint16_t lsl)\n{\n\tuint32_t base "
  },
  {
    "path": "control",
    "chars": 185,
    "preview": "Package: com.opa334.opainject\nName: opainject\nVersion: 1.0.6\nArchitecture: iphoneos-arm\nDescription: Injection tool!\nMai"
  },
  {
    "path": "dyld.h",
    "chars": 597,
    "preview": "extern vm_address_t getRemoteImageAddress(task_t task, vm_address_t imageStartPtr, const char* imageName);\nextern vm_add"
  },
  {
    "path": "dyld.m",
    "chars": 10217,
    "preview": "#import <stdio.h>\n#import <stdlib.h>\n#import <unistd.h>\n#import <dlfcn.h>\n#import <mach-o/getsect.h>\n#import <mach-o/dyl"
  },
  {
    "path": "entitlements.plist",
    "chars": 497,
    "preview": "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1"
  },
  {
    "path": "main.m",
    "chars": 3752,
    "preview": "#import <stdio.h>\n#import <stdlib.h>\n#import <unistd.h>\n#import <dlfcn.h>\n#import <mach-o/getsect.h>\n#import <mach-o/dyl"
  },
  {
    "path": "pac.h",
    "chars": 1305,
    "preview": "#ifndef PTRAUTH_HELPERS_H\n#define PTRAUTH_HELPERS_H\n// Helpers for PAC archs.\n\n// If the compiler understands __arm64e__"
  },
  {
    "path": "rop_inject.h",
    "chars": 108,
    "preview": "extern void injectDylibViaRop(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr);"
  },
  {
    "path": "rop_inject.m",
    "chars": 15933,
    "preview": "#import <stdio.h>\n#import <unistd.h>\n#import <stdlib.h>\n#import <dlfcn.h>\n#import <errno.h>\n#import <string.h>\n#import <"
  },
  {
    "path": "sandbox.h",
    "chars": 4078,
    "preview": "enum sandbox_filter_type {\n\tSANDBOX_FILTER_NONE,\n\tSANDBOX_FILTER_PATH,\n\tSANDBOX_FILTER_GLOBAL_NAME,\n\tSANDBOX_FILTER_LOCA"
  },
  {
    "path": "shellcode_inject.h",
    "chars": 106,
    "preview": "int injectDylibViaShellcode(task_t task, pid_t pid, const char* dylibPath, vm_address_t allImageInfoAddr);"
  },
  {
    "path": "shellcode_inject.m",
    "chars": 14985,
    "preview": "#include <stdlib.h>\n#include <sys/wait.h>\n#include <stdio.h>\n#import <unistd.h>\n#import <dlfcn.h>\n#import <mach-o/getsec"
  },
  {
    "path": "task_utils.h",
    "chars": 238,
    "preview": "kern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_size_t size);\nchar *task_copy_string(task_t "
  },
  {
    "path": "task_utils.m",
    "chars": 931,
    "preview": "#import <mach/mach.h>\n#import <stdlib.h>\n\nkern_return_t task_read(task_t task, vm_address_t address, void *outBuf, vm_si"
  },
  {
    "path": "thread_utils.h",
    "chars": 1668,
    "preview": "#import <mach/arm/thread_status.h>\n#import <mach/thread_status.h>\n#import <mach/mach.h>\n#import <mach/error.h>\n#import <"
  },
  {
    "path": "thread_utils.m",
    "chars": 6839,
    "preview": "#import \"thread_utils.h\"\n\n#import <stdio.h>\n#import <unistd.h>\n#import <stdlib.h>\n\n#import <mach-o/getsect.h>\n#import <m"
  }
]

About this extraction

This page contains the full source code of the opa334/opainject GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (85.1 KB), approximately 24.5k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!