2016年9月1日星期四

ANDROID 逆向学习笔记 (一)- smali 简单介绍


由于 Web 安全相对来二进制来说更容易上手,最早的时候选择了相对简单的路。随着接触的时间变长,感觉局限于 Web 而抵触二进制实在狭隘,同时也是生活的百无聊赖给了充足的时间,开始尝试学一些新的事物。


在此之前没有接触过安卓或者是 Java 方面的内容,学起来难免有些迟钝,不过好在时间很多~

OK,闲扯到此结束,q

我是通过《Android 软件安全与逆向分析》这本书结合网上的资源(Drops/Pediy/博客)来开始学习的。书能够给予部分基础知识的详解,而网上的资源则大多偏向于实践,针对具体某个apk进行分析,结合起来学习加上自己亲身实践效果很好。不过有一点需要指出的是《Android 软件安全与逆向分析》中的内容并不完全正确,有描述不准确或是明显有错误的地方最好是多印证。

BTW,为了节省时间部分显而易见的地方只做简单的文字说明,可以通过自己实践或者是简单的搜索解决问题,除非必要关键的地方不上传步骤图。另外本文中操作系统为 OS X,部分终端指令与其他系统有所差异 。

接触安卓逆向最早了解到的是 smali,smali 具体定义是什么不过分追究了,先来看一下语法,下面代码用的是 安卓动态调试七种武器之长生剑 - Smali Instrumentation 中的内容。

package com.mzheng; # JAVA 源代码

public class MZLog {

    public static void Log(String tag, String msg)
    {
        Log.d(tag, msg);
    }

    public static void Log(Object someObj)
    {
        Log("mzheng", someObj.toString());
    }

    public static void Log(Object[] someObj)
    {
        Log("mzheng",Arrays.toString(someObj));
    }

}
对应的 smali 代码
.class public Lcom/mzheng/MZLog; # class的名字
.super Ljava/lang/Object;  #这个类继承的对象
.source "MZLog.java" # java的文件名


# direct methods #直接方法
.method public constructor ()V  #这是class的构造函数实现
    .registers 1 #这个方法所使用的寄存器数量

    .prologue  # prologue并没有什么用
    .line 7 #行号
    invoke-direct {p0}, Ljava/lang/Object;->()V #调用Object的构造方法,p0相当于"this" 指针
    return-void #返回空
.end method

.method public static Log(Ljava/lang/Object;)V # Log(Object)的方法实现
    .registers 3
    .param p0, "someObj"    # Ljava/lang/Object; 参数信息

    .prologue
    .line 16
    const-string v0, "mzheng"  #给v0赋值”mzheng”

    invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String; #调用toString()函数

    move-result-object v1 #将toString()的结果保存在v1

    invoke-static {v0, v1}, Lcom/mzheng/MZLog;->Log(Ljava/lang/String;Ljava/lang/String;)V #调用MZLog的另一个Log函数,参数是v0和v1

    .line 17
    return-void
.end method

.method public static Log(Ljava/lang/String;Ljava/lang/String;)V #Log(String, String)的方法实现
    .registers 2
    .param p0, "tag"    # Ljava/lang/String;
    .param p1, "msg"    # Ljava/lang/String;

    .prologue
    .line 11
    invoke-static {p0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I #调用android API里的Log函数实现Log功能

    .line 12
    return-void
.end method

.method public static Log([Ljava/lang/Object;)V #Log(Object[])函数实现 ‘[’符号是数组的意思
    .registers 3
    .param p0, "someObj"    # [Ljava/lang/Object;

    .prologue
    .line 21
    const-string v0, "mzheng"

    invoke-static {p0}, Ljava/util/Arrays;->toString([Ljava/lang/Object;)Ljava/lang/String;  #将Object数组转换为String

    move-result-object v1 #转换后的结果存在v1中

    invoke-static {v0, v1}, Lcom/mzheng/MZLog;->Log(Ljava/lang/String;Ljava/lang/String;)V #调用Log(String, String)函数

    .line 22
    return-void
.end method
可以看到 smali 相对于 JAVA 源代码来说虽然复杂了一点,但是比二进制文件逆向得到的汇编代码简单和直观的多,可读性还是很高的。smali 的具体语法和格式在上面已经很清楚了,更加详细的也在《Android 软件安全与逆向分析》中有所说明,这里不加赘述。

知道了 smali 是什么东西之后再来看看它是怎么来的。每个安卓 apk 其实都是一个压缩文件,解压缩之后可以看到有一个 classes.dex ,而反编译 classes.dex 即可得到很多 smali 文件。

反编译 classes.dex 的方法我这里使用的是 baksmali,反编译后会在当前目录生成一个 out 文件夹,smali 文件就在这个文件夹里。
https://bitbucket.org/JesusFreke/smali/downloads
baksmali classes.dex

另外一种比较傻瓜的方法是直接使用 apktool 反编译整个 apk 文件,反编译后会在当前目录生成 crackme 文件夹,下面有一个 smali 子文件夹就存放了 smali 文件。

brew install apktool
apktool d crakeme.apk

apktool 相对于 baksmali 来说就是很多种工具的结合体,把各个步骤结合起来了。推荐使用 baksmali 自己一步步动手操作,这样对各个流程能够有更清晰地认识,而 apktool 的好处是反编译得到的 xml 内容可以直接查看,通过解压缩得到的部分 xml 存在乱码的情况。