如何检测越狱手机一直是iOS应用安全防护的第一道门槛。
早在2018年的时候就写过一篇文章来介绍越狱检测,但是由于时间久远,技术在不断地推陈出新,因此当初的代码大多已经被各种反越狱插件研究透彻,因此才有了这篇新的文章。
首先最好使的依然是检测各个越狱常用的软件是否存在:
static char *paths[] = {
"/Applications/ALS.app"
"/Applications/Cydia.app",
"/Applications/FakeCarrier.app",
"/Applications/Filza.app",
"/Applications/FlyJB.app",
"/Applications/IntelliScreen.app",
"/Applications/MTerminal.app",
"/Applications/SBSetttings.app",
"/Applications/Snoop-itConfig.app"
"/Applications/WinterBoard.app",
"/Applications/blackra1n.app",
"/Library/LaunchDaemons/com.openssh.sshd.plist"
"/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/Library/LaunchDaemons/com.tigisoftware.filza.helper.plist",
"/Library/LaunchDaemons/com.rpetrich.rocketbootstrapd.plist",
"/Library/LaunchDaemons/dhpdaemon.plist",
"/Library/LaunchDaemons/re.frida.server.plist",
"/Library/MobileSubstrate",
"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/User/Applications/",
"/bin.sh",
"/bin/bash",
"/etc/apt",
"/etc/ssh/sshd_config",
"/private/etc/apt",
"/private/etc/apt/preferences.d/checkra1n",
"/private/etc/apt/preferences.d/cydia",
"/private/etc/dpkg/origins/debian",
"/private/etc/ssh/sshd_config",
"/private/var/lib/apt",
"/private/var/lib/cydia",
"/private/var/mobileLibrary/SBSettingsThemes/",
"/private/var/stash",
"/private/var/tmp/cydia.log",
"/usr/bin/cycript",
"/usr/bin/ssh",
"/usr/lib/libcycript.dylib",
"/usr/libexec/cydia/",
"/usr/libexec/sftp-server",
"/usr/libexec/ssh-keysign",
"/usr/local/bin/cycript",
"/usr/sbin/frida-server",
"/usr/sbin/sshd",
"/var/lib/cydia",
"/var/lib/dpkg/info"
};
BOOL checkPath() {
for (int i = 0;i < sizeof(paths) / sizeof(char *);i++) {
struct stat stat_info;
if (0 == stat(paths[i], &stat_info)) {
return YES;
}
}
return NO;
}
这么做其实有问题,如果大家知道有个叫fishhook的东西,其实很容易就能hook stat函数,那么应该怎么办呢?
我们可以用函数指针调用的方式隐藏一下:
BOOL checkPath() {
void * handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
stat_ptr_t stat_ptr = dlsym(handle, "stat");
for (int i = 0;i < sizeof(paths) / sizeof(char *);i++) {
struct stat stat_info;
if (0 == stat_ptr(paths[i], &stat_info)) {
return YES;
}
}
return NO;
}
但是dlopen调用的方式,想必大家也能看出如何破解,fishhook既然能hook你的stat,难道就不能hook你的dlopen和dlsym吗?
所以我们再加大些力度,直接用汇编搞一个stat的调用:
__attribute__((always_inline)) long f_stat(const char * s, struct stat * stat_info) {
long ret = 0;
__asm__ volatile(
"mov x0, %[s_p]\n"
"mov x1, %[stat_info_p]\n"
"mov x16, #338\n"
"svc #0x80\n"
"mov %[ret_p], x0\n"
: [ret_p]"=r"(ret)
: [s_p]"r"(s), [stat_info_p]"r"(stat_info)
);
return ret == 0 ? ret : -1;
}
BOOL checkPath() {
for (int i = 0;i < sizeof(paths) / sizeof(char *);i++) {
struct stat stat_info;
if (0 == f_stat(paths[i], &stat_info)) {
return YES;
}
}
return NO;
}
其中mov x16, #338代表的是stat函数的系统调用号,完整的系统调用号可以从这里查看
当然,我们除了stat函数,还可以使用open函数来判断:
__attribute__((always_inline)) long f_open(const char *path) {
long rs = 0;
__asm__ volatile(
"mov x0, %[path]\n"
"movz x1, #0\n"
"movz x16, #5\n"
"svc #0x80\n"
"mov %[result], x0\n"
: [result]"=r"(rs)
: [path]"r"(path)
);
return rs > 2 ? rs : -1;
}
BOOL checkPath() {
for (int i = 0;i < sizeof(paths) / sizeof(char *);i++) {
if (-1 != f_open(paths[i])) {
return YES;
}
}
return NO;
}
至于汇编代码为什么要这么写,可以看一下这篇文章,自己领悟一下。
防护做到这里,我们其实可以成功的防御fishhook的攻击,但是目前国内的大神做了一个叫Dobby的inlinehook框架,专治各种防御,那么针对Dobby这种inlinehook的方式我们又应该如何防御呢?
网友评论