抖音越狱检测深度分析

抖音越狱检测深度分析

准备工作

工具

  在本文中,用到的工具有IDA Pro, Frida, TheOS,文本编辑器(如VSCode)

脱壳

  首先我们进行静态分析,第一步要脱壳,市面上常见的工具有dump-decrypted/frida-ios-dump/clutch等工具。使用frida查看App加载了哪些自带动态库,并对这些模块进行脱壳

Process.enumerateModulesSync().forEach(function(e){if(e.path.indexOf('.app')!=-1){console.log(e.path)}})
.../Aweme.app/Aweme
.../Aweme.app/Frameworks/AgoraRtcEngineKit.framework/AgoraRtcEngineKit
.../Frameworks/ByteRtcEngineKit.framework/ByteRtcEngineKit
.../Aweme.app/Frameworks/AwemeDylib.framework/AwemeDylib

越狱检测分析

  由于抖音二进制相当大,单单IDA分析便需要几十分钟,而越狱逻辑可能隐藏在任何地方,而不限于在进程初始化时(mod_init_func),如果在浩如烟海的代码里去慢慢分析是不可能的,下面就使用最快的思路来解决这个问题

第一阶段 初步分析

  首先找到比较明显的痕迹,例如函数名,字符串,http请求body中的关键字(例如jail/break/jeil/jb/cydia/substrate/apt之类的),函数名和字符串可在IDA分析后进行搜索,而http请求可以通过fiddler等抓包工具进行分析:

相关的函数:
[ANSMetadata computeIsJailbroken];
[ASSStaticInfoCollector checkJB)];
[ASSStaticInfoCollectorOpen checkJB]
[AWECloudJailBreakUtility jailbroken];
[AWEYAMInfoHelper isJailBroken]
[BDADeviceHelper isJailBroken];
[BDLogDeviceHelper isJailBroken]
[IESLiveDeviceInfo isJailBroken]
[HMDCrashBinaryImage isJailBroken];
[HMDInfo isJailBroken]
[MobClick isJailbroken]
[TTAdSplashDeviceHelper isJailBroken];
[TTInstallDeviceHelper isJailBroken]
[UAConveniece deviceWasJailed];
[UIDevice btd_isJailBroken]
[UMANProtocolData isDeviceJailBreak]
[WXOMTAHelper isJB]

检测逻辑(参考我的另一篇文章"iOS越狱检测研究"):
(1)文件系统检测
/bin/bash
/Applications/Cydia.app
/Library/MobileSubstrate/MobileSubstrate.dylib
/user/Applictations
/user/Continers/Bundle/Application
/usr/sbin/sshd
/etc/apt
/Applications/RockApp.app,
/Applications/Icy.app,
/usr/sbin/sshd,
/usr/bin/sshd,
/usr/libexec/sftp-server,
/Applications/WinterBoard.app,
/Applications/SBSettings.app,
/Applications/MxTube.app,
/Applications/IntelliScreen.app,
/Library/MobileSubstrate/DynamicLibraries/Veency.plist,
/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist,
/private/var/lib/apt,
/private/var/stash,
/private/{uuid}可写
/System/Library/LaunchDaemons/com.ikey.bbot.plist,
/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist,
/private/var/tmp/cydia.log,
/private/var/lib/cydia,
/etc/clutch.conf,
/var/cache/clutch.plist,
/etc/clutch_cracked.plist,
/var/cache/clutch_cracked.plist,
/var/lib/clutch/overdrive.dylib,
/var/root/Documents/Cracked/
/etc/fstab 分区检测
(2)沙盒完整性检测
system函数
(3)环境变量检测
使用environ检测MobileSubstrate环境变量
(4)mmap检测
(5)进程检测
MobileCydia
Cydia
afpd
(6)schema检测
cydia://

第二阶段 深度分析

  上述使用的方式只能找到一些比较肤浅的检测手段,下面来深入挖掘隐藏检测手段。这里我们选择文件系统访问作为切入点,因为越狱检测最基本的方式就是文件系统检测。我们使用frida工具,只需要针对以下系统调用进行跟踪,就可以跟踪出所有可能的函数调用级别的文件操作: access,creat,faccessatgetxattr,getxattr,link,listxattr,lstat,open,opendir,readlink,realpath,stat,statfs,symlink
  这里需要注意的一点是,frida工具虽然方便,但是必须在进程启动后才能附加(启动进程的-n参数在老版本iOS有效,10以上失灵),这里用到一个小技法是写一个抖音的tweak,在%ctor中延迟几秒以便我们附加。用此方法也可以跟踪启动时刻的网络请求调用,好处多多。根据附录中的脚本跟踪App得到以下结果:

