02. 静态逆向、Manifest 分析与 Smali 重打包

本章目标是在不依赖运行时 Hook 的情况下,通过 APK 文件、Manifest、资源、DEX 和 Smali 理解 App 行为,并在自建授权 Demo 上验证"客户端本地判断不可信"。

1. 静态逆向的工作边界

静态逆向不是"打开 jadx 看一眼源码"。完整静态分析应回答:

问题 证据来源 输出
App 是谁 包名、版本、签名、渠道、targetSdk 基本信息表
入口在哪里 Manifest、launcher Activity、Deep Link 页面入口图
权限和组件是否危险 Manifest、Provider、Receiver、Service 暴露面清单
关键逻辑在哪里 DEX、字符串、调用链、资源 ID 关键类和方法列表
是否有 Native 逻辑 lib/System.loadLibrary so 清单
是否可被重打包 apktool、apksigner、安装结果 重打包验证记录

静态分析的结论必须能被后续动态分析验证。只凭反编译代码下结论,容易误判混淆、反射、动态下发和服务端逻辑。

2. APK 初步拆解

2.1 基本信息提取

bash 复制代码
mkdir -p case-reversedemo/02-static
cp app-debug.apk case-reversedemo/02-static/
unzip -l app-debug.apk > case-reversedemo/02-static/01-zip-list.txt
apkanalyzer manifest application-id app-debug.apk
apkanalyzer manifest version-name app-debug.apk
apkanalyzer manifest target-sdk app-debug.apk
apksigner verify --print-certs --verbose app-debug.apk

如果没有 apkanalyzer,可以先用 apktool:

bash 复制代码
apktool d -f app-debug.apk -o decoded-debug
sed -n '1,220p' decoded-debug/AndroidManifest.xml

2.2 文件结构解释

检查点 怎么看 风险判断
classes.dex 数量 `unzip -l app.apk grep classes`
lib/ ABI `unzip -l app.apk grep "lib/"`
assets/ 内容 `unzip -l app.apk grep assets`
res/raw 内容 apktool 解码后查看 证书、公钥、配置可能硬编码
META-INF 或签名块 apksigner verify --verbose 判断签名方案和证书信息

3. Manifest 分析

Manifest 是静态分析的第一张地图。重点不是把每一行抄下来,而是判断每个声明是否扩大了攻击面。

3.1 必查字段

字段 知识点讲解 Demo 检查
package / namespace 包名是 adb、Frida、日志过滤、启动组件的基础 记录 com.example.reversedemo
android:debuggable 正式包如果可调试,会降低攻击门槛 release 必须为 false 或不存在
android:allowBackup 允许备份可能导致数据被导出 敏感 App 应关闭或配置规则
android:usesCleartextTraffic 允许 HTTP 明文传输 正式业务应避免明文
android:exported 组件是否可被外部 App 调用 非必要组件应为 false
intent-filter Deep Link、分享入口、系统广播入口 参数必须做鉴权和校验
provider authorities ContentProvider 的访问入口 导出时必须权限控制

3.2 Demo:导出 Activity 检查

假设 Demo 中有一个测试页:

xml 复制代码
<activity
    android:name=".DebugPanelActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.reversedemo.OPEN_DEBUG" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

验证命令:

bash 复制代码
adb shell am start -a com.example.reversedemo.OPEN_DEBUG

风险解释:

  • 如果未登录也能打开 DebugPanel,说明组件导出缺少业务鉴权。
  • 如果页面显示 token、环境、配置,说明内部调试能力泄露。
  • 修复方式不是"隐藏按钮",而是关闭导出或在入口处做权限和登录态校验。

4. jadx 代码分析

4.1 检索策略

不要只搜索一个关键词。建议按主题分组检索:

主题 关键词
登录认证 loginpasswordtokenAuthorizationBearer
会员权益 vippremiumisVipmemberpay
签名加密 signsignatureHmacSHAAESRSA
环境检测 rootsudebugproxyemulatorfrida
网络请求 OkHttpClientRetrofitInterceptorbaseUrl
本地存储 SharedPreferencesSQLiteRoomDataStore
WebView addJavascriptInterfacesetJavaScriptEnabledloadUrl

4.2 Demo:定位本地会员判断

在 jadx 中搜索:

