Android改造者之路 04 · 开打·第一次改造实战
第 04 章 开打·第一次改造实战
续写章节|沿用「原木」系列的武侠讲解体,非原作者(dayeve)作品
前情提要
我抱着万能君从藏兵阁出来,天已经黑了。
老头儿在后面喊:“小子,记住——兵器再利,也替你想不了问题。 真正让你走远的,是第二章那七个字。”
我愣了一下:“哪七个字?”
老头儿笑了:“洞察、推算、分析、辨别……剩下的,自己想去。”
不管了。剑已在手,先找个 apk 练练手再说。
嗯,就先从那个满屏广告的天气 app 开始吧。
剧情片段
导演: 这一章就到这儿,收工收工。
助理: 导演,观众问下一章讲啥?
导演: 兵器到手了,自然是——开打。
剧情片段
前情提要
兵器借到手,剑也磨亮了,可真要动手的时候,我反而站在原地发愣。
师傅说过一句话我一直没懂:“看懂一百遍,不如动手改一遍。”
现在懂了——因为当你真的打开那堆 smali,你会发现它跟你想象中的完全不是一回事。
那么,开打。
一、先定一个小目标
新手第一次实战,千万别一上来就挑"去广告 + 破会员 + 过校验"这种组合拳。那不是练手,那是找死。
我们把目标拆成三级,一级一级来:
| 级别 | 目标 | 用到的招式 | 难度 |
|---|---|---|---|
| 第一式 | 改掉界面上一句文案 | const-string |
★☆☆☆☆ |
| 第二式 | 改掉一个数字(比如试用天数) | const / const-wide |
★★☆☆☆ |
| 第三式 | 绕过一个判断(比如跳过会员校验) | if-eqz / return-void |
★★★☆☆ |
三式练完,你就走完了完整的改造闭环:拆 → 看 → 改 → 装 → 签 → 验。
后面的路,都是这个闭环的重复与加深。
二、战前准备
兵器清单(上一章借的那几把,这里只带必要的):
- jadx —— 用来"看"
- ApkTool —— 用来"拆"和"装"
- MT管理器 —— 手机上的快捷路线(第三章的万能君)
- zipalign + apksigner —— 用来"盖章"
样本的选择,是新手最容易踩的第一个坑。
别一上来就挑微信、抖音这种加固加壳的大块头。建议按这个顺序挑:
- 自己写的 demo —— 最理想。你知道代码长什么样,改起来心里有底
- 体积在 10MB 以内的小工具 app —— 没加固、单 dex、逻辑简单
- 等熟练了,再去碰加壳的
怎么判断有没有加固?把 apk 解压后看
lib目录——如果里面躺着libjiagu.so、libdexhelper.so、libBugly.so这类奇怪的 so,而且classes.dex小得离谱(只有几十 KB),那基本就是套了壳。新手先绕开。
环境:一台开了 USB 调试的手机,或者一个模拟器。装 apk 用的就是 adb install。
adb install -r app_modified.apk
三、标准六步流程
不管你改什么,流程都是这六步。把它背下来:
① 观察 → ② 定位 → ③ 拆解 → ④ 动刀 → ⑤ 回装 → ⑥ 盖章验证
① 观察:jadx 打开 apk,先整体逛一圈,看包名、看入口 Activity、看有几个 dex。
② 定位:用搜索找关键词(提示文字、方法名、字符串),锁定要改的那几行。
③ 拆解:apktool d 出 smali。
④ 动刀:编辑 smali。
⑤ 回装:apktool b 打包。
⑥ 盖章验证:zipalign → apksigner → adb install → 打开看效果。
记住:jadx 是用来"看"的,ApkTool 是用来"改"的。 不要试图在 jadx 里改——它改不了;也不要试图在 smali 里读逻辑——那太累。
jadx 找到位置 → 记住类名和方法名 → 去 ApkTool 出的 smali 里找到同一个方法动手,这才是标准工作流。
四、三式实战
下面用一个虚构的 demo 来演示,包名叫 com.demo.weather。真实操作时换成你自己的目标即可。
4.1 第一式:改文案(const-string)
目标:把启动后显示的那句"免费版"改成"尊享版"。
先在 jadx 里搜索字符串"免费版",会看到类似这样的 Java 代码:
private void initView() {
this.tvTitle.setText("免费版");
}
记住它在哪个类的哪个方法里,然后去 smali/ 目录按路径找到同名文件(com/demo/weather/MainActivity.smali),找到 initView():
改之前:
.method private initView()V
.registers 3
const-string v0, "免费版"
invoke-virtual {p0, v1}, Lcom/demo/weather/MainActivity;->setTitle(Ljava/lang/CharSequence;)V
return-void
.end method
改之后:
.method private initView()V
.registers 3
const-string v0, "尊享版"
invoke-virtual {p0, v1}, Lcom/demo/weather/MainActivity;->setTitle(Ljava/lang/CharSequence;)V
return-void
.end method
就改一个字符串。保存,回装,签名,安装——你会看到界面上的字变了。
这一刻,你就正式入门了。
坑点:如果你在 smali 里搜不到这句文案,说明它不在代码里,而在资源文件里(
res/values/strings.xml),代码只引用了一个0x7f0xxxxx的 id。这时候要去改res/values/strings.xml里对应的那一条,或者用 MT管理器 的 Arsc 编辑器直接改。
4.2 第二式:改数值(const / const-wide)
目标:把 7 天试用改成 99 天。
jadx 里看到:
private static final int TRIAL_DAYS = 7;
在 smali 里,这个 7 会变成十六进制 0x7:
改之前:
const/16 v0, 0x7
改之后(0x63 = 99):
const/16 v0, 0x63
几个常识要记住:
- smali 里的数字是十六进制,
0x10是 16 不是 10。改之前先换算 const/4范围很小(-8 ~ 7),const/16到 0xFFFF,const到 0xFFFFFFFF- 改到期时间通常用
const-wide(64 位时间戳,单位毫秒) - 数字改太大可能溢出,比如
const/16塞不下 99999
坑点:本地改天数,只在 app 自己算的情况下有效。如果试用天数是服务器下发的,你改了本地也没用——打开就又被服务端改回去了。判断是否本地计算,最简单的办法是断网打开 app 看看。
4.3 第三式:绕过判断(if-eqz / return-void)
这是真正有意思的地方,也是改造者最常用的两招。
招式 A:强行翻转判断
jadx 里看到:
private void checkVip() {
if (!isVip()) {
showBuyDialog(); // 不是会员就弹购买框
}
}
对应的 smali:
改之前:
invoke-direct {p0}, Lcom/demo/weather/MainActivity;->isVip()Z
move-result v0
if-eqz v0, :cond_vip # v0 == 0(不是会员)就跳到弹窗
:cond_vip
invoke-direct {p0}, Lcom/demo/weather/MainActivity;->showBuyDialog()V
改法一(翻转条件):把 if-eqz 改成 if-nez。
if-nez v0, :cond_vip # 改成 v0 != 0 才跳,逻辑正好反过来
改法二(强行赋值,更常用也更稳):在 move-result 后面插一行,强制把结果置为 1(真)。
invoke-direct {p0}, Lcom/demo/weather/MainActivity;->isVip()Z
move-result v0
const/4 v0, 0x1 # ← 加这一行:不管算出来是什么,强制为真
if-eqz v0, :cond_vip
为什么改法二更常用?因为你不用去推理原来的判断逻辑。不管它是
if-eqz还是if-nez,把结果钉死成 1 或 0,后面的分支自然就走你想要的那条。这就是第二章说的"掌控关键片段的表演进度,即可影响全局剧情"。
招式 B:让方法直接空手而归
想去掉开屏广告?找到那个显示广告的方法,在第一行就让它返回。
改之前:
.method private showSplashAd()V
.registers 4
invoke-direct {p0}, Lcom/demo/weather/MainActivity;->loadAd()V
# ... 一堆广告逻辑 ...
return-void
.end method
改法一(方法内加 return-void):
.method private showSplashAd()V
.registers 4
return-void # ← 加这一行,进来就走,什么也不干
invoke-direct {p0}, Lcom/demo/weather/MainActivity;->loadAd()V
return-void
.end method
改法二(把调用处注释掉,更安全):找到谁在调用它,在那一行的 invoke 前面加个 #。
# invoke-direct {p0}, Lcom/demo/weather/MainActivity;->showSplashAd()V
推荐新手用改法二。原因:改法一会在方法里留下不可达代码,某些严格校验下反而会出问题;而注释掉调用行,等于"压根没叫它",最干净。
注意 smali 里的注释符号是
#,跟 Java 的//不一样。
五、小剧场模式:第一次翻车
小剧场模式
导演: 这一场拍主角第一次翻车,表情要到位。
助理: 3 2 1。。。action
我: (信心满满)改完了!回装!签名!安装!
手机: 解析包时出现问题。
我: ???
钤印君: (慢悠悠走过来)你是不是忘了 zipalign 要在 apksigner 之前?
我: 啊……我好像是先签的名。
钤印君: 那可不就白忙了。先对齐,后盖章,顺序反了签名就碎了。
我: (重做一遍)好了!这回装上了!点开——
手机: 很抱歉,"天气"已停止运行。
我: ……又怎么了?
拆解君: 你看看我回编译时报的警告。.registers 3,但你实际用了 v0、v1、v2、v3 四个寄存器——多出来的那个没地方站。
我: 那我改回 .registers 4?
拆解君: 对。寄存器宁可多,不可少。少了必崩,多了只是浪费。
我: (再改再装)这次起来了!可是……文案怎么没变?
执行君: 你改的是 smali/ 里那个 dex。我这 app 有两个 dex——你找错片场了,smali_classes2/ 里还有一份同名的呢。
我: ……(瘫坐)
透视君: 别急。jadx 里你搜到几处"免费版"?
我: 三处。
透视君: 那你就得改三处。改造最忌讳的就是改了一处就以为大功告成。
导演: cut!! 这条过。
我: (小声)所以第三章老头儿说的"洞察、推算、分析、辨别",是这个意思啊……
小剧场模式
六、翻车急救手册
上面那场戏里的坑,新手基本都会踩一遍。整理成表,翻车时对着查:
| 症状 | 最可能的原因 | 怎么查 |
|---|---|---|
| 解析包时出现问题 | ① 没签名 ② zipalign/apksigner 顺序反了 ③ 包本身损坏 | 重跑 zipalign → apksigner,用 apksigner verify 验证 |
| 安装后立刻闪退 | ① smali 语法错误 ② .registers 数量不够 ③ 返回值类型不匹配 |
看 apktool b 的报错;寄存器改大一点;logcat 看崩溃栈 |
| 改了没效果 | ① 有多 dex,改错文件 ② 同一个判断有多处 ③ 数据在服务端 | jadx 里全局搜,看命中几处;断网试一次 |
| 回编译失败 | ① aapt 版本不兼容 ② 缺 framework ③ apk 被加固 | 升级 ApkTool;apktool if framework-res.apk;新手先换未加固的样本 |
| 装上能开,一联网就打回原形 | 服务端校验 | 说明本地改无效,需要配合抓包或放行服务端逻辑(后话) |
排查的通用姿势:
adb logcat | grep -i "AndroidRuntime" # 看崩溃原因
崩溃日志里会直接告诉你哪个类、哪个方法、出了什么错。学会看 logcat,你的排错速度能快十倍。
七、手机上的快捷路线
如果你嫌电脑上敲命令麻烦,第三章那位**万能君(MT管理器)**可以一把梭:
- 打开 MT管理器,点进 apk 文件
- 点
classes.dex→ 选「Dex 编辑器」 - 搜索类名或字符串,定位到方法
- 直接编辑 smali,保存
- 退出时自动重新打包 + 自动签名
- 安装
优点:不用配环境、不用记命令、自动签名。
缺点:改大段逻辑不如电脑舒服;多 dex 要逐个进。
建议:练手用 MT管理器找感觉,正经干活回到 ApkTool + jadx。两条路线不冲突,混着用最舒服。
八、一条铁律
最后,说点比技术更重要的事。
藏兵阁门口那副对联的下联是"术不在玄,管用则行",但老头儿没告诉我,那副对联还有个横批没挂出来——他是在我第三次来借兵器时才说的:
器可用之于人者,亦可用之于己。
改造技术本身是中立的,就像第二章说的——改造者是唯一中立的存在,最终会变成什么样子,取决于你自己的决定。
所以:
- 拿自己写的 app 或明确授权的 app 练手
- 不要去改别人的付费功能并分发
- 不要碰涉及账号、支付、隐私的东西
- 只做技术讨论与学习,不传播成品
这不只是"政治正确"——这也是这行能走多远的天花板。
我把那个天气 app 的开屏广告去掉了,界面清爽了不少。
虽然只是注释掉了一行 invoke。
但那是我第一次,真正意义上让一个程序听我的话。
师傅端着茶杯走过来,瞟了一眼屏幕:“改了?”
“改了。”
“改了几处?”
“……三处。前两处没反应。”
师傅笑了:“记住这个感觉。改造这件事,八成的时间花在找对地方,两成的时间花在动手。 新手总觉得是自己招式不行,其实多半是压根没找对地方。”
我点点头,正想说话,手机突然一震。
屏幕亮起来,是那个天气 app 弹出的新提示:
「检测到应用已被修改」
我:???
师傅放下茶杯,笑得更开心了:“哦?看来是时候跟你讲讲——那些不希望你改造的人,都藏了什么手段了。”
(未完待续。。。)
剧情片段
导演: 收工。下一场,该轮到保卫者登场了吧?
助理: 对,听说还要讲签名校验、加固、混淆那一套。
导演: 行,那就叫——道高一尺。
剧情片段


