美文网首页
自娱自乐 -- 自写apk进行smali分析

自娱自乐 -- 自写apk进行smali分析

作者: 似羽 | 来源:发表于2018-04-26 16:11 被阅读0次

    0x01 前言

    演示APK: 自写的一个简单CrackMe
    测试平台: Win7 64
    反编译工具: APK改之理3.5

    懵懵懂懂的第一次接触apk调试,有太多不懂与自我理解的偏差,还望大家指正

    为了不对上千行的代码产生恐惧,决定新人还是自写小代码进行分析

    先看下AndroidManifest.xml,因为是自写,代码不多

    <?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.a3st.demo">
        <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
            <activity android:name="com.a3st.demo.MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
            <meta-data android:name="android.support.VERSION" android:value="26.1.0"/>
            <meta-data android:name="android.arch.lifecycle.VERSION" android:value="27.0.0-SNAPSHOT"/>
        </application>
    </manifest>
    

    从中可以看出:

    1. 只有一个主活动.MainActivity
    2. 包名为com.a3st.demo

    先贴上此文章需分析的MainActivity的smali源码

    .class public Lcom/a3st/demo/MainActivity;
    .super Landroid/support/v7/app/AppCompatActivity;
    .source "MainActivity.java"
    
    # interfaces
    .implements Landroid/view/View$OnClickListener;
    
    # instance fields
    .field private btnRegister:Landroid/widget/Button;
    
    .field private editPassword:Landroid/widget/EditText;
    
    .field private editUsername:Landroid/widget/EditText;
    
    
     
    # direct methods
    .method public constructor <init>()V
        .locals 0
    
        .prologue
        .line 12
        invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V
    
        return-void
    .end method
    
    .method private static reg(Ljava/lang/String;)Ljava/lang/String;
        .locals 10
        .param p0, "_username"    # Ljava/lang/String;
    
        .prologue
        .line 31
        invoke-virtual {p0}, Ljava/lang/String;->toCharArray()[C
    
        move-result-object v4
    
        .line 32
        .local v4, "username":[C
        array-length v1, v4
    
        .line 33
        .local v1, "len":I
        const-wide/16 v2, 0x0
    
        .line 34
        .local v2, "result":J
        const/4 v0, 0x0
    
        .local v0, "i":I
        :goto_0
        if-ge v0, v1, :cond_1
    
        .line 35
        aget-char v5, v4, v0
    
        const/16 v6, 0x61
    
        if-lt v5, v6, :cond_0
    
        .line 36
        aget-char v5, v4, v0
    
        add-int/lit8 v5, v5, -0x20
    
        int-to-char v5, v5
    
        aput-char v5, v4, v0
    
        .line 38
        :cond_0
        aget-char v5, v4, v0
    
        int-to-long v6, v5
    
        add-long/2addr v2, v6
    
        .line 34
        add-int/lit8 v0, v0, 0x1
    
        goto :goto_0
    
        .line 40
        :cond_1
        const-wide/16 v6, 0x2
    
        mul-long/2addr v6, v2
    
        const-wide/16 v8, -0x1
    
        and-long v2, v6, v8
    
        .line 41
        invoke-static {v2, v3}, Ljava/lang/Long;->toHexString(J)Ljava/lang/String;
    
        move-result-object v5
    
        invoke-virtual {v5}, Ljava/lang/String;->toUpperCase()Ljava/lang/String;
    
        move-result-object v5
    
        return-object v5
    .end method
    
    
    # virtual methods
    .method public onClick(Landroid/view/View;)V
        .locals 7
        .param p1, "v"    # Landroid/view/View;
    
        .prologue
        const/4 v6, 0x0
    
        .line 46
        invoke-virtual {p1}, Landroid/view/View;->getId()I
    
        move-result v4
    
        packed-switch v4, :pswitch_data_0
    
        .line 84
        :goto_0
        return-void
    
        .line 48
        :pswitch_0
        iget-object v4, p0, Lcom/a3st/demo/MainActivity;->editUsername:Landroid/widget/EditText;
    
        invoke-virtual {v4}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    
        move-result-object v4
    
        invoke-virtual {v4}, Ljava/lang/Object;->toString()Ljava/lang/String;
    
        move-result-object v3
    
        .line 49
        .local v3, "username":Ljava/lang/String;
        iget-object v4, p0, Lcom/a3st/demo/MainActivity;->editPassword:Landroid/widget/EditText;
    
        invoke-virtual {v4}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    
        move-result-object v4
    
        invoke-virtual {v4}, Ljava/lang/Object;->toString()Ljava/lang/String;
    
        move-result-object v1
    
        .line 50
        .local v1, "password":Ljava/lang/String;
        invoke-virtual {v3}, Ljava/lang/String;->length()I
    
        move-result v4
    
        const/4 v5, 0x4
    
        if-lt v4, v5, :cond_0
    
        invoke-virtual {v3}, Ljava/lang/String;->length()I
    
        move-result v4
    
        const/16 v5, 0xc
    
        if-le v4, v5, :cond_1
    
        .line 51
        :cond_0
        const-string v4, "\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a \u6216 \u8d85\u8fc7\u6700\u5927\u957f\u5ea6"
    
        invoke-static {p0, v4, v6}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
    
        move-result-object v4
    
        invoke-virtual {v4}, Landroid/widget/Toast;->show()V
    
        goto :goto_0
    
        .line 54
        :cond_1
        invoke-static {v3}, Lcom/a3st/demo/MainActivity;->reg(Ljava/lang/String;)Ljava/lang/String;
    
        move-result-object v2
    
        .line 55
        .local v2, "result":Ljava/lang/String;
        invoke-virtual {v2, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
    
        move-result v4
    
        if-eqz v4, :cond_2
    
        .line 56
        new-instance v0, Landroid/app/AlertDialog$Builder;
    
        invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
    
        .line 57
        .local v0, "msg":Landroid/app/AlertDialog$Builder;
        const-string v4, "\u4fe1\u606f\u6846:"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 58
        const-string v4, "\u6ce8\u518c\u6210\u529f!"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 59
        invoke-virtual {v0, v6}, Landroid/app/AlertDialog$Builder;->setCancelable(Z)Landroid/app/AlertDialog$Builder;
    
        .line 60
        const-string v4, "OK"
    
        new-instance v5, Lcom/a3st/demo/MainActivity$1;
    
        invoke-direct {v5, p0}, Lcom/a3st/demo/MainActivity$1;-><init>(Lcom/a3st/demo/MainActivity;)V
    
        invoke-virtual {v0, v4, v5}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;
    
        .line 66
        invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog;
    
        goto :goto_0
    
        .line 68
        .end local v0    # "msg":Landroid/app/AlertDialog$Builder;
        :cond_2
        new-instance v0, Landroid/app/AlertDialog$Builder;
    
        invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
    
        .line 69
        .restart local v0    # "msg":Landroid/app/AlertDialog$Builder;
        const-string v4, "\u4fe1\u606f\u6846:"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 70
        const-string v4, "\u6ce8\u518c\u5931\u8d25!"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 71
        invoke-virtual {v0, v6}, Landroid/app/AlertDialog$Builder;->setCancelable(Z)Landroid/app/AlertDialog$Builder;
    
        .line 72
        const-string v4, "OK"
    
        new-instance v5, Lcom/a3st/demo/MainActivity$2;
    
        invoke-direct {v5, p0}, Lcom/a3st/demo/MainActivity$2;-><init>(Lcom/a3st/demo/MainActivity;)V
    
        invoke-virtual {v0, v4, v5}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;
    
        .line 78
        invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog;
    
        goto :goto_0
    
        .line 46
        :pswitch_data_0
        .packed-switch 0x7f070021
            :pswitch_0
        .end packed-switch
    .end method
    
    .method protected onCreate(Landroid/os/Bundle;)V
        .locals 1
        .param p1, "savedInstanceState"    # Landroid/os/Bundle;
    
        .prologue
        .line 20
        invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
    
        .line 21
        const v0, 0x7f09001b
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->setContentView(I)V
    
        .line 23
        const v0, 0x7f070021
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->findViewById(I)Landroid/view/View;
    
        move-result-object v0
    
        check-cast v0, Landroid/widget/Button;
    
        iput-object v0, p0, Lcom/a3st/demo/MainActivity;->btnRegister:Landroid/widget/Button;
    
        .line 24
        iget-object v0, p0, Lcom/a3st/demo/MainActivity;->btnRegister:Landroid/widget/Button;
    
        invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
    
        .line 26
        const v0, 0x7f07002f
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->findViewById(I)Landroid/view/View;
    
        move-result-object v0
    
        check-cast v0, Landroid/widget/EditText;
    
        iput-object v0, p0, Lcom/a3st/demo/MainActivity;->editUsername:Landroid/widget/EditText;
    
        .line 27
        const v0, 0x7f07002d
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->findViewById(I)Landroid/view/View;
    
        move-result-object v0
    
        check-cast v0, Landroid/widget/EditText;
    
        iput-object v0, p0, Lcom/a3st/demo/MainActivity;->editPassword:Landroid/widget/EditText;
    
        .line 28
        return-void
    .end method
    
    

    0x02 包名与类

    先来看看smali源码的前几行

    // 类名为public MainActivity,包名为com.a3st.demo
    .class public Lcom/a3st/demo/MainActivity;
    // 此类的父类为AppCompatActivity
    .super Landroid/support/v7/app/AppCompatActivity;
    // 文件名称
    .source "MainActivity.java"
    
    // 此类调用的所有接口: View.OnClickListener
    // #号在smali中表示注释
    # interfaces
    .implements Landroid/view/View$OnClickListener;
    
    // 此类的成员属性有三个(Java标准说法是字段,这里注明一下)
    # instance fields
    .field private btnRegister:Landroid/widget/Button;
    
    .field private editPassword:Landroid/widget/EditText;
    
    .field private editUsername:Landroid/widget/EditText;
    

    整理得到:

    package com.a3st.demo;
    public MainActivity extends AppCompatActivity implements View.OnClickListener{
        private Button btnRegister;
        private EditText editPassword;
        private EditText editUsername;
    }
    

    0x03 构造函数

    下来就是方法了, 看下这段:

    // 注释direct methods表示下列的方法都在本类中,直译就是直接方法
    # direct methods
    // 可能是构造函数。本人没写构造函数,这个是编译时自动生成的,所以我也不好解释init是啥
    .method public constructor <init>()V  // @1
        // 局部变量个数0
        .locals 0
    
        // 代码块开始
        .prologue
        // 下面代码在Java源码中的行数: 第12行
        .line 12
        // 调用函数: AppCompatActivity()
        invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V // @2
    
        // 返回 void
        return-void
    .end method
    
    @1: .method public constructor <init>()V

    .method 表明这是一个方法
    public 访问权限为公共
    constructor 函数名称
    constructor<init> 看到一些文章把init当成函数名称
    () 表明此函数无参,有参的话,括号内显示的是参数的数据类型
    V 表示返回值。 V表示void

    @2: invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

    invoke-direct 调用直接函数
    {p0} 对应Landroid/support/v7/app/AppCompatActivity
    从上面得出源代码: AppCompatActivity()

    整理下:
    从行号与Java源码中的对比 + 其他文章来看,本人猜测为:
    该类的不带参数缺省的构造方法

    public {
        // 可能是因为extends AppCompatActivity,会自动调用父类的构造函数
        AppCompatActivity()
    }
    

    0x04 Android入口函数

    注意: 以后的smali代码,如果之前已解释过,后面将不会再做解释,除非特殊说明

    //  protected void onCreate(Bundle savedInstanceState)
    .method protected onCreate(Landroid/os/Bundle;)V
        // 局部变量总数: 1。 它的变量名默认为v0
        .locals 1
        // 参数变量名为p1, 参数名称为savedInstanceState
        // 参数名称后有个类据类型,但别忘了前面有个#,说明只是个注释
        .param p1, "savedInstanceState"    # Landroid/os/Bundle;
    
        .prologue
        .line 20
        // 调用父类的onCreate
        invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
    
        .line 21
        // 给v0赋值0x7f09ff1b
        const v0, 0x7f09001b  // @3
    
        // 调用方法
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->setContentView(I)V
    
        .line 23
        const v0, 0x7f070021
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->findViewById(I)Landroid/view/View;
    
        // 把上条指令的返回值(对象)赋值给v0
        move-result-object v0
    
        // 把v0的数据类型强制转换成Button
        check-cast v0, Landroid/widget/Button;
    
        // 把v0的值赋值给p0.btnRegister,也就是this.btnRegister
        iput-object v0, p0, Lcom/a3st/demo/MainActivity;->btnRegister:Landroid/widget/Button;
    
        .line 24
        // 把this.btnRegister的值赋值给v0
        iget-object v0, p0, Lcom/a3st/demo/MainActivity;->btnRegister:Landroid/widget/Button;
    
         // btnRegister.setOnClickListener(this)
        invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
    
        .line 26
        const v0, 0x7f07002f
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->findViewById(I)Landroid/view/View;
    
        move-result-object v0
    
        check-cast v0, Landroid/widget/EditText;
    
        iput-object v0, p0, Lcom/a3st/demo/MainActivity;->editUsername:Landroid/widget/EditText;
    
        .line 27
        const v0, 0x7f07002d
    
        invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->findViewById(I)Landroid/view/View;
    
        move-result-object v0
    
        check-cast v0, Landroid/widget/EditText;
    
        iput-object v0, p0, Lcom/a3st/demo/MainActivity;->editPassword:Landroid/widget/EditText;
    
        .line 28
        return-void
    .end method
    
    @3 const v0, 0x7f09001b

    0x7f09001b是一个资源id,当创建资源文件,创建控件id等时会自动生成
    当编译成apk后,通过反编译发现这些资源id存放在:
    项目/app/src/main/res/values/public.xml
    或 项目/app/src/main/java 目录下的R$layout.smali文件中

    如内容: <public type="layout" name="layout_main" id="0x7f09001b" />
    可以看出是layout.main.xml的资源id

    整理下:
    让我感到头晕的是:
    invoke-virtual{p0, v0}
    invoke-virtual{v0, p0}

    个人理解, 举例:
    invoke-virtual {p0, v0}, Lcom/a3st/demo/MainActivity;->setContentView(I)V

    我把大括号后的Lcom/a3st/demo/MainActivity;->setContentView(I)V分成三段:

    1. Lcom/a3st/demo/MainActivity 方法所在的类
    2. setContentView(I) 方法名称及参数
    3. V 方法的返回类型

    大括号的第一个参数p0表示实例对象,同时可以从第一段中进行验证
    如第一个参数为p0,而第一段为本类类名,则100%为本类的实例对象this
    如第一个参数为其它寄存器,而第一段也不是本类类名,则100%为其它类的实例对象

    从大括号的第二个参数开始分别表示第二段中的各个参数,这里只有一个int型参数
    得出源码: p0.setContentView(v0);

    @Override   // 此@Override在接下来分析onCreate函数时会讲到
    protected void onCreate(Bundle savedInstanceState) {
        int v0;
        super.onCreate(savedInstanceState);
        setContentView(0x7f09001b); // 布局id
        
        // 小提示: 上面空了一行,从smali中看到上一行是line21,而此行代码在line23...少了个22,表示空行
        this.btnRegister = (Button)findViewById(0x7f070021);
        this.btnRegister.setOnClickListener(this);
        
        // 下面的代码差不多,不全写了
        /* 
            另外说下,这些还原的代码并不是从源码中直接抄来的,而是从smali中分析得来
            如: this.btnRegister = (Button)findViewById(0x7f070021);
            在源码中是: btnRegister = findViewById(R.id.btn_register);
            源码中并没有进行强制转换,是编译时自动转成Button的
        */
    }
    

    0x05 按钮事件

    // virtual methods表示下面的所有方法都为重载方法
    # virtual methods
    .method public onClick(Landroid/view/View;)V
        .locals 7
        .param p1, "v"    # Landroid/view/View;
    
        .prologue
        const/4 v6, 0x0
    
        .line 46
        // 如0x04节所说,第一个参数为实例对象,所以代码为v.getId();
        invoke-virtual {p1}, Landroid/view/View;->getId()I
    
        move-result v4
    
        // switch开始
        // :pswitch_data_0 这是一个标签,用于表示从此开到标签处为所有case的区域 ,而标签后为case分支流程表
        // 此指令意思: 在case分支流程表中查看是否有v4的值,如有则进入case代码块,没有则退出或进入default代码块
        packed-switch v4, :pswitch_data_0
    
        // 如果defaule代码块中有代码的话,在这里会显示
        // default代码块写在此处
        .line 84
        // 所有case分支的出口
        :goto_0
        return-void
    
        .line 48
        // 从下面的case分支流程表看出,这里是case register按钮id的代码块
        :pswitch_0
        iget-object v4, p0, Lcom/a3st/demo/MainActivity;->editUsername:Landroid/widget/EditText;
    
        invoke-virtual {v4}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    
        move-result-object v4
    
        invoke-virtual {v4}, Ljava/lang/Object;->toString()Ljava/lang/String;
    
        move-result-object v3
    
        .line 49
        // 表明变量作用域。local到end local之间
        // 并明确v3的变量名称为String username
        // 也就是String username = v3,但它不是出现在第49行,而是在上一行(48行)代码中,这是smali中需注意的一点
        .local v3, "username":Ljava/lang/String;
        iget-object v4, p0, Lcom/a3st/demo/MainActivity;->editPassword:Landroid/widget/EditText;
    
        invoke-virtual {v4}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
    
        move-result-object v4
    
        invoke-virtual {v4}, Ljava/lang/Object;->toString()Ljava/lang/String;
    
        move-result-object v1
    
        .line 50
        .local v1, "password":Ljava/lang/String;
        invoke-virtual {v3}, Ljava/lang/String;->length()I
    
        move-result v4
    
        const/4 v5, 0x4
    
        // 如果v4<v5的话,则跳到cond_0标签处
        if-lt v4, v5, :cond_0
    
        invoke-virtual {v3}, Ljava/lang/String;->length()I
    
        move-result v4
    
        const/16 v5, 0xc
    
        // 如果v4<=v5的话,则跳到cond_1标签处
        if-le v4, v5, :cond_1
    
        .line 51
        // 从utf8编码的字符串中可以看出这是失败信息
        :cond_0
        const-string v4, "\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a \u6216 \u8d85\u8fc7\u6700\u5927\u957f\u5ea6"
    
        invoke-static {p0, v4, v6}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
    
        move-result-object v4
    
        invoke-virtual {v4}, Landroid/widget/Toast;->show()V
    
        // 跳到所有case分支的出口处
        goto :goto_0
    
        .line 54
        :cond_1
        // 因为是invoke-static,大括号内的参数对应方法中的参数
        // 得出源码为: MainActivity.reg(username);
        invoke-static {v3}, Lcom/a3st/demo/MainActivity;->reg(Ljava/lang/String;)Ljava/lang/String;
    
        move-result-object v2
    
        .line 55
        .local v2, "result":Ljava/lang/String;
        invoke-virtual {v2, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
    
        move-result v4
    
        // v4等于0时,则跳到cond_2标签处
        if-eqz v4, :cond_2
    
        // 从utf8编码的字符串看出这里为注册成功
        .line 56
        new-instance v0, Landroid/app/AlertDialog$Builder;
    
        invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
    
        .line 57
        .local v0, "msg":Landroid/app/AlertDialog$Builder;
        const-string v4, "\u4fe1\u606f\u6846:"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 58
        const-string v4, "\u6ce8\u518c\u6210\u529f!"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 59
        invoke-virtual {v0, v6}, Landroid/app/AlertDialog$Builder;->setCancelable(Z)Landroid/app/AlertDialog$Builder;
    
        .line 60
        const-string v4, "OK"
    
        new-instance v5, Lcom/a3st/demo/MainActivity$1;
    
        invoke-direct {v5, p0}, Lcom/a3st/demo/MainActivity$1;-><init>(Lcom/a3st/demo/MainActivity;)V
    
        invoke-virtual {v0, v4, v5}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;
    
        .line 66
        invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog;
    
        goto :goto_0
    
        .line 68
        .end local v0    # "msg":Landroid/app/AlertDialog$Builder;
        :cond_2
        new-instance v0, Landroid/app/AlertDialog$Builder;
    
        invoke-direct {v0, p0}, Landroid/app/AlertDialog$Builder;-><init>(Landroid/content/Context;)V
    
        .line 69
        .restart local v0    # "msg":Landroid/app/AlertDialog$Builder;
        const-string v4, "\u4fe1\u606f\u6846:"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 70
        const-string v4, "\u6ce8\u518c\u5931\u8d25!"
    
        invoke-virtual {v0, v4}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder;
    
        .line 71
        invoke-virtual {v0, v6}, Landroid/app/AlertDialog$Builder;->setCancelable(Z)Landroid/app/AlertDialog$Builder;
    
        .line 72
        const-string v4, "OK"
    
        new-instance v5, Lcom/a3st/demo/MainActivity$2;
    
        invoke-direct {v5, p0}, Lcom/a3st/demo/MainActivity$2;-><init>(Lcom/a3st/demo/MainActivity;)V
    
        invoke-virtual {v0, v4, v5}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder;
    
        .line 78
        invoke-virtual {v0}, Landroid/app/AlertDialog$Builder;->show()Landroid/app/AlertDialog;
    
        goto :goto_0
    
        .line 46
        :pswitch_data_0
        // case分支流程表
        // 0x7f070021 表示递增的初始值
        .packed-switch 0x7f070021
             // pswitch_0指的是0x7f070021(递增的初始值)
             // 0x7f070021是一个按钮id
            :pswitch_0
        .end packed-switch
    .end method
    
    @4 switch说明

    switch格式:
    // 在case分支流程表中查看是否有v4的值,如有则进入case代码块,没有则退出或进入default代码块
    packed-switch v4, :pswitch_data_0
    // defaule代码块
    ......
    // 所有case分支出口
    :goto_0
    return-void
    //各个case代码块
    ......
    // case分支流程表
    :pswitch_data_0
    // 因为是packed-switch,所以有个递增的初始值(0x7F070021)
    .packed-switch 0x7f070021
    // 各个case代码块位置
    :pswitch_0
    :pswitch_1
    :pswitch_2
    // switch结束
    .end packed-switch

    在smali中,有packed-switch和sparse-switch二种switch语法(其它的没见到过)

    packed-switch 表示紧凑的switch,它的分支流程表以一个递增初始值开始,每个case分支位置渐进递增。常见的就是case 1, case 2, case 3...

    sparse-switch 表示稀疏的switch,它的格式为(简单写下,与packed-switch一样,只是名字变了):
    sparse-switch v1, :sswitch_data_0
    default: code
    ....
    // 所有case分支出口
    :goto_0
    return-void
    // 各个case代码块
    :sswitch_0
    ...
    // case分支流程表
    .sparse-switch
    0x5ec5cc94 -> :sswitch_0
    0x5ec5cc96 -> :sswitch_1
    0x5ec5cc98 -> :sswitch_2
    0x5ec5cc99 -> :sswitch_3
    0x5ec5ccb0 -> :sswitch_4
    .end sparse-switch

    可以看出,在分支流程表中,没有了递增初始值
    与之取代的是各个case位置都指定了值,可以看出这此值并不是有序的,所以不能用packed-switch

    这里就只逆个switch出来吧,其它与前面几节差不多,重点还是讲switch语句

    @Override
    public void onClick(View v) {
        switch(v.getId()) {
            case 0x7f070021:
                // 代码
                String username = editUsername.getText().toString();
                ......
                break;
        }
    }
    

    0x06 算法函数

    此apk的关键函数

    // private static String reg(_username)
    .method private static reg(Ljava/lang/String;)Ljava/lang/String;
        .locals 10
        .param p0, "_username"    # Ljava/lang/String;
    
        .prologue
        .line 31
        // char[] username = _username.toCharArray();
        // 返回值[C表示char[]
        invoke-virtual {p0}, Ljava/lang/String;->toCharArray()[C
    
        move-result-object v4
    
        .line 32
        .local v4, "username":[C
        // 取数组长度赋值给v1
        // 看到第33行中的第一条指令现在大家都知道怎么逆出源码了吧:
        // int len = username.length;
        array-length v1, v4
    
        .line 33
        .local v1, "len":I
        // 把0在放到v2中,如超出放到v3中
        // winde可能就是把0扩展long型,这时v2,v3都为0
        // long result = 0
        const-wide/16 v2, 0x0
    
        .line 34
        .local v2, "result":J
        // i = 0
        const/4 v0, 0x0
    
        .local v0, "i":I
        // 循环开始
        :goto_0
        // 当 i>= len 时,跳出循环
        if-ge v0, v1, :cond_1
    
        .line 35
        // 读取数组元素: v5 = username[i]
        aget-char v5, v4, v0
    
        const/16 v6, 0x61
    
        // 如果 v5<'a'',则跳到cond_0标签处
        if-lt v5, v6, :cond_0
    
        .line 36
        aget-char v5, v4, v0
    
        // v5 -= 0x20
        add-int/lit8 v5, v5, -0x20
    
        // 将整形转换成字符型
        int-to-char v5, v5
    
        // 把结果保存到username[i]中
        aput-char v5, v4, v0
    
        .line 38
        :cond_0
        aget-char v5, v4, v0
    
        // 转换成long型
        int-to-long v6, v5
    
        // result += username[i]
        add-long/2addr v2, v6
    
        .line 34
        // i++
        add-int/lit8 v0, v0, 0x1
    
        // 继续循环
        goto :goto_0
    
        .line 40
        :cond_1
        const-wide/16 v6, 0x2
    
        // v6 *= result
        mul-long/2addr v6, v2
    
        const-wide/16 v8, -0x1
    
        // result = v6 & 0xFFFFFFFF
        and-long v2, v6, v8
    
        .line 41
        // 注意,这里不是二个参数,而是一个,因为是long型
        // 前面(const-wide/16 v2, 0x0)时有详细说过
        // Long.toHexString(result);
        invoke-static {v2, v3}, Ljava/lang/Long;->toHexString(J)Ljava/lang/String;
    
        move-result-object v5
    
        // v5.toUpperCase();
        invoke-virtual {v5}, Ljava/lang/String;->toUpperCase()Ljava/lang/String;
    
        move-result-object v5
    
        // 返回一个字符串
        return-object v5
    .end method
    

    整理下:
    此节我是琢行分析的,逆出后与源码对比下,完美

    private static String reg(_username) {
        char[] username = _username.toCharArray();  // .line31
        int len = usernema.length;                  // .line32
        long result = 0;                            // .line33
        for(int i = 0; i < len; i++) {              // .line34
            if(username[i] >= 'a') {                // .line35
                username[i] -= 0x20;                // .line36
            }                                       // .line37,相当于一个空行
            result += username[i];                  // .line38
        }                                           // .line39
        result = (2 * result) & 0xFFFFFFFF;         // .line40
        return Long.toHexString(result).toUpperCase();  // .line41
    }
    

    0x07 修改函数

    既然使用CrackMe来进行演示,当然要有个CrackMe的样子
    修改方法有二种:

    1. 更改跳转语句的流程
    2. 修改关键函数reg的返回值

    本人用第二种方法进行修改:
    在0x05中节可能看到取password与reg函数的返回值进行比较
    本人就把0x06节中的第.line41行所有代码删除,并修改返回值为字符串123:

    .line 41
        const string v5, "\u0031\u0032\u0033"
    
        return-object v5
    

    对修改后的smali进行编译,修改后的结果如下


    图1

    相关文章

      网友评论

          本文标题:自娱自乐 -- 自写apk进行smali分析

          本文链接:https://www.haomeiwen.com/subject/fapelftx.html