Frida学习笔记(二十四):Stalker指令级追踪

admin 2026-06-30 08:03:32 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了Frida内置的Stalker代码追踪引擎,这是一种线程级指令跟踪工具,基于动态重编译机制实现逐条汇编指令的追踪。文章对比了Stalker与Interceptor的差异,系统讲解了9个核心API的使用方法,包括follow/unfollow/flush/exclude等高频操作,并提供了性能优化建议如exclude排除系统库减少噪音。文档强调Stalker在分析OLLVM混淆、VMP保护和代码覆盖率场景中的关键作用,建议采用Interceptor定位后Stalker深入分析的策略。 综合评分: 85 文章分类: 逆向分析,安全工具,移动安全,WEB安全,二进制安全


8.2 Stalker 被检测的风险

性能解决了,第二个最容易踩的坑就是被 App 反检测干掉。Stalker 的动态重编译机制可能被高级的反分析保护检测到:

  • 代码完整性校验:App 可能计算代码段(.text section)的 hash。虽然 Stalker 不修改原始代码,但某些更激进的检测方式可能检查代码执行路径的完整性
  • 时间检测:Stalker 显著增加执行时间(典型 3-5x,稳态可降到 1.5-2x),可被精确的计时器(如 clock_gettime)检测到。某些 App 会在关键操作前后设置时间窗口
  • 内存特征:Stalker 分配的代码块有特定的内存布局特征(大量可执行的匿名 mmap 区域),可通过读取 /proc/self/maps 来检测

商业反检测 App(银行 / 直播 / 加固后的国民级 App)往往同时上多种检测。具体绕过手法见第 17/18 篇反调试章节。

8.3 自修改代码(SMC)

如果目标代码会在运行时修改自身(常见于加壳/VMP/代码混淆保护),需要调整信任阈值:

// 处理自修改代码
Stalker.trustThreshold = -1;  // 永不信任,每次执行基本块都重新编译
// 注意:这会显著降低性能,仅在确认存在 SMC 时使用

// 或者设为 0:立即信任(忽略自修改)
// 适用于代码虽然有解密过程,但解密后不会再变化的场景
Stalker.trustThreshold = 0;

何时怀疑存在 SMC? 如果你发现 Stalker 追踪的指令与 IDA 中看到的反汇编不一致,或者 Stalker 在某些基本块崩溃,可能是代码在运行时被解密/修改了。

8.4 多线程竞态

  • Stalker.follow() 跟踪的是特定线程,不会自动跟踪该线程创建的子线程
  • 多个线程同时被 follow 时,各自的 transform 回调是独立的,不会相互干扰
  • onReceive / onCallSummary 回调是在 Frida 的 JS 线程中串行执行的,不存在并发问题

8.5 ARM Thumb/ARM 模式切换

在 32 位 ARM 上,代码可能在 ARM(32 位指令)和 Thumb(16 位指令)模式之间切换。确保传给 Stalker 的地址考虑了 Thumb 位——地址的最低位为 1 表示 Thumb 模式:

// 在 32 位 ARM 上,注意 Thumb 位
const addr = Module.findExportByName("libtarget.so", "target_func");
// addr 可能包含 Thumb 位(最低位为 1),Stalker 会自动处理
// 不需要手动清除 Thumb 位

为什么要关注这个? 在 64 位 AArch64 上不存在 Thumb 模式,因此只有在分析 32 位 ARM 代码时需要注意。如果你的目标设备和 App 都是 64 位的,可以跳过此节。

九、进阶:与其他工具联动

Stalker 自己就能完成数据采集,但真要做大型项目逆向,3 个搭子缺一不可:IDA Lighthouse 把覆盖率可视化(看路径)、CModule 把热点回调从 JS 拉到 C(看性能)、Python 把 trace 数据自动化分析(看结论)。下面三段独立可读,但实战通常三者都会用上——大项目 = 数据采集(Stalker)+ 可视化(Lighthouse)+ 性能(CModule)+ 分析(Python),四件套一起转才转得顺。

9.1 Stalker + IDA Lighthouse(覆盖率可视化)

将 Stalker 收集的覆盖率数据导出为 drcov 格式,然后在 IDA 中通过 Lighthouse 插件加载,实现代码覆盖率的可视化着色:

# ida_coverage.py — 生成 Lighthouse 兼容的 drcov 二进制文件

import struct

def write_drcov(module_name, module_base, module_size, module_path, blocks, output_path):
    """
    生成 drcov 格式的覆盖率文件
    blocks: [(offset, size), ...] 列表,每个元素是一个基本块的偏移和大小
    """
    with open(output_path, "wb") as f:
        # 写入文本格式的头部
        f.write(b"DRCOV VERSION: 2\n")
        f.write(b"DRCOV FLAVOR: frida-stalker\n")
        f.write(f"Module Table: version 2, count 1\n".encode())
        f.write(b"Columns: id, base, end, entry, checksum, timestamp, path\n")
        f.write(f"0, {module_base:#x}, {module_base + module_size:#x}, 0, 0, 0, {module_path}\n".encode())
        f.write(f"BB Table: {len(blocks)} bbs\n".encode())

        # 写入二进制格式的基本块数据
        for offset, size in blocks:
            # drcov BB entry 结构:uint32 start_offset, uint16 size, uint16 module_id
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.write(struct.pack("<IHH",&nbsp;offset,&nbsp;size,&nbsp;0))

&nbsp;&nbsp;&nbsp;&nbsp;print(f"[*] 覆盖率文件已保存: {output_path}")
&nbsp;&nbsp;&nbsp;&nbsp;print(f"[*] 共 {len(blocks)}&nbsp;个基本块")
&nbsp;&nbsp;&nbsp;&nbsp;print(f"[*] 在 IDA 中加载: File -> Load File -> Code Coverage File")

9.2 Stalker + CModule(高性能 C 回调)

当 JavaScript 回调的性能开销无法接受时,可以使用 Frida 的 CModule 将 transform 和事件处理逻辑用 C 语言实现。C 回调直接在目标进程的上下文中执行,没有 JS ↔ Native 的跨边界调用开销:

// cmodule_stalker.js — 使用 CModule 实现高性能 Stalker 回调

const&nbsp;cm&nbsp;=&nbsp;new&nbsp;CModule(`
#include <gum/gumstalker.h>
#include <stdio.h>

// 这些变量由 JS 端设置(通过 CModule 构造函数的第二个参数)
extern guint64 module_start;
extern guint64 module_end;
extern guint64 call_count;

// C 语言版本的 transform 回调
// 直接操作 Stalker 的 C API,没有 JS 桥接开销
void
transform (GumStalkerIterator * iterator,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GumStalkerOutput * output,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gpointer user_data)
{
&nbsp;&nbsp;cs_insn * insn;

&nbsp;&nbsp;while (gum_stalker_iterator_next (iterator, &insn))
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;// 只对目标模块内的代码进行插桩
&nbsp;&nbsp;&nbsp;&nbsp;if (insn->address >= module_start && insn->address < module_end)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 在 RET 指令前插入 C 回调
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (insn->id == ARM64_INS_RET)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gum_stalker_iterator_put_callout (iterator,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;on_ret, NULL, NULL);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;gum_stalker_iterator_keep (iterator);
&nbsp;&nbsp;}
}

// C 语言版本的回调函数 — 直接处理,无 JS 调用开销
static void
on_ret (GumCpuContext * cpu_context,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gpointer user_data)
{
&nbsp;&nbsp;call_count++;
&nbsp;&nbsp;// 在 C 层直接处理数据,避免频繁的 JS 回调
&nbsp;&nbsp;if (cpu_context->x[0] != 0)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;// 只记录非零返回值(减少输出量)
&nbsp;&nbsp;&nbsp;&nbsp;printf ("RET x0=0x%llx pc=0x%llx\\n",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(unsigned long long) cpu_context->x[0],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(unsigned long long) cpu_context->pc);
&nbsp;&nbsp;}
}
`,&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;// 将 JS 端的变量传递给 CModule
&nbsp;&nbsp;&nbsp;&nbsp;module_start:&nbsp;ptr(targetModule.base),
&nbsp;&nbsp;&nbsp;&nbsp;module_end:&nbsp;ptr(targetModule.base.add(targetModule.size)),
&nbsp;&nbsp;&nbsp;&nbsp;call_count:&nbsp;Memory.alloc(8)&nbsp;&nbsp;// 分配 8 字节作为计数器
});