0x107b49140 AwemeDylib!YjA2MGM5NGQ3MTI5MjljYTA2Y2Y2Yjc3YzM2ZjAxYzAK
0x107b69938 AwemeDylib!0x1f19938
0x107b6e794 AwemeDylib!0x1f1e794

stat /Applications/Cydia.app
0x107b49150 AwemeDylib!YjA2MGM5NGQ3MTI5MjljYTA2Y2Y2Yjc3YzM2ZjAxYzAK
0x107b69938 AwemeDylib!0x1f19938
0x107b6e794 AwemeDylib!0x1f1e794

stat /Library/MobileSubstrate/MobileSubstrate.dylib
syscall 5
syscall open /Library/MobileSubstrate/MobileSubstrate.dylib
0x107b49140 AwemeDylib!YjA2MGM5NGQ3MTI5MjljYTA2Y2Y2Yjc3YzM2ZjAxYzAK
0x107b69938 AwemeDylib!0x1f19938
0x107b68c20 AwemeDylib!YzE0NjZkZDJjZWMyYzdhODdkMzNjNjhkYTI2ZmJhNGIK

stat /Applications/Cydia.app
0x107b49150 AwemeDylib!YjA2MGM5NGQ3MTI5MjljYTA2Y2Y2Yjc3YzM2ZjAxYzAK
0x107b69938 AwemeDylib!0x1f19938
0x107b68c20 AwemeDylib!YzE0NjZkZDJjZWMyYzdhODdkMzNjNjhkYTI2ZmJhNGIK

stat /Library/MobileSubstrate/MobileSubstrate.dylib
syscall open /Library/MobileSubstrate/MobileSubstrate.dylib
0x107b4f700 AwemeDylib!MmNiMjczYTdiMjAzZjc5ODljZDg5MDBlZGQ3NmZmOTUK
0x107b68c84 AwemeDylib!YzE0NjZkZDJjZWMyYzdhODdkMzNjNjhkYTI2ZmJhNGIK

stat /Applications/iGameGuardian.app
0x107b4f800 AwemeDylib!MmNiMjczYTdiMjAzZjc5ODljZDg5MDBlZGQ3NmZmOTUK
0x107b68c84 AwemeDylib!YzE0NjZkZDJjZWMyYzdhODdkMzNjNjhkYTI2ZmJhNGIK

stat /Applications/GamePlayer.app

  由上面结果的调用栈,分析AwemeDylib中的代码,可以找到若干个经过函数名混淆的函数,其中两个函数长这样:

__int64 ?????()
{
  v2 = __ldar((unsigned int *)&unk_25B617C);
  v27 = (unsigned __int64)&unk_25B617C;
  v21 = "stringWithCString:encoding:";
  v20 = "defaultManager";
  v19 = "fileExistsAtPath:";
  v3 = 3649499418LL;
  do
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          while ( 1 )
          {
            while ( (signed int)v3 > 39081286 )
            {
              if ( (signed int)v3 <= 244706462 )
              {
                if ( (signed int)v3 > 126009263 )
                {
                  if ( (_DWORD)v3 == 229581047 )
                  {
                    v0 = v30;
                    v3 = 3270936340LL;
                  }
                }
                else if ( (_DWORD)v3 == 39081287 )
                {
                  __stlr(0, (unsigned int *)&unk_25B617C);
                  v28 = (Dl_info *)&v18;
                  v6 = __ldar((unsigned int *)&v18);
                  if ( !(unsigned int)&v18 )
                  {
                    cydialib = 47;              // /Applications/Cydia.app
                    byte_2443F01 = 65;
                    ....
                  }
                  __stlr(0, (unsigned int *)&unk_25B6180);
                  v7 = objc_msgSend(&OBJC_CLASS___NSString, v21, &cydialib, 4LL);
                  objc_retainAutoreleasedReturnValue(v7);
                  v8 = objc_msgSend(&OBJC_CLASS___NSFileManager, v20);
                  v9 = v8;
                  v10 = (void *)objc_retainAutoreleasedReturnValue(v8);
                  v11 = (unsigned __int64)objc_msgSend(v10, v19, v7);
                  objc_release(v9);
                  if ( v11 & 1 )
                    v12 = 1;
                  else
                    v12 = access(&cydialib, 0) == 0;
                  v31 = 538;
                  objc_release(v7);
                  if ( v12 )
                    v3 = 4208507086LL;
                  else
                    v3 = 2699417432LL;
                }
              }
              else if ( (signed int)v3 <= 1095875126 )
              {
                if ( (_DWORD)v3 == 1034641429 )
                  v4 = 1;
                else
                  v4 = v1;
                if ( (_DWORD)v3 == 1034641429 )
                  v5 = -257687223;
                else
                  v5 = v3;
                if ( (_DWORD)v3 == 244706463 )
                  v1 = 0;
                else
                  v1 = v4;
                if ( (_DWORD)v3 == 244706463 )
                  v3 = 4037280073LL;
                else
                  v3 = v5;
              }
              else
              {
                switch ( (_DWORD)v3 )
                {
                  case 0x4151BA37:
                    v0 = 1;
                    v3 = 3270936340LL;
                    break;
                  case 0x5B3EC3D2:
                    v29 = v28;
                    if ( dladdr(&_stat, v28) )
                      v3 = 3416709257LL;
                    else
                      v3 = 244706463LL;
                    break;
                  case 0x60C3A04F:
                    v23 = 304;
                    v22 = 214;
                    v3 = 1530840018LL;
                    break;
                }
              }
            }
            if ( (signed int)v3 > -1024030957 )
              break;
            if ( (signed int)v3 > -1595549865 )
            {
              if ( (_DWORD)v3 == -1595549864 )
              {
                v13 = __ldar((unsigned int *)v3);
                __stlr(0, (unsigned int *)&unk_25B6184);
                v14 = objc_msgSend(&OBJC_CLASS___NSFileManager, v20);
                objc_retainAutoreleasedReturnValue(v14);
                v15 = objc_msgSend(&OBJC_CLASS___NSString, v21, &byte_2443F20, 4LL);
                objc_retainAutoreleasedReturnValue(v15);
                v16 = (unsigned __int64)objc_msgSend(v14, v19, v15);
                objc_release(v15);
                objc_release(v14);
                if ( v16 )
                  v3 = 1095875127LL;
                else
                  v3 = 1623433295LL;
              }
            }
            else if ( (_DWORD)v3 == -1895695304 )
            {
              libsyskern = 47;                  // /usr/lib/system/libsystem_kernel.dylib
              byte_2443ED1 = 117;
              .....
            }
            else if ( (_DWORD)v3 == -1799416459 )
            {
              if ( !strcmp(v28->dli_fname, &libsyskern) )
                v3 = 244706463LL;
              else
                v3 = 1034641429LL;
            }
          }
          if ( (signed int)v3 <= -257687224 )
            break;
          if ( (_DWORD)v3 == -257687223 )
          {
            v30 = v1 & 1;
            v26 = 240;
            v25 = 208;
            v3 = 229581047LL;
          }
          else
          {
            if ( (_DWORD)v3 == -86460210 )
              v0 = 1;
            if ( (_DWORD)v3 == -86460210 )
              v3 = 3270936340LL;
            else
              v3 = (unsigned int)v3;
          }
        }
        if ( (_DWORD)v3 != -878258039 )
          break;
        v24 = 583;
        v3 = 2495550837LL;
      }
      if ( (_DWORD)v3 != -645467878 )
        break;
      if ( v27 )
        v3 = 39081287LL;
      else
        v3 = 2399271992LL;
    }
  }
  while ( (_DWORD)v3 != -1024030956 );
  return v0 & 1;
}

