吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3059|回复: 23
收起左侧

[Android 原创] ai逆向某智能摄像头APK逆向分析实战:脱壳、去签名校验全流程

  [复制链接]
220e285 发表于 2026-6-24 23:05

[md][md]# 🔐 某智能摄像头 APK 逆向分析实战

<h4 align="center">脱壳 · 去签名校验 — 全流程深度拆解</h4>

<p align="center">
<img src="https://img.shields.io/badge/目标-智能摄像头%20IoT-blue?style=flat-square&logo=android" alt="目标">
<img src="https://img.shields.io/badge/壳-爱加密%20v4.6.4.7-red?style=flat-square" alt="壳">
<img src="https://img.shields.io/badge/平台-Android%208.1%20ARM64-green?style=flat-square&logo=android" alt="平台">
<img src="https://img.shields.io/badge/工具-Frida%2017.15.3-orange?style=flat-square" alt="Frida">
<img src="https://img.shields.io/badge/状态-已完成-success?style=flat-square" alt="状态">
</p>


🎯 目标 📱 平台 🛠️ 工具链
com.xxx.smartcamera Android 8.1 (ARM64) Frida 17.15.3
某智能摄像头 vX.X.X 真机 (ROOT) JDK 11 · Python 3.12

📑 目录

<p align="center">

Step 1 Step 2 Step 3
🔍 壳识别与分析 🧬 DEX 内存脱壳 ✍️ 去除签名校验
→ 跳转 → 跳转 → 跳转

</p>


🔍 Step 1 — 壳识别与分析

1.1 识别壳特征

拿到 APK 后,第一件事就是解剖它的内部结构:

$ unzip -l sample.apk | grep "classes.*\.dex\|libshell\|libshella\|IJm"

01-shell-files.png

🎯 关键发现:一眼就能看出典型的加固痕迹——1 个正常的 classes.dex + 14 个体积异常小的 stub。

📄 文件 📏 大小 💡 说明
classes.dex 195 KB 壳的 Wrapper 代码
classes2.dex ~ classes15.dex 各 2.4 KB ⚠️ 虚假 stub DEX(全部相同)
lib/arm64-v8a/libshell-super.com.xxx.smartcamera.so 446 KB 🔴 壳核心 Native 库
lib/arm64-v8a/libshella-4.6.4.7.so 6 KB 辅助壳模块
res/IJm.xml 496 B 壳配置文件

<div align="center">

┌─────────────────────────────────────────────────────┐
│  🏷️  结论:爱加密 (AiJiaMi) Shell-Super v4.6.4.7  │
└─────────────────────────────────────────────────────┘

</div>

14 个 classes2~15.dex 的 DEX 魔数完全相同且仅有 2432 字节,确认为占位空壳:

$ xxd classes2.dex | head -1
00000000: 6465 780a 3033 3700   dex.037      ← DEX 魔数正确但内容为空

02-stub-dex-hex.png


1.2 分析壳加载流程

classes.dex(壳的 Wrapper)中提取关键类名:

$ strings classes.dex | grep "com/wrapper/proxy"

03-jadx-decompile.png

com/wrapper/proxyapplication/WrapperProxyApplication   ← 壳 Application
com/wrapper/proxyapplication/MultiDex                  ← DEX 动态加载
com/wrapper/proxyapplication/AndroidNClassLoader        ← 自定义 ClassLoader
com/xxx/smartcamera/StubWrapperProxyApplication         ← Manifest 入口

📌 壳加载链路:从 Native 层到 Java 层,每一步都环环相扣。

<!-- 📸 截图建议:可以用 draw.io 或 Excalidraw 绘制此流程图导出为 03-loading-chain.png -->

