转自 : http://blog.sina.com.cn/s/blog_5674d18801019i89.html

应用场景

Smali代码注入只能应对函数级别的移植,对于类级别的移植是无能为力的。具体的说,如果你想修改一个类的继承、包含关系,接口结构等是非常困难的。但对于修改类成员变量访问控制权限,类方法实现,Smali代码注入的方法是可以实现的。这主要是因为Samli级代码的灵活性已经远低于java源代码,而且经过编译优化后,更注重程序的执行效率。

  Smali代码注入

本质上讲,Smali代码注入就是在已有APK或JAR包中插入一些Dalvik虚拟机的指令,从而改变原来程序执行的路径或行为。

这个过程大致分为五步——确定需要注入的Samli代码,确定注入位置,注入Smali代码,编译Smali代码,调试Smali代码。

总体流程如下图:

下面详细说明:

  确定需要注入的Smali代码

首先,确定基线文件——待移植的APK包或JAR包,使用APKTOOL反汇编,生成原始的Samli文件。

其次,修改对应的APK包或JAR包的java源代码,使用编译系统重新生成新的APK包或JAR包,并用APKTOOL反汇编,生成包含修改后的Samli文件。

最后,使用比较工具(例如BeyondCompare)比较两次Smali文件,即可提取出需要注入的Smali代码。

例如下图红色区域就是需要注入的Smali代码

  确定注入位置

这一步的看似简单,实际工作中有很多难点,主要是有些注入位置比较难确定,需要不断的尝试。

使用APKTOOL反汇编待注入的APK或JAR包后,首先需要确认需要注入的Smali文件是哪个。这个主要是针对含有匿名内部类的Java文件而言。例如,移植PhoneWindowManager.java文件的修改时,反汇编之后会有很多PhoneWindowManager$1.smali, PhoneWindowManager$2.smali...类似的文件。这些文件就是匿名内部类的Smali代码,由于没有名字,所以编译后只能用$XXX来区分。

如果带注入的Smali代码是从PhoneWindowManager$5.smali提取的,一般不能够直接将其注入到目标机型的PhoneWindowManager$5.smali文件中,因为不同机型的匿名内部类顺序不同,实现不同,Smali文件也不同。一般需要通过逐个比较PhoneWindowManager$5.smali附近的几个文件的Smali代码,看看其函数调用,函数名字,类继承关系是否相同来确定注入哪个文件。

当然对于没有匿名内部类的Java文件可以直接使用对应的Smali文件注入即可。

其次,确定了注入文件之后,就需要进一步确认待注入区域。由于Smali代码中的每个变量的类型是不固定的,再加上编译器的优化,导致不同ROM的APK或JAR包反汇编后,会有很多不同。这个并不影响我们的工作,我们重点关注Smali代码的“行为”——函数调用顺序,逻辑判断顺序,类成员变量访问顺序,即可大致确定注入区域。另外,对于新增的Smali代码区域可以随意些,新增变量直接追加在变量声明尾部即可,新增函数直接增加在文件尾部。总的来说这个工作还是非常经验化的,需要长时间的反复尝试才能更准确的确定注入区域。

最后,继续上面例子,如图:


 图中有很多红色的不同,其中蓝框是我们刚才确定的需要注入的Samli代码。通过上下文匹配,可以发现绿框的位置是Samli代码需要注入的区域。尽管上下有很多指令和变量不同,但是这并不影响我们的工作。

注入代码

首先,将待注入的Smali代码注入对应的区域。

其次,对注入的Smali代码进行“本地化”——修改变量、跳转标号、逻辑判断标号等,使之符合当前的Smali代码实现,完成“嫁接”工作。当然,如果情况很复杂,需要重写对应的Smali代码或者重构java源代码,来完成最终的代码注入。Dalvik 虚拟机每条指令含义请参见这里

最后,继续刚才的例子,如图:


  其中蓝框内是最终移植的代码,可以看到其中修改了move-result-object v2和invoke-virtual {v2, v0, v1, v15}, Lmiui/net/FirewallManager;->onStartUsingNetworkFeature(III)V的变量,这主要是因为invoke-virtual {v2, v0, v1, v15}, Lmiui/net/FirewallManager;->onStartUsingNetworkFeature(III)V在新的Smali代码中v15变量有其他的用途,我们需要找一个上下文无关的变量完成函数调用时的变量传递,所以这里将move-result-object v15改为move-result-object v2。并且修改了onStartUsingNetworkFeature()函数参数列表。

另外,移植中还有一类关于资源ID的代码注入比较特殊,这里举例说明一下:

