Android反编译smali文件的修改

文章链接:http://www.liuschen.com

之前提到过Android的反编译,知道了Android的apk反编译后依然是一个完整的项目。资源文件易于修改,不提了,这里主要记录一下smali文件的一些特性和修改方式。

smali文件是Dalvik虚拟机的指令码,和汇编文件有点像。

进入到smali包下面,往往会发现诸如R$id.smali等R$*.smali格式的文件,这是自动生成的R文件,也就是资源映射文件,这些文件一旦编译好都是死的,没法变,所以不到万不得已不要smali码和资源文件一起添加,太麻烦。

包里面的smali文件都是一个名字可以对应多个文件,这是将写在Java中的内部类拆分的结果。后面用${数字}来区分,也可以是${数字}${数字}表示内部类的内部类,只不过这样增加代码复杂度的写法不多见而已。

然后就可以修改smali码了,需要注意两点:

  1. 了解指令码的含义(Dalvik操作指令
  2. 注意代码中的汉字字符会转成Unicode码,所以添加需要将汉字字符会转成Unicode码(如何转)

这里我主要修改一个Handler的内部,它的头如下,这也是所有内部类的共性

.class L该文件的完整路径名;
.super Landroid/os/Handler(父类);
.source "原始从属的Java类.java"

# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
    value = L该文件原始从属的Java类的完整路径名(在哪个类里面定义就是那个类);
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x0
    name = null
.end annotation

# instance fields
.field final synthetic this$0:L该文件原始从属的Java类的完整路径名;

# direct methods
.method constructor <init>(L该文件原始从属的Java类的完整路径名;)V
.locals 0

.prologue
.line 1
iput-object p1, p0, L该文件的完整路径名;->this$0:L该文件原始从属的Java类的完整路径名;

.line 278
invoke-direct {p0}, Landroid/os/Handler;-><init>()V

return-void
.end method

可能单看没什么用,还是不能理解,就需要自己写些例子,生成apk反编译对比看看,就会发现规律

.field故名思议就是变量,而且一般是全局的

.method和end method之间是方法体,.method constructor 表示是该handler内部类的构造方法

一个字符串常量v2:

const-string v2, "SOMETHING"

新建一个对象并存到v4:

new-instance v4, L对象完整路径

调用方法(这个是要结果的):

invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

调用方法后将结果保存到v2:

move-result-object v2

调用方法(这个不要结果,是用来初始化v4这个内部类对象的,v6存储的是默认内部类所属外部类对象,v2存储的是外部类的一个final修饰的参数,都不是正经传入的参数,是编译器处理的结果)

invoke-direct {v4, v6, v2}, Lv4存储的实例对应的类的路径;-><init>(Lv6存储的实例对应的类的路径;Ljava/lang/String;)V

重新打包时经常有错误报v数字不在v1-v15范围内(reason),又因为v可以反复使用,所以这个应该是如汇编一样是对寄存器的操作,v1-v15表示寄存器,所以只要明白了类是如何取到的,其他的就好理解了。

if结构:

if-eqz v1, :cond_1
.
.
:cond_1

外部的final定义的对象,内部类是如何取到实例的?首先做如下定义:

.local v2, "对象名(在Java中自己起的)":Ljava/lang/String;

然后用上面的invoke-direct方法将v2塞进去,然后再内部类中的构造方法里就会有:

iput-object p2, p0, L该类完整路径名;->val$对象名:Ljava/lang/String;

在具体方法中会通过

iget-object v2, p0, L该类完整路径名;->val$对象名:Ljava/lang/String;

得到该值,即存储在v2中

我是通过查找字符串来定位修改的大致位置的,然后通过if结构的关键字cond来确定最终的修改坐标,比较粗浅,因为关于smali文件我还有许多不明白的地方,日后再有需要再去详细了解。

还有一点,我发现.line {数字}这种似乎只是记录行号,具体作用不明,随便添加删除也看不出影响,所以修改代码时不用考虑就好。

Loading Disqus comments...
Table of Contents