flowchart TD
    A["📦 System.loadLibrary('shell-super')"] --> B["🔧 libshell-super.so → .init_array"]
    B --> B1["🛡️ ELF 完整性检查"]
    B1 --> C["🔑 JNI_OnLoad"]
    C --> C1["🔓 解密 DEX → prodexdir/"]
    C1 --> D["🏗️ WrapperProxyApplication.attachBaseContext()"]
    D --> E["📚 MultiDex.install()"]
    E --> E1["🔄 自定义 ClassLoader 加载解密 DEX"]
    E1 --> F["🎯 反射创建 RealApplication"]

    style A fill:#1a1a2e,stroke:#e94560,color:#eee
    style B fill:#16213e,stroke:#0f3460,color:#eee
    style B1 fill:#16213e,stroke:#e94560,color:#eee
    style C fill:#16213e,stroke:#0f3460,color:#eee
    style C1 fill:#16213e,stroke:#e94560,color:#eee
    style D fill:#1a1a2e,stroke:#533483,color:#eee
    style E fill:#1a1a2e,stroke:#533483,color:#eee
    style E1 fill:#16213e,stroke:#e94560,color:#eee
    style F fill:#0f3460,stroke:#00ff88,color:#eee

1.3 防护系统分析

$ strings libshell-super...so | grep -E "SIGSEGV|hook|LD_PRELOAD|inotify|check"

壳内置了多层防护,防护密度远超普通加固:

🛡️ 检测项 ⚙️ 实现方式 🔴 威胁等级
ELF 完整性 catch SIGSEGV when check_elfheader 🔴 高
注入检测 LD_PRELOAD 环境变量扫描 🟠 中
文件监控 inotify_add_watch (CLOSE_WRITE/DELETE_SELF/MOVE_SELF) 🔴 高
Hook 检测 libxhook 1.1.12 + hooking %s in %s 日志 🔴 高
反调试 catch SIGSEGV when init or hook 🔴 高
字节码校验 DEX 中部分类字节码被故意破坏 🟡 中

⚠️ 注意:这套反作弊系统是后续脱壳和 Hook 操作的主要障碍,直接 Frida spawn 会触发 SIGSEGV 崩溃。


🧬 Step 2 — DEX 内存脱壳

2.1 脱壳策略

爱加密的 DEX 保护有两个关键特征:

<div align="center">

🔴 挑战 💡 策略
磁盘上的 DEX 字节码被破坏 不从磁盘读,从内存中 dump
壳的 ClassLoader 在运行时修复字节码 在壳完成修复 之后 再 dump

</div>

核心思路:让壳自己把 DEX 解密并修复好,然后我们直接从它的工作目录拿走成品。


2.2 部署 Frida Server

# 1️⃣ 下载 ARM64 frida-server
curl -L -o frida-server-arm64.xz \
  "https://github.com/frida/frida/releases/download/17.15.3/frida-server-17.15.3-android-arm64.xz"

# 2️⃣ 解压并推送到设备
xz -d frida-server-arm64.xz
adb push frida-server-arm64 /data/local/tmp/fs
adb shell "su -c 'chmod 755 /data/local/tmp/fs'"

# 3️⃣ 以后台模式启动
adb shell "su -c '/data/local/tmp/fs -D &'"
✅ Frida Server 已就绪

2.3 🔑 关键发现:prodexdir 自动解密

启动 APP 后,壳会自动将解密后的 DEX 写入可读目录——这是整个脱壳过程最大的突破口:

$ adb shell "su -c 'ls -la /data/data/com.xxx.smartcamera/files/prodexdir/'"

04-prodexdir-listing.png

total 175160
-r--r--r-- 1 u0_a106 14,139,272  00O000ll111l_0.dex
-r--r--r-- 1 u0_a106 11,098,960  00O000ll111l_1.dex
-r--r--r-- 1 u0_a106 11,855,208  00O000ll111l_2.dex
  ...
-r--r--r-- 1 u0_a106  8,822,496  00O000ll111l_14.dex

<div align="center">

┌──────────────────────────────────────────┐
│  📦 15 个 DEX · 总计 120 MB · 全部有效 │
└──────────────────────────────────────────┘

</div>

一键拉取所有 DEX:

for i in $(seq 0 14); do
  adb shell "su -c 'cp \
    /data/user/0/com.xxx.smartcamera/files/prodexdir/firstLoad/00O000ll111l_${i}.dex \
    /sdcard/fixed_${i}.dex'"
  adb pull "/sdcard/fixed_${i}.dex" "fixed_dex/classes${i}.dex"
done

验证 DEX 有效性:

$ xxd classes0.dex | head -1
00000000: 6465 780a 3033 3700    ← "dex.037" — 标准 DEX 魔数 ✅

05-dex-verify.png

<div align="center">

文件 大小 状态
classes0.dex 14,139,272 bytes ✅ 有效
classes1.dex 11,098,960 bytes ✅ 有效
... ... ✅ 有效
classes14.dex 8,822,496 bytes ✅ 有效

</div>

💡 经验之谈:很多时候脱壳不需要写复杂的 Frida dump 脚本。先观察壳的行为——它自己就会把解密产物放在某个目录里,直接 cp 出来即可。


✍️ Step 3 — 去除签名校验

3.1 ❌ 错误尝试:直接 Patch ELF

最直觉的做法是直接修改 libshell-super.so 中的签名字符串——但这条路是死胡同:

06-elf-patch-error.png

java.lang.UnsatisfiedLinkError: dlopen failed:
  ".dynamic section header was not found"

<div align="center">

🔴 Patch ELF 字符串 → 长度不一致 → .dynamic 段偏移量被破坏 → dlopen 失败

</div>

教训:不要直接修改加固 SO 的字符串常量——ELF 结构对字节偏移极其敏感。


3.2 ✅ 正确方案:Python zipfile 流式重签

🕳️ 核心坑点

APK 中存在 238 对大小写碰撞的文件

res/3UU.xml  ≠  res/3uU.xml    ← Windows 上被视为同一文件!

Windows JDK 的 jar 命令因文件系统大小写不敏感,在重新打包时会静默丢失这 238 个资源文件,导致 APP 启动即崩溃。

🔑 解法:绕过 jar 命令,直接用 Python zipfile 流式复制——它对文件名大小写完全敏感。

07-apk-structure.png

核心代码
import zipfile

SRC = 'sample.apk'
DST = 'sample_resign.apk'

with zipfile.ZipFile(SRC, 'r') as zf_in:
    with zipfile.ZipFile(DST, 'w', zipfile.ZIP_DEFLATED) as zf_out:
        for item in zf_in.infolist():
            # ⏭️ 跳过旧签名文件
            if item.filename.startswith('META-INF/') and (
                item.filename.endswith('.RSA') or
                item.filename.endswith('.SF')
            ):
                continue
            zf_out.writestr(item, zf_in.read(item.filename))
✅ 完整性校验

改完必须验证,否则到安装阶段才发现丢文件就晚了:

orig  = set(zipfile.ZipFile(SRC, 'r').namelist())
final = set(zipfile.ZipFile(DST, 'r').namelist())
missing = orig - final - {'META-INF/ORIGKEY.SF', 'META-INF/ORIGKEY.RSA'}

print(f"Missing entries: {len(missing)}")  # → 0 ✅
🔏 重新签名
jarsigner -sigalg SHA256withRSA -digestalg SHA-256 \
  -keystore ~/.android/debug.keystore \
  -storepass android -keypass android \
  sample_resign.apk androiddebugkey

3.3 🎉 安装验证

$ adb install sample_resign.apk
Success ✅

08-resign-success.png
<p>
<sub>这是MT直接签名打开的图</sub>
</p>
屏幕截图 2026-06-24 225031.png
09-app-login.png

<div align="center">