text 复制代码
isVip
vip-video
free-video

找到类似代码:

java 复制代码
public final boolean isVip(String userId) {
    return Intrinsics.areEqual(userId, "10001");
}

知识点:

  • Java/Kotlin 反编译结果是近似源码,不是原始源码。
  • Kotlin 的 object、伴生对象、默认参数、协程会生成额外类和方法。
  • 如果开启混淆,类名可能变成 a.b.c,但字符串、调用行为、资源 ID 仍可作为线索。

输出记录:

markdown 复制代码
## 会员判断定位

- 类:`com.example.reversedemo.UserCenter`
- 方法:`isVip(String userId): Boolean`
- 证据:反编译代码显示只比较本地字符串 `10001`
- 风险:会员权益依赖客户端判断,可能被 Smali 修改或 Hook
- 后续验证:修改 Smali 返回 true,观察非会员是否进入会员功能

5. apktool 与 Smali 基础

5.1 反编译与目录结构

bash 复制代码
apktool d -f app-debug.apk -o decoded-debug
find decoded-debug -maxdepth 2 -type d | sort

常见目录:

目录 说明
smali/smali_classes2/ DEX 转换后的 Smali 代码
res/ 解码后的资源
AndroidManifest.xml 解码后的 Manifest
apktool.yml 反编译元信息

5.2 Smali 语法最小知识

语法 含义
.class 类定义
.super 父类
.method / .end method 方法开始和结束
.locals 本地寄存器数量
const/4 v0, 0x1 给寄存器赋小整数
invoke-virtual 调用实例方法
invoke-static 调用静态方法
move-result 接收上一次调用返回值
if-eqz / if-nez 条件跳转
return v0 返回寄存器值

Smali 修改要克制。学习目标是验证风险,不是把业务逻辑改得不可维护。

6. Demo:修改 isVip() 返回值

6.1 定位 Smali 文件

bash 复制代码
grep -R "isVip" -n decoded-debug/smali*

可能找到:

text 复制代码
decoded-debug/smali/com/example/reversedemo/UserCenter.smali

原始片段可能类似:

smali 复制代码
.method public final isVip(Ljava/lang/String;)Z
    .locals 1
    const-string v0, "10001"
    invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->areEqual(Ljava/lang/Object;Ljava/lang/Object;)Z
    move-result v0
    return v0
.end method

修改成固定返回 true:

smali 复制代码
.method public final isVip(Ljava/lang/String;)Z
    .locals 1
    const/4 v0, 0x1
    return v0
.end method

6.2 重打包

bash 复制代码
apktool b decoded-debug -o reversedemo-vip-patched-unsigned.apk
keytool -genkeypair \
  -alias reverse-demo \
  -keyalg RSA \
  -keysize 2048 \
  -validity 3650 \
  -keystore reverse-demo.jks
apksigner sign \
  --ks reverse-demo.jks \
  --ks-key-alias reverse-demo \
  --out reversedemo-vip-patched.apk \
  reversedemo-vip-patched-unsigned.apk
apksigner verify --verbose reversedemo-vip-patched.apk
adb install -r reversedemo-vip-patched.apk

6.3 验证

验证项 方法 通过标准
APK 可安装 adb install -r 显示 Success
签名有效 apksigner verify --verbose 验证通过
行为改变 用非会员账号打开会员功能 原本不可进入,现在可进入
证据完整 截图、命令、修改前后代码 可复现

结论写法:

markdown 复制代码
本地 `isVip()` 返回值被 Smali 修改后,非会员账号可以进入 Demo 的会员功能。
该风险说明核心权益不能依赖客户端布尔判断,必须由服务端按登录态、订单状态和风控结果做最终授权。

7. 静态分析常见风险

风险 静态证据 影响 修复方向
硬编码密钥 jadx 搜到 SECRETAES_KEY 签名或加密可被复现 密钥服务端化,客户端只保留短期凭证
本地会员判断 isVip()premium 直接返回 权益可能被绕过 服务端授权,客户端只展示结果
组件导出 exported=true 且无权限 外部可拉起敏感页面 关闭导出或加权限、登录态校验
明文传输 usesCleartextTraffic=true 请求可被窃听篡改 HTTPS、HSTS、证书校验
WebView 暴露 addJavascriptInterface JSBridge 可能被滥用 限制域名、最小接口、参数校验
日志泄露 Log.d 输出 token 敏感数据泄露 release 删除敏感日志
备份开启 allowBackup=true 私有数据可能被备份 关闭或配置备份规则

