OWASP MASTG 工具实战:使用 jdb 与 JDWP 协议调试 Android 应用
文档教程网络安全【免费下载链接】mastgThe OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security Weakness Enumeration (MASWE) weaknesses, which are in alignment with the OWASP MASVS.项目地址https://gitcode.com/gh_mirrors/ow/mastg点击查看免费下载导读本文介绍 OWASP Mobile Application Security Testing GuideMASTG中的调试工具jdbJava Debugger。jdb 是随 JDK 一同发布的命令行调试器基于 Java Debug Wire ProtocolJDWP与 Android 的 Dalvik/ART 虚拟机通信允许安全测试人员在命令行下设置断点、打印与修改应用变量。在 Android 逆向工程中jdb 常用于对发布版非 debuggable应用进行运行时分析、绕过反调试/反篡改逻辑、提取明文敏感数据并可作为执行追踪Execution Tracing的轻量级手段。读完本文你将掌握 jdb 的完整使用流程从adb jdwp定位进程、adb forward建立转发通道、jdb -attach挂接调试器到使用stop in、locals、set、trace go methods等核心命令完成一次真实的逆向调试任务。jdb 是什么基于 JDWP 的 Java 层调试器jdb 是 Java 平台自带的命令行调试器其官方定义为允许设置断点并打印应用变量且使用 JDWP 协议与目标虚拟机通信。对应到 Android 平台它调试的对象是运行在 Dalvik/ART 虚拟机上的 Java/Kotlin 字节码DEX而非 native 层代码。Android 应用支持两种层级的调试参见 MASTG-TECH-0031Java 运行时层通过 JDWP 协议调试jdb、Android Studio 等工具均基于此协议Native 层基于 Linux 的ptrace系统调用调试如 lldb、gdb适用于 ELF 共享库。两者对逆向工程都有价值。jdb 属于前者用于调试普通 Android 应用即不大量调用 native 库的应用。一个 JDWP 调试器可以单步执行 Java 代码、在 Java 方法上设置断点、检查并修改局部变量与实例变量这些能力正是动态分析的核心。JDWP 线程与 debuggable 标志JDWP 调试的前提是目标进程存在一个负责处理 JDWP 协议包的特殊线程。如 MASTG-KNOW-0007 所述这个线程仅当应用在 AndroidManifest.xml 中声明android:debuggabletrue时才会启动。这也意味着对发布版android:debuggable缺失或为false应用直接附加 jdb 通常会失败逆向测试中需要先通过补丁 Manifest 重打包、hook 框架改写FLAG_DEBUGGABLE、或修改系统属性ro.debuggable等方式让进程变为可调试详见 MASTG-TECH-0031 的 Debugging Release Apps 一节与 MASTG-KNOW-0028 的 JDWP 反调试讨论。环境准备与调试前提确认应用是否可调试在开始 jdb 会话前先用静态与动态手段确认目标应用的调试状态参考 MASTG-TEST-0039静态检查查看AndroidManifest.xml中是否包含android:debuggabletrue。可用aapt快速验证# 输出 1 表示存在 android:debuggabletrue 指令 $ aapt d xmltree sieve.apk AndroidManifest.xml | grep -Ec android:debuggable\(0x[0-9a-f]\)\(type\s0x[0-9a-f]\)0xffffffff 1动态检查通过dumpsys查看应用包标志中是否含DEBUGGABLE# 输出大于 0 说明应用带调试标志 $ adb shell dumpsys package com.mwr.example.sieve | grep -c DEBUGGABLE 2另一种直接的方法就是尝试附加 jdb如果附加成功说明调试已激活——这正是本文的核心工作流。让发布版应用变为可调试如果应用不是 debuggableMASTG 给出了多种方案详见 MASTG-TECH-0031补丁 Manifest 并重打包修改android:debuggabletrue后重签名。代价是侵入性强可能引发应用不稳定或触发完整性校验。Hook 框架改写标志用 Xposed/LSPosed 等框架如 MASTG-TOOL-0151hook 对ApplicationInfo中FLAG_DEBUGGABLE的检查使应用看似可调试。需要 root 与 hook 框架且可能被检测。系统级开启调试在 root 后的特权 ADB shell 中执行resetprop ro.debuggable 1若引起不稳定可临时setenforce 0将 SELinux 设为 permissive。此方式较粗暴容易被应用检测。jdb 完整调试工作流第一步列出 JDWP 进程并建立转发通道使用 MASTG-TOOL-0004adb的jdwp子命令列出设备上所有托管 JDWP transport 的可调试进程 PIDadb jdwp12167随后用adb forward在宿主机上打开一个监听 socket并将该 socket 的入站 TCP 连接转发到所选进程的 JDWP transportadb forward tcp:7777 jdwp:12167这里7777是宿主机本地端口12167是上一步查到的目标进程 PID。第二步附加 jdb 并保持进程挂起直接jdb -attach会使应用恢复执行这通常不是逆向分析想要的——我们希望在探索前保持进程挂起。解决办法是把suspend命令通过管道喂给调试器{ echo suspend; cat; } | jdb -attach localhost:7777 Initializing jdb ... All threads suspended. 此时已成功挂接到挂起状态的进程。输入?可打印完整命令列表。注意Android 虚拟机并不支持全部 JDWP 特性。例如redefine重定义类代码不可用发布版字节码不含行号信息因此行断点失效但方法断点method breakpoint可用。第三步掌握核心调试命令在 jdb 会话中以下命令是 Android 逆向中最常用且可用的来源MASTG-TECH-0031命令作用classes列出所有已加载类class/methods/fieldsclass id打印类详情列出其方法与字段locals打印当前栈帧中的局部变量print/dumpexpr打印对象信息stop in method设置方法断点clear method移除方法断点set lvalue expr为字段/变量/数组元素赋新值resume恢复全部线程执行cont继续执行当前线程step up执行到当前方法返回此外jdb 还能用作执行追踪工具详见 MASTG-TECH-0032利用 Android 的 Wait for Debugger 功能或kill -STOP让应用在启动之初暂停附加 jdb 后在任意初始化方法上设置延迟方法断点命中后执行trace go methods并resumejdb 便会从该点起转储所有方法的进入与退出记录适合在混淆或动态加载场景下观察控制流。$ adb forward tcp:7777 jdwp:7288 $ { echo suspend; cat; } | jdb -attach localhost:7777 Set uncaught java.lang.Throwable Set deferred uncaught java.lang.Throwable Initializing jdb ... All threads suspended. stop in com.acme.bob.mobile.android.core.BobMobileApplication.clinit() Deferring breakpoint com.acme.bob.mobile.android.core.BobMobileApplication.clinit(). It will be set after the class is loaded. resume All threads resumed. Set deferred breakpoint com.acme.bob.mobile.android.core.BobMobileApplication.clinit() Breakpoint hit: threadmain, com.acme.bob.mobile.android.core.BobMobileApplication.clinit(), line44 bci0 main[1] trace go methods main[1] resume Method entered: All threads resumed.实战案例一绕过反篡改对话框并提取明文密钥MASTG 文档以 MASTG-APP-0003Android UnCrackable L1为演示目标展示如何仅用 jdb 完成一次完整的破解流程详见 MASTG-TECH-0031。该 crackme 的目标是应用内隐藏了一个字符串需要找到方法提取它。说明文档明确指出用 jdb 解决此 crackme 并非最高效的路径用 Frida 等工具更快但它非常适合演示 Java 调试器的能力。步骤 1定位目标逻辑回顾反编译代码sg.vantagepoint.uncrackable1.MainActivity.a方法负责显示 This is unacceptable... 消息框。它创建一个AlertDialog为onClick事件设置监听类b其回调会在用户点击OK时终止应用并调用setCancelable防止用户直接取消对话框private void a(final String title) { final AlertDialog create new AlertDialog$Builder((Context)this).create(); create.setTitle((CharSequence)title); create.setMessage((CharSequence)This in unacceptable. The app is now going to exit.); create.setButton(-3, (CharSequence)OK, (DialogInterface$OnClickListener)new b(this)); create.setCancelable(false); create.show(); }应用在启动时即执行 root/篡改检测因此若不先处理反篡改逻辑根本无法进入能读到明文密钥的状态。步骤 2运行时篡改setCancelable在应用保持挂起的状态下对android.app.Dialog.setCancelable设置方法断点并恢复执行 stop in android.app.Dialog.setCancelable Set breakpoint android.app.Dialog.setCancelable resume All threads resumed. Breakpoint hit: threadmain, android.app.Dialog.setCancelable(), line1,110 bci0 main[1]应用现在停在setCancelable方法的第一条指令。用locals打印传入的参数注意参数会被错误地显示在 Local variables 之下main[1] locals Method arguments: Local variables: flag truesetCancelable(true)不是我们要找的调用resume继续main[1] resume Breakpoint hit: threadmain, android.app.Dialog.setCancelable(), line1,110 bci0 main[1] locals flag false这次参数是false。用set命令把它改为true再恢复执行main[1] set flag true flag true true main[1] resume重复上述过程每次断点命中都把flag设为true断点大约会被命中五六次直到消息框最终显示。此时对话框已变为可取消——点击对话框旁边区域即可关闭它而不会终止应用。步骤 3在String.equals断点处读取明文反篡改逻辑解除后开始提取密钥。静态分析已知密钥用 AES 解密后会与输入框中的字符串在java.lang.String.equals方法内比较。因此 stop in java.lang.String.equals Set breakpoint java.lang.String.equals 在输入框输入任意文本并点击 verify 按钮断点命中后执行localsBreakpoint hit: threadmain, java.lang.String.equals(), line639 bci2 main[1] locals Method arguments: Local variables: other radiusGravity main[1] cont Breakpoint hit: threadmain, java.lang.String.equals(), line639 bci2 main[1] locals Method arguments: Local variables: other I want to believe main[1] contequals的参数other就是应用持有的明文密钥字符串——这正是我们要找的答案。整个过程仅依赖 jdb 的断点、变量查看与变量改写三项能力完整演示了方法断点 locals 读参 set 改值这一核心逆向调试套路。实战案例二混合调试中为 lldb 铺路jdb 的价值还体现在与 native 调试器如 MASTG-TOOL-0152lldb的配合上详见 MASTG-TECH-0031 的 Debugging Native Code 一节。当目标 JNI 函数如Java_sg_vantagepoint_helloworldjni_MainActivity_stringFromJNI只在启动时执行一次时直接附加 lldb 往往为时已晚——此时libnative-lib.so尚未映射进进程内存。解决办法是先用 jdb 把进程温和地调到理想状态adb jdwp adb forward tcp:7777 jdwp:14342 { echo suspend; cat; } | jdb -attach localhost:777714342然后在 Java 运行时加载 native 库的方法上设断点并恢复命中后执行step up让进程运行至loadLibrary返回——此时libnative-lib.so已被加载 stop in java.lang.System.loadLibrary resume step upAll threads resumed. Breakpoint hit: threadmain, java.lang.System.loadLibrary(), line988 bci0 step up main[1] step up Step completed: threadmain, sg.vantagepoint.helloworldjni.MainActivity.clinit(), line12 bci5随后再启动lldb-server附加即可在 native 层对已加载的库设置断点。这是 JDWP 控制 Java 层执行节奏 ptrace 调试 native 层 的典型组合用法。进阶基于 jdb 的可调试性判定与注意事项用 jdb 附加判定应用是否可调试在安全测试中jdb 附加成功与否本身就是一条判定信号如果 jdb 能成功挂接说明应用的调试标志已激活参考 MASTG-TEST-0039。完整的判定流程# 1. 用 adb jdwp 识别目标应用 PID $ adb jdwp 2355 16346 最后启动的进程对应我们的应用 # 2. 建立本地端口与应用进程间的转发通道 # adb forward tcp:[LOCAL_PORT] jdwp:[APPLICATION_PID] $ adb forward tcp:55555 jdwp:16346 # 3. 用 jdb 附加本地端口开始调试会话 $ jdb -connect com.sun.jdi.SocketAttach:hostnamelocalhost,port55555 Set uncaught java.lang.Throwable Set deferred uncaught java.lang.Throwable Initializing jdb ... help常见问题排查the connection to the debugger has been closed 错误当 jdb 绑定本地通信端口时报此错可杀掉所有 adb 会话并重新开启一个全新会话再试MASTG-TEST-0039。断点不生效发布版字节码没有行号信息行断点无效应改用stop in方法断点。redefine不可用Android 虚拟机不支持类代码重定义JDWP 的该特性未实现。反调试对抗应用可能检测 JDWP 线程并主动退出参考 MASTG-KNOW-0028 的 JDWP 反调试一节以及 MASTG-TEST-0046 中 Attaching jdb and ptrace-based debuggers fails or causes the app to terminate 的观察点。此时需先绕过反调试逻辑再附加。适用范围与限制jdb 仅作用于Java 层Dalvik/ART对 native 库中的逻辑无能为力需配合 lldb 等 native 调试器附加 jdb 会使进程整体挂起/恢复对依赖时间敏感逻辑或强反调试的应用可能触发检测本文涉及的命令与行为以当前 MASTG 仓库所描述的 Android 平台Dalvik/ART、JDWP 实现为前提不同 Android 版本对 JDWP 特性的支持可能存在差异。总结jdb 是 MASTG Android 逆向测试工具箱中一个轻量而关键的成员它不依赖图形界面仅需 JDK 与 adb 即可对 Java 层应用执行断点调试、变量读写与执行追踪。其完整工作流可概括为adb jdwp定位进程 →adb forward建立 JDWP 转发 →{ echo suspend; cat; } | jdb -attach挂接并保持挂起 →stop in设方法断点 →locals/print/set观察与篡改运行时状态。无论是绕过反篡改对话框、在String.equals断点处提取明文密钥还是为 native 层 lldb 调试铺路jdb 都是值得优先掌握的入门动态分析工具。与之配套的更多调试与追踪手段Android Studio Profiler、strace、ftrace、lldb 等可进一步查阅 MASTG-TECH-0031 与 MASTG-TECH-0032。赞分享文档教程网络安全【免费下载链接】mastgThe OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security Weakness Enumeration (MASWE) weaknesses, which are in alignment with the OWASP MASVS.项目地址https://gitcode.com/gh_mirrors/ow/mastg点击查看免费下载相关推荐OWASP MASTG Android 可调试应用Debuggable Apps检测指南从 Manifest 标志到 JDWP 反调试OWASP MASTG Android 可调试应用Debuggable Apps检测指南从 Manifest 标志到 JDWP 反调试 导读 本文基于 O文档教程网络安全OWASP MASTG Android 反调试Anti-Debugging实战指南从 JDWP 检测到 ptrace 对抗OWASP MASTG Android 反调试Anti Debugging实战指南从 JDWP 检测到 ptrace 对抗 导读 本文基于 OWASP M文档教程网络安全OWASP MASTG 实战用 Semgrep 静态检测 Android StrictMode 使用MASTG-DEMO-0039OWASP MASTG 实战用 Semgrep 静态检测 Android StrictMode 使用MASTG DEMO 0039 导读 本文基于 OW文档教程网络安全创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考