吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 186|回复: 2
上一主题 下一主题
收起左侧

[Android 分享] 安卓 APK 逆向经验分享

[复制链接]
跳转到指定楼层
楼主
1245124511 发表于 2026-8-31 11:47 回帖奖励

安卓 APK 逆向经验分享:会员、广告与启动流程分析

本文基于授权的本地测试 APK 整理,不包含目标软件名称、包名、接口地址、广告位 ID 或账号信息。仅用于学习 Android 逆向、调试和兼容性分析。

一、问题背景

在实际分析中,经常会遇到以下现象:

  • 应用启动时显示开屏广告,并可能跳转到广告页面;
  • 首页或内容页出现服务端下发的推广横幅;
  • 部分功能根据会员状态限制次数或直接隐藏;
  • APK 重打包后,登录、短信验证码或第三方 SDK 出现异常。

这类问题不能只依赖字符串搜索,通常需要结合静态分析、运行时日志和模拟器测试,先区分:

  1. 客户端界面限制
  2. 客户端本地会员判定
  3. 服务端真实权限校验
  4. 第三方 SDK 的签名或包校验

二、推荐工具链

  • JADX:查看 Java/Kotlin 反编译结果;
  • Apktool:解码资源、Manifest 和 Smali;
  • Smali/Baksmali:重新汇编或拆解 DEX;
  • ADB:安装 APK、抓取日志、截图和导出 UI 层级;
  • Android 模拟器:进行干净安装、流程回归和崩溃定位;
  • Zip 对齐与签名工具:生成可安装的测试 APK。

建议将项目分为以下目录:

project/
├─ input/       原始 APK
├─ backup/      原始备份
├─ work/        解码文件、反编译代码、临时 DEX
├─ build/       中间包和最终包
└─ test/        日志、截图、测试记录

三、第一步:建立基线

在修改前记录:

  • 文件大小和 SHA-256;
  • 包名、版本号、最低及目标 SDK;
  • Manifest 中的 Application、Launcher Activity 和 Provider;
  • DEX 数量、ABI、是否存在加固或动态加载;
  • 是否包含广告 SDK、支付 SDK、登录 SDK 和完整性校验。

同时对原始 APK 做一次干净安装,记录启动页面、登录页面、广告出现时机和异常日志。后续每次改包都应该与这份基线比较。

四、定位开屏广告和跳转

常见结构是:

Launcher/Main Activity
        ↓
启动管理器
        ↓
广告配置判断
        ↓
开屏广告加载
        ↓
广告关闭或失败回调
        ↓
进入主页面

分析时重点关注:

  • startLoadAdloadSplashshowAd 等方法;
  • onAdDismissonAdFailonComplete 等回调;
  • 是否通过 Handler 延迟进入主页面;
  • 广告点击回调是否调用 WebView、浏览器或外部 Intent。

在授权的本地测试版本中,较稳妥的处理方式是让启动管理器直接执行“完成”回调,而不是删除整个广告 SDK。这样可以保留类和资源依赖,降低启动崩溃概率。

五、定位首页和内容页广告

广告不一定来自广告 SDK,也可能是服务端返回的推广 Banner。常见特征包括:

  • 数据模型中存在 bannerlogourlitem_type 等字段;
  • Adapter 根据列表数量决定是否创建 Banner View;
  • 点击 Banner 后调用统一跳转方法;
  • 页面刷新时重新请求动态配置。

因此只屏蔽广告 SDK 还不够,还要检查服务端 Banner 的 Adapter。常见的客户端处理点有:

  • 让广告 Adapter 的条目数量返回 0;
  • 在设置 Banner 数据时清空列表;
  • 将广告容器设置为隐藏;
  • 对广告点击跳转增加空操作保护。

六、定位会员功能判定

会员限制通常集中在少数公共方法中,而不是每个页面单独实现。重点搜索:

  • isVipisSVipisMember
  • getVipgetVipTypevipTime
  • canUsecanAndDousageLimit
  • “次数限制”“开通会员”“会员中心”等资源文本。

常见逻辑如下:

读取本地 UserInfo
        ↓
读取 vip 数值或会员类型
        ↓
判断是否大于 0 / 是否等于高级会员类型
        ↓
决定功能次数、界面样式和是否跳转购买页

如果多个功能都调用同一个会员判断方法,优先分析公共方法,而不是逐个修改页面。对于显示层,还要同步检查会员名称和有效期字段,否则可能出现“功能已开放但页面仍显示普通用户”的不一致。

七、重签名导致的登录异常

