移动安全:Android 与 iOS 逆向

移动应用逆向的授权分析场景:从 APK 结构与 DEX 字节码入手,讲清 smali 修改与重打包、Frida 动态 Hook 与脚本注入、加固与脱壳思路,再到 iOS IPA 解密、Mach-O 与 Objective-C 运行时分析、越狱与反调试检测绕过,并给出移动 CTF 题目的常见考点与防御建议。

引言

移动逆向是 CTF 里「门槛最高、收益最直接」的方向之一。它和 PC 端二进制逆向共享同一套底层能力(汇编、调试、算法识别),但多了三层平台特有的复杂度:字节码层(Android 的 DEX 与 ART、iOS 的 Objective-C 运行时)、打包与加固层(壳、混淆、完整性校验)、设备与权限层(root/越狱检测、反调试、模拟器检测)。

工程上的难点集中在三处。第一是分析对象是多层的:一个 Android 应用从 APK 到最终执行的机器码要经过 DEX → ART 字节码 → JIT/AOT 编译三次转换,静态看到的 smali 与动态跑起来的机器码可能完全不同。第二是对抗是默认配置:商业加固产品(梆梆、爱加密、360 加固)会把 DEX 整体加密、运行时在内存里解密执行,静态分析直接失效,必须转动态脱壳。第三是环境敏感:反调试、反 root、反模拟器会主动改变行为,分析环境本身成为一道门槛。

本文按「安全模型 → Android 静态 → Android 动态 → 加固对抗 → iOS 静态 → iOS 动态 → 反检测 → 考点与防御」的顺序组织,所有实验在自建靶场 APK、公开 CTF 附件与自己拥有或获授权测试的应用上完成。移动端漏洞的系统性防护可参考 移动与 IoT 安全 ;底层静态分析与反编译方法论与 Reverse 方向静态分析与反编译 一脉相承,动态调试与脱壳思路则可对照 Reverse 方向动态调试与脱壳 。

目录

  1. 移动应用的安全模型与题目形态
  2. APK 结构与 DEX 字节码格式
  3. 反编译、smali 修改与重打包
  4. Frida 动态 Hook 与脚本注入
  5. 加固、脱壳与反调试对抗
  6. iOS IPA 解密与 Mach-O 结构
  7. Objective-C 运行时与 Swift 符号
  8. 越狱检测、反调试与绕过
  9. 移动题目的常见考点与防御要点

1. 移动应用的安全模型与题目形态

Android 与 iOS 的安全模型差异,直接决定了两个平台的逆向手法不同。

维度AndroidiOS
应用格式APK(ZIP 容器)IPA(ZIP 容器)
代码载体DEX 字节码 + 原生 .soMach-O 机器码
运行时ART(解释 + JIT + AOT)Objective-C runtime + Swift
签名v1/v2/v3/v4 签名,重签名后需重签代码签名 + 描述文件(provisioning)
权限模型应用沙箱 + 运行时权限沙箱 + entitlements
开放度可直接安装任意 APK需越狱或自签才能分析

Android 的开放性让分析更「容易」:APK 就是一个 ZIP,解压即得 DEX 与资源;但签名机制意味着任何修改都必须重新签名才能安装。iOS 的闭源性让分析更「难」:IPA 里的可执行文件在 App Store 分发时被 FairPlay 加密,必须先解密(dump)才能反编译。

CTF 里的移动题目大致有三类形态:

  • 静态分析题:给一个 APK 或 IPA,flag 藏在某个校验函数里,要求还原算法。这类题的核心是反编译 + 算法识别。
  • 动态对抗题:应用有反调试、反 root 或完整性校验,要求先绕过这些检测再拿到 flag。核心是 Hook 与 patch。
  • 协议与存储题:flag 藏在加密的 SharedPreferences、Keychain 条目或与服务器通信的协议里,要求解密本地数据或重放协议。

识别题型后立刻能确定路线:静态题优先反编译,动态题优先 Frida,协议题优先抓包 + 密钥提取。

2. APK 结构与 DEX 字节码格式

APK 是一个 ZIP,解压后的关键成员:

classes.dex          Dalvik 字节码(多 dex 时为 classes2.dex, classes3.dex ...)
AndroidManifest.xml  二进制 XML,声明组件、权限、入口 Activity
resources.arsc       资源索引表
res/                 资源文件
lib/<abi>/*.so       原生库(arm64-v8a / armeabi-v7a / x86_64)
META-INF/            签名信息(v1 签名在 MANIFEST.MF / CERT.SF / CERT.RSA)
assets/              原样打包的资产,常用于藏加密数据或第二个 dex

AndroidManifest.xml 是二进制的(AXML 格式),不能直接读,需要 apktool 解码或 androguard 解析。它的价值在于确定入口点与组件边界:哪个 Activity 是 LAUNCHER、有没有 ContentProvider(可能是 IPC 攻击面)、android:debuggable 是否为 true(为 true 时可任意调试,是送分题)。

DEX 文件结构的关键部分:

header          魔数 "dex\n035\0",含校验和与 SHA-1 签名
string_ids      字符串池索引
type_ids        类型索引
proto_ids       方法原型(参数与返回类型)
field_ids       字段索引
method_ids      方法索引(class_idx + proto_idx + name_idx)
class_defs      类定义(含方法字节码的偏移与长度)
data            字节码与调试信息

反编译链通常是:apktool 反编译出 smali(Dalvik 汇编的可读形式)与资源,jadx 或 dex2jar + jd-gui 直接产出 Java 伪代码。两条路的取舍很实际:

  • jadx 更快:一步出 Java,适合快速定位校验逻辑;但遇到混淆(如 ProGuard 后的 a.a.a 类名)可读性骤降,且对某些控制流还原不准。
  • smali 更准:与字节码一一对应,改动可控,是重打包与 patch 的必需形式;但读起来冗长。
# 本地教学:APK 静态分析标准动作
unzip -l app.apk | head -30          # 看结构,确认 dex 数量与 so 架构
apktool d app.apk -o app_smali       # 反编译为 smali + 解码资源
jadx -d app_java app.apk             # 直接产出 Java 伪代码
grep -rn "flag" app_smali/smali/ | head

DEX 的字符串池是静态分析的第一个富矿:所有硬编码字符串(含 flag 片段、密钥、URL)都在 string_ids 里,用 strings classes.dex 或 jadx 的搜索窗口直接捞。若字符串被加密,通常会在某个 static {} 静态初始化块或 <clinit> 里解密,这是下一步的定位目标。

3. 反编译、smali 修改与重打包

当静态分析定位到校验函数后,有两种利用路径:直接读出算法逆推,或者修改字节码绕过校验。后者是 CTF 里更快的做法。

smali 的基本语法要点:寄存器用 v0–v15(局部)与 p0–pN(参数,p0 通常是 this);方法以 .method 声明、.end method 结束;指令形如 const-string v0, "hello"、invoke-virtual {p0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z。

一个典型的「绕过校验」patch,把条件跳转反转或直接返回 true:

# 修改前:if-eqz v0, :cond_fail —— v0 为 0(false)则跳去失败分支
# 修改后:把跳转删掉,或者插入 const/4 v0, 0x1 强制返回 true
.method public static check(Ljava/lang/String;)Z
    .registers 3
    const/4 v0, 0x1          # 直接返回 true,跳过全部校验
    return v0
.end method

改完后必须重新打包并重新签名,否则安装被拒:

# 本地教学:重打包与重签名
apktool b app_smali -o patched.apk          # 回编译
keytool -genkey -v -keystore my.keystore -alias k -keyalg RSA \
        -keysize 2048 -validity 10000       # 生成自签名证书(首次)
apksigner sign --ks my.keystore patched.apk # 用 apksigner 签名
adb install -r patched.apk                  # 安装到自建测试设备

重打包常见的三个失败原因:apktool 回编译后资源 ID 错乱(用 --use-aapt2 解决);签名方案不匹配(Android 7+ 要求 v2 签名,只用 jarsigner 做 v1 会被拒);应用有签名校验——启动时读自己的签名哈希与预期比对,重签名后哈希变了,直接闪退。

签名校验是最常见的「反重打包」手段,绕过思路是定位校验代码(通常在 Application.onCreate 或某个 native 函数里)并 patch 掉,或者用 Frida 在运行时把 PackageManager.getPackageInfo().signatures 的返回值 Hook 成原始值。

检测与防御:签名校验的强度有限,因为校验逻辑本身在客户端。工程上应把关键判定放服务端,客户端校验只作为「提高门槛」而非安全边界。这条原则与 移动与 IoT 安全 里关于客户端不可信的论述一致。

4. Frida 动态 Hook 与脚本注入

Frida 是移动逆向的主力动态工具:它把一段 JS 引擎注入到目标进程,让你在运行时读写内存、替换函数实现、追踪调用。相比重打包,Frida 的优势是不改动应用文件,因此能绕过完整性校验与签名校验。

前提是设备已 root(Android)或越狱(iOS),并在设备上运行 frida-server:

# 本地教学:Frida 环境准备(自建测试设备)
adb push frida-server /data/local/tmp/ && adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"
frida-ps -U                        # 列出设备上的进程
frida -U -f com.example.app -l hook.js --no-pause   # 启动并注入脚本

Frida 脚本的核心 API 是 Java.perform(Android)与 Interceptor(native)。三个最常用的模式:

// 教学片段:Hook Java 方法,打印参数与返回值
Java.perform(function () {
    var Target = Java.use("com.example.app.Checker");
    Target.verify.implementation = function (input) {
        console.log("[*] verify called with: " + input);
        var ret = this.verify(input);        // 调用原实现
        console.log("[*] returned: " + ret);
        return ret;                           // 也可直接 return true 绕过
    };
});
// 教学片段:Hook native 函数(.so 里的导出函数)
var addr = Module.findExportByName("libnative.so", "check_flag");
Interceptor.attach(addr, {
    onEnter: function (args) {
        console.log("arg0 = " + Memory.readUtf8String(args[0]));
    },
    onLeave: function (retval) {
        console.log("ret = " + retval);
        retval.replace(ptr(1));               // 篡改返回值
    }
});
// 教学片段:主动调用静态方法枚举爆破(当校验是逐字节比较时)
Java.perform(function () {
    var C = Java.use("com.example.app.Checker");
    var charset = "abcdefghijklmnopqrstuvwxyz0123456789_";
    for (var i = 0; i < charset.length; i++) {
        var ok = C.checkChar(i, charset[i]);
        if (ok) console.log("pos " + i + " = " + charset[i]);
    }
});

第四类用法是内存扫描:Memory.scan 按特征搜索,Java.choose 枚举堆上的对象实例(对单例或反序列化后的对象特别有用)。当静态分析完全被加固挡住时,Java.choose 常能直接在运行时把解密后的对象捞出来。

Frida 也有反制:应用可以检测 frida-server 的进程名、扫描 /proc/self/maps 里的 frida 映射、检查 27042 默认端口。绕过手段是改名重编译 frida-server、用 frida-gadget 以库形式嵌入、或换用非默认端口。这是一场持续的猫鼠游戏。

5. 加固、脱壳与反调试对抗

商业加固(壳)的核心是把原始 DEX 加密存放,应用启动时由一段 native 代码在内存里解密并让 ART 直接加载解密后的字节码,磁盘上永远不出现明文 DEX。这意味着静态分析看到的 classes.dex 是壳,不是应用。

主流加固手段与对抗思路:

加固手段原理对抗思路
DEX 整体加密磁盘 DEX 加密,运行时解密内存 dump 解密后的 DEX
抽取壳(VMP 类)方法体被抽空,执行时按需还原主动调用触发还原后再 dump
指令混淆OLLVM 平坦化、虚假控制流反混淆工具 + 手工还原
完整性校验校验自身 DEX 哈希Hook 校验函数或改返回值
反调试ptrace 自附加、检测 TracerPid绕过 ptrace、隐藏调试器
反 Frida扫描进程名与端口改名、gadget 注入、端口伪装

脱壳的核心思路是「壳最终必须把明文 DEX 交给 ART,所以在内存里一定能抓到」。常用工具是 frida-dexdump(扫描进程内存里的 DEX 魔数 dex\n035\0 并 dump)与 FRIDA-DEXDump 的变体:

# 本地教学:对自建靶场 APK 做内存脱壳
frida-dexdump -U -f com.example.packed      # 启动并 dump 内存中的 DEX
# 产出多个 dex,用 jadx 逐个打开,找出含真实逻辑的那个

抽取壳更麻烦:方法体在磁盘上是空的(只有 nop),只有在方法被调用时才由 native 层还原。对抗方法是主动调用 + 遍历:用 Frida 遍历所有方法并逐个调用一次,触发全部还原后再 dump。这类工具(如 Youpk、fart)在社区里已有成熟实现。

反调试的常见实现与绕过:

// 教学片段:Hook 掉常见的反调试检测点(自建靶场)
Java.perform(function () {
    // 绕过 Debug.isDebuggerConnected
    var Debug = Java.use("android.os.Debug");
    Debug.isDebuggerConnected.implementation = function () { return false; };

    // 绕过读取 TracerPid(/proc/self/status)
    var File = Java.use("java.io.File");
    // 更彻底的做法是在 native 层 hook fopen/fgets,把 TracerPid 改成 0
});

native 层的反调试(ptrace(PTRACE_TRACEME) 自附加)需要在 native 层 Hook ptrace 让它直接返回 0,或在启动应用前先附加调试器让 ptrace 失败。Android 上还可通过 magisk 模块隐藏 root 与调试状态。

检测与防御:加固提升了攻击成本但无法根除风险——内存里的明文永远存在。防守方的正确姿势是「不把秘密放客户端」:密钥由服务端下发并绑定设备与时效,关键判定服务端完成,客户端加固只用于延缓逆向进度。

6. iOS IPA 解密与 Mach-O 结构

iOS 的分析起点比 Android 高一级:从 App Store 下载的 IPA 里,主可执行文件被 FairPlay DRM 加密,只有 cryptid 字段非 0 的 __TEXT 段被加密,必须先在越狱设备上运行并 dump 出解密后的镜像。

# 本地教学:IPA 结构与解密状态检查
unzip app.ipa -d app_dir
file app_dir/Payload/App.app/App           # 确认是 Mach-O 与架构
otool -l app_dir/Payload/App.app/App | grep -A4 LC_ENCRYPTION_INFO
# cryptid = 1 表示已加密,0 表示已解密

解密(dump)的经典方案是在越狱设备上用 frida-ios-dump:它启动应用后,在内存里定位解密后的 __TEXT 段并写回文件,得到 cryptid = 0 的可分析二进制。

Mach-O 的结构是理解 iOS 逆向的基础,它由「头 + Load Commands + 段」组成:

Mach-O Header     魔数(0xFEEDFACF 为 64 位)、CPU 类型、文件类型、Load Commands 数量
LC_SEGMENT_64     __TEXT(代码,只读可执行)、__DATA(可读写)、__LINKEDIT(符号等)
LC_SYMTAB         符号表(剥离后为空)
LC_DYLD_INFO      动态链接信息(绑定、重定位)
LC_MAIN           入口点
LC_ENCRYPTION_INFO 加密信息(cryptid)

与 ELF 的对照记忆法:__TEXT 对应 ELF 的 .text+.rodata,__DATA 对应 .data+.bss,LC_SYMTAB 对应 .symtab,dyld 对应 Linux 的 ld.so。PIE 在 iOS 上是默认且强制的,因此静态地址同样需要加运行时基址。

# 本地教学:Mach-O 分析常用命令
otool -h App            # Mach-O 头
otool -l App | grep -E "segname|sectname"   # 段与节
otool -Iv App           # 间接符号表
nm -m App | head        # 符号(未剥离时)
strings -a -n 6 App | head -50

7. Objective-C 运行时与 Swift 符号

Objective-C 的方法调用不是静态绑定,而是通过 objc_msgSend(receiver, selector, ...) 在运行时查找。这意味着逆向 ObjC 应用时,方法名与类名大量保留在二进制里——除非被刻意混淆,否则 strings 就能捞出绝大部分逻辑线索。

# 本地教学:从 Mach-O 里提取 ObjC 类与方法
otool -ov App | grep -E "name|class" | head -60   # 输出 ObjC 元数据
class-dump -H App -o headers/                     # 还原出 .h 头文件(未加密时)

objc_msgSend 在汇编里的形态是 bl _objc_msgSend,调用前 x0 是 receiver、x1 是 selector。分析时的技巧是:看到 adrp/add 加载一个字符串地址,紧接着 bl _objc_msgSend,那多半是在构造 selector 或日志字符串。

Swift 的符号经过了名字修饰(name mangling),形如 $s10MyAppName8Checkerv,可用 swift demangle 或 xcrun swift-demangle 还原:

# 本地教学:还原 Swift 修饰名
nm App | grep '\$s' | head
xcrun swift-demangle '$s10MyAppName8Checkerv'   # -> MyAppName.Checker

Swift 的方法分发比 ObjC 复杂:@objc 标注的方法走 objc_msgSend,纯 Swift 方法可能走虚表(vtable)分发或静态分发。虚表分发在汇编上表现为从对象头偏移处读函数指针再 br,静态分析较难跟进,需要动态调试辅助。Swift 字符串也不再是 C 字符串,而是带内联标志的结构体,strings 抓不到短字符串——这是 Swift 应用逆向的一个常见坑。

8. 越狱检测、反调试与绕过

iOS 应用常见的检测手段与绕过思路:

检测手段实现方式绕过思路
越狱文件检测检查 /Applications/Cydia.app 等路径Hook fileExistsAtPath: 返回 false
URL Scheme 检测canOpenURL:@"cydia://"Hook canOpenURL: 返回 false
fork() 检测越狱后可 fork,沙箱内不可Hook fork 返回 -1
dyld 镜像检测遍历已加载镜像找可疑 dylibHook _dyld_image_count
反调试ptrace(PT_DENY_ATTACH)Hook ptrace,或调试前先禁用
完整性校验校验自身签名与哈希Hook 校验函数

用 Frida 在 iOS 上 Hook ObjC 方法比 Android 更直接,因为 ObjC 的方法替换是语言内置能力:

// 教学片段:绕过越狱检测(自建靶场)
if (ObjC.available) {
    var NSFileManager = ObjC.classes.NSFileManager;
    var original = NSFileManager['- fileExistsAtPath:'];
    Interceptor.attach(original.implementation, {
        onEnter: function (args) { this.path = new ObjC.Object(args[2]).toString(); },
        onLeave: function (retval) {
            if (this.path.indexOf("Cydia") !== -1) retval.replace(ptr(0));  // 谎报不存在
        }
    });
}

ptrace(PT_DENY_ATTACH) 是 iOS 上最经典的反调试:进程调用后无法被附加,且附加会导致进程被杀。绕过方法是在 main 之前就用 Frida 的 spawn 模式附加并 Hook ptrace,或者 patch 掉那次调用(把 bl _ptrace 改成 nop)。

spawn 模式是关键:frida -U -f com.example.app 会先挂起进程、注入脚本、再恢复执行,因此能在任何反调试代码运行前完成 Hook。如果应用反调试足够强(如检测到 Frida 就自杀),可考虑用 frida-gadget 静态注入 IPA 后重签,或改用 lldb + 越狱设备上的 debugserver。

检测与防御:越狱检测与反调试都只能提高成本。金融类应用的正确做法是「检测到越狱环境时降级功能并上报服务端」,而不是本地硬拒绝——本地拒绝容易被绕过,服务端风控才是有效边界。

9. 移动题目的常见考点与防御要点

把两个平台的考点合并成一张速查表:

考点Android 手法iOS 手法
定位校验逻辑jadx 搜索 + smali 交叉引用class-dump + strings 找方法名
绕过校验smali patch 重打包 / Frida HookFrida Hook ObjC 方法
抓解密后代码frida-dexdump 内存 dumpfrida-ios-dump 解密 __TEXT
绕过完整性校验Hook 签名校验函数Hook codeSigning 相关调用
绕过环境检测magisk 隐藏 + Hook 检测点Hook fileExistsAtPath: 等
提取本地密钥Hook SharedPreferences / KeystoreHook Keychain 读写
协议重放抓包 + 证书固定绕过抓包 + SSLKillSwitch

防御侧的可落地清单:

  • 客户端不存秘密:密钥、算法、判定逻辑尽量上服务端;本地只保留必要的缓存并加密。
  • 传输层加固:启用证书固定(certificate pinning),并对关键请求做参数签名与时间戳防重放。
  • 本地数据加密:Android 用 EncryptedSharedPreferences 或 Keystore 支撑的密钥;iOS 用 Keychain 并设置 kSecAttrAccessibleWhenUnlockedThisDeviceOnly。
  • 完整性作为门槛而非边界:签名校验、越狱/root 检测用于提高成本与风控信号,不作为唯一防线。
  • 不记录敏感信息:关闭 release 构建的日志,避免密钥出现在 logcat 或 NSLog 里。
  • 混淆与加固适度:ProGuard/R8 混淆类名与字符串,配合商业加固;但要认识到加固只延缓逆向,不改变「客户端不可信」的事实。

关于移动端与 IoT 设备的整体防护体系,可延伸阅读 移动与 IoT 安全 ;若题目把 flag 藏在原生 .so 里,那么分析手法就回到 Reverse 方向静态分析与反编译 的范畴。

权衡取舍

场景优先手段理由局限
无加固、逻辑简单jadx 静态读代码最快,一步出伪代码混淆后类名不可读
需要改行为smali patch 重打包改动持久、可反复运行有签名校验即失效
有签名或完整性校验Frida 动态 Hook不改文件,天然绕过需 root/越狱设备
DEX 被整体加密frida-dexdump 内存脱壳明文必然出现在内存抽取壳需主动调用触发
抽取壳 / VMP主动调用遍历 + dump唯一可行路径耗时长,可能触发崩溃
iOS 加密 IPAfrida-ios-dump内存里是解密态需越狱设备
纯 Swift 应用动态调试 + 符号还原虚表分发静态难跟进符号修饰名需 demangle
反调试极强spawn 模式 / gadget 注入抢在检测代码前 Hook部分环境仍被识别

核心判断只有一条:先确认「有没有壳」,再决定走静态还是动态。无壳直接静态,有壳先脱壳;能改文件就重打包,不能改就 Frida。跳过这一步直接上手 jadx,在有壳的应用上只会看到一堆无意义的壳代码。

常见坑清单

  1. 以为 classes.dex 就是全部逻辑:多 dex 应用(classes2.dex 等)与 assets/ 里隐藏的 dex 常被忽略,先用 unzip -l 数一遍。
  2. 重打包后闪退:多半是签名校验或 v2 签名缺失;先用 apksigner verify -v 确认签名方案,再定位校验函数。
  3. Frida 注入失败报「unable to find process」:frida-server 版本与本地 frida 工具版本不一致,或设备未以 root 运行 server。
  4. Hook 不到 Java 方法:方法可能被内联到 native 层,或者名字被混淆;改用 Java.enumerateLoadedClasses 反查真实类名。
  5. strings 抓不到 Swift 字符串:Swift 短字符串内联在指令里,需用 xcrun swift-demangle 与反汇编交叉定位,不能只靠 strings。
  6. dump 出的 DEX 打不开:抽取壳 dump 到的可能是残缺方法体,需先主动调用触发全部还原。
  7. 反调试导致进程秒退:没有用 spawn 模式,脚本注入时反调试已执行;改用 -f 挂起启动。
  8. 模拟器上行为与真机不一致:反模拟器检测会改变分支,且某些 native 库只有真机 ABI;尽量用真机或对应架构的模拟器。
  9. 把 iOS cryptid = 1 的二进制直接丢进 IDA:__TEXT 段是密文,反汇编出来是乱码;必须先 dump 解密。
  10. 对未授权应用做上述操作:本文方法仅适用于 CTF 靶场与自己拥有或书面授权测试的应用,其余情形属违法行为。

小结

移动逆向的知识骨架可以压缩成一句话:平台层决定「从哪读代码」,加固层决定「静态还是动态」,设备层决定「能不能动态」。Android 从 APK/DEX 入手,iOS 从 IPA/Mach-O 入手;有壳先脱壳;反调试强就抢在检测之前用 spawn 模式注入。这三层判断一旦清晰,具体工具只是实现细节。

工具链上,静态侧 jadx/apktool/class-dump/otool 是标配,动态侧 Frida 几乎是唯一选择,脱壳侧 frida-dexdump 与 frida-ios-dump 覆盖了绝大多数商业壳。真正需要投入时间的是「反检测对抗」——它没有通用解,只能针对具体实现逐个分析。

最后回到防守视角:移动端逆向之所以有效,根本原因是客户端处于攻击者的完全控制之下。加固与混淆能提高成本,但任何纯客户端的判定最终都会被绕过。把密钥、算法与判定上移到服务端,把客户端检测当作风控信号而非安全边界,才是这个领域的正确姿势。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「网络安全攻防」更多文章

  1. 流量分析与协议逆向
  2. 椭圆曲线与格攻击
  3. Windows 提权与 AD 内网渗透