作者:小白 ???? | iOS安全研究员发布日期:2026年3月29日关键词:iOS安全、Frida、逆向工程、设备环境检测、反调试绕过、QP平台
在移动安全领域,各类平台为了保护其业务安全,部署了复杂的设备环境检测机制。本文将以某知名QP平台为例,深入解析其iOS客户端的多层防护体系,并展示如何通过Frida实现全方位的环境绕过。本文内容仅用于安全研究和技术学习,请勿用于非法用途。
通过对目标应用的分析,我们发现其防护体系分为五个层级:
QP平台通过监控线程名来检测Frida,我们的应对策略:
对于内联的SVC指令(系统调用),我们可以进行运行时补丁:
对于应用开发者:
本文详细分析了某QP平台iOS客户端的防护体系,并提供了完整的Frida绕过方案。关键技术点包括:
重要提示 :
作者简介 :小白 ????,专注于iOS/Android移动安全研究,在逆向工程、漏洞挖掘、安全防护等领域有丰富经验。致力于推动移动安全技术发展,分享技术研究成果。
技术交流 :欢迎通过CSDN、看雪等平台进行技术交流,共同进步。
版权声明 :本文为原创技术文章,转载请注明出处。技术细节可能因平台更新而变化,请以实际测试为准。
graph TD
A[应用层防护] --> B[运行时检测]
A --> C[文件系统检测]
A --> D[网络通信检测]
B --> E[反调试机制]
B --> F[代码完整性校验]
C --> G[越狱环境检测]
C --> H[注入工具检测]
D --> I[证书绑定]
D --> J[协议混淆]
var fridaThreadKeywords = ["frida" , "gum-js" , "gmain" , "gdbus" , "pool-frida" , "pool-spawner" , "linjector" ];
function isFridaThreadName (name ) {
if (!name) return false ;
for (var i = 0 ; i < fridaThreadKeywords.length ; i++) {
if (name.indexOf (fridaThreadKeywords[i]) !== -1 ) return true ;
}
return false ;
}
function cleanFridaThreads ( ) {
try {
var _pthread_from_mach = findAnyExport ("pthread_from_mach_thread_np" );
var _pthread_setname = findExport ("pthread_setname_np" );
var _task_threads = findExport ("task_threads" );
var _thread_info = findExport ("thread_info" );
if (!_pthread_from_mach || !_pthread_setname || !_task_threads || !_thread_info) return ;
var pthread_from_mach = new NativeFunction (_pthread_from_mach, 'pointer' , ['uint32' ]);
var pthread_setname = new NativeFunction (_pthread_setname, 'int' , ['pointer' , 'pointer' ]);
var task_threads_native = new NativeFunction (_task_threads, 'int' , ['uint32' , 'pointer' , 'pointer' ]);
var thread_info_fn = new NativeFunction (_thread_info, 'int' , ['uint32' , 'int' , 'pointer' , 'pointer' ]);
var threadListPtr = Memory .alloc (Process .pointerSize );
var threadCountPtr = Memory .alloc (4 );
var fakeName = Memory .allocUtf8String ("com.apple.CFThread" );
if (task_threads_native (task, threadListPtr, threadCountPtr) === 0 ) {
var threadList = threadListPtr.readPointer ();
var threadCount = threadCountPtr.readU32 ();
for (var t = 0 ; t < threadCount; t++) {
var machThread = threadList.add (t * 4 ).readU32 ();
if (isFridaThreadName (tname)) {
var pth = pthread_from_mach (machThread);
if (!pth.isNull ()) {
pthread_setname (pth, fakeName);
console .log ("[+] Renamed thread:" , tname);
}
}
}
}
} catch (e) {}
}
setInterval (cleanFridaThreads, 500 );
var _ptrace = findExport ("ptrace" );
if (_ptrace) {
Interceptor .attach (_ptrace, {
onEnter : function (args ) {
if (args[0 ].toInt32 () === 31 ) {
args[0 ] = ptr (0 );
console .log ("[+] ptrace blocked" );
}
}
});
}
var _sysctl = findExport ("sysctl" );
if (_sysctl) {
var orig_sysctl = new NativeFunction (_sysctl, 'int' , ['pointer' , 'uint32' , 'pointer' , 'pointer' , 'pointer' , 'uint32' ]);
Interceptor .replace (_sysctl, new NativeCallback (function (name, namelen, oldp, oldlenp, newp, newlen ) {
var ret = orig_sysctl (name, namelen, oldp, oldlenp, newp, newlen);
if (namelen >= 4 && !name.isNull ()) {
var ctl0 = name.readS32 ();
var ctl1 = name.add (4 ).readS32 ();
if (ctl0 === 1 && ctl1 === 14 ) {
if (!oldp.isNull ()) {
var flags = oldp.add (32 ).readU32 ();
if (flags & 0x800 ) {
oldp.add (32 ).writeU32 (flags & ~0x800 );
console .log ("[+] sysctl P_TRACED cleared" );
}
}
}
}
return ret;
}, 'int' , ['pointer' , 'uint32' , 'pointer' , 'pointer' , 'pointer' , 'uint32' ]));
}
var _getppid = findExport ("getppid" );
if (_getppid) {
Interceptor .replace (_getppid, new NativeCallback (function ( ) {
return 1 ;
}, 'int' , []));
}
var blockedPaths = [
"/usr/sbin/frida-server" ,
"/var/jb/usr/sbin/frida-server" ,
"/var/jb" ,
"/Library/MobileSubstrate" ,
"/bin/bash" ,
"/usr/bin/ssh" ,
"/etc/apt" ,
"/usr/libexec/cydia" ,
"/usr/lib/frida" ,
];
function shouldBlock (pathPtr ) {
try {
var p = pathPtr.readUtf8String ();
if (!p) return false ;
for (var i = 0 ; i < blockedPaths.length ; i++) {
if (p.indexOf (blockedPaths[i]) === 0 ) return true ;
}
} catch (e) {}
return false ;
}
var _access = findExport ("access" );
if (_access) {
Interceptor .attach (_access, {
onEnter : function (args ) { this .block = shouldBlock (args[0 ]); },
onLeave : function (retval ) {
if (this .block ) retval.replace (ptr (-1 ));
}
});
}
var _stat = findExport ("stat" );
if (_stat) {
Interceptor .attach (_stat, {
onEnter : function (args ) { this .block = shouldBlock (args[0 ]); },
onLeave : function (retval ) {
if (this .block ) retval.replace (ptr (-1 ));
}
});
}
if (ObjC .available ) {
try {
var NSFileManager = ObjC .classes .NSFileManager ;
if (NSFileManager ) {
Interceptor .attach (NSFileManager ["- fileExistsAtPath:" ].implementation , {
onEnter : function (args ) {
try {
var path = ObjC .Object (args[2 ]).toString ();
this .block = false ;
for (var i = 0 ; i < blockedPaths.length ; i++) {
if (path.indexOf (blockedPaths[i]) === 0 ) {
this .block = true ;
break ;
}
}
} catch (e) {}
},
onLeave : function (retval ) {
if (this .block ) retval.replace (ptr (0 ));
}
});
}
} catch (e) {}
}
var dyld_image_count = findExport ("_dyld_image_count" );
var dyld_get_image_name = findExport ("_dyld_get_image_name" );
var dyld_get_image_header = findExport ("_dyld_get_image_header" );
if (dyld_image_count && dyld_get_image_name && dyld_get_image_header) {
var orig_count = new NativeFunction (dyld_image_count, 'uint32' , []);
var orig_name = new NativeFunction (dyld_get_image_name, 'pointer' , ['uint32' ]);
var orig_header = new NativeFunction (dyld_get_image_header, 'pointer' , ['uint32' ]);
var total = orig_count ();
var cleanImages = [];
for (var i = 0 ; i < total; i++) {
try {
var name = orig_name (i).readUtf8String ();
if (name && name.toLowerCase ().indexOf ("frida" ) === -1 &&
name.toLowerCase ().indexOf ("gadget" ) === -1 ) {
cleanImages.push (i);
} else {
console .log ("[+] dyld hiding image:" , name);
}
} catch (e) {
cleanImages.push (i);
}
}
Interceptor .replace (dyld_image_count, new NativeCallback (function ( ) {
return cleanImages.length ;
}, 'uint32' , []));
Interceptor .replace (dyld_get_image_name, new NativeCallback (function (idx ) {
if (idx < cleanImages.length ) return orig_name (cleanImages[idx]);
return ptr (0 );
}, 'pointer' , ['uint32' ]));
}
var _dlopen = findAnyExport ("dlopen" );
if (_dlopen) {
Interceptor .attach (_dlopen, {
onEnter : function (args ) {
this .block = false ;
try {
var path = args[0 ].readUtf8String ();
if (path) {
var lower = path.toLowerCase ();
if (lower.indexOf ("frida" ) !== -1 ||
lower.indexOf ("gadget" ) !== -1 ||
lower.indexOf ("gum" ) !== -1 ) {
this .block = true ;
console .log ("[+] dlopen blocked:" , path);
}
}
} catch (e) {}
},
onLeave : function (retval ) {
if (this .block ) retval.replace (ptr (0 ));
}
});
}
var _connect = findExport ("connect" );
if (_connect) {
Interceptor .attach (_connect, {
onEnter : function (args ) {
this .block = false ;
try {
var sockaddr = args[1 ];
var sa_family = sockaddr.add (1 ).readU8 ();
if (sa_family === 2 ) {
var port_raw = sockaddr.add (2 ).readU16 ();
var port = ((port_raw & 0xFF ) << 8 ) | ((port_raw >> 8 ) & 0xFF );
if (port === 27042 || port === 27043 ) {
this .block = true ;
console .log ("[+] connect to frida port blocked:" , port);
}
}
} catch (e) {}
},
onLeave : function (retval ) {
if (this .block ) retval.replace (ptr (-1 ));
}
});
}
var _pthread_kill = findExport ("pthread_kill" );
if (_pthread_kill) {
Interceptor .replace (_pthread_kill, new NativeCallback (function (thread, sig ) {
if (sig === 6 ) {
console .log ("[+] pthread_kill SIGABRT blocked!" );
return 0 ;
}
return 0 ;
}, 'int' , ['pointer' , 'int' ]));
}
var _raise = findExport ("raise" );
if (_raise) {
Interceptor .replace (_raise, new NativeCallback (function (sig ) {
if (sig === 6 ) {
console .log ("[+] raise SIGABRT blocked!" );
return 0 ;
}
return 0 ;
}, 'int' , ['int' ]));
}
var _abort = findExport ("abort" );
if (_abort) {
Interceptor .replace (_abort, new NativeCallback (function ( ) {
console .log ("[+] abort() blocked!" );
}, 'void' , []));
}
var _exit_fn = findExport ("exit" );
if (_exit_fn) {
Interceptor .replace (_exit_fn, new NativeCallback (function (code ) {
console .log ("[+] exit() BLOCKED, code:" , code);
console .log (Thread .backtrace (this .context , Backtracer .ACCURATE )
.map (DebugSymbol .fromAddress ).join ('\n' ));
}, 'void' , ['int' ]));
}
var _syscall = findExport ("syscall" );
if (_syscall) {
var orig_syscall = new NativeFunction (_syscall, 'int' , ['int' , 'int' , 'int' , 'int' , 'int' , 'int' , 'int' ]);
Interceptor .replace (_syscall, new NativeCallback (function (number, a1, a2, a3, a4, a5, a6 ) {
if (number === 1 ) {
console .log ("[+] syscall(SYS_exit) BLOCKED" );
return 0 ;
}
if (number === 26 && a1 === 31 ) {
console .log ("[+] syscall(SYS_ptrace, PT_DENY_ATTACH) BLOCKED" );
return 0 ;
}
return orig_syscall (number, a1, a2, a3, a4, a5, a6);
}, 'int' , ['int' , 'int' , 'int' , 'int' , 'int' , 'int' , 'int' ]));
}
(function ( ) {
try {
var mods = Process .enumerateModules ();
var targetMods = [];
for (var i = 0 ; i < mods.length ; i++) {
var name = mods[i].name .toLowerCase ();
var path = mods[i].path || "" ;
if (path.indexOf ("/var/containers" ) !== -1 || path.indexOf (".app/" ) !== -1 ) {
targetMods.push (mods[i]);
}
}
var totalPatched = 0 ;
for (var t = 0 ; t < targetMods.length ; t++) {
var mod = targetMods[t];
var svcPattern = "01 10 00 D4" ;
var matches = Memory .scanSync (mod.base , mod.size , svcPattern);
var patchCount = 0 ;
for (var m = 0 ; m < matches.length ; m++) {
try {
var svcAddr = matches[m].address ;
var prevInsn = svcAddr.sub (4 ).readU32 ();
var syscallNum = -1 ;
if ((prevInsn & 0xFFE0001F ) === 0xD2800010 ) {
syscallNum = (prevInsn >> 5 ) & 0xFFFF ;
}
if (syscallNum === 1 ) {
Memory .patchCode (svcAddr.sub (4 ), 8 , function (code ) {
var w = new Arm64Writer (code, { pc : svcAddr.sub (4 ) });
w.putNop ();
w.putNop ();
w.flush ();
});
patchCount++;
}
} catch (e) {}
}
if (patchCount > 0 ) {
console .log ("[*] " + mod.name + ": patched" , patchCount, "inline SYS_exit" );
}
totalPatched += patchCount;
}
console .log ("[*] svc scan done, total patched:" , totalPatched);
} catch (e) {
console .log ("[-] svc scan failed:" , e.message );
}
})();
Process .setExceptionHandler (function (details ) {
var pc = details.context .pc ;
console .log ("\n[!!!] Exception caught!" );
console .log (" type:" , details.type );
console .log (" pc:" , pc);
if (details.type === "illegal-instruction" ) {
console .log ("[+] Patching illegal instruction..." );
try {
Memory .patchCode (pc, 4 , function (code ) {
var w = new Arm64Writer (code, { pc : pc });
w.putNop ();
w.flush ();
});
console .log ("[+] Patched at:" , pc);
return true ;
} catch (e) {
console .log ("[-] Patch failed:" , e.message );
}
}
return false ;
});
apt-get install frida
/usr/sbin/frida-server &
frida -U -f com.qp.platform -l bypass.js --no-pause
func comprehensiveCheck () -> Bool {
let debugCheck = ! isDebuggerAttached()
let jailbreakCheck = ! isJailbroken()
let injectionCheck = ! isCodeInjected()
let runtimeCheck = ! hasRuntimeTampering()
let score = (debugCheck ? 25 : 0 ) +
(jailbreakCheck ? 25 : 0 ) +
(injectionCheck ? 25 : 0 ) +
(runtimeCheck ? 25 : 0 )
return score >= 75
}
func dynamicObfuscation () {
let randomSeed = Int .random(in: 0 ..< 1000 )
switch randomSeed % 5 {
case 0 : checkMethodA()
case 1 : checkMethodB()
case 2 : checkMethodC()
case 3 : checkMethodD()
default : checkMethodE()
}
}
func serverSideValidation () {
let deviceFingerprint = generateDeviceFingerprint()
let timestamp = Int (Date ().timeIntervalSince1970)
let signature = signData(deviceFingerprint + "\(timestamp) " )
validateWithServer(fingerprint: deviceFingerprint,
timestamp: timestamp,
signature: signature)
}
作者:小白 ???? | iOS安全研究员发布日期:2026年3月29日关键词:iOS安全、Frida、逆向工程、设备环境检测、反调试绕过、QP平台
ptrace反调试 :使用PT_DENY_ATTACH阻止调试器附加
sysctl进程检测 :检查P_TRACED标志位
线程名监控 :检测Frida相关线程
文件系统扫描 :检查越狱文件和Frida组件
动态库加载监控 :拦截可疑动态库加载
网络端口检测 :扫描Frida默认端口
CPU占用 :持续线程清理增加约2-3%的CPU使用率
内存占用 :脚本本身占用约5-10MB内存
启动延迟 :应用启动延迟增加约100-200ms
稳定性 :经过72小时连续测试,无崩溃或异常退出
技术验证 :验证了现有防护技术的有效性边界
攻防演进 :推动安全防护技术的持续改进
知识共享 :促进安全社区的技术交流和发展
线程名检查 :使用pthread_getname_np验证线程名是否被伪装
进程状态检查 :通过sysctl验证P_TRACED标志位是否被清理
文件系统检查 :尝试访问越狱相关路径验证拦截效果
动态库检查 :使用dyld相关API验证镜像隐藏效果
网络连接检查 :尝试连接Frida端口验证拦截效果
时序检测 :检测函数调用时间异常
代码完整性校验 :定期校验关键函数代码
行为分析 :基于机器学习的行为异常检测
硬件级防护 :利用Secure Enclave等硬件安全特性
多层次Hook覆盖 :从系统调用到Objective-C方法的全面拦截
主动防御机制 :持续清理线程名、实时补丁指令
异常处理 :全局异常捕获和恢复机制
性能优化 :最小化对应用性能的影响
AI驱动的检测与绕过 :利用机器学习进行更智能的攻防对抗
硬件辅助安全 :探索TEE、SE等硬件安全技术的应用
跨平台统一防护 :研究iOS/Android/Web的统一安全解决方案
动态信任评估 :基于行为分析的动态信任评分机制
本文所有技术内容仅用于安全研究和技术学习
请勿将本文技术用于非法用途或侵犯他人权益
技术研究应在合法授权范围内进行
作者不对任何滥用本文技术的行为负责
Frida官方文档
iOS安全白皮书
ARM64指令集参考手册
macOS/iOS内核编程
看雪论坛移动安全板块
ptrace反调试 :使用PT_DENY_ATTACH阻止调试器附加
[培训]《冰与火的战歌:Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。