这是重打包中最容易遇到的问题之一。APK 修改后必须重新签名,签名证书变化可能导致:

  • 短信 SDK 报应用签名未配置;
  • 一键登录或风控 SDK 拒绝请求;
  • 服务端返回通用的“验证码错误”;
  • 第三方支付、分享或推送功能失效。

定位方法:

  1. 清空 Logcat;
  2. 点击一次获取验证码;
  3. 搜索 SMSverifymd5signaturestatus 等关键词;
  4. 区分是 SDK 本地失败,还是应用自有接口返回失败。

如果应用本身已经存在备用的验证码接口或备用登录流程,应优先使用应用已有的正常流程进行本地兼容测试。不要把“客户端提示验证码错误”直接当成真实验证码错误。

八、构建和打包经验

1. 不要直接用 --no-res 的目录回编

只解码 DEX 而不解码资源时,回编后的 APK 可能缺少布局文件,启动时出现 Resources$NotFoundException。需要完整解码资源,或者采用“保留原 APK、只替换目标 DEX”的方式。

2. 资源异常要单独记录

部分 APK 的资源属性值对新版 AAPT2 不兼容,例如非法的重力或颜色值。修复时只改动报错资源,并记录原始内容和修改原因,避免大范围重排资源 ID。

3. 每次都要重新验证

签名后至少检查:

  • ZIP 对齐是否成功;
  • v1/v2/v3 签名是否有效;
  • DEX 数量是否变化异常;
  • Manifest 的启动 Activity 是否仍正确;
  • APK 是否能在干净模拟器安装。

九、测试清单

  • [ ] 冷启动不再出现开屏广告;
  • [ ] 广告点击不会跳转外部页面;
  • [ ] 首页和主要内容页没有服务端推广 Banner;
  • [ ] 未登录状态仍能正常进入登录流程;
  • [ ] 获取验证码和输入验证码流程正常;
  • [ ] 登录后会员标识、会员有效期和受限功能状态一致;
  • [ ] 会员功能不会因后台刷新立即恢复为普通状态;
  • [ ] 页面旋转、后台恢复和进程重启不崩溃;
  • [ ] Logcat 中没有新的致命异常。

十、经验总结

  1. 先找公共判定点,再改具体页面;
  2. 广告既可能来自 SDK,也可能来自服务端动态数据;
  3. 重签名问题要通过日志确认,不能只看界面提示;
  4. 尽量保留原有类、资源和 native 库,减少无关改动;
  5. 任何本地会员修改都不等于服务端真实开通,服务端权限仍可能独立校验;
  6. 所有测试应限定在自己拥有或明确授权的 APK、账号和设备上。

十一、一个可复现的完整流程

下面以“某健康类 APK”为例,演示从拿到文件到完成回归测试的完整过程。代码中的类名已经泛化,不能直接代表任何特定软件。

11.1 建立工程和记录基线

$root = "D:\lab\android_sample"
New-Item -ItemType Directory -Force -Path `
  "$root\input", "$root\backup", "$root\work", "$root\build", "$root\test"

Copy-Item .\sample.apk "$root\input\sample.apk"
Copy-Item .\sample.apk "$root\backup\sample.original.apk"
Get-FileHash "$root\input\sample.apk" -Algorithm SHA256

记录以下信息:

package: com.example.sample
versionName: 3.7.0
versionCode: 370
minSdk: 24
targetSdk: 31
abi: arm64-v8a, armeabi-v7a
dex: classes.dex ... classes12.dex

这里的包名和版本只是示例,实际项目中应从 Manifest 或 APK 分析工具获取,不能凭文件名猜测。

11.2 解码和反编译

$apk = "D:\lab\android_sample\input\sample.apk"
$decoded = "D:\lab\android_sample\work\decoded"
$apktool = "D:\tools\apktool.jar"
$jadxOut = "D:\lab\android_sample\work\jadx"

java -jar $apktool d --force $apk -o $decoded
jadx.bat --show-bad-code -d $jadxOut $apk

常用搜索:

rg -n -i "isVip|isSVip|member|premium|vip|ads|banner|splash|startActivity|showAd" `
  "$decoded\smali*" "$jadxOut\sources"

搜索结果需要回到调用链确认,单独命中字符串不能证明它就是实际控制点。

11.3 运行时确认调用链

$adb = "D:\Android\platform-tools\adb.exe"
& $adb devices -l
& $adb -s emulator-5554 install -r $apk
& $adb -s emulator-5554 logcat -c
& $adb -s emulator-5554 shell monkey -p com.example.sample 1
Start-Sleep 8
& $adb -s emulator-5554 logcat -d -v time > "$root\test\baseline.log"

