smali語法以及通過org.jf.dexlib2工具來實現smali插入dex文件中


前言

需要將特定的smali代碼插入到dex文件中起到特殊作用,但是對於smali語法一知半解,這次來總結一下,並介紹如何使用工具來講smali代碼插入dex文件中。

Smali語法

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

源碼:

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代碼:

其中#后面為注釋

# 類名 .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實例

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

一個 DexBackedDexFile 實例代表一個dex文件,通過 DexBackedDexFile 我們可以訪問到所有Class

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

2.ClassDef

ClassDef 代表一個類,我們可以通過 DexBackedDexFile 實例來獲取一個類,也可以自行創建一個類.

//從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.修改某類的方法返回值

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代碼:

# 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 類的定義:

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文件來獲得一些特殊功能。另外,研究它也可以增加自己反編譯的知識,畢竟翻譯這個還是很需要時間的,一些巨人已經將自己的成果發出來供大家使用,我們只是站在巨人的肩膀上。天下沒有絕對的安全。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM