1. 파일 탐지
1-1. Objective-C 메소드
- – (BOOL)isReadableFileAtPath:(NSString *)path;
- – (BOOL)fileExistsAtPath: (NSString *)path isDirectory: (BOOL *)isDirectory
- – (BOOL)fileExistsAtPath: (NSString *)path
- – (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError * _Nullable *)error;
Example
if ([[NSFileManager defaultManager] fileExistsAtPath:@”/Applications/Sileo.app”]]) {
return YES; //Detected Jailbroken
}
1-2. C System Library
- FILE *fopen(const char *pathname, const char *mode);
- int access(const char *path, int amode);
- int open(const char *path, int oflag, …);
- int lstat(const char *path, struct stat * buf);
Example
#include <unistd.h>
if (access("/Applications/Sileo.app", F_OK) == 0) {
return YES; //Detected Jailbroken
}
1-3. Supervisor Call (Low-level, SVC #0x80)
Example
static inline int SVC_statfs64(const char* path, struct statfs *buf) {
int64_t flag = 0;
__asm __volatile("mov x0, %0" :: "r" ((int64_t)SYS_statfs64)); //SYS_statfs64
__asm __volatile("mov x1, %0" :: "r" (path)); //path
__asm __volatile("mov x2, %0" :: "r" (buf)); //struct statfs
__asm __volatile("mov x16, %0" :: "r" ((int64_t)SYS_syscall)); //SYS_syscall
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
return (int)flag;
}
static inline int SVC_Access(const char* detectionPath, int64_t mode) {
int64_t flag = 0;
__asm __volatile("mov x0, %0" :: "r" ((int64_t)SYS_access)); //SYS_access
__asm __volatile("mov x1, %0" :: "r" (detectionPath)); //path
__asm __volatile("mov x2, %0" :: "r" (mode)); //mode
__asm __volatile("mov x16, %0" :: "r" ((int64_t)SYS_syscall)); //SYS_syscall
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
return (int)flag;
}
if(SVC_Access("Applications/Sileo/app", F_OK) != ENOENT) {
return YES; //Detected Jailbroken
}
-(NSArray *)jailbreakFiles {
NSArray *file = [NSArray arrayWithObjects:
@"/Applications/Cydia.app",
@"/Applications/Sileo.app",
@"/var/binpack",
@"/Library/MobileSubstrate/DynamicLibraries",
@"/Library/PreferenceBundles/LibertyPref.bundle",
@"/Library/PreferenceBundles/ShadowPreferences.bundle",
@"/Library/PreferenceBundles/ABypassPrefs.bundle",
@"/Library/PreferenceBundles/FlyJBPrefs.bundle",
@"/usr/lib/libhooker.dylib",
@"/usr/lib/libsubstitute.dylib",
@"/usr/lib/substrate",
@"/usr/lib/TweakInject",
nil];
return file;
}
...
+(BOOL)isJailbreakFileExist {
BOOL check = NO;
NSArray *jbPatternFile = [[[XFJailbreakPattern alloc] init] jailbreakFiles];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *jbFile in jbPatternFile) {
const char *jbFile2 = [jbFile cStringUsingEncoding:NSUTF8StringEncoding];
//NSFileManager fileExistsAtPath
if ([fileManager fileExistsAtPath:jbFile]) {
NSLog(@"NSFilemanager: %@", jbFile);
check = YES;
}
//System Library - opendir: Sustitute doesn't like hooking opendir :)
DIR *dirPoint = opendir(jbFile2);
if (dirPoint != NULL) {
NSLog(@"opendir: %@ - %p", jbFile, dirPoint);
check = YES;
}
//syscall - SYS_access
if(syscall(SYS_access, jbFile2, F_OK) == 0) {
NSLog(@"Syscall SYS_access: %@", jbFile);
check = YES;
}
//SVC #0x80 - SYS_syscall - SYS_access, SYS_access, SYS_lstat64, SYS_stat64, SYS_statfs64, SYS_open
#if defined __arm64__ || defined __arm64e__
int64_t flag = ENOENT;
__asm __volatile("mov x0, #0x21"); //access
__asm __volatile("mov x1, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x2, #0"); //mode
__asm __volatile("mov x16, #0"); //syscall
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
#else
int flag = ENOENT;
__asm __volatile("mov r0, #0x21"); //access
__asm __volatile("mov r1, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov r2, #0"); //mode
__asm __volatile("mov r12, #0"); //syscall
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, r0" : "=r" (flag));
#endif
if (flag != ENOENT ) {
NSLog(@"SVC #0x80 SYS_syscall - SYS_access: %s", jbFile2);
check = YES;
}
#if defined __arm64__ || defined __arm64e__
flag = ENOENT;
__asm __volatile("mov x0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, #0"); //mode
__asm __volatile("mov x16, #0x21"); //access
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
#else
flag = ENOENT;
__asm __volatile("mov r0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov r1, #0"); //mode
__asm __volatile("mov r12, #0x21"); //access
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, r0" : "=r" (flag));
#endif
if (flag != ENOENT ) {
NSLog(@"SVC #0x80 SYS_access: %s", jbFile2);
check = YES;
}
struct stat statPoint;
#if defined __arm64__ || defined __arm64e__
flag = ENOENT;
__asm __volatile("mov x0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, %0" :: "r" (&statPoint)); //struct stat
__asm __volatile("mov x16, #0x154"); //lstat64
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
#else
flag = ENOENT;
__asm __volatile("mov r0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, %0" :: "r" (&statPoint)); //struct stat
__asm __volatile("mov r12, #0x154"); //lstat64
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, r0" : "=r" (flag));
#endif
if (flag != ENOENT ) {
NSLog(@"SVC #0x80 SYS_lstat64: %s", jbFile2);
check = YES;
}
#if defined __arm64__ || defined __arm64e__
flag = ENOENT;
__asm __volatile("mov x0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, %0" :: "r" (&statPoint)); //struct stat
__asm __volatile("mov x16, #0x152"); //stat64
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
#else
flag = ENOENT;
__asm __volatile("mov r0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, %0" :: "r" (&statPoint)); //struct stat
__asm __volatile("mov r12, #0x152"); //stat64
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, r0" : "=r" (flag));
#endif
if (flag != ENOENT ) {
NSLog(@"SVC #0x80 SYS_stat64: %s", jbFile2);
check = YES;
}
struct statfs statfsPoint;
#if defined __arm64__ || defined __arm64e__
flag = ENOENT;
__asm __volatile("mov x0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, %0" :: "r" (&statfsPoint)); //struct statfs
__asm __volatile("mov x16, #0x159"); //statfs64
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
#else
flag = ENOENT;
__asm __volatile("mov r0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, %0" :: "r" (&statfsPoint)); //struct statfs
__asm __volatile("mov r12, #0x159"); //statfs64
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, r0" : "=r" (flag));
#endif
if (flag != ENOENT ) {
NSLog(@"SVC #0x80 SYS_statfs64: %s", jbFile2);
check = YES;
}
#if defined __arm64__ || defined __arm64e__
flag = 0;
__asm __volatile("mov x0, %0" :: "r" (jbFile2)); //path
__asm __volatile("mov x1, #0");
__asm __volatile("mov x2, #0");
__asm __volatile("mov x16, #0x5"); //open
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("bcc #0xC");
__asm __volatile("mov x0, #0x0");
__asm __volatile("b #0x8");
__asm __volatile("mov x0, #0x1");
__asm __volatile("mov %0, x0" : "=r" (flag));
#else
flag = 0;
__asm __volatile("mov r0, %0" :: "r" (jbFile2)); // path
__asm __volatile("mov r1, #0");
__asm __volatile("mov r2, #0");
__asm __volatile("mov r12, #0x5"); // open
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("bcc #0x6");
__asm __volatile("mov r0, 0x0");
__asm __volatile("b #0x4");
__asm __volatile("mov r0, #0x1");
__asm __volatile("mov %0, r0" : "=r" (flag));
#endif
if(flag == 1) {
NSLog(@"SVC #0x80 SYS_open: %s", jbFile2);
check = YES;
}
}
return check;
}
2. 샌드박스 우회 탐지
2-1. Objective-C / C System Library
Example
NSError *error;
[@"Jailbreak Test" writeToFile:@”/private/var/sandbox.txt” atomically:YES encoding:NSUTF8StringEncoding error:&error];
if(error == nil) {
return YES; //Detected Jailbroken
}
2-2. Private API
- int sandbox_check(pid_t, const char *operation, int sandbox_filter_type, …);
(operation: file-read*)
Example
void *sandbox = dlopen("/usr/lib/system/libsystem_sandbox.dylib", RTLD_NOW);
if(sandbox != NULL)
{
_sandbox_check = dlsym(sandbox, "sandbox_check");
if(_sandbox_check != NULL)
{
int filter = SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT;
if(_sandbox_check(getpid(), "file-read*", filter, "/Library") != 1)
{
return YES; //Detected Jailbroken
}
}
}
3. URL Scheme 탐지
- -(BOOL)canOpenURL:(NSURL *)arg1;
Example
NSURL *url = [NSURL URLWithString:@"sileo://"];
if([[UIApplication sharedApplication] canOpenURL:url]) {
return YES; //Detected Jailbroken
}
4. 후킹 라이브러리 탐지
4-1. Private API
- kern_return_t task_info(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt);
- uint32_t _dyld_image_count(void);
- const struct mach_header* _dyld_get_image_header(uint32_t image_index);
- const char* _dyld_get_image_name(uint32_t image_index);
- kern_return_t vm_region_recurse_64(vm_map_read_t target_task, vm_address_t *address, vm_size_t *size, natural_t *nesting_depth, vm_region_recurse_info_t info, mach_msg_type_number_t *infoCnt)
- kern_return_t vm_region_64(vm_map_read_t target_task, vm_address_t *address, vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name);
- int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize);
Example
+(BOOL)isJailbreakInjectExist {
BOOL check = NO;
integer_t task_info_out[TASK_DYLD_INFO_COUNT];
mach_msg_type_number_t task_info_outCnt = TASK_DYLD_INFO_COUNT;
if(task_info(mach_task_self_, TASK_DYLD_INFO, task_info_out, &task_info_outCnt) == KERN_SUCCESS) {
struct task_dyld_info dyld_info = *(struct task_dyld_info*)(void*)(task_info_out);
struct dyld_all_image_infos* infos = (struct dyld_all_image_infos *) dyld_info.all_image_info_addr;
struct dyld_uuid_info* pUuid_info = (struct dyld_uuid_info*) infos->uuidArray;
for( int i = 0; i < infos->uuidArrayCount; i++, pUuid_info += 1)
{
const struct mach_header_64* mheader = (const struct mach_header_64*)pUuid_info->imageLoadAddress;
if (mheader->filetype == MH_DYLIB) {
if(mheader->magic == MH_MAGIC_64 && mheader->ncmds > 0)
{
void *loadCmd = (void*)(mheader + 1);
struct segment_command_64 *sc = (struct segment_command_64 *)loadCmd;
for (int index = 0; index < mheader->ncmds; ++index, sc = (struct segment_command_64*)((BYTE*)sc + sc->cmdsize))
{
if (sc->cmd == LC_ID_DYLIB) {
struct dylib_command *dc = (struct dylib_command *)sc;
struct dylib dy = dc->dylib;
const char *detectedDyld = (char*)dc + dy.name.offset;
for (NSString *jbDyld in jbPatternDyld) {
if([[NSString stringWithUTF8String:detectedDyld] containsString:jbDyld]) {
NSLog(@"dyld2: %s", detectedDyld);
check = YES;
break;
}
}
}
}
}
}
}
}
return check;
}
Reference
https://knight.sc/reverse%20engineering/2019/04/15/detecting-task-modifications.html
https://www.romainthomas.fr/post/22-08-singpass-rasp-analysis/
https://gist.github.com/ddrccw/8412847#file-hello-h-L122
4-2. C System Library
- int dladdr(void *addr, Dl_info *info);
- void *dlopen(const char *filename, int flag);
- void *dlsym(void *handle, const char *symbol);
Example
-(NSArray *)jailbreakSymbols {
NSArray *symbol = [NSArray arrayWithObjects:
@"MSHookFunction",
@"MSHookMessageEx",
@"MSFindSymbol",
@"MSGetImageByName",
@"ZzBuildHook",
@"DobbyHook",
@"LHHookFunctions",
nil];
return symbol;
}
...
NSArray *jbPatternSymbol = [[[XFJailbreakPattern alloc] init] jailbreakSymbols];
for (NSString *jbSymbol in jbPatternSymbol) {
const char *jbSymbol2 = [jbSymbol cStringUsingEncoding:NSUTF8StringEncoding];
void* dlpoint = dlsym((void *)RTLD_DEFAULT, jbSymbol2);
if(dlpoint != NULL) {
return YES; //Detected Jailbroken
}
}
static void _check_image(const struct mach_header *header, intptr_t slide) {
NSSet *dylibSet = [NSSet setWithObjects:
@"/usr/lib/CepheiUI.framework/CepheiUI",
@"/usr/lib/libsubstitute.dylib"
@"/usr/lib/substitute-inserter.dylib",
@"/usr/lib/substitute-loader.dylib",
nil];
Dl_info info;
if (dladdr(header, &info) == 0) {
char *dlerro = dlerror();
if(dlerro == NULL && info.dli_fname != NULL) {
NSString *libName = [NSString stringWithUTF8String:info.dli_fname];
if ([dylibSet containsObject:libName]) {
return YES; //Detected Jailbroken
}
}
return;
}
}
bool hasHookedMethods(void)
{
bool ret = false;
MethodsList = [[NSMutableArray alloc] init];
int classCount = 5;
const char* classes[5] =
{
"NSFileManager",
"UIApplication",
"NSString",
"NSData",
"NSBundle",
};
for(int i = 0; i < classCount; i++)
{
Class ourClass = objc_getClass(classes[i]);
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(ourClass, &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
Dl_info image_info;
if(dladdr(class_getMethodImplementation(ourClass, method_getName(method)), &image_info) != 0)
{
struct mach_header_64 *header = image_info.dli_fbase;
if(header && header->magic == MH_MAGIC_64)
{
struct load_command *loadCmd = (struct load_command *) (header + 1);
struct segment_command_64 *sc = (struct segment_command_64 *)loadCmd;
for (int index = 0; index < header->ncmds; ++index, sc = (struct segment_command_64*)((char*)sc + sc->cmdsize))
{
if(sc->cmd == LC_LOAD_DYLIB)
{
struct dylib_command *dc = (struct dylib_command *)sc;
struct dylib dy = dc->dylib;
const char *detectedDyld = (char*)dc + dy.name.offset;
if(strcmp(detectedDyld, "/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate") == 0)
{
NSString *func = [NSString stringWithFormat:@"%s %s", class_getName(ourClass), sel_getName(method_getName(method))];
[MethodsList addObject:func];
ret = true;
}
}
}
}
}
}
}
return ret;
}
5. 커널 접근 탐지
- kern_return_t task_for_pid(mach_port_t parent, int pid, mach_port_t *task_out );
- kern_return_t host_get_special_port(host_priv_t host_priv, int node, int which, mach_port_t *port);
Example
bool hasKernelTaskPort(void)
{
mach_port_t kernel_task = MACH_PORT_NULL;
kern_return_t ret = task_for_pid(mach_task_self(), 0, &kernel_task);
if(ret == KERN_SUCCESS && MACH_PORT_VALID(kernel_task))
{
kernelTaskPort = kernel_task;
mach_port_deallocate(mach_task_self(), kernel_task);
return true;
}
else
{
host_t host = mach_host_self();
ret = host_get_special_port(host, HOST_LOCAL_NODE, 4, &kernel_task);
if(ret == KERN_SUCCESS && MACH_PORT_VALID(kernel_task))
{
kernelTaskPort = kernel_task;
mach_port_deallocate(mach_task_self(), kernel_task);
return true;
}
mach_port_deallocate(mach_task_self(), host);
}
return false;
}
if(hasKernelTaskPort()) {
return YES; //Detected Jailbroken
}
6. 환경 탐지
- char ***_NSGetEnviron(void);
- char *getenv(const char *varname);
- extern char **environ;
- [[NSProcessInfo processInfo] environment];
Example
-(NSArray *)jailbreakEnvs {
NSArray *env = [NSArray arrayWithObjects:
@"JB_ROOT_PATH",
@"_MSSafeMode",
@"DYLD_INSERT_LIBRARIES",
@"substitute",
nil];
return env;
}
+(BOOL)isJailbreakInjectExist {
BOOL check = NO;
NSArray *jbPatternEnv = [[[XFJailbreakPattern alloc] init] jailbreakEnvs];
char ***envp = _NSGetEnviron();
if (envp) {
char **env = *envp;
while (*env) {
for (NSString *jbEnv in jbPatternEnv) {
if([[NSString stringWithUTF8String:*env] containsString:jbEnv]) {
check = YES;
}
}
env++;
}
}
//Env Check2
extern char **environ;
for(int i=0; environ[i]; i++)
{
for (NSString *jbEnv in jbPatternEnv) {
if([[NSString stringWithUTF8String:environ[i]] containsString:jbEnv]) {
check = YES;
}
}
}
return check;
}
7. 루트 파일 시스템 쓰기 여부 / 리마운트 (iOS ~14.x)
rootless 탈옥 환경에서는 /usr/standalone/firmware 마운트된 Device 경로를 참고하여
/private/preboot/(UUID)에 접근해서 stat으로 파일 갯수 확인
- int statfs(const char *path, struct statfs *buf);
- int getmntinfo(struct statfs **mntbufp, int flags);
Example
bool hasRenamedRootFS(void)
{
struct statfs *st;
int num_fs = getmntinfo(&st, MNT_NOWAIT);
if(num_fs != 0) {
for (int i = 0; i < num_fs; i++) {
if(strstr(st[i].f_mntfromname, "com.apple.os.update-") != NULL) {
return false;
}
if(strstr(st[i].f_mntfromname, "orig-fs") != NULL) {
return true;
}
if(strcmp(st[i].f_mntfromname, "/dev/disk0s1s1") == 0) {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.3"))
return true;
}
}
}
return false;
}
static inline int SVC_statfs64(const char* path, struct statfs *buf) {
int64_t flag = 0;
__asm __volatile("mov x0, %0" :: "r" ((int64_t)SYS_statfs64)); //SYS_statfs64
__asm __volatile("mov x1, %0" :: "r" (path)); //path
__asm __volatile("mov x2, %0" :: "r" (buf)); //struct statfs
__asm __volatile("mov x16, %0" :: "r" ((int64_t)SYS_syscall)); //SYS_syscall
__asm __volatile("svc #0x80"); //supervisor call
__asm __volatile("mov %0, x0" : "=r" (flag));
return (int)flag;
}
bool hasRootFSmountedRW(void)
{
struct statfs rootfs;
if(SVC_statfs64("/", &rootfs) == 0)
{
if(rootfs.f_flags & MNT_RDONLY)
{
return false;
}
}
return true;
}
8. 포트
- int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
- int socket(int domain, int type, int protocol);
- SSH 포트: 22, 2222 | Frida 포트: 27042
Example
bool hasSSHAvailable(void)
{
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if(server_socket == -1)
{
return false;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(22);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int status = bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(status == 0) {
close(server_socket);
return false;
}
return true;
}
9. 디버깅
- pid_t getppid(void);
- int sysctl(const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen);
- long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
- int isatty(int fd);
- int ioctl(int fd, unsigned long request, …);
- kern_return_t task_get_exception_ports(task_t task, exception_mask_t exception_mask, exception_mask_array_t masks, mach_msg_type_number_t *masksCnt, exception_handler_array_t old_handlers, exception_behavior_array_t old_behaviors, exception_flavor_array_t old_flavors);
Example
bool hasDebuggerAttached(void)
{
bool ret = false;
int mib[4];
struct kinfo_proc info;
size_t info_size = sizeof(info);
info.kp_proc.p_flag = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
if (sysctl(mib, 4, &info, &info_size, NULL, 0) == 0)
{
int traceStatus = info.kp_proc.p_flag & P_TRACED;
if(traceStatus != 0)
ret = true;
}
int launchdPid = 1;
if (getppid() != launchdPid)
ret = true;
if(isatty(STDERR_FILENO) != 0)
ret = true;
struct winsize ws;
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws))
ret = true;
mach_msg_type_number_t count = 0;
exception_mask_t masks[EXC_TYPES_COUNT];
mach_port_t ports[EXC_TYPES_COUNT];
exception_behavior_t behaviors[EXC_TYPES_COUNT];
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &count, ports, behaviors, flavors);
if (result == KERN_SUCCESS)
{
for (mach_msg_type_number_t portIndex = 0; portIndex < count; portIndex++)
{
if (MACH_PORT_VALID(ports[portIndex]))
{
ret = true;
}
}
}
return ret;
}
10. 코드 서명
- int fcntl(int fildes, int cmd, …); (cmd = F_ADDSIGS, F_CHECK_LV)
- int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize); (ops = CS_OPS_MARKKILL)
Example
bool hasCodeSigningValidated(void)
{
bool ret = false;
FILE* dyld_file = fopen("/usr/lib/dyld", "rb");
fsignatures_t siginfo;
if(dyld_file != NULL)
{
uint8_t firstPage[4096];
if(fread(firstPage, 1, 4096, dyld_file) == 4096)
{
struct mach_header *mh = (struct mach_header*)firstPage;
uint32_t cmd_count = mh->ncmds;
struct load_command *cmds = (struct load_command*)((char*)firstPage+(sizeof(struct mach_header_64)));
struct load_command *cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i)
{
if (cmd->cmd == LC_CODE_SIGNATURE)
{
const struct linkedit_data_command *sigcmd = (struct linkedit_data_command*) cmd;
siginfo.fs_file_start = O_DIRECTORY;
siginfo.fs_blob_start = malloc(sigcmd->datasize);
siginfo.fs_blob_size = sigcmd->datasize;
}
cmd = (struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
fclose(dyld_file);
int dyld_fd = open("/usr/lib/dyld", O_RDONLY, 0);
if(dyld_fd != -1)
{
int result = fcntl(dyld_fd, F_ADDSIGS, &siginfo);
if(result == -1)
{
if(errno == EBADEXEC || errno == EPERM)
{
ret = true;
}
}
}
close(dyld_fd);
return ret;
}
Reference
https://lapcatsoftware.com/articles/hardened-runtime-xpc.html
https://www.synacktiv.com/sites/default/files/2021-10/2021_sthack_jailbreak.pdf
11. Mach 접근
- int sandbox_check(pid_t, const char *operation, int sandbox_filter_type, …);
(operation: mach-lookup) - kern_return_t bootstrap_check_in(mach_port_t bp, const name_t service_name, mach_port_t *sp);
- kern_return_t bootstrap_look_up(mach_port_t bp, const name_t service_name, mach_port_t *sp);
Reference
https://github.com/Lessica/shadow/blob/master/Shadow.dylib/hooks/mach.x#L3
https://github.com/Lessica/shadow/blob/master/Shadow.dylib/hooks/sandbox.x#L104
좋은 글 공유해주셔서 감사합니다.
현재 fcntl(F_ADDSIGS)를 사용해서 Dopamine jailbreak를 감지하려고 하는데요,
혹시 iphoneos 환경에서도 /usr/lib/dyld로 가능한 부분일까요?
공유해주신 예시 코드에서
siginfo.fs_file_start = O_DIRECTORY;
siginfo.fs_blob_start = malloc(sigcmd->datasize);
siginfo.fs_blob_size = sigcmd->datasize;
여기서 fs_file_start가 enum값인 O_DIRECTORY인 것은 어떤 의미이실까요?
그리고 fs_blob_start가 sigcmd->dataoff가 아니라 malloc으로 들어가는 이유를 알 수 있을까요?
안녕하세요!
저 코드는 많이 오래되어서 기억이 가물가물하긴한데 제가 잘못된 코드를 적어넣었을 가능성이 크고 작동될지는 잘 모르겠습니다 ㅠㅠ
대신에 도파민 탈옥을 감지하고 싶으시다면, /usr/lib 경로에 bindfs 형태로 마운트되었는지 아닌지 판단해서 탈옥감지해보는건 어떨까요?
답변 감사합니다!!