首页
社区
课程
招聘
[原创]smali语法以及通过org.jf.dexlib2工具来实现smali插入dex文件中
2021-2-19 19:08 10959

[原创]smali语法以及通过org.jf.dexlib2工具来实现smali插入dex文件中

2021-2-19 19:08
10959

前言

需要将特定的smali代码插入到dex文件中起到特殊作用,但是对于smali语法一知半解,这次来总结一下,并介绍如何使用工具来讲smali代码插入dex文件中。

Smali语法

可以使用IDEA或者Android Studio的Java2Smali插件来查看smali代码。

 

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.soulapp;
 
import android.os.Bundle;
 
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
 
import com.example.souldemo.R;
 
public class MainAcitivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

smali代码:

 

其中#后面为注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 类名 .class
.class public Lcn/soulapp/MainAcitivity;
# 该类的父类
.super Landroidx/appcompat/app/AppCompatActivity;
# 文件名
.source "MainAcitivity.java"
 
 
# direct methods
.method public constructor <init>()V
    # 表示需要寄存器数量,这里表示需要1个寄存器
    .registers 1
 
    # 表示逻辑代码的开始处
    .prologue
    # 表示Java源文件名的行数
    .line 10
    # invoke-direct 用于调用非 static 直接方法(也就是说,本质上不可覆盖的实例方法,即 private 实例方法或构造函数)。
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V
    # 表示该方法无返回值
    return-void
# 表示方法执行结束
.end method
# 后面的可以参考给出的Dalvik 字节码链接中的信息,很多都是从这里获得的。
 
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .registers 3
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;
        .annotation build Landroidx/annotation/Nullable;
        .end annotation
    .end param
 
    .prologue
    .line 13
    invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
 
    .line 14
    const v0, 0x7f0a001c
 
    invoke-virtual {p0, v0}, Lcn/soulapp/MainAcitivity;->setContentView(I)V
 
    .line 15
    return-void
.end method

Java类型对应的类型描述符

