这一章我们来详细的看看反编译,想要修改一个系统自带的应用程序和它的代码,在没有源码的情况下,我们就不得不用反编译来修改。
和很多书籍一样,为了向经典的”Hello,World”致敬,我们也从一个简单的程序开始HelloActivity.apk。当你把这个APK安装到手机上运行后,在屏幕上就显示一行文字”Hello, World!”
1.反编译
假定在patchrom目录下:
$tools/apktooldHelloActivity.apk
这条命令运行完后,在当前目录下会生成一个名为HelloActivity的目录。
该目录的结构为(名称后跟/表示这是一个目录):
HelloActivity/
|————–AndroidManifest.xml
|————–apktool.yml
|————–res/
|————–smali/
apktool.yml是apktool生成的一个配置文件,基本上你不需要修改这个文件。下面的章节我们逐个介绍剩下的AndroidManifest.xml文件和res,smali目录。
2.AndroidManifest.xml
要想完全理解这个文件,你得对Android的内部运作机制非常清楚。幸好我们修改一个APK的时候基本上不改这个文件。这里帮助你有个大致的了解。
Android安装程序一般叫apk文件(apk是AndroidPackage的缩写,表示Android安装包)。一般来说,程序都会有一个或多个Activity,Activity是什么呢,从概念说它是一个和用户交互的窗口,你每天使用Android手机的时候基本上你打交道的每个界面都是一个Activity,和Windows下的窗口类似。AndroidManifest.xml是一个xml格式的清单文件,就像你去超市买东西会打印出一个购物清单,AndroidManifest.xml也起着一个清单的作用,它告诉系统,我有这些Activity。(实际情况远比这复杂,想学Android编程的同学请看这个http://developer.android.com/guide/index.html,好好学习其中的内容)。
具体到HelloActivity下的AndroidManifest.xml文件,大家可以找到如下内容:
其中有一行,包含这一行的Activity会显示在桌面中,就是说你可以通过桌面显示的图标启动这个Activity。里面还有android:label=”@string/app_name”android:icon=”@drawable/ic_launcher_hello”。这两个属性是做什么的呢,android:label表示程序显示在桌面上的名字,android:icon表示程序显示 在桌面上的图标。如果想要改显示的名字和图标,修改其后的两个资源,如何修改资源,下一节详细介绍。
3.资源
res目录下放置了程序所需要的所有资源。资源是什么呢,一般来说,一个图形用户界面(GUI)程序总是会使用一些图片,或者显示的文字的大小和颜色等。或者界面的布局,比如显示的界面上面是文字,下面是两个按钮等等。这些程序的一个重要特点就是用户界面和代码逻辑的分离。当我们需要替换图片或者简单修改界面布局的时候,不需要改变代码。而Android程序会将这些代码中需要用到的文件都放在res目录下,称之为资源。
可以看到res目录的内容为:
res/
|——–drawable-hdpi/
|———–ic_launcher_hello.png
|——–layout/
|———hello_activity.xml
|——–values/
|———ids.xml
|———public.xml
|———strings.xml
对于HelloActivity来说,res目录下有三个子目录drawable-hdpi,layout,values。由于HelloActivity比较简单,因此res下内容不多,但是一个复杂的程序res目录下内容相应的也会比较多,但是基本原理都是一样的。
res下面的子目录基本上是按照资源类型来分类组织的,以drawable开头的表示图片资源,大家可能会看到drawable-hdpi,drawable-mdpi,drawable-ldpi等,这些hdpi,mdpi,lpid分别表示高/中/低分辨率,会根据不同的屏幕分辨率选择不同的图片。要想替换图片,替换这些目录下的图片就可以了。(替换图片比这稍复杂点,一般替换图片,最好保持和原图片兼容,比如说色系,尺寸以及点9图片的一些参数等)。
以layout开头的表示布局文件,用来描述程序的界面。anim子目录存放程序使用到的动画,xml开头的目录存放程序用到的一些xml文件等。
values开头的目录下面存放一些我们称之为基本元素的定义,比如说colors.xml给出颜色值的定义,dimens.xml给出一些大小的定义。strings.xml是一些字符串的定义。我们看看HellloActivity的strings.xml文件。
Hello,World!
HelloWorld
其中的以
你好,世界!
你好世界
现在我们需要把修改后的文件在编回apk文件,运行如下命令:
$tools/apktoolbHelloActivityHelloActivity.apk
这条命令表示编译HelloActivity目录的内容,输出文件为HelloActivity.apk,如果你不想覆盖原有的文件,可以换一个名字或者放在另外一个目录下。
接下来我们需要对生成的APK进行签名,
$tools/sign.shHelloActivity.apk
$adbinstall-rHelloActivity.apk.signed.aligned
注意最后一条命令如果失败,如果你不是用我们提供的HelloActivity做实验的话,会发生签名不一致的错误,这个时候先卸载原来的,再安装。运行看看,对的,现在显示在你面前的是“你好,世界!”
汉化成功了,是的,汉化就这么简单。如果你只想停留在汉化或者替换图片这个阶段,从这里开始以后的文章不用看了。如果你没有Android编程基础,从这里开始以后的文章也不用看了。
到底发生了什么魔法,为什么这样替换一下图片或者改字符串就能改变程序最终运行的结果呢,想要理解这个,我们就的大致的了解一下资源的编译过程。首先我们看看values目录下一个有意思的文件publics.xml,它的内容如下:
每一行的id后面都有一个看起来很奇怪的数字,这个数字是干嘛的呢?Android下有一个资源编译器会编译res目录下的所有文件,它为每一个资源名字分配一个数字标识符,这个标识符分成3个部分,最前面的1个字节表示包名,所有的apk这个字节都是7f。表示这些资 源是非共享的,其它APK访问不到。
所有那些可以共享的资源放在system/framework/framework-res.apk下。/system/framework往往还有其它共享的资源包。这些共享的资源包前面的1个字节从0x1开始,依次增加。中间的一个字节表示资源的类型,每一个类型的数字标识符是不一样的,最后的2个字节是资源的序号,统一类型的资源序号从0依次往上递增。一般来说,资源id是由资源编译器(aapt)自动产生的,但是定义在publics.xml中的值告诉编译器,你必须为这个id使用这个值。
apktool会为所有的资源名称定义这个值在publics.xml里,这样可以保证替换资源后资源的id不会变化。
为啥资源的id这么重要,如果变了,会怎么样呢,这得结合代码理解。我们在Java代码里通常这样引用资源,比如R.string.app_name。这个Java代码经过编译后,这条引用直接变成了资源id,即0x7f040001,所以你在下面反编译后的smali代码里面是看不到R.string.app_name这个东西的,只能看到0x7f040001。资源编译器会生成一个查找表,对于每一个id,查找表中保存了这个id对应的名字和值(如果是文件,则为文件所在路径)。程序在运行的时候,会根据id去查询这个查找表找到对应的资源的值或文件。
最后说一句,资源的ID非常重要,运行adbpull/system/framework/framework-res.apk反编译这个文件,好好的消化一下这一节的内容吧。
4.smali
终于迎来我们最重要的部分了smali目录,smali目录存放的是反编译后的Java代码,文件名以smali结尾,故称作smali文件。这些代码比一般的Java代码可读性差太多了,但是和传统的x86或者其他体系结构下的汇编文件那又是好读多了。虽然有工具可以直接把这些反汇编成java代码,但是好不了太多,我们还是直接读取修改smali文件。
我们来看一下反编译后的smali目录下的HelloActivity.smali文件,和Java组织源代码的方式一样,smali目录下的文件也是按文件包的包名结构组织目录结构的,文件的内容如下:
.classpublicLcom/example/android/helloactivity/HelloActivity;
.superLandroid/app/Activity;
.source”HelloActivity.java”
#directmethods
.methodpublicconstructor()V
.locals0
.prologue
.line27
invoke-direct{p0},Landroid/app/Activity;->()V
return-void
.endmethod
#virtualmethods
.methodpubliconCreate(Landroid/os/Bundle;)V
.locals2
.parameter”savedInstanceState”
.prologue
.line33
invoke-super{p0,p1},Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
.line37
const/high16v1,0x7f03
invoke-virtual{p0,v1},
Lcom/example/android/helloactivity/HelloActivity;->setContentView(I)V
.line38
const/high16v1,0x7f05
invoke-virtual{p0,v1},
Lcom/example/android/helloactivity/HelloActivity;->findViewById(I)Landroid/view/View;
move-result-objectv0
check-castv0,Landroid/widget/TextView;
.line39
.localv0,txtView:Landroid/widget/TextView;
const/high16v1,0x7f04
invoke-virtual{v0,v1},Landroid/widget/TextView;->setText(I)V
.line40
return-void
.endmethod
文件中的以#开头的文字表示注释,以.开头的叫做annotations,其中的.line表示对应的源代码的行号,这个对调试很重要。.metho和.endmethod表示一个方法定义的开始和结束。Smali文件中的这些指令的功能请参照http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html,有过Java编程基础的很容易理解这些指令。
其中.line39的代码对应的源代码是
txtView.setText(R.string.hello_activity_text_text)
我们现在想将这行代码改成txtView.setText(“Happy,Cracker!”),将.line39到.line40行的 代码改为:
.line39
.localv0,txtView:Landroid/widget/TextView;
const-stringv1,”Happy,Cracker!”
invoke-virtual{v0,v1},Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V再按照上一节所说得重新编译,签名,安装运行,好了,现在出现在你面前的是Happy,Cracker!了,真happy!
接下来的两章我们都会介绍如何直接修改smali代码从而改变程序的功能,这种方法我们叫做代码插桩,接下来的两章我们将会用代码插桩的方法将MIUI的功能加到原生ROM中去。
可能对你有帮助的内容:极客币获取 | 话费充值 | 下载帮助 | 刷机必看 | 阿里云代金券
文章名称:【JK团队|MIUI移植第四课】反编译
文章链接:https://www.xtdiguo.com/7210.html
免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。