吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 428|回复: 0
上一主题 下一主题
收起左侧

[Android 分享] 匿名 APK 逆向经验分享:从问题定位到可回滚验证

[复制链接]
跳转到指定楼层
楼主
1245124511 发表于 2026-9-4 10:29 回帖奖励

匿名 APK 逆向经验分享:从问题定位到可回滚验证

本文将一次 Android 棋类应用的逆向、功能分析和本地测试过程整理成通用流程。软件名称、包名、路径、哈希和服务器地址均已匿名化。示例只针对自己拥有或明确获准测试的 APK。

1. 目标和范围

这次工作关注的是本地 APK 的结构和行为:高级功能开关、本地引擎、棋盘识别、悬浮窗、WDL 评估和 JSON 记录。网络服务器上的账户权限、支付状态和服务端校验,不能简单等同于本地布尔值。

2. 工作区和备份

不要直接覆盖原 APK;每个阶段都保留可回滚文件:

$stamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$root = 'D:\lab\boardapp'
New-Item -ItemType Directory -Force "$root\backup_$stamp" | Out-Null
Copy-Item '.\sample.apk' "$root\backup_$stamp\sample_original.apk"
Get-FileHash '.\sample.apk' -Algorithm SHA256

建议目录:

boardapp/
  original/       原 APK、版本信息、哈希
  decoded/        apktool 解码结果
  jadx/           Java 反编译结果
  smali_work/     实际编辑的 smali
  build/          dex、对齐包、最终 APK
  logs/           logcat、测试记录
  backup_*/       每个阶段的回滚点

每次只改一个主题,并给产物加后缀,例如 v1_gatev2_wdlv3_ui,方便定位回归问题。

3. 第一轮基线检查

先记录文件、签名和设备状态:

Get-Item '.\sample.apk' | Select-Object FullName,Length,LastWriteTime
Get-FileHash '.\sample.apk' -Algorithm SHA256

adb devices -l
adb -s <serial> shell getprop ro.product.cpu.abilist
adb -s <serial> shell wm size
adb -s <serial> shell wm density

需要记录 APK 大小、版本号、最低/目标 SDK、原生库 ABI、模拟器分辨率、内存和线程数。多模拟器环境中,所有命令都显式写 -s <serial>,不要使用默认设备。

4. 解码和静态定位

常用工具:apktool、jadx、baksmali/smali、ripgrep、zipalign、apksigner、adb 和 Python。

apktool d -f '.\sample.apk' -o '.\decoded'
jadx -d '.\jadx' '.\sample.apk'
rg -n -i 'vip|member|premium|expire|license|feature|engine|overlay|accessibility' .\decoded .\jadx

按三层分析:

  1. Manifest:入口 Activity、Service、AccessibilityService、悬浮窗权限和 FileProvider;
  2. DEX:会员判断、按钮回调、引擎命令、棋盘识别和 JSON 写入;
  3. Native/资源.so、可执行引擎、NNUE/模型文件、配置和 ABI。

不要只搜索“会员”。实际判断经常是普通布尔值、时间戳比较、枚举状态或远程配置字段。

5. 运行时观察

静态结果必须用运行时行为确认:

adb -s <serial> logcat -c
adb -s <serial> shell monkey -p com.example.boardapp 1
adb -s <serial> logcat -v time -d > .\logs\startup.log
Select-String -Path .\logs\startup.log -Pattern 'FATAL|AndroidRuntime|VerifyError|Exception|engine|overlay'

辅助命令:

adb -s <serial> shell dumpsys activity activities
adb -s <serial> shell dumpsys activity services com.example.boardapp
adb -s <serial> shell dumpsys window windows

对正在进行的对局,不要为了取日志而强制停止应用。读取记录文件可以使用只读的 adb pull

adb -s <serial> pull '/sdcard/BoardApp/records' '.\export'

6. 定位本地高级功能开关

先建立“现象—调用链—真正判定点”表:

现象 可能位置 要确认的内容
按钮灰色 布局或点击回调 是 UI 限制还是权限限制
点击后提示需要高级权限 Activity/Presenter 判断函数的返回值和调用者
功能能勾选但不执行 Service/Worker 是否还有第二层运行时校验
重启后状态恢复 Preferences/数据库 状态是否只是缓存
登录后才有效 网络接口 是否存在服务端签名或令牌校验

稳定的定位顺序:

  1. 找到按钮点击回调;
  2. 沿调用链找到 isFeatureEnabled()hasPrivilege() 一类的判定;
  3. 确认返回值是否又被其他位置覆盖;
  4. 在授权的本地样本中只做最小改动;
  5. 同时验证 UI、后台任务、引擎连接和实际效果。

抽象的 smali 结构:

.method public isFeatureEnabled()Z
    .registers 2
    iget-boolean v0, p0, Lcom/example/FeatureState;->enabled:Z
    return v0
.end method

只改按钮颜色通常只是“看起来解锁”,并不能保证后台功能真的执行。

7. smali 中最容易踩的坑

7.1 参数寄存器和临时寄存器别名

.registers 2 的静态单参数方法中,p0 会映射到最后一个物理寄存器。不要把同一个寄存器同时当临时变量:

# 容易出错:v1 可能与 p0 是同一个物理寄存器
.method private static toPercent(I)I
    .registers 2
    const/16 v1, 0x64
    div-int/2addr p0, v1
    return p0
.end method

更安全的写法:

.method private static toPercent(I)I
    .registers 2
    const/16 v0, 0xa
    div-int/2addr p0, v0
    return p0
.end method

这类寄存器错误曾导致 WDL 三项全部显示为 100%。排查时要分别查看引擎原始输出、字段复制和显示转换。