8. 本章 Demo:完整静态分析任务

8.1 任务清单

  1. 提取 APK 基本信息、签名、包名、版本。
  2. 分析 Manifest,列出导出组件和敏感权限。
  3. 用 jadx 找到登录、会员、签名、环境检测、网络请求相关代码。
  4. 用 apktool 找到 isVip() 对应 Smali。
  5. 修改 Smali 并重打包签名。
  6. 安装修改后的 APK,验证行为变化。
  7. 输出静态分析报告。

8.2 报告模板

markdown 复制代码
# Static Analysis Report

## APK 信息
- 包名:
- 版本:
- targetSdk:
- 签名摘要:

## Manifest 风险
| 组件 | exported | 权限 | 风险 | 建议 |

## 关键逻辑定位
| 逻辑 | 类/方法 | 证据 | 后续验证 |

## Smali 修改验证
- 修改目标:
- 修改前:
- 修改后:
- 重打包命令:
- 安装结果:
- 行为变化:

## 结论
- 已验证风险:
- 需要动态验证的项:
- 修复建议:

9. 常见问题

问题 原因 处理
jadx 代码看不懂 混淆、Kotlin 语法、反射 结合字符串、调用链、动态日志
apktool 编译失败 资源异常、工具版本不兼容 升级 apktool,保留报错,尝试 --use-aapt2
重签名后无法安装 原包签名不同或版本降级 卸载原包,检查 versionCode
修改后闪退 寄存器数量、类型、控制流错误 对比 Smali,查看 logcat 崩溃栈
行为没变化 修改点不是实际调用点 用动态 Hook 或日志验证调用链

10. 本章交付物

text 复制代码
case-reversedemo/
  02-static/
    01-zip-list.txt
    02-manifest.md
    03-key-methods.md
    04-smali-before.smali
    05-smali-after.smali
    06-build-sign-install.txt
    07-static-report.md

11. 静态分析方法论

11.1 静态分析的四层证据

静态逆向不能只依赖一个工具输出。建议把证据分成四层:

层级 证据 工具 适合回答的问题
包结构层 APK 文件列表、签名、Manifest unzipapksignerapktool 这个 App 有什么组件和资源
字节码层 DEX、Smali、近似源码 jadx、apktool 业务逻辑在哪里
资源层 字符串、布局、raw、assets apktool、jadx 页面、配置、证书、隐藏入口在哪里
关联层 调用链、字符串引用、类关系 jadx、IDE 全文搜索 某个风险点如何被触发

每个结论至少要有两个证据来源。例如"会员判断在客户端"最好同时有 jadx 代码、Smali 位置、动态行为验证。

11.2 静态分析流程详解

text 复制代码
APK 基本信息
  -> Manifest 暴露面
  -> 资源和字符串线索
  -> Java/Kotlin 调用链
  -> Smali 可修改点
  -> Native 入口线索
  -> 风险假设
  -> 动态验证计划

每一步要输出文档,不要靠记忆推进:

阶段 输出文件 内容
基本信息 apk-basic.md 包名、版本、签名、hash、targetSdk
Manifest manifest-audit.md 权限、组件、导出状态、Deep Link
字符串 string-clues.md URL、密钥、错误文案、开关
代码 code-map.md 关键类、方法、调用链
Smali smali-targets.md 可验证修改点
风险 risk-hypothesis.md 风险假设和验证计划

12. Manifest 审计扩展

12.1 application 级别字段

字段 风险解释 Demo 验证 修复建议
android:debuggable 可被调试器附加,降低动态分析门槛 release 检查是否为 false 正式包关闭
android:allowBackup 可能导出私有数据 adb backup 或配置检查 敏感 App 关闭或配置规则
android:usesCleartextTraffic 允许 HTTP 明文请求 抓包检查 HTTP 正式环境禁用
android:networkSecurityConfig 决定信任哪些 CA 查看 XML debug/release 分离
android:extractNativeLibs so 是否解压到文件系统 查看安装目录 了解 Native 分析路径
android:requestLegacyExternalStorage 旧外部存储模型 检查数据落盘位置 最小化外部存储