void ???(__int64 a1)
{
  v68 = a1;
  v2 = __ldar(__stack_chk_guard);
  if ( !__stack_chk_guard )
  {
    v1 = (unsigned int *)&cydia;
    cydia = 47;                                 // /Applications/Cydia.app
    byte_24435C1 = 65;
    ....
  }
  __stlr(0, (unsigned int *)&unk_25B5CDC);
  v3 = __ldar(v1);
  if ( !(_DWORD)v1 )
  {
    substrate = 47;                             // /Library/MobileSubstrate/MobileSubstrate.dylib
    byte_24435E1 = 76;
    ....
  }
  __stlr(0, (unsigned int *)&unk_25B5CE0);
  v4 = __ldar((unsigned int *)((char *)&dword_0 + 1));
  __stlr(0, (unsigned int *)&unk_25B5CE4);
  v5 = __ldar(v1);
  if ( !(_DWORD)v1 )
  {                                             // /Library/TweakInject
    twea = 47;
    byte_2443621 = 76;
    ....
  }
  __stlr(0, (unsigned int *)&unk_25B5CE8);
  v69 = &v50;
  memset(&v67, 0, 0x90uLL);
  v66 = 0;
  v64 = 0u;
  v65 = 0u;
  v63 = 0u;
  v62 = 0;
  v60 = 0u;
  v61 = 0u;
  v58 = 0;
  v56 = 0u;
  v57 = 0u;
  v55 = 0u;
  v51 = 0u;
  v52 = 0u;
  v54 = 0;
  v53 = 0u;
  strcpy((char *)&v63, &cydia);
  strcpy((char *)&v59, &substrate);
  strcpy((char *)&v55, &byte_244360F);          // file_sys
  strcpy((char *)&v51, &twea);
  if ( !__libc_do_syscall(573, v6, v7, v8, v9, v10, v11, v12, &v51, &v67, v37, v38, v39, v40, v41)
    && (WORD2(v67) & 0xF000) == 40960
    || !stat((const char *)&v63, (struct stat *)&v67) )
  {
    goto LABEL_14;
  }
  v13 = stat((const char *)&v59, (struct stat *)&v67);
  if ( !(_DWORD)v13 )
    JUMPOUT(__CS__, sub_1EF91A0(v13, v14, v15, v16, v17, v18, v19, v20) + 52);
  v21 = (void *)syscall(5, &v59, 0LL);
  if ( (signed int)v21 >= 1 )
  {
    __libc_do_syscall(239, v22, v23, v24, v25, v26, v27, v28, v21, v36, v37, v38, v39, v40, v41);
LABEL_14:
    qword_25B5CB0(6LL, 77LL);
    v29 = 1LL;
    goto LABEL_15;
  }
  if ( !access((const char *)&v63, 4) )
    goto LABEL_14;
  v29 = 0LL;
LABEL_15:
  v30 = (__int64)v69;
  v69[1] = 0LL;
  *(_QWORD *)(v30 + 16) = 0LL;
  *(_QWORD *)v30 = 0LL;
  v47 = &off_206CD00;
  v38 = 0LL;
  std::__1::ios_base::init((std::__1::ios_base *)&v47, &v40);
  v48 = 0LL;
  v49 = -1;
  v37 = off_206CB88;
  v47 = (void **)off_206CBD8;
  v39 = off_206CBB0;
  std::__1::basic_streambuf<char,std::__1::char_traits<char>>::basic_streambuf(&v40);
  v42 = 0LL;
  v40 = off_20C7460;
  v44 = 0LL;
  v45 = 0LL;
  v43 = 0LL;
  v46 = 24;
  v31 = strlen((const char *)&v55);
  v32 = sub_DF61FC(&v39, (__int64)&v55, v31);
  std::__1::basic_ostream<char,std::__1::char_traits<char>>::operator<<(v32, v29);
  sub_E21700((__int64 *)&v37, v30);
  v33 = *(char *)(v30 + 23);
  if ( v33 >= 0 )
    v34 = (void *)v30;
  else
    v34 = *(void **)v30;
  if ( v33 >= 0 )
    v35 = *(unsigned __int8 *)(v30 + 23);
  else
    v35 = *(_QWORD *)(v30 + 8);
  std::__1::basic_string<char,std::__1::char_traits<char>,std::__1::allocator<char>>::append(v68, v34, v35);
  v37 = off_206CB88;
  v47 = (void **)off_206CBD8;
  v39 = off_206CBB0;
  v40 = off_20C7460;
  if ( SHIBYTE(v44) & 0x80000000 )
    operator delete(v42);
  std::__1::basic_streambuf<char,std::__1::char_traits<char>>::~basic_streambuf(&v40);
  std::__1::basic_iostream<char,std::__1::char_traits<char>>::~basic_iostream(&v37, off_206CBF8);
  std::__1::basic_ios<char,std::__1::char_traits<char>>::~basic_ios(&v47);
  if ( *(char *)(v30 + 23) & 0x80000000 )
    operator delete(*(void **)v30);
}

  从上面结果可以看到,App使用了syscall函数,以及汇编级别的SVC指令来进行越狱检测,其中SVC指令调用的函数都会经过一个导出函数__libc_do_syscall,我们使用frida跟踪一下该函数:

Interceptor.attach(Module.findExportByName(null, "__libc_do_syscall"), {
    onEnter: function(args) {
        var callnum = args[0].toInt32() - 233;
        if (false) {
        } else if (callnum == 3) {
            // read
        } else if (callnum == 5) {
            console.log('open ' + args[8].readUtf8String());
        } else if (callnum == 6) {
            // close
        } else if (callnum == 20) {
            // getpid
        } else if (callnum == 39) {
            // getppid
        } else if (callnum == 202) {
            // sysctl
        } else if (callnum == 294) {
            // shared_region_check_np
        } else if (callnum == 340) {
            console.log('stat64 ' + args[8].readUtf8String());
        } else if (callnum == 344) {
            // getdirentries64
        } else {
            console.log('__libc_do_syscall() ' + callnum);
        }
    }
});

可得到以下结果:

stat64 /Library/TweakInject
stat64 /Library/TweakInject
open /Library/LaunchDaemons/
open /Library/MobileSubstrate/DynamicLibraries
open /Library/MobileSubstrate/DynamicLibraries/ALS.plist.bak
open /Library/MobileSubstrate/DynamicLibraries/AppList.plist
open /Library/MobileSubstrate/DynamicLibraries/FLEXing.plist
open /Library/MobileSubstrate/DynamicLibraries/GPSTravellerTweakVIP.plist.bak
open /Library/MobileSubstrate/DynamicLibraries/MobileSafety.plist
open /Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.plist
open /Library/MobileSubstrate/DynamicLibraries/RocketBootstrap.plist
open /Library/MobileSubstrate/DynamicLibraries/SSLKillSwitch2.plist
open /Library/MobileSubstrate/DynamicLibraries/TSActivator.plist
open /Library/MobileSubstrate/DynamicLibraries/TSEventTweak.plist

系统调用检测

function logtrace(ctx) {
    var content = Thread.backtrace(ctx.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n';
    if (content.indexOf('SubstrateLoader') == -1 && content.indexOf('JavaScriptCore') == -1 &&
        content.indexOf('FLEXing.dylib') == -1 && content.indexOf('NSResolveSymlinksInPathUsingCache') == -1 &&
        content.indexOf('MediaServices') == -1 && content.indexOf('bundleWithPath') == -1 &&
        content.indexOf('CoreMotion') == -1 && content.indexOf('infoDictionary') == -1 &&
        content.indexOf('objectForInfoDictionaryKey') == -1)  {
            console.log(content);
        return true;
    }
    return false;
}

function iswhite(path) {
    if (path == null) return true;
    if (path.startsWith('/var/mobile/Containers')) return true;
    if (path.startsWith('/var/containers')) return true;
    if (path.startsWith('/var/mobile/Library')) return true;
    if (path.startsWith('/var/db')) return true;
    if (path.startsWith('/private/var/mobile')) return true;
    if (path.startsWith('/private/var/containers')) return true;
    if (path.startsWith('/private/var/mobile/Library')) return true;
    if (path.startsWith('/private/var/db')) return true;
    if (path.startsWith('/System')) return true;
    if (path.startsWith('/Library/Preferences')) return true;
    if (path.startsWith('/Library/Managed')) return true;
    if (path.startsWith('/usr')) return true;
    if (path.startsWith('/dev')) return true;
    if (path == '/AppleInternal') return true;
    if (path == '/etc/hosts') return true;
    if (path == '/Library') return true;
    if (path == '/var') return true;
    if (path == '/private/var') return true;
    if (path == '/private') return true;
    if (path == '/') return true;
    if (path == '/var/mobile') return true;
    if (path.indexOf('/containers/Bundle/Application') != -1) return true;
    return false;
}

Interceptor.attach(Module.findExportByName(null, "access"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        console.log("access " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "creat"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("creat " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "dlopen"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (!iswhite(path)) console.log("dlopen " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "dlopen_preflight"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (!iswhite(path)) console.log("dlopen_preflight " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "faccessat"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[1].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("faccessat " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "getxattr"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("getxattr " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "link"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("link " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "listxattr"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("listxattr " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "lstat"), {
    block: false,
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("lstat " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "open"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = Memory.readUtf8String(args[0]);
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("open " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "opendir"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("opendir " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "__opendir2"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("opendir2 " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "readlink"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("readlink " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "realpath"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("realpath " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "realpath$DARWIN_EXTSN"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("realpath$DARWIN_EXTSN " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "stat"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("stat " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "statfs"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("statfs " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "symlink"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var path = args[0].readUtf8String();
        if (iswhite(path)) return;
        if (logtrace(this)) console.log("symlink " + path);
    }
})

Interceptor.attach(Module.findExportByName(null, "syscall"), {
    onEnter: function(args) {
        if (args[0].isNull()) return;
        var callnum = args[0].toInt32();
        if (callnum == 180) return;
        console.log("syscall " + args[0].toInt32());
    }
})