Flutter如何检测Dart调试器是否附着 实现应用反调试防护
核心结论
可以在Dart层通过平台通道调用原生反调试能力,但不推荐仅在Dart层做检测——Dart运行在Flutter引擎沙箱内,很容易通过hook引擎消息通道绕过,可靠方案是原生层多节点埋点检测+Dart层辅助校验的组合实现,满足安全团队的预防性防护要求。
原有实现失效原因
- 检测时机太晚:把逻辑写在
MainActivity的onCreate/onResume生命周期里,等逻辑执行时调试器完全可以在Application初始化阶段就附着进程,甚至提前hook掉isDebuggerConnected()方法篡改返回值 - 检测频率太低:仅在生命周期触发时做单次检测,根本捕获不到“应用启动后再附着调试器”的常见逆向场景
- 检测覆盖不全:
android.os.Debug.isDebuggerConnected()只能识别Java层标准调试,检测不到ptrace实现的native层调试附着
可落地方案
Android端核心防护(必须放在原生层实现)
- 把检测逻辑前置到
Application.attachBaseContext()阶段,这是应用进程启动后最早执行的业务逻辑点,不要放在Activity生命周期内 - 不要依赖单一检测接口,做多维度交叉校验:
- 轮询检测
Debug.isDebuggerConnected()、Debug.waitingForDebugger()返回值 - 读取
/proc/self/status文件内的TracerPid字段,字段值非0即代表有进程附着调试 - 补充native层自ptrace防护,比Java层接口更难绕过
- 轮询检测
- 检测到调试状态后直接调用
Process.killProcess(Process.myPid())终止进程,不要用finish()、System.exit()这类容易被hook拦截的退出方式
参考实现代码:
class App : Application() { override fun attachBaseContext(base: Context?) { super.attachBaseContext(base) // 启动独立守护线程做高频轮询,避免单点hook绕过 Thread { while (true) { val tracerPid = runCatching { File("/proc/self/status").readLines() .first { it.startsWith("TracerPid:") } .split("\\s+".toRegex())[1].toInt() }.getOrDefault(0) val isJavaDebug = Debug.isDebuggerConnected() || Debug.waitingForDebugger() if (tracerPid != 0 || isJavaDebug) { Process.killProcess(Process.myPid()) } Thread.sleep(100) } }.start() } }
Dart层辅助检测
如果需要在Dart侧补充校验,直接通过MethodChannel调用原生层的检测逻辑即可,不要在Dart层自行实现检测规则——Dart层快照很容易被逆向工具直接篡改绕过。
Dart侧参考实现:
const antiDebugChannel = MethodChannel("com.example.antidebug"); // 应用启动后立刻启动全周期轮询,不要绑定单个页面生命周期 void initAntiDebug() { Timer.periodic(const Duration(milliseconds: 200), (timer) async { final isDebugged = await antiDebugChannel.invokeMethod<bool>("checkDebugStatus") ?? false; if (isDebugged) { exit(0); } }); }
原生侧MethodChannel handler直接返回前面多维度检测的结果即可,不要只返回isDebuggerConnected()的单接口结果。
iOS端对应实现
在application:didFinishLaunchingWithOptions最早执行阶段调用ptrace接口,传入PT_DENY_ATTACH参数阻止调试器附着,同样建议补充native层轮询检测,避免被绕过。
参考代码:
#import <ptrace.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ptrace(PT_DENY_ATTACH, 0, 0, 0); // 补充其他反调试校验逻辑 return YES; }
优化注意点
- 所有核心检测逻辑建议下沉到native层实现,配合代码混淆、加壳处理,避免被逆向者快速定位到检测点直接patch绕过
- 不要做单次检测,必须覆盖应用全运行周期做高频轮询
- 不要把反调试能力完全放在Dart层,Dart层仅做辅助校验即可
内容的提问来源于stack exchange,提问作者Antonycx
相关产品推荐
相关产品推荐