重点观察:

  • 首屏出现前后的 Activity 变化;
  • 点击会员入口后是否只是本地提示,还是发起网络请求;
  • 点击获取验证码时,失败来自第三方 SDK 还是应用自有接口;
  • 页面广告是否来自 SDK View,还是服务端返回的 Banner 数据。

十二、会员判定的具体代码示例

12.1 Java 层原始逻辑

一种常见实现如下:

public boolean isVipValid() {
    UserInfo info = AppPref.get().getUserInfo();
    return info != null && info.getVip() > 0;
}

public boolean isSuperVip() {
    UserInfo info = AppPref.get().getUserInfo();
    return info != null && info.getVip() == 314;
}

调用方通常是:

if (Vip.isVip()) {
    openFeature();
} else {
    showVipPage();
}

12.2 Smali 层识别方法

对应的关键字节码可能类似:

invoke-virtual {v0}, Lcom/example/UserInfo;->getVip()I
move-result v0
if-lez v0, :not_vip

const/4 v0, 0x1
return v0

:not_vip
const/4 v0, 0x0
return v0

如果多个页面都调用同一个 isVipValid(),优先修改这个公共方法,而不是逐个改页面。一个保持登录状态、只改变会员分支的本地测试写法是:

.method public isVipValid()Z
    .locals 1

    invoke-static {}, Lcom/example/AppPref;->getUserInfo()Lcom/example/UserInfo;
    move-result-object v0

    # 未登录仍然返回 false
    if-eqz v0, :not_vip

    const/4 v0, 0x1
    return v0

:not_vip
    const/4 v0, 0x0
    return v0
.end method

如果页面还会根据会员等级显示不同样式,可同步修改等级读取方法:

.method public getVip()I
    .locals 1

    const/16 v0, 0x13a    # 314,示例中的高级会员类型
    return v0
.end method

这样可以覆盖以下类型的客户端逻辑:

  • 每日次数限制;
  • 高级解析或高级处理入口;
  • 无广告标识;
  • 会员页面的头像、标签和有效期展示;
  • 高级会员专属页面入口。

但这只改变客户端判断,服务端仍然可能再次校验用户权限。

十三、广告和开屏的具体修改示例

13.1 统一广告结果判断

原始逻辑常见为:

public ShowType getShowType(Context context) {
    if (VipManager.getInstance(context).isVipValid()) {
        return ShowType.HIDE;
    }
    return showType;
}

本地测试版可以将结果统一设为隐藏:

.method public getShowType(Landroid/content/Context;)Lcom/example/ShowType;
    .locals 0

    sget-object p1, Lcom/example/ShowType;->HIDE:Lcom/example/ShowType;
    return-object p1
.end method

13.2 开屏广告直接结束

开屏管理器通常在广告关闭、失败或超时后调用完成回调:

private void startLoadAd() {
    loadSplashAd();
}

private void notifyComplete() {
    listener.onComplete();
}

本地测试版可以让入口直接走完成回调:

.method public startLoadAd()V
    .locals 0

    invoke-direct {p0}, Lcom/example/SplashAdManager;->notifyComplete()V
    return-void
.end method

这种方式比删除整个广告 SDK 更稳定,因为其他类仍然可以找到 SDK 类型和资源。

13.3 服务端 Banner 不是广告 SDK

有些首页广告来自接口返回的列表:

public void setDataList(List<BannerBean> list) {
    bannerList.clear();
    bannerList.addAll(list);
    notifyDataSetChanged();
}

public int getItemCount() {
    return hide ? 0 : 1;
}

只屏蔽广告 SDK 时,这类 Banner 仍然会显示。可以在 Adapter 层让广告条目不创建:

.method public getItemCount()I
    .locals 1

    const/4 v0, 0x0
    return v0
.end method

或者让 setDataList() 清空后不再触发显示。修改后应检查页面布局是否会留下空白区域。

十四、验证码异常的定位实例

一次测试中,点击“获取验证码”后界面显示“验证码错误”,但实际上还没有输入验证码。日志显示:

SMS failed: {"status":489,"detail":"application signature is not configured"}

根因不是用户输入错误,而是 APK 重签名后证书指纹发生变化,第三方短信 SDK 拒绝请求。

排查步骤:

1. 清空 Logcat;
2. 点击一次获取验证码;
3. 搜索 SMS、md5、signature、status、verify;
4. 记录失败回调携带的原始字符串;
5. 对比原始 APK 的调用链。

如果应用本身已经存在备用的自有验证码接口,代码可能同时存在两条路径:

// 第三方 SDK 路径
toSendCode(false);

// 应用自有接口路径
toSendOwnCode();

在授权测试版本中,可以将首次获取验证码改为应用已有的自有接口,并在成功回调中切换到对应的登录提交流程:

private void requestCode() {
    showLoading();
    api.getVerificationCode(phone, new Callback() {
        @Override public void onSuccess() {
            useSdkVerification = false;
            showCodeInputPage();
        }

        @Override public void onFailure(String message) {
            hideLoading();
            showError(message);
        }
    });
}

提交验证码时也必须匹配同一条路径:

if (useSdkVerification) {
    sdk.submitVerificationCode(phone, code);
} else {
    api.loginByCode(phone, code);
}

这里的关键经验是:先确认失败位置,再决定修复点;不要把所有“验证码错误”都当成需要修改验证码校验。

十五、资源回编失败的处理

如果使用只解码 DEX 的目录回编,可能出现:

Resources$NotFoundException: File res/xxxxx.xml

原因是资源表仍在,但布局文件没有被解码回写。处理方式有两种:

方案 A:完整资源回编

java -jar apktool.jar d --force sample.apk -o work\decoded_full
java -jar apktool.jar b work\decoded_full -o build\unsigned.apk

如果 AAPT2 报某个资源属性非法,只修复报错行,并保留修改记录。

方案 B:保留原 APK,只替换 DEX

适合只改动少量 Smali 的情况。示例脚本:

import copy
import zipfile

src = "build/original_signed.apk"
dst = "build/patched_unsigned.apk"
replace = {
    "classes5.dex": "work/classes5_fixed.dex",
    "classes7.dex": "work/classes7_fixed.dex",
}

with zipfile.ZipFile(src, "r") as zin, zipfile.ZipFile(dst, "w") as zout:
    for info in zin.infolist():
        if info.filename in replace:
            data = open(replace[info.filename], "rb").read()
        else:
            data = zin.read(info)
        zout.writestr(copy.copy(info), data)

之后必须重新 ZIP 对齐和签名:

java -jar uber-apk-signer.jar -a build\patched_unsigned.apk --allowResign

十六、Smali 修改后的验证方法

16.1 重新汇编单个 DEX

java -cp "smali.jar;libs\*" org.jf.smali.Main assemble `
  --api 31 -j 16 `
  -o work\classes7_fixed.dex `
  work\decoded\smali_classes7

16.2 安装和启动验证

adb -s emulator-5554 uninstall com.example.sample
adb -s emulator-5554 install build\patched-aligned-debugSigned.apk
adb -s emulator-5554 logcat -c
adb -s emulator-5554 shell monkey -p com.example.sample 1
Start-Sleep 10
adb -s emulator-5554 logcat -d -v time > test\patched.log

16.3 关键日志筛选

rg -n -i "FATAL EXCEPTION|AndroidRuntime|VerifyError|NoSuchMethodError|Resources\$NotFound|Splash|Vip|SMS|signature" test\patched.log

十七、回归测试记录模板

测试包:patched-aligned-debugSigned.apk
设备:Android 模拟器 / 实机
安装:成功
启动:成功
冷启动开屏:未显示广告
首页 Banner:未显示
未登录流程:仍进入登录页
获取验证码:接口返回成功 / 失败原因:________
输入验证码:正常 / 异常:________
登录后会员标识:________
会员功能入口:________
后台恢复:正常 / 异常:________
致命日志:无 / 有:________

十八、常见问题和判断表

现象 优先检查 常见原因
启动白屏后退出 Logcat、Manifest、资源文件 资源缺失或 Application 初始化失败
仍然出现开屏广告 Splash 管理器和热启动入口 只改了首次启动,未改热启动
首页仍有商品横幅 Banner Adapter、动态数据模型 这是服务端 Banner,不是 SDK 广告
点击获取验证码即报错 SDK 失败回调原文 重签名证书未在第三方平台配置
会员页面变了但功能仍受限 具体功能调用链和请求参数 服务端再次校验会员状态
安装提示应用未安装 签名、ABI、Manifest 与原版签名不同或架构不兼容
修改后闪退 新增日志和 VerifyError 寄存器数量、方法签名或 DEX 回编错误

十九、最终经验

一个完整的 Android 逆向流程不是“搜索一个字符串后改一行代码”,而是:

基线记录
  → 静态定位
  → 运行时确认
  → 找公共判定点
  → 最小化修改
  → 回编/签名
  → 干净安装
  → 功能回归
  → 日志和截图归档

最重要的三个原则:

  1. 先判断问题属于客户端、服务端还是第三方 SDK;
  2. 优先修改公共入口,避免在几十个页面重复打补丁;
  3. 每次修改都保留原包、日志、截图和可复现命令。

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

沙发
zkyuan 发表于 2026-8-31 19:27
感谢分享,收藏备用
3#
agnostic 发表于 2026-8-31 22:40
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2026-9-1 00:50

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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