Java 类型 类型描述符
char C
byte B
short S
int I
long J
float F
double D
boolean Z
void V
对象 L
数组 [
 

基本类型的表示很简单,int 用 I 表示即可。对象的表示,顶级父类 Object 的表示方法 Ljava/lang/Object;,再比如 String 类型,就用 Ljava/lang/String 表示。

 

对于数组,DalviK 有特殊的表示方法 [ 后面跟上数组元素的类型。int[] 的表示方式就是 [I, String[] 的表示方法是 [Ljava/lang/String;。二维数组用 [[ 表示,[[Ljava/lang/String 就是指 String[][],以此类推。

org.jf.dexlib2工具

github地址:https://github.com/JesusFreke/smali/tree/master/dexlib2/

 

下载地址:https://bitbucket.org/JesusFreke/smali/downloads/

 

dexlib2工具可以修改dex文件内容,你可以插入想要的代码或者重写一个dex文件。当然一些反编译apk的工具也引用了该工具,比如apktool。

步骤

1.创建DexBackedDexFile

我们可以通过 DexFileFactory 类来创建一个 DexBackedDexFile实例

1
val dexBackedDexFile = DexFileFactory.loadDexFile(File("classes.dex"), Opcodes.getDefault())

一个 DexBackedDexFile 实例代表一个dex文件,通过 DexBackedDexFile 我们可以访问到所有Class

1
2
3
//所有classes
    val classes = dexBackedDexFile.classes
    println(classes.size)

2.ClassDef

ClassDef 代表一个类,我们可以通过 DexBackedDexFile 实例来获取一个类,也可以自行创建一个类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//从DexBackedDexFile获取ClassDef
val zzz = dexBackedDexFile.classes.find {
        //type 代表class name
        it.type == "Lcom.xxx.yyy.zzz;"
    }
    //父类
    println(zzz?.superclass)
复制代码
val type = "Lcom.xxx.yyy.zzz;"
    val defClass = ImmutableClassDef(
        //类名
        type,
        AccessFlags.PUBLIC.value,
        //父类
        "Ljava/lang/Object;",
        //继承的接口
        null,
        //源文件
        null,
        //注解
        null,
        //静态成员变量
        listOf(
            ImmutableField(
                //所属类
                type,
                //成员名称
                "field1",
                //成员类型
                "I",
                AccessFlags.PRIVATE.value,
                //初始化值
                ImmutableIntEncodedValue(666),
                null,
                null
            )
        ),
        //成员变量
        null,
        //直系方法(自定义)
        null,
        //非直系(继承重载等)
        null
    )

3.修改某类的方法返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fun main() {
 
    val dexBackedDexFile = DexFileFactory.loadDexFile(File("classes.dex"), Opcodes.getDefault())
 
    val type = javaClassoType("com.uzmap.pkg.EntranceActivity")
 
    //定义一个DexRewriter
    val reWriter = DexRewriter(object : RewriterModule() {
        //修改方法
        override fun getMethodRewriter(rewriters: Rewriters): Rewriter<Method> = Rewriter {
            //判断类名和方法名
            if (it.definingClass == type && it.name == "isFromNativeSDK") {
                //返回修改后的方法
                return@Rewriter MethodWrapper(it).apply {
                    //修改方法的实现
                    methodImplementation = ImmutableMethodImplementation(
                        //寄存器个数
                        1, listOf(
                            ImmutableInstruction11n(
                                //指令
                                Opcode.CONST_4,
                                //寄存器
                                0,
                                //
                                1),
                            //return p0
                            ImmutableInstruction11x(
                                Opcode.RETURN,
                                0
                            )
                        ), null, null
                    )
                }.build()
            }
            it
        }
    })
 
    val newDexFile = reWriter.dexFileRewriter.rewrite(dexBackedDexFile)
    DexFileFactory.writeDexFile("new.dex", newDexFile)
}
 
fun javaClassToType(clz: String): String = "L${clz.replace(".", "/")};"

上面我们修改了 isFromNativeSDK 方法使其返回true,这里有关于寄存器的知识:

 

当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。如果一个方法有2个参数,5个寄存器(v0-v4),那么参数将置于最后2个寄存器——v3和v4。 非静态方法中的第一个参数总是调用该方法的对象。

 

这里指定了一个寄存器,就是第一个参数this——代表EntranceActivity实例。

 

修改后的smali代码:

1
2
3
4
5
6
7
8
# virtual methods
.method protected final isFromNativeSDK()Z
    .locals 0
 
    const/4 p0, 0x1
 
    return p0
.end method

​ 寄存器都是32位的,能够支持任何类型。64位类型(Long和Double型)用2个寄存器表示。 有两种方式指定一个方法中有多少寄存器是可用的。.registers指令指定了方法中寄存器的总数。.locals指令表明了方法中非参寄存器的数量。

 

MethodWrapper 类的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.immutable.ImmutableMethod
 
class MethodWrapper(method: Method) {
 
    var definingClass = method.definingClass
    var name = method.name
    var parameters = method.parameters
    var returnType = method.returnType
    var accessFlags = method.accessFlags
    var annotations = method.annotations
    var hiddenApiRestrictions = method.hiddenApiRestrictions
    var methodImplementation = method.implementation
 
    fun build(): ImmutableMethod = ImmutableMethod(
        definingClass,
        name,
        parameters,
        returnType,
        accessFlags,
        annotations,
        hiddenApiRestrictions,
        methodImplementation
    )
}

总结

smali就相当于汇编一样,是比较底层的语言。研究它无非是对于安卓逆向或者事安卓加固有些许帮助,并可以通过修改dex文件来获得一些特殊功能。另外,研究它也可以增加自己反编译的知识,毕竟翻译这个还是很需要时间的,一些巨人已经将自己的成果发出来供大家使用,我们只是站在巨人的肩膀上。天下没有绝对的安全。

参考


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞4
打赏
分享
最新回复 (3)
雪    币: 3730
活跃值: (3961)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 2021-2-22 08:58
2
0
学习了。。
雪    币: 62
活跃值: (572)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
万里星河 2021-2-23 02:47
3
0
为啥没有java版本的代码
雪    币: 36
活跃值: (288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
弄好了 2021-3-24 13:27
4
0
比较详细的讲解
游客
登录 | 注册 方可回帖
返回