// 使用 CModule 导出的 transform 函数
Stalker.follow(threadId,&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;transform:&nbsp;cm.transform,
&nbsp;&nbsp;&nbsp;&nbsp;data:&nbsp;ptr(0)
});

CModule 跨边界类型对照:JS 端传 ptr(...)(NativePointer),C 端 extern guint64 收 ── Frida 会把 NativePointer 当成 64 位无符号整数原样写入符号地址,C 代码里直接当地址用即可。Memory.alloc(N) 同理传指针。详见 frida-gum 文档 CModule symbol values

性能对比:使用 CModule 的 transform 回调比纯 JavaScript 版本通常快 5-10 倍,因为省去了每次 putCallout 时从 Native 调用 JS 引擎的开销。在需要对大量指令进行插桩时,CModule 是必须的。

9.3 Stalker + Python 数据分析

对大规模 trace 数据进行自动化分析。当追踪数据量达到数万甚至数十万条记录时,人工查看不现实,需要自动化分析框架:

# trace_analyzer.py — 自动化 trace 数据分析框架

import&nbsp;json
from&nbsp;collections&nbsp;import&nbsp;Counter,&nbsp;defaultdict

class&nbsp;StalkerTraceAnalyzer:
&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;__init__(self,&nbsp;trace_file):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""从 JSONL 文件加载追踪数据"""
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.events&nbsp;=&nbsp;[]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;with&nbsp;open(trace_file)&nbsp;as&nbsp;f:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;f:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.events.append(json.loads(line))

&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;call_frequency(self,&nbsp;top_n=20):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""分析函数调用频率 — 找出最常被调用的函数(可能是循环体内的热点)"""
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;targets&nbsp;=&nbsp;Counter()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;ev&nbsp;in&nbsp;self.events:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;ev.get("type")&nbsp;==&nbsp;"call":
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;targets[ev["target"]]&nbsp;+=&nbsp;1

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"\n=== Top {top_n}&nbsp;调用目标 ===")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;target,&nbsp;count&nbsp;in&nbsp;targets.most_common(top_n):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"{count:6d}x  {target}")

&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;call_sequence(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""提取函数调用的时间序列 — 还原执行的先后顺序"""
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;calls&nbsp;=&nbsp;[ev&nbsp;for&nbsp;ev&nbsp;in&nbsp;self.events&nbsp;if&nbsp;ev.get("type")&nbsp;==&nbsp;"call"]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"\n=== 调用序列(共 {len(calls)}&nbsp;次调用)===")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;i,&nbsp;call&nbsp;in&nbsp;enumerate(calls[:50]):&nbsp;&nbsp;# 只显示前 50 个
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"&nbsp;&nbsp;[{i:3d}] {call['target']}")

&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;detect_loops(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""检测循环模式 — 通过 PC 重复出现来识别循环"""
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pc_sequence&nbsp;=&nbsp;[ev["pc"]&nbsp;for&nbsp;ev&nbsp;in&nbsp;self.events&nbsp;if&nbsp;"pc"&nbsp;in&nbsp;ev]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("\n=== 热点地址(可能的循环)===")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pc_counts&nbsp;=&nbsp;Counter(pc_sequence)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;pc,&nbsp;count&nbsp;in&nbsp;pc_counts.most_common(10):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;count&nbsp;>&nbsp;5:&nbsp;&nbsp;# 执行超过 5 次的地址很可能在循环内
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"&nbsp;&nbsp;0x{pc}: {count}&nbsp;次执行")

&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;data_flow(self,&nbsp;register="x0"):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"""追踪特定寄存器值的变化 — 观察数据如何在算法中流动"""
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;values&nbsp;=&nbsp;[]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;ev&nbsp;in&nbsp;self.events:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;register&nbsp;in&nbsp;ev:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;values.append((ev.get("pc",&nbsp;"?"),&nbsp;ev[register]))

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"\n=== {register}&nbsp;值变化追踪 ===")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;pc,&nbsp;val&nbsp;in&nbsp;values[:30]:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"&nbsp;&nbsp;[{pc}] {register}&nbsp;= {val}")