12.2 Activity 审计

检查项 说明 命令
launcher Activity App 默认入口 dumpsys package
exported Activity 可被外部启动 adb shell am start -n
Deep Link URL 可拉起页面 adb shell am start -a VIEW -d
taskAffinity 任务栈劫持相关 Manifest 检查
excludeFromRecents 是否隐藏任务 Manifest 检查

导出页面验证模板:

markdown 复制代码
## Activity 导出验证
- 组件:
- exported:
- 触发命令:
- 未登录是否可访问:
- 参数校验:
- 敏感数据:
- 结论:

12.3 Service 审计

Service 风险通常来自外部 Intent 触发后台逻辑。

检查点:

  • 是否 exported=true
  • 是否声明权限。
  • 是否处理外部传入 action。
  • 是否启动下载、同步、支付、推送、清理等任务。
  • 是否能被重复触发导致资源消耗。

验证命令:

bash 复制代码
adb shell am startservice -n com.example.reversedemo/.SyncService
adb shell am start-foreground-service -n com.example.reversedemo/.SyncService

12.4 Receiver 审计

Receiver 重点看广播是否可伪造。

bash 复制代码
adb shell am broadcast -a com.example.reversedemo.DEBUG_ACTION --es cmd dump

风险判断:

  • 广播触发敏感动作。
  • action 未做权限保护。
  • 参数直接参与文件、网络或账号操作。
  • Receiver 在锁屏、后台也能触发。

12.5 Provider 审计

Provider 是数据泄露高风险点。

bash 复制代码
adb shell content query --uri content://com.example.reversedemo.provider/user
adb shell content insert --uri content://com.example.reversedemo.provider/user --bind name:s:test

检查:

  • 是否导出。
  • 是否有 read/write 权限。
  • authorities 是否可预测。
  • SQL 查询是否可注入。
  • 返回字段是否包含 token、手机号、身份证、内部 ID。

13. jadx 深度分析

13.1 关键词字典

类型 关键词
登录 loginauthtokensessionrefresh
会员 vipmemberpremiumsubscribeentitlement
支付 payorderpurchasereceiptbilling
签名 signsignaturehmacsha256nonce
加密 aesrsacipherencryptdecrypt
风控 riskrootdebugproxyemulator
Hook 检测 fridaxposedsubstrateptrace
WebView webviewjavascriptinterfaceloadUrl
存储 sharedpreferencessqlitedatastorekeystore
网络 okhttpretrofitinterceptorbaseUrl

13.2 调用链记录方法

不要只记录"找到了方法"。要记录它从哪里来、到哪里去。

markdown 复制代码
## 调用链:会员功能

1. `VipActivity.onCreate()`
2. `VipViewModel.loadVipStatus()`
3. `UserCenter.isVip(userId)`
4. `ApiClient.getVipResource(token)`

判断:
- UI 展示依赖本地 `isVip`。
- 真实资源是否依赖服务端 `getVipResource` 还需抓包验证。

13.3 反编译误差识别

现象 说明 处理
代码出现 throw new UnsupportedOperationException jadx 反编译失败 看 Smali 或换 CFR/FernFlower
Kotlin 代码非常啰嗦 编译器生成空安全和默认参数逻辑 回到业务语义,不纠结每行
变量名无意义 混淆或无调试信息 用字符串和调用关系命名
方法很多但无引用 反射或动态注册 搜索字符串和运行时 Hook
资源 ID 不直观 编译后的整数 ID 对照 R 类和 resources.arsc

14. Smali 深入

14.1 寄存器模型

Smali 方法里的寄存器分为:

类型 说明
v0v1 本地寄存器
p0 实例方法中的 this
p1p2 方法参数
.locals 本地寄存器数量
.registers 总寄存器数量

修改 Smali 时最常见错误是寄存器数量不够、类型不匹配、返回类型错误。

14.2 常见返回类型

Java 类型 Smali 返回 示例
boolean Z return v0v00x00x1
int I const/4 v0, 0x1
long J 使用宽寄存器
String Ljava/lang/String; const-string v0, "demo"
void V return-void

14.3 条件跳转理解

