[转]微信零基础美化反编译教程 – 8 – 修改按钮图标

在这篇文章里,我们会详细的讲述微信的按钮要如何修改,其中的原理是什么。不仅仅适用于文章中提到的输入框按钮,其他的类似的按钮也可以用这个方法修改。

修改输入框按钮图标展开目录

首先我们先定位到我们需要修改的按钮处,使用「开发者助手」中的「界面资源分析」,但是这里我们要注意的是:我们不能在主界面点进聊天之后直接进行界面资源分析,因为从主界面点击进入聊天实质上并没有开启一个新的 Activity,只是覆盖了一个聊天的界面在主界面上面,如果我们直接进行解析的话就会同时解析到主界面的元素,这是我们不想看到的,如下图左 (可以看到底部明显有主界面底栏的元素混进来了)

正确的解决办法是:从通讯录进入聊天界面再解析,如下图右

注意不要在主界面点击聊天之后直接分析资源

我们先从语音按钮开始,点击语音按钮,然后复制它的资源 ID:

获取按钮的资源ID

然后到 apk 中按 16 进制搜索,得到一个文件,这个文件应该就是聊天界面输入框的布局文件了:

搜索资源ID

我们打开 g0.xml,再一次搜索这个 ID,定位到这个布局文件里定义语音按钮的位置,我们注意到它的 android:background 背景属性是 @null,也就是说这个按钮没有定义背景,但是我们看到 android:src 前景属性 (基本我们要改显示的图片资源也就是改这两个属性了) 指向一个 ID 为 7F020202 的文件,我们在 resources.arsc 中「ID 搜索资源」,发现这是一个 xml 文件,存放于 r/k/,也就是我之前对照表中的 res/drawable/ 文件夹

继续顺藤摸瓜

我们找到 r/k/dp.xml,打开以后看到如下代码,为了详细说明问题,我就不把相关的文字放在注释里了

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@7F070349" />
    <item
        android:state_selected="true"
        android:drawable="@7F070349" />
    <item android:drawable="@7F070348" />
</selector>

我们看到有两个属性比较惹人注意 android:state_pressed以及android:state_selected,而它们后面的 android:drawable是不一样的,我们从中可以看出来,这是一个定义按钮的 drawable xml 文件

  • 在按钮被按下 (pressed) 的时候,显示相应条目后面的 android:drawable 所指向的图片
  • 在按钮没有被按下的时候,显示最下面没有任何限定条件的 android:drawable 所指向的图片

那理论上来说我们现在只要去搜索这两个 ID 所指向的图片,然后替换掉就好了吧?但是事情没有这么简单,我们先来搜索一下看一看最下面的 ID 指向的图片到底是什么:

指向的图片

可以看到指向的是一个 svg 矢量图文件

关于 svg 矢量图,我们需要知道的是:

  • svg 矢量图是一种使用代码来编写的矢量图文件,因为是用代码记录如何绘制图像,所以不论放大到多少都是按照既有的代码来重新绘图,图片的分辨率不会降低

虽然我们自己不会写 svg 矢量图,但是网上也有不少的资源,比如阿里巴巴做的图标素材库 Iconfont,里面可以选择下载 svg 格式的图标

可是,这毫无意义

因为,当我们打开这个指向的文件的时候,我们会发现:

svg 是空文件

不仅如此,我们返回,会看到 r/k 这个放满了 svg 图片的文件夹里面的 svg 文件的大小都是 0 Byte

svg 全是空文件

哪怕你把这些文件给删了微信照样可以正常运作……

为什么微信的 svg 文件都是空文件?展开目录

其实最初我遇到这个问题的时候,我也是很蒙圈的,经过长时间的摸索,我终于发现:

因为微信把 svg 图片的代码都放到 Java 里去了

在微信的 Java 中,各个 svg 文件的实际代码存放在 com.tencent.mm.svg.a.a 中,同时有一个叫做 SVGPreload 的类,负责加载这些存放在 Java 中的 svg,关于这一点我们在之后修改底栏图标颜色的时候还会再次提到,这里暂时先把原理摆出来

替换 ID展开目录

直接替换文件这条路我们走不通,但是我们可以在资源 ID 上做文章:

  • 既然每一个文件都对应一个资源 ID,那么,我只需要把上文中那个定义语音按钮的 dp.xml 的 android:drawable后面的资源 ID 替换,换成指向 apk 中别的没有用的图片文件的 ID,然后去替换那个没有用的图片文件不就可以了吗?

你可能会感到奇怪,apk 里怎么会有没有用的文件,你别说,还真有..

没有用的兔斯基文件

没错,不知道为什么微信把现在已经几乎没有人用的兔斯基表情包内置在了 apk 里.. 而且这么多个版本过去了依然没有来清除它们.. 不论如何,我们先来找到其中第一个 png 文件,icon\_002\_cover.png 的资源 ID:

  • MT 管理器 – 在 resources.arsc > drawable > drawable-hdpi-v4 中找到这个文件并复制 ID
  • APKDB – 直接打开 /res/values/public.xml 搜索文件名获取 ID

