# 免费XV 逆向经验笔记 > **应用名称**:免费X-V > **版本**:1.6.0 > **包名**:com.grass.mh > **逆向日期**:2026-09-18 > **修改内容**:去除开屏广告 > **状态**:✅ 已验证有效 --- ## 一、应用基本信息 | 项目 | 内容 | |------|------| | 应用名 | 免费X-V | | 版本 | 1.6.0 | | 包名 | com.grass.mh | | 主Activity | com.grass.mh.ui.SplashActivity | | 主页面 | com.grass.mh.ui.MainActivity | | APK大小 | ~19 MB | | 加固情况 | 未加固(可直接反编译) | | 开发框架 | 原生 Android(Kotlin/Java) | --- ## 二、分析流程(SOP) ### 2.1 第一步:确定入口 Activity ```bash # 用 aapt 查看启动 Activity aapt dump badging app.apk | grep -i "launchable-activity" ``` 结果:`com.grass.mh.ui.SplashActivity` ### 2.2 第二步:反编译 APK ```bash apktool d app.apk -o decoded/ ``` ### 2.3 第三步:定位开屏广告逻辑 **搜索关键词**: - `splash` → 闪屏页 - `ad` / `Ad` / `AD` → 广告相关 - `startMain` / `goMain` / `jumpMain` → 跳转主页面 - `onAdSuccess` / `onAdFail` / `onLvSuccess` → 广告回调 - `adShow` / `showAd` → 展示广告 **定位到的关键文件**: ``` smali_classes2/com/grass/mh/ui/SplashActivity.smali smali_classes2/com/grass/mh/ui/SplashActivity$5.smali ``` ### 2.4 第四步:梳理调用链 ``` SplashActivity.onCreate() → initView() # 初始化视图 → next(UserInfo) # 登录/初始化完成后 → getAdInfoNew() # 请求广告信息 → onLvSuccess() # ← 广告请求成功回调(关键!) → adShowMine() # 展示开屏广告 → 倒计时结束 → startMainUI() + finish() → onLvFail() # 广告请求失败回调 → 直接跳转或等待 ``` --- ## 三、修改方案:去除开屏广告 ### 3.1 修改思路(核心原则) **在成功回调的最开头直接跳转主页面,跳过所有广告展示逻辑。** > 💡 **经验总结**:开屏广告的标准套路是「请求广告 → 成功则展示 → 倒计时结束跳转」。 > 最快的修改方式不是删广告代码,而是在**成功回调开头**直接跳转,让后面的广告展示代码完全不执行。 > 这样改动最小、风险最低、不会因为删除代码导致引用错误。 ### 3.2 修改文件 **文件路径**:`smali_classes2/com/grass/mh/ui/SplashActivity$5.smali` > 为什么是 `$5`?因为 `getAdInfoNew()` 里的匿名内部类(HttpCallback 的子类),在 Smali 中编译为 `SplashActivity$5`。 ### 3.3 修改位置 `.method public onLvSuccess(Lcom/grass/mh/bean/BaseRes;)V` **原代码(第60-80行左右)**: ```smali .method public onLvSuccess(Lcom/grass/mh/bean/BaseRes;)V .locals 11 # 方法开头是获取参数、判空等逻辑 const/4 v1, 0x0 # ... 各种初始化代码 ... # 后面才是广告展示逻辑 invoke-virtual {v0}, Lcom/grass/mh/bean/BaseRes;->getData()Ljava/lang/Object; # ... 保存广告数据到SP ... # ... 调用 adShowMine() 展示广告 ... ``` ### 3.4 修改方法 在 `onLvSuccess` 方法**最开头**插入以下代码: ```smali # === 去开屏广告:成功回调直接跳转主页面 === # 获取外部类引用 (this$0) iget-object v0, p0, Lcom/grass/mh/ui/SplashActivity$5;->this$0:Lcom/grass/mh/ui/SplashActivity; # 调用 startMainUI()(通过 access$100 合成方法) invoke-static {v0}, Lcom/grass/mh/ui/SplashActivity;->access$100(Lcom/grass/mh/ui/SplashActivity;)V # 关闭 SplashActivity invoke-virtual {v0}, Lcom/grass/mh/ui/SplashActivity;->finish()V # 直接返回,跳过所有后续代码 return-void # ======================================== ``` ### 3.5 为什么用 access$100? 在 Smali 中,内部类访问外部类的 `private` 方法时,编译器会生成一个 `static synthetic` 方法,命名为 `access$XXX`。 ```smali .method static synthetic access$100(Lcom/grass/mh/ui/SplashActivity;)V .locals 0 .param p0 # synthetic invoke-direct {p0}, Lcom/grass/mh/ui/SplashActivity;->startMainUI()V return-void .end method ``` 所以 `access$100` 就是 `startMainUI()` 的包装。 ### 3.6 验证 access$100 的方法 ```bash grep -n "access\$100" SplashActivity.smali # 找到定义后,看它调用的方法 ``` --- ## 四、修改前后对比 ### 修改前 ``` 广告请求成功 ↓ 解析广告数据 ↓ 保存到 SharedPreferences ↓ 调用 AdNewUtils.getStartAd() 加载广告 ↓ 调用 adShowMine() 展示开屏广告 ↓ ⏳ 倒计时 3~5 秒 ↓ 用户点击广告 → 跳转浏览器 倒计时结束 → 跳转 MainActivity + finish() ``` ### 修改后 ``` 广告请求成功 ↓ 🚀 直接跳转 MainActivity ↓ finish() 关闭 Splash ↓ (后面所有广告代码完全不执行) ``` --- ## 五、关键定位技巧 ### 5.1 快速找跳转主页面的方法 ```bash # 搜索 MainActivity 的引用 grep -rn "MainActivity" smali_classes2/com/grass/mh/ui/SplashActivity.smali ``` 找到 `new-instance` Intent 指向 MainActivity 的地方,往上找就是 `startMainUI()` 方法。 ### 5.2 快速找广告回调 ```bash # 搜索成功/失败回调 grep -rn "onLvSuccess\|onAdSuccess\|onSuccess" smali_classes2/com/grass/mh/ui/ grep -rn "onLvFail\|onAdFail\|onFailure" smali_classes2/com/grass/mh/ui/ ``` ### 5.3 确认匿名内部类编号 ```bash # 看 SplashActivity$n.smali 里哪个有 onLvSuccess grep -l "onLvSuccess" smali_classes2/com/grass/mh/ui/SplashActivity\$*.smali ``` --- ## 六、重新编译与签名 ### 6.1 回编译 ```bash apktool b decoded/ -o modified.apk ``` ### 6.2 签名(必须!) 修改后的 APK 必须重新签名,否则安装失败。 ```bash # 使用 apksigner(推荐,支持 v2/v3) apksigner sign --ks test.keystore --ks-key-alias test \ --out modified_signed.apk modified.apk # 或者用 uber-apk-signer(更简单) java -jar uber-apk-signer.jar -a modified.apk ``` ### 6.3 验证签名 ```bash apksigner verify -v modified_signed.apk ``` --- ## 七、验证修改是否生效 ### 7.1 代码级验证 1. **确认修改位置正确**:`onLvSuccess` 方法开头 2. **确认调用的方法正确**:`access$100` → `startMainUI()` → `MainActivity` 3. **确认 return-void 在广告代码之前**:后面的 `adShowMine()` 不会执行 4. **确认失败回调安全**:失败时原逻辑就不展示广告,无需修改 ### 7.2 运行时验证 1. 安装修改后的 APK 2. 冷启动应用 3. 观察是否直接进入主页面,没有开屏广告 4. 检查应用功能是否正常(网络请求、登录等) --- ## 八、踩坑记录 ### 坑1:paste.c-net.org 链接全部 404 **现象**:从 paste.c-net.org 下载的脚本全是 404 错误页(86字节)。 **原因**:paste 服务的链接已过期或被删除。 **解决**:从 mpimg.cn 的脚本包(ZIP)中提取,里面有完整的29个脚本。 ### 坑2:mpimg.cn 下载链接用 token 不是 hash **现象**:用 MD5 hash 构造的下载链接 `down.php/{hash}.apk` 返回 404。 **原因**:mpimg.cn 的下载链接使用的是上传返回的 `token` 字段,不是文件 MD5。 **正确格式**:`https://mpimg.cn/down.php/{token}.{ext}` **验证方法**: ```bash # 从首页文件列表提取下载链接 curl -s https://mpimg.cn/ | grep -o 'data-down="[^"]*"' ``` ### 坑3:每次上传 token 不一样 **现象**:同一个文件每次上传/查询返回的 token 不同。 **原因**:mpimg.cn 每次请求都会生成新的下载 token,但都指向同一个文件。 **验证**:用不同 token 下载的文件 MD5 完全一致。 --- ## 九、举一反三:其他广告位的修改思路 ### 9.1 插屏广告 - 找 `InterstitialAd` / `interstitial` 关键词 - 修改展示前的判断条件,直接 return ### 9.2 激励视频广告 - 找 `RewardVideoAd` / `reward` 关键词 - 在加载成功回调里直接调用奖励回调,跳过播放 ### 9.3 Banner 广告 - 找 `BannerAd` / `banner` 关键词 - 设置 View 为 GONE,或直接不加载 ### 9.4 VIP/会员限制 - 找 `isVip` / `isMember` / `vipState` 关键词 - 修改返回值为 true / 1 --- ## 十、MT管理器操作步骤(手机端修改) ### 10.1 搜索定位 1. 打开 MT管理器 → 选择 APK → 查看 2. 进入 `classes2.dex` → Dex编辑器++ 3. 搜索方法名:`onLvSuccess` 4. 找到 `SplashActivity$5` 中的 `onLvSuccess` 方法 ### 10.2 修改代码 在方法最开头插入: ```smali iget-object v0, p0, Lcom/grass/mh/ui/SplashActivity$5;->this$0:Lcom/grass/mh/ui/SplashActivity; invoke-static {v0}, Lcom/grass/mh/ui/SplashActivity;->access$100(Lcom/grass/mh/ui/SplashActivity;)V invoke-virtual {v0}, Lcom/grass/mh/ui/SplashActivity;->finish()V return-void ``` > ⚠️ 注意:寄存器 v0 可能被占用,根据实际情况选择一个空闲的寄存器。 > 可以看方法开头 `.locals` 后面的数字,选一个编号大的寄存器。 ### 10.3 保存签名 1. 保存修改 2. 退出 Dex 编辑器 3. 功能 → APK 签名 4. 安装签名后的 APK --- ## 十一、相关文件备份 | 文件 | 位置 | 说明 | |------|------|------| | 原始APK | `/workspace/backup/apk_original/` | 未修改的原版 | | 修改后APK | `/workspace/backup/apk_modified/` | 去开屏广告版 | | 二级备份 | `/data/user/work/backup_secondary/apk/` | 本地冗余备份 | | 云端备份 | mpimg.cn | 服务器永久备份 | | 下载链接 | https://mpimg.cn/down.php/0de89b0257f986aecab89c53487bfaab.apk | 去广告版下载 | --- ## 十二、核心经验总结 1. **改动最小原则**:能加一行就不改一片,能改条件就不删代码 2. **找对入口是关键**:SplashActivity → 广告回调 → 成功回调开头 3. **内部类访问外部类方法**:用 `access$XXX` 合成方法 4. **修改后必须重签名**:否则安装失败 5. **验证要全面**:代码级验证 + 运行时验证 6. **多重备份**:本地 + 云端,防止丢失 --- *笔记创建时间:2026-09-19* *最后更新:2026-09-19*