现在我们需要将蓝框内的代码注入到绿框的位置,但是其中const v6, 0x10403c1 阻挡了前进的步伐。我们不能鲁莽的将蓝框代码合入绿框,这样会导致资源无法找到运行时错误。我们需要使用反汇编原始ROM的framework-res.apk和目标ROM的framework-res.apk,根据0x10403c1 ID值找到原始ROM framework-res.apk中对应的资源名字,再根据资源名字到目标ROM的framework-res.apk中查找对应的资源ID值。而后将其替换为目标ROM中的资源ID值。所以最终移植后的代码如下图:

需要说明的是0x1开头的资源都是framework-res.apk中的资源,0x2开头的一般是厂商自己的资源,例如摩托的是moto-res.apk,HTC的是com.htc.resources.apk。miui自己的资源是0x6开头,位于framework-miui-res.apk中。

  编译Smali 代码

Smali编译过程相对简单,使用apktool b XXX XXX.apk 即可将Smali代码编译成apk或jar包。但是当遇到编译错误时,apktool工具给出的错误信息少之又少,以至于我们只能手动查找哪个文件Samli代码移植错误。

这里,我总结了一些Smali代码移植时可能遇到的编译错误。希望对各位有用。

1.函数调用(invoke-virtual等指令)的参数只能使用v0~v15,使用超过v15的变量会报错。修复这个问题有两种方法:

A.使用invoke-virtual/range {p1 .. p1}指令,但是这里要求变量名称需要连续。

B.增加move-object/from16 v0, v18类似指令,调整变量名,使之小于等于v15。

2.函数调用中p0相当于函数可用变量值+1,pN相当于函数可用变量值+N。例如函数.local值为16,表明函数可用变量值为v0~v15,则p0相当于v16,p1相当于v17。

例如,下图左侧蓝框所在代码编译不过,后来检查了代码所在的函数.local为33,p0相当于v33,所以编译不过,修改为右侧绿框才正常。

3.跳转标号重叠。

这里主要是指出现了两个相同的标号的情况,例如cond_11等,导致无法编译过。解决方法就是修改冲突的标号以及相关跳转语句。其实这个标号叫什么都无所谓,你甚至可以叫ABCD_XXX,只要可以与对应的goto语句呼应即可。

4.使用没有定义的变量

每个函数可以使多少变量都在函数体内的第一句.local中声明,例如.local 30表明这个函数可以使用v0~v29,如果使用v30就会编译错误。

 

  调试Smali 代码

调试Smali代码主要任务是解决注入代码后导致的运行时错误。具体的说,就是使注入后的Smali代码通过dalvik虚拟机的字节码校验。获取错误的方法相对简单,使用下面两条命令即可:

adb logcat | grep dalvikvm

adb logcat | grep VFY

其中VFY的信息会给出Smali代码出错的文件、函数以及错误原因,dalvikvm的信息可以给出调用栈,以及上下文执行过程,都比较贴心。

这里总结一下主要的运行时错误:

1.函数变量列表与声明不同,这个主要体现在下面两个方面:

A.函数调用的变量类型与函数声明不同。

通过追踪变量在上下文的赋值动作来解决。

B.函数变量列表中变量少于或者多于函数声明的变量。

通过核对函数声明来解决。

2.函数调用方式不正确。

例如:public和包访问函数使用invoke-virtual调用,private函数使用invoke-director调用,接口函数使用invoke-interface调用。如果使用错误,会导致运行时错误。需要调整相关的Smali代码。

3.类接口没有实现。

主要是由于增加了新的子类没有实现原有父类接口导致的,只需增加空实现即可修复。

4.签名不正确。

可以通过adb logcat | grep mismatch命令确认哪个package签名不正确。只需对签名不正确的包重新签名即可。当然如果有很多签名不一致的错误,建议大家对所有的APK重新签名。

5.资源找不到。

这个问题的原因有很多种,我这里列举一些常见的原因:

A.系统资源文件签名不正确,导致没有加载系统资源,进而无法找到相应的资源。其中,系统资源文件是指system/framework/目录下的apk文件。

B.Smali代码中的资源ID移植错误,无法在系统资源中找到对应的资源。

C.资源相关的类移植存在问题,导致无法加载相关资源。

另外,调试时,大家可能需要要追踪代码执行路径,但又苦于无法Debug。我这里分享一些简单的追踪方法,希望对大家有用。

1.增加简单的Smali日志信息:

A.修改函数的.local变量,在原来基础上增加两个变量,例如v11,v12。

B.在需要打印日志的地方增加如下Smali代码

const-string v11, "@@@@"

const-string v12, "interceptPowerKeyDown enter"

invoke-static {v11, v12}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

如果增加的变量为v28和v29,则需要使用下面的语句。

invoke-static/range {v28 .. v29}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

2.打印程序调用栈的方法:

A.修改函数的.local变量,在原来基础上增加一个变量,例如v11。

B.在需要打印调用栈的地方增加如下Smali代码

new-instance v1 Ljava/lang/Exception;