# 使用示例
analyzer&nbsp;=&nbsp;StalkerTraceAnalyzer("stalker_trace.jsonl")
analyzer.call_frequency()&nbsp;&nbsp;&nbsp;&nbsp;# 哪些函数被调用最多?
analyzer.call_sequence()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# 调用的先后顺序是什么?
analyzer.detect_loops()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# 有哪些循环?
analyzer.data_flow("x0")&nbsp;&nbsp;&nbsp;&nbsp;# x0 寄存器的值如何变化?

十、总结

Stalker 是 Frida 的指令级代码追踪引擎,填补了 Interceptor(方法级 Hook)的盲区——能看到函数内部每一条指令的执行。核心能力分五类:指令追踪(exec 事件)、调用追踪(call/ret 事件)、基本块追踪(block 事件)、编译时注入(transform + putCallout)、覆盖率收集(compile 事件配合 Lighthouse 可视化)。

Stalker 使用流程总结

1.&nbsp;确定目标&nbsp;→&nbsp;用&nbsp;Interceptor/Java&nbsp;Hook&nbsp;定位关键&nbsp;Native&nbsp;函数
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓
2.&nbsp;配置环境&nbsp;→&nbsp;Stalker.exclude&nbsp;排除无关模块
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓
3.&nbsp;初步追踪&nbsp;→&nbsp;call/block&nbsp;事件,了解整体调用结构
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓
4.&nbsp;深入分析&nbsp;→&nbsp;transform&nbsp;+&nbsp;putCallout,在关键位置记录状态
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓
5.&nbsp;数据分析&nbsp;→&nbsp;Python&nbsp;端自动化分析&nbsp;trace&nbsp;数据
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↓
6.&nbsp;算法还原&nbsp;→&nbsp;根据&nbsp;trace&nbsp;数据还原逻辑,编写验证代码

核心要点

  1. 先粗后细:从宏观的调用统计开始,逐步深入到指令级追踪。不要一上来就开 exec: true
  2. 精确限定范围:善用 Stalker.exclude() 和地址过滤,避免信息过载和性能崩溃
  3. 合理选择事件:按需开启事件类型——compile 适合覆盖率,call 适合调用链,exec 仅作为最后手段
  4. transform 是核心:掌握 putCallout 和指令过滤,可以实现几乎任何分析需求
  5. 注意性能:始终在精度和性能之间找到平衡,必要时使用 CModule 将热点逻辑用 C 实现
  6. 配合使用:Stalker 不是孤立使用的,与 Interceptor、Java Hook、IDA、Python 分析工具配合效果最佳

下次再遇到一段 OLLVM 混淆 / 未知签名算法,推荐工作流

  1. 入口定位 — 抓包看异常字段(X-Sign / sign 头),Java 层 hook 反查到 Native 函数
  • JNI 静态注册 → 直接 Module.findExportByName
  • JNI 动态注册 → 走 第 22 篇 hook RegisterNatives 拿真实地址
  • 符号被 strip → 走 第 21 篇 多策略兜底(特征码 / 字符串交叉引用 / 调用点反查)
  1. 粗追踪 — Interceptor.attach + Stalker.follow + events: { call: true } + onCallSummary
  • 拿调用统计,看「我的 Native 函数内部都调了谁」
  • 必开 Stalker.exclude 把 libc / linker / frida-agent 全排除,只追目标 SO
  1. 精细 dump — 用 transform + putCallout 在关键 mnemonic(eor / lsl / bl / blr 等)处插桩
  • 把 PC / 寄存器值 / 栈值打成结构化 record,push 到数组
  • onLeave 时一次性 send() 给 Python 端
  1. 算法还原 — Python 端拿 trace 数据反推
  • 看 call 序列 → 哪些是 libc 函数(memcpy / sha256)、哪些是自定义

    看 ALU 序列 → 推算 XOR 密钥 / 偏移变换

  1. 独立验证 — 用 Python 复现整套算法,跟 Frida 抓到的真实输入/输出比对
  • 完全一致 → 还原成功,可用于黑盒批量签名 / fuzz / 自动化

按这条流程走,大多数「Native 层签名/加密算法」逆向都能拿下。exec 事件永远是最后手段——除非你需要看每一条指令的执行,否则 call/block 已经够用。



免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:泡泡以安 泡泡以安 泡泡以安《Frida学习笔记(二十四):Stalker 指令级追踪》

评论:0   参与:  0