指令 含义
if-eqz v0, :label v0 等于 0 跳转
if-nez v0, :label v0 不等于 0 跳转
if-eq v0, v1, :label 两寄存器相等跳转
if-ne v0, v1, :label 两寄存器不相等跳转
goto :label 无条件跳转

Demo 练习:

  1. 找到 if-eqz 会员判断。
  2. 只改跳转方向,不改返回值。
  3. 重打包后观察页面是否变化。
  4. 对比"改返回值"和"改跳转"的差异。

14.4 插入日志示例

Smali 插日志复杂度高,适合进阶练习。更推荐先用 Frida 打印参数。如果必须插入日志,要注意寄存器和方法签名:

smali 复制代码
const-string v0, "ReverseDemo"
const-string v1, "isVip called"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

插入日志后要检查:

  • .locals 是否足够。
  • 返回值是否被覆盖。
  • 日志是否只用于 debug,不进入 release。

15. 重打包和签名

15.1 APK 签名知识

签名方案 说明 检查
V1 JAR 签名,兼容老版本 apksigner verify --verbose
V2 APK Signature Scheme v2 Android 7+
V3 支持密钥轮换 Android 9+
V4 增量安装相关 新版本设备

重签名会改变证书摘要。任何依赖原签名的逻辑都可能失败,例如登录、SDK 初始化、服务端签名白名单。

15.2 签名冲突处理

错误 原因 处理
INSTALL_FAILED_UPDATE_INCOMPATIBLE 已安装同包名不同签名 卸载原 App
INSTALL_FAILED_VERSION_DOWNGRADE versionCode 降级 提高 versionCode 或卸载
INSTALL_PARSE_FAILED_NO_CERTIFICATES 未签名或签名损坏 重新签名
App not installed ABI、SDK、签名、包名冲突 查看 adb install 详细错误

15.3 重打包实验记录

markdown 复制代码
## 重打包记录
- 原 APK SHA256:
- apktool 版本:
- 修改文件:
- 修改说明:
- 构建命令:
- 签名证书:
- 安装结果:
- 行为变化:
- 崩溃日志:

16. 静态风险库

风险编号 风险 证据 验证方式 修复
S-001 Debuggable 开启 Manifest dumpsys package release 关闭
S-002 allowBackup 开启 Manifest 备份或配置检查 关闭或配置规则
S-003 导出 Activity Manifest am start 关闭导出或鉴权
S-004 导出 Provider Manifest content query 权限控制
S-005 Deep Link 未鉴权 intent-filter am start -d 登录态和参数校验
S-006 硬编码密钥 jadx 字符串 Hook 签名函数 服务端密钥
S-007 本地会员判断 jadx/Smali Smali/Frida 服务端授权
S-008 明文 HTTP Manifest/代码 抓包 HTTPS
S-009 敏感日志 Log.d logcat release 移除
S-010 WebView 暴露 addJavascriptInterface 页面测试 域名白名单
S-011 不安全存储 SharedPreferences 文件检查 Keystore/最小化
S-012 弱加密模式 AES/ECB 代码审计 AEAD/GCM
S-013 无重放防护 签名缺 nonce 接口重放 nonce 和过期时间
S-014 Native 单点检测 nativeCheck Hook 风险评分
S-015 混淆缺失 release 类名清晰 jadx 对比 R8/ProGuard

17. 完整 Demo 作业

17.1 作业目标

完成一次从静态分析到可复现验证的闭环:

  1. 找出 Demo 中 5 个静态风险点。
  2. 至少选择 2 个风险点做 Smali 或命令验证。
  3. 对每个风险点写"证据、影响、验证、修复"。
  4. 输出 static-risk-report.md

17.2 评分表

项目 分值 要求
基本信息完整 10 包名、版本、签名、hash
Manifest 审计 20 覆盖四大组件和 application 字段
代码定位 20 至少 5 个关键方法
Smali 验证 20 至少 1 个可运行修改
风险表达 20 影响和修复写清楚
证据归档 10 文件命名规范

18. 静态逆向与 Smali

静态逆向的重点是证据链:从包结构、Manifest、资源、DEX、Smali 到重打包验证,逐步证明风险是否存在。

静态分析核心