┌──────────────────────────────────────────────────────────┐
│                                                          │
│   🎯 爱加密 v4.6.4.7 签名容忍度较高                     │
│   重签后直接到达登录页,无需额外绕过                     │
│                                                          │
│   ⚠️ 主要检测点在运行时注入(Frida spawn),非签名本身  │
│                                                          │
└──────────────────────────────────────────────────────────┘

</div>


🏁 成果总览

📦 输出产物

📄 文件 💡 说明 📏 大小
fixed_dex/classes0~14.dex 运行时修复后的完整 DEX 120 MB
sample_final.apk 去签名校验版本 249 MB

🛠️ 关键工具链

flowchart LR
    subgraph 分析阶段
        A1["📦 unzip"] --> A2["📝 strings"] --> A3["🔢 xxd"] --> A4["🐍 Python"]
    end

    subgraph 脱壳阶段
        B1["📱 ADB Root"] --> B2["📂 prodexdir 直接拉取"]
    end

    subgraph 签名阶段
        C1["🐍 zipfile 流式复制"] --> C2["🔏 jarsigner"]
    end

    分析阶段 --> 脱壳阶段 --> 签名阶段

    style A1 fill:#1a1a2e,stroke:#e94560,color:#eee
    style A2 fill:#1a1a2e,stroke:#e94560,color:#eee
    style A3 fill:#1a1a2e,stroke:#e94560,color:#eee
    style A4 fill:#1a1a2e,stroke:#e94560,color:#eee
    style B1 fill:#16213e,stroke:#533483,color:#eee
    style B2 fill:#16213e,stroke:#533483,color:#eee
    style C1 fill:#0f3460,stroke:#00ff88,color:#eee
    style C2 fill:#0f3460,stroke:#00ff88,color:#eee

⚠️ 踩坑记录

🐛 问题 🔍 原因 ✅ 解决
APK 重打包后崩溃 Windows 大小写不敏感丢失 238 个资源文件 Python zipfile 流式复制
直接 patch ELF 导致 dlopen 失败 字符串长度不一致破坏 .dynamic 放弃 patch,改用纯重签
Frida spawn 触发 SIGSEGV 壳检测 frida-agent 注入 先启动 APP 再 attach
模拟器缺 drawable 资源 x86_64 架构资源不完整 换 ARM64 真机

🔑 关键经验

<div align="center">

# 💎 经验
1 壳的签名校验未必严格 — 爱加密 v4.6.4.7 对签名变更容忍度较高,重签即可通过
2 不要 patch ELF — 字符串长度变化会破坏 ELF 结构,宁可用运行时 Hook 或放弃 patch

</div>


<br>

<div align="center">

╔══════════════════════════════════════════════════════════╗
║  ⚠️  本文仅供安全研究和学习交流使用,请勿用于非法用途  ║
╚══════════════════════════════════════════════════════════╝

<p>
<sub>本文全程主要由claude code和deepseek-v4完成,欢迎讨论。</sub>
</p>

</div>

[/md][/md]

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
ciker_li + 2 + 1 谢谢@Thanks!
weidechan + 1 用心讨论,共获提升!
buluo533 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

铭焱 发表于 2026-6-25 00:07
学习一下大佬的分析过程
laoser 发表于 2026-6-25 00:10
忧郁之子 发表于 2026-6-25 07:26
cqj2025 发表于 2026-6-25 08:35
我是普通人,看热闹
otg007 发表于 2026-6-25 08:44
我也是看热闹,根本看不懂
xiayutianz 发表于 2026-6-25 10:03
laoser 发表于 2026-6-25 00:10
感觉是AI生成的文章

不需要感觉 这不一眼ai嘛
ilil 发表于 2026-6-25 10:16
又跟大佬学习上了,但是看不太懂
dddddddhh 发表于 2026-6-25 11:20
这是做什么的
dork 发表于 2026-6-25 11:26
这是用什么软件排的版?乱糟糟的感觉
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-22 04:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表