找到它的 ID

拿到 ID,我们回到 dp.xml,把 android:drawable后面的 ID 改成我们拿到的 7F02038E

替换

现在我们可以返回保存一下,重启微信看看效果:

变成了兔斯基

原本的语音按钮确实变成了兔斯基…… 同时我们也可以发现,更改后的图片并不会自动缩放,会按照图片原来的大小原原本本的显示在上面,所以我们在修改的时候需要控制好图片的大小,并且给按钮周边留足空白,否则会使按钮塌在下面,当然这是非常简单的图片处理,用 PS 调整图像和画布大小就可以了,根据我的试验,这样处理的按钮有以下几点要注意:

  • dpi 在 480 以下,按钮的高度是 70 像素
  • 70 像素高度的按钮图片,在 dpi 高于 480 上的手机会错位 (比如 2K 屏一般的默认 dpi 是 560)
  • 这样替换的图片因为不会自己缩放,所以会很模糊,屏幕越大越明显

关于模糊的问题,我自己还在探索,所以这里只能暂时用普通的未缩放 png 来凑合了

我们把处理好的语音按钮的图片放到 r/z/,覆盖掉原本的 icon\_002\_cover.png,再次重启微信:

语音按钮替换完成

这样的话语音按钮就替换完成了,其他按钮 (键盘、表情、加号、微信公众号界面的一些按钮) 的替换步骤也是一样的,在这里再次贴上前面已经贴过的一张图:

svg 全是空文件

我们刚才改动的 dp.xml 里面的 ID 所指向的文件名是 textfield_icon_voice_pressed.svg 和 textfield_icon_voice_normal.svg,那么以此类推,剩下的 textfield 开头的 svg 文件当然也就是输入框其他的按钮的 svg 文件了

我们只需要获取到这些文件的 ID,在 resources.arsc > com.tencent.mm > raw > raw (之所以在这里是因为 r/a9/ 这个文件夹本来就应该叫 raw,微信混淆了,这一点在之前的对照表里也有所体现,此后不再赘述) 中就有这些文件的 ID,并搜索 XML,就一定可以搜索出一个类似 dp.xml 的定义剩下按钮呈现的 drawable xml(这些文件在 PC 端反编译后的 /res/drawable 中) 再用类似的方法全部替换掉就好了

(不过其实微信用的这些 svg 文件也有一点好,就是文件名没有被混淆,从文件名可以非常准确的判断出来它用于哪一个元素,有时候也可以作为一个可靠的定位布局文件的起点)

回编译塞新的文件进入安装包展开目录

我们前面看到,虽然无用的兔斯基封面图片比较多,但是如果我们需要替换大量的 svg 图片,那么势必会用掉全部的兔斯基图片位置,超出的部分我们就束手无策了

  • 但是我们可以通过回编译的方式直接往里面塞图片,并使之获得一个资源 ID

前面已经提到,资源 ID,是在「编译的时候」给每一个资源赋予的,关于这一点,我们来看一下反编译出来的布局文件就一目了然了,这里贴上前文提到过的底栏布局文件反编译后的开头:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@drawable/akd" 
    android:paddingTop="@dimen/ie" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" >

再来回顾一下我们在 MT 管理器里看到底栏布局文件的样子:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@7F02051F"
    android:paddingTop="@7F0C0154"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

我们可以看到,完全反编译的包中的布局文件的「资源 ID」都变回了它们分别「指向的目标」,而在 MT 管理器中,因为是局部的反编译,所以只能显示成分配后的 ID

那么,完全反编译之后,再次回编译,会给所有的资源再次赋予资源 ID,包括我们任意塞进去的图片 (即便这个图片并没有被调用)

比如我们这里把之前使用的加号图片直接塞进 drawable-xxhdpi-v4,然后回编译:

右键文件夹点击「使用 APKDB 编译」之后会出现熟悉的 CMD 界面:

直接回车会提示是否重新签名,我们最好签名,如果塞了新的文件进去却不签名会导致安装时解析包错误

等待编译完成后我们会自动跳转到下图右的文件夹中,返回可以看到它在反编译的文件夹中建立了一个新的「已编译_153629」,每一次编译都将建立一个新的文件夹

编译好的 apk

现在我们把这个 apk 用 MT 管理器打开,看看我们塞进去的图片是否被赋予了自己的资源 ID:

被赋予了新的 ID

可以看到我们塞进去的 voice.png 在这一页的最底部,被赋予了新的 ID,总之,有了回编译就可以为所欲为了

实际上,我们在 PC 上操作,把图片塞进去的时候,就可以到反编译后的相关的布局文件中直接更改了,如下图,我更改了前文提到的 dp.xml(定义语音按钮的 drawable xml)

为所欲为为所欲为为所欲为

只要这样输入,在编译的时候就会自动匹配到我刚刚塞进去的 voice.png 了,图片命名的时候不要和原有的相撞了就好,当然微信这样的命名混淆想撞也基本是不可能的..

文 / anson
LEAVE A REPLY
loading