知识点 核心理解 Demo/验证 常见误区
证据等级 Manifest、Smali、签名信息更接近真实产物;jadx 是近似源码。 同一方法同时记录 jadx 和 Smali 证据。 只凭反编译 Java 下结论。
调用链 风险点是否成立取决于入口、参数来源、校验和最终行为。 VipActivity 追到 UserCenter.isVip() 找到危险函数但不证明可触发。
字符串线索 URL、token、sign、错误文案和检测路径常在混淆后保留。 搜索 vipsign/api/ 字符串命中就直接判漏洞。
资源分析 布局、raw、assets、证书文件可能暴露隐藏入口和配置。 用 apktool 查看 res/xmlassets 只看代码,不看资源文件。
多 DEX 大型 App 可能有多个 classes*.dex,检索必须覆盖全部。 检查 APK 中 classes2.dex 等文件。 只分析第一个 DEX。
混淆判断 混淆改变名字,不改变所有字符串和运行行为。 对比 debug/release jadx 输出。 看到类名短就停止分析。
反射线索 反射会打断静态调用链,但字符串和动态 Hook 可补齐。 搜索 Class.forNamegetMethod 调用链断了就认为不可达。
动态加载 DexClassLoader、PathClassLoader 可能加载插件或二级 dex。 搜索 DexClassLoaderloadClass 只分析 base APK。

Manifest 与组件

知识点 核心理解 Demo/验证 常见误区
导出 Activity 外部可启动页面,风险取决于是否绕过鉴权或展示敏感信息。 am start 打开 Debug 页面。 导出就一律判高危。
导出 Service 外部可触发后台任务,可能导致越权或资源消耗。 am startservice 验证。 只看 Activity,忽略 Service。
导出 Receiver 外部广播可触发逻辑,参数可能被伪造。 am broadcast 构造 action。 认为广播只能系统发送。
ContentProvider Provider 可能直接暴露本地数据。 content query 验证 URI。 忽略 authorities 和权限配置。
Deep Link URL 可从浏览器或其他 App 拉起页面。 构造 reversedemo://open/vip 只验证打开,不验证参数和鉴权。
allowBackup 可能影响私有数据备份暴露。 检查 Manifest 和备份规则。 不区分普通 App 和敏感 App 场景。

Smali 与重打包

知识点 核心理解 Demo/验证 常见误区
寄存器 Smali 使用寄存器传参和保存局部变量。 分析 p0p1v0 随意加指令不调整 locals。
返回类型 方法签名决定返回值类型,boolean、String、void 不能混用。 修改 ()Z 返回 true。 返回类型错导致崩溃。
条件跳转 if-eqzif-nez 控制分支。 反转会员判断分支。 不知道原条件含义就盲改。
方法调用 invoke-virtualinvoke-static 等表示调用类型。 定位签名函数调用。 只改被调用函数,不确认调用点。
重打包 apktool 重建 APK 后必须签名才能安装。 修改 Smali 后 apktool b 构建成功就认为行为已改变。
签名冲突 不同证书不能覆盖安装同包名 App。 处理 INSTALL_FAILED_UPDATE_INCOMPATIBLE 把安装失败误判为防护成功。
完整性影响 重签名可能触发签名校验或 SDK 校验。 对比原包和重签名包行为。 只看本地安装,不测关键接口。
相关推荐
一拳一个娘娘腔1 小时前
告别图形化界面:基于CLI的Windows系统入侵排查与防御实战手册
windows·安全
计算机安禾2 小时前
【计算机网络】第3篇:网络编程范式的演进——阻塞IO、非阻塞IO与IO多路复用的比较研究
网络·计算机网络
江南十四行2 小时前
AI Agent应用类型及Function Calling开发实战(三)
服务器·前端·javascript
炘爚2 小时前
TCP三次握手和四次挥手
服务器·网络·tcp/ip
你的保护色2 小时前
光纤到户常用架构介绍(无源光网络PON,有源光网络AON)
网络·架构
疋瓞2 小时前
批处理_基础补充、文件和文件夹处理_02
windows
zzzyyy5382 小时前
Linux之缓冲区
linux·运维·服务器
hwscom2 小时前
Linux服务器如何进行安全加固,防止黑客攻击(Windows也适用)
linux·服务器·安全
学编程就要猛3 小时前
JavaEE初阶:网络原理-HTTP(下)
网络·网络协议·http