Android反编译smali文件的修改
之前提到过Android的反编译,知道了Android的apk反编译后依然是一个完整的项目。资源文件易于修改,不提了,这里主要记录一下smali文件的一些特性和修改方式。
smali文件是Dalvik虚拟机的指令码,和汇编文件有点像。
进入到smali包下面,往往会发现诸如R$id.smali等R$*.smali格式的文件,这是自动生成的R文件,也就是资源映射文件,这些文件一旦编译好都是死的,没法变,所以不到万不得已不要smali码和资源文件一起添加,太麻烦。
包里面的smali文件都是一个名字可以对应多个文件,这是将写在Java中的内部类拆分的结果。后面用${数字}来区分,也可以是${数字}${数字}表示内部类的内部类,只不过这样增加代码复杂度的写法不多见而已。
然后就可以修改smali码了,需要注意两点:
- 了解指令码的含义(Dalvik操作指令)
- 注意代码中的汉字字符会转成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
一个字符串常量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 {数字}这种似乎只是记录行号,具体作用不明,随便添加删除也看不出影响,所以修改代码时不用考虑就好。