Android逆向之初识smali语言

434 阅读4分钟

一、Android应用程序架构

  • 安卓应用程序使用JAVA语言编写。安卓的SDK工具负责将你编写的代码,用到的数据和资源文件编译进APK文件中。apk文件包含了一个安卓应用程序的所有内容,并且被安卓设备用来安装应用程序。
  • apk实际上就是一个标准的zip格式,修改后缀名,进行解压就可以看到内部结构。 image.png
  • 如上图所示,每个文件夹都有各自不同的作用。
    • assets文件夹
      • 保存一些额外的资源文件,如游戏的声音文件,字体文件等等,在代码中可以用AssetManager获取assets文件夹的资源。
    • lib文件夹
      • 存放用C/C++编写的,用NDK编译生成的so文件,供java端调用。
    • META-INF文件夹
      • 存放apk签名信息,用来保证apk包的完整性和系统安全。
      • 在IDE编译生成一个apk包时,会对里面所有的文件做一个校验计算,并把计算结果存放在META-INF文件夹内,apk在安装的时候,系统会按照同样的算法对apk包里面的文件做校验,如果结果与META-INF里面的值不一样,系统就不会安装这个apk,这就保证了apk包里的文件不能被随意替换。
    • res文件夹
      • 存放资源文件,包括icon,xml文件。
    • AndroidManifest.xml文件
      • 应用程序配置文件,每个应用都必须定义和包含的,它描述了应用的名字、版本、权限、引用的库文件等信息。
    • classes.dex文件
      • 传统Class文件是由一个Java源码文件生成的.Class文件,而Android是把所有Class文件进行合并优化,然后生成一个最终的class.dex文件。它包含APK的可执行代码,是分析Android软件时最常见的目标。
    • resources.arsc文件
      • 二进制资源文件,包括字符串等。

二、何为smali语言?

  • smali是将Android字节码用可阅读的字符串形式表现出来的一种语言,可以称之为Android字节码的反汇编语言。
  • 利用apktool或者Android Killer工具,反编classes.dex文件,就可以得到以smali为后缀的文件。
  • 通过对smali文件的解读可以获取源码的信息。 image.png image.png

三、如何将Java文件转换成smali文件?

  1. 打开idea,依次点击菜单栏上的File->Settings->Plugins->Marketplace,下载java2smali插件。下载好后重启idea。
  2. 新建一个class文件,简单写一个demo类。
  3. 鼠标放到demo类中,依次点击菜单栏上的Build->Compile to Smali,则会自动生成一个.smali文件。

四、如何看懂smali代码?

  • 用Java简单写了一个demo类,Demo.class文件
package com.crawler;

public class Demo {

    private static String name;

    public static void main(String[] args) {
        Demo demo = new Demo();
        Integer calc = demo.calc(1);
        System.out.println(calc);
    }

    private Integer calc(Integer i){
        ++i;
        return i;
    }
}
  • 自动转换生成的Demo.smali文件
.class public Lcom/crawler/Demo;
.super Ljava/lang/Object;
.source "Demo.java"


# static fields
.field private static name:Ljava/lang/String;

# direct methods
.method public constructor <init>()V
    .registers 1

    .prologue
    .line 3
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

.method private calc(Ljava/lang/Integer;)Ljava/lang/Integer;
    .registers 3
    .param p1, "i"    # Ljava/lang/Integer;

    .prologue
    .line 12
    invoke-virtual {p1}, Ljava/lang/Integer;->intValue()I

    move-result v0

    add-int/lit8 v0, v0, 0x1

    invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object p1

    .line 13
    return-object p1
.end method

.method public static main([Ljava/lang/String;)V
    .registers 4
    .param p0, "args"    # [Ljava/lang/String;

    .prologue
    .line 6
    new-instance v1, Lcom/crawler/Demo;

    invoke-direct {v1}, Lcom/crawler/Demo;-><init>()V

    .line 7
    .local v1, "demo":Lcom/crawler/Demo;
    const/4 v2, 0x1

    invoke-static {v2}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v2

    invoke-direct {v1, v2}, Lcom/crawler/Demo;->calc(Ljava/lang/Integer;)Ljava/lang/Integer;

    move-result-object v0

    .line 8
    .local v0, "calc":Ljava/lang/Integer;
    sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;

    invoke-virtual {v2, v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V

    .line 9
    return-void
.end method
  • smali语言基础语法
    • 示例1: .class关键字
          .class public Lcom/crawler/Demo;
      
      • 这是一个公开类,在com.crawler包下的Demo类。
      • L 表示类
    • 示例2: .super关键字
          .super Ljava/lang/Object;
      
      • 父类是Object,所有的类都继承Object类。
    • 示例3: .source关键字
          .source "Demo.java"
      
      • 当前类的源文件名。
    • 示例4: .method关键字和.end method关键字
          # direct methods
          .method public constructor <init>()V
              .registers 1
      
              .prologue
              .line 3
              invoke-direct {p0}, Ljava/lang/Object;-><init>()V
      
              return-void
          .end method
      
      • 这是一个无参构造方法。
      • 每个类都会有一个构造方法,虽然在java中省略了。
        public Demo(){
            super();
        }
        
      • .method开始,.end method结束。
      • V 表示void。
      • invoke-direct 表示调用方法。其后{}表示传入的参数。
      • p0 在构造方法中表示this,指一个对象;在非静态方法表示this;在静态方法中表示传进来的参数。
    • 示例5: .registers关键字
        .registers 1  
      
      • 表示寄存器有1个,寄存器是存储数据的,每个寄存器的大小都是32位。
    • 示例6: .prologue关键字
        .prologue
      
      • 代表函数的开始位置,其上为类似.registers的关键字,其下为函数逻辑。
    • 示例7: .line N关键字
        .line 3  
      
      • 代表还原成java代码在源文件第3行。
    • 示例8: .field关键字
          .field private static name:Ljava/lang/String;
      
      • 私有静态字段name,字符串类型。