7.2 方法描述符和调用点

如果把:

onInfo(Lq2/h;IZ)V

改成:

onInfo(Lq2/h;IZZ)V

必须同步检查所有调用点,否则会出现 NoSuchMethodErrorVerifyError。方法签名、参数顺序和返回类型尽量保持不变。

8. 引擎、NNUE、WDL 和视角

本地 UCI 引擎通常通过标准输入输出通信:

uci
isready
setoption name Threads value 2
setoption name Hash value 256
setoption name UCI_ShowWDL value true
position fen <fen>
go movetime <ms>

引擎负责搜索,NNUE 负责局面评估。启动成功时一般能看到:

NNUE evaluation using <network>.nnue (...)

WDL 常见格式是千分比:

info ... score cp 120 wdl 420 500 80 nodes ...
                         胜   和  负

统一红方、黑方和“棋盘下方/我的棋子”视角时,必须确认:

  • FEN 中的 side-to-move;
  • 引擎分数相对于哪一方;
  • 棋盘下方识别出的阵营;
  • 交换胜/负时是否需要同步交换 cp 符号。

wdl 1000 0 0 表示引擎认为当前局面极端优势,不一定是显示错误。wdl 29 942 29 则约为 3%/94%/3%。

9. JSON 记录设计

每步保存原始走法、局面、引擎数据和胜率:

{
  "fileVersion": 1,
  "engine": "local-uci",
  "scorePerspective": "playerSide",
  "moves": [
    {
      "ply": 16,
      "side": "black",
      "move": "c7d5",
      "fenAfter": "<fen>",
      "scoreCp": 134,
      "depth": 12,
      "timeMs": 11,
      "pv": "h6g8 e9d9 ...",
      "playerWinRate": 83,
      "playerDrawRate": 17,
      "playerLossRate": 0
    }
  ]
}

胜率只是“当时引擎的看法”,不能直接当作最终胜负。若要判断真实结果,还要记录结算页面、超时、认输或将死事件。

10. 重新打包、对齐和签名

java -cp '.\tools\smali_deps\*' org.jf.smali.Main assemble `
  '.\smali_work' -o '.\build\classes.dex'

python '.\tools\replace_dex.py' `
  '.\original\sample.apk' '.\build\classes.dex' `
  '.\build\unsigned.apk'

& '.\tools\zipalign.exe' -f -p 4 `
  '.\build\unsigned.apk' '.\build\aligned.apk'

java -jar '.\tools\apksigner.jar' sign `
  --ks '.\tools\lab.jks' --ks-pass pass:<password> `
  --ks-key-alias lab --out '.\build\sample_patched.apk' `
  '.\build\aligned.apk'

java -jar '.\tools\apksigner.jar' verify --verbose `
  '.\build\sample_patched.apk'
Get-FileHash '.\build\sample_patched.apk' -Algorithm SHA256

常见失败原因:旧 META-INF 未清除、签名方案不匹配、dex 未替换、原生库误删、模型路径错误、ABI 不匹配,以及安装后残留旧数据。

11. 安装和冒烟测试

adb -s <serial> install -r '.\build\sample_patched.apk'
adb -s <serial> shell monkey -p com.example.boardapp 1
adb -s <serial> logcat -d -v time > '.\logs\after_install.log'

检查顺序:启动、功能入口、引擎 bestmove、悬浮窗触摸、棋盘识别、红黑视角、WDL 变化、JSON 写入、关闭悬浮窗后的底层点击和重开一局后的状态清零。

12. 典型问题和经验

顶部文字或胜率被截断

常见原因是 TextView 单行、固定高度或悬浮层覆盖。应调整最大行数、布局高度和触摸区域,而不是简单缩小字体。

点击选择框后崩溃

检查 Popup/Dialog 是否成功创建、回调是否引用了销毁的 View,以及异常发生在 UI 线程还是后台线程。以完整 AndroidRuntime 堆栈为准。

游戏界面像暂停,关闭脚本后才突然超时

通常是悬浮窗或 Accessibility/屏幕捕获层拦截了触摸、改变焦点或占用了 UI 线程。应缩小覆盖区域,让非控件区域不消费触摸,并降低截图和写文件频率。

引擎启动后一直等待

依次检查 ABI、可执行权限、模型文件路径、工作目录、标准输入输出、线程数和内存。不要直接把所有问题归结为棋力。

13. 回滚和证据链

每个版本至少保留:原 APK、哈希、修改前后 smali、构建命令、签名结果、设备 serial/ABI、测试截图、logcat、JSON、已知问题和回滚方法。

版本:v3_wdl_fix
输入:sample_original.apk / SHA256=...
改动:Recorder、EngineParser、OverlayTextView
验证:assemble、zipalign、apksigner、启动冒烟测试
回滚:恢复 backup_*/sample_original.apk

14. 最终总结

  1. 先备份、再基线、后修改;
  2. 先确认调用链,再改判定点;
  3. 一次只改一个变量;
  4. 静态分析和 logcat 必须互相验证;
  5. 引擎、网络、FEN、视角和 UI 是不同层次的问题;
  6. “能点”不等于“功能真的解锁”;
  7. 胜率是评估,不是结果;
  8. 测试设备必须显式指定,避免误操作正在使用的实例。

15. 合规说明

逆向分析应限定在自己拥有、得到授权或明确用于安全研究的样本。不要把账号、密钥、个人数据、服务器令牌或未公开商业资源写进分享文档。发布修改后的 APK 前,还应检查原项目许可证、网络模型许可证和目标平台规则。

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

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-9-5 10:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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