invoke-direct {v1, Ljava/lang/Exception;-><init>()V

invoke-virtual {v1, Ljava/lang/Exception;->printStackTrace()V

apk反编译(4)Smali代码注入的更多相关文章

  1. apk反编译、smali修改、回编译笔记

    最近下了一个apk程序,但是一启动会弹出一个流氓广告.这个广告不是原厂商加的,而是有人在原有apk程序的基础上,加了一个壳,让apk先启动他加的广告,再启动原来的程序,很恶心.于是想去掉它. 试了几个 ...

  2. Android: apk反编译 及 AS代码混淆防反编译

    一.工具下载: 1.apktool(资源文件获取,如提取出图片文件和布局文件) 反编译apk:apktool d file.apk –o path 回编译apk:apktool b path –o f ...

  3. 防止 apk反编译 jocky-- java混淆代码 (转至:http://my.oschina.net/f839903061/blog/72554)

    1.下载jocky,解压后把整个文件夹复制到Eclipse的plugin目录.2.重启Eclipse,在项目上点右键,如果出现jocky菜单,则安装成功. 3.在项目上点右键,选菜单jocky-> ...

  4. apk反编译(2)smali语言及文件

    Smali语言是Davlik的虚拟机使用的一种语言,用toolapk反编译apk后,可以见到大量的.smali文件. 可以按照smali语法对其修改,然后重新生成一个未签名的apk. 下面是一个示例: ...

  5. apk反编译(3)smali语法

    from http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html Dalvik opcodes Author: Gabor Paller Vx ...

  6. Android Studio 动态调试 apk 反编译出的 smali 代码

    在信安大赛的准备过程中,主要通过 Android Studio 动态调试 apk 反编译出来的 smali 代码的方式来对我们分析的执行流程进行验证.该技巧的主要流程在此记录.以下过程使用 Andro ...

  7. APK反编译之一:基础知识—APK、Dalvik字节码和smali文件

    refs: APK反编译之一:基础知识http://blog.csdn.net/lpohvbe/article/details/7981386 APK反编译之二:工具介绍http://blog.csd ...

  8. Android开发:APK的反编译(获取代码和资源文件)

    一.反编译工具: 1.APKTool: APKTool是由GOOGLE提供的APK编译工具,能够完成反编译及回编译apk的工作.同时,它也有着安装反编译系统apk所需要的framework-res框架 ...

  9. 【转】代码混淆和apk反编译

    代码混淆 http://blog.csdn.net/vipzjyno1/article/details/21042823 apk反编译 http://blog.csdn.net/vipzjyno1/a ...

随机推荐

  1. ios开发之NavBar和TarBar使用技巧

    1  改变NavBar颜色:选中Navigation Bar 的Tint属性.选中颜色. 2  隐藏“back”按钮: self.navigationItem.hidesBackButton = YE ...

  2. (Android学习系列)三,窗口的常用事件

    设置窗口标题事件和在Activity之间跳转 新建一个项目,新建两个Activity:MainActivity,TitleActivity ,然后再AnroidManifest.xml 中注册Titl ...

  3. 问题 K: 【USACO2012Feb】植草 {Bronze题2}

    按着矩形周长的思路,到当前边的时候,前一层的覆盖数乘以高度加入 ans 就行,然而真正的算法可能并不是这个..只能想到这个了 ; type node=record l,r,mid,sum,delta: ...

  4. Palindrome Partitioning

    Palindrome Partitioning Given a string s, partition s such that every substring of the partition is ...

  5. When to use Class.isInstance() & when to use instanceof operator?

    I think the official documentation gives you the answer to this one (albeit in a fairly nonspecific ...

  6. QQ炫舞官网选项卡效果

    这篇博文里需要注意的是当点击事件发生的时候,需要用循环,重置标题的classname和标题底部都设置成隐藏,当点击的时候在加上标题的active属性和显示属性 代码地址:https://github. ...

  7. 2007: [Noi2010]海拔 - BZOJ

    Description YT市是一个规划良好的城市,城市被东西向和南北向的主干道划分为n×n个区域.简单起见,可以将YT市看作一个正方形,每一个区域也可看作一个正方形.从而,YT城市中包括(n+1)× ...

  8. 学习Ember遇到的一些问题

    1.在模板中不能省略结束标签: 在Ember的模板中,如果省略结束标签的话,会有好多无解的问题(可能是:不更新.更新后结构不对.model和view不同步等),苦苦找了很久.... 2.childVi ...

  9. sampler2d

    Here is the syntax for a sampler in Direct3D 9. sampler Name = SamplerType{   Texture = <texture_ ...

  10. 高性能网络编程2----TCP消息的发送

    转 陶辉 taohui.org.cn 在上一篇中,我们已经建立好的TCP连接,对应着操作系统分配的1个套接字.操作TCP协议发送数据时,面对的是数据流.通常调用诸如send或者write方法来发送数据 ...