V4if 's Blogwebsite

Android Studio + ideasmali动态调试smali

发表于2016-09-21
默认

我更喜欢把题目取做另外一个名字:Debug apk step by step,这个是阿里聚安全一篇文章前言里写到的。

这年头,apk全都是加密啊,加壳啊,反调试啊,小伙伴们表示已经不能愉快的玩耍了。静态分析越来越不靠谱了,apktool、ApkIDE、jd-GUI、dex2jar等等等等已经无法满足大家的需求了。那么问题就来了,小伙伴们真正需要的是什么?好的,大家一起呐喊出你内心的欲望吧,我们的目标是 —— “debug apk step by step”
来自阿里聚安全《解密所有APP运行过程中的内部逻辑》

原文地址:解密所有APP运行过程中的内部逻辑

把文中的流程认认真真走下来之后,会发现整个环境的构建对于app的逆向来说简直强大的不要不要的,就像从手工时代的ida动态调试.dex,.so文件一下过渡到了新石器时代,我们可以把逆向出来的smali文件,即Dalvik虚拟机指令直接扔到编译器里面去一步一步Debug,下断点查看变量的值,得到动态执行的结果,只能说太强大!!!

我的理解smali就相当于win下的汇编,dex相当于win下的PE文件,也就是说dex是放在Dalvik上执行的文件格式,smali是为了方便程序员阅读而存在的语法格式。

最近在做关于app漏洞挖掘的工作,所以肯定绕不过逆向这个坎,拿到app第一件事就是逆向,得到smali代码或者java代码,然后放到sublime等等编辑器下面review。
先明确一下安卓应用src的逆向和res的反编译并不是在一起进行的,需要用到不同的工具,先来看一下在这之前的通常做法。。。

我准备了安卓逆向这本书里的一个crackme02.apk用于逆向

套路一:apktool、dex2jar + jd-jui
apktool用于反编译资源文件,可以看到代码也被以smali文件的格式被逆向出来,后面我们会用dex2jar结合jd-jui逆向出来java源码类似的结构

debug_apk_step_by_step

逆向java源码需要先将apk解压,linux用户解压命令推荐unar,支持各种格式的压缩,并且win上的zip格式解压没有乱码,其他解压缩命令经常会出现乱码,然后通过dex2jar将解压得到的classes.dex转换为.jar格式,最后从jd-jui里面打开我们刚才转换的classes_dex2jar.jar文件就可以看到被逆向出来的java源码了
debug_apk_step_by_step
套路二:jadx + Sublime Text
这也是我比较喜欢的一个方式,简单省事,一键解压,可以一步直接得到crackme02.apk的res资源文件和java源码文件,并且AndroidManifest.xml文件是可读的状态

jadx -d out crackme02.apk

将输出的out文件拖动到Sublime Text里面,整个结构一目了然

debug_apk_step_by_step
套路三: jeb
这个软件简直太强大,无奈它是收费的,我这里用的是一个破解版的,原版据说可以动态调试smali,支持smali转java,java转smali,还可以对我们分析的源码进行标注,方便查找

debug_apk_step_by_step
以上就是手工时代的review代码,然后就想怎么才能像程序开发人员一样去逆向调试没有源码,只有打包好的apk程序呢,接下来我们转向动态调试逆向的app。。。

工具下载

1、Android Studio
2、smalidea-0.03.zip
3、baksmali-2.1.3.jar

阿里聚安全《解密所有APP运行过程中的内部逻辑》这篇文章给出的方案是
apktool+eclipse
apktool+android studio
apktool+idea
但是这三种方案都没有走通,原因如下:

使用apktool反编译apk:
java ­jar apktool_2.0.0b9.jar d ­-d xxx.apk ­o out
加上­-d选项之后反编译出的文件后缀为.java,而不是.smali,每个.java文件立马都伪造成了一个类,语句全都是“a=0;”这一句,smali语句成为注释,小伙伴们自己看看打开就知道了,做这些都是为了后面欺骗idea、eclipse、android studio这些ide的

这就是软件的发展导致的。
这三种方案的基础都是先把通过apktool反编译apk将得到的smali源码文件伪装成java文件去欺骗编译器,然后在IDE里面remote连接模拟器进行动态调试。
本文测试的时候apktool的版本是2.2.0-dirty,但是这个版本已经不支持-d选项了,具体原因我们一会再说

debug_apk_step_by_step

文章中测试的版本是apktool_2.0.0b9.jar,所以我们也下下来测试一下
debug_apk_step_by_step

debug_apk_step_by_step

上面两张图中是分别测试的两个apk文件,第一张是crackme02.apk,这个是安卓逆向那本书里面提供的apk,即开发年代比较久,版本比较低,第二张是testSmali.apk,这个是我用Android Studio 2.1.3写的一个小的测试apk,可以看到报了一个Could not decode arsc file错误,这是由于编译与反编译总是在不停的对抗中升级,apktool的源码是公开的,所以说编译器就找apktool的漏洞,怎么样绕过反编译,apktool就不停的升级,所以解决办法就是update。。。update。。。update。。。
但是我们前面也说了当apktool升级到2.2.0-dirty版本之后,是不支持-d选项的,这个怎么办,要陷入死循环吗?
前面说具体原因我们一会再说,这里我们解释一下,只需要点开2.2.0-dirty下面给的那个网址,就可以得到详细的答案了 Remove SmaliDebugging #1061

1、We lose SmaliDebugging (our version), which is hit/miss in terms of IDE and support these days.
2、Those who still depend on this feature will have to transition to smali idea plugin.
3、smali-idea is in Alpha still

具体原因就是这个工作像idea这样的编译器已经做了,就是我们前面说的 baksmali-2.1.3.jar 这个插件,意思是idea & Android Studio有了这个插件之后已经支持调试Smali文件了,所以就没有必要去把Smali文件伪装成java文件去欺骗IDE进行远程动态调试了。
下面我们贴一下刚才testSmali.apk的源码,然后进入具体步骤,后面我们的测试也是基于这个apk的,在activity_main.xml文件里定义了一个@+id/button的按钮,然后MainActivity里面有一个source()和sink()函数用于测试,我们利用Android Studio的Build->Generate Signed Apk生成一个带有签名的Apk文件,最后生成的文件是{Workspace}/app/app-release.apk,我们重命名为testSmali.apk,然后移动到自己的测试目录下进行测试
源码如下:

package me.v4if.sourcedecode;

import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    String Tag = "ForDebug";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                System.out.println("Smali Test");
                int y = 0;
                y = 5;
                y = source();
                sink(y);
            }
        });
    }

    private int source() {
        int x = 3;
        return x;
    }

    private void sink(int x) {
        Log.i(Tag, String.format("%d", x));
    }
}

具体步骤

终于等到了这一步。。。

启动模拟器,安装生成的apk

emulator -avd AVD_24
等模拟器起来之后将我们签名之后的apk拖动到模拟器上进行安装,也可以使用adb install命令

以调试状态启动app

adb shell am start -D -n me.v4if.sourcedecode/.MainActivity
-n后面参数的形式是app包名/app入口,然后现在模拟器是等待调试的一个状态:

debug_apk_step_by_step

然后打开ddms查看一下等待调试的端口:
cd Software/Android_Sdk/tools
./ddms
debug_apk_step_by_step

上面有个小虫子的那一行,一般都是8700,后面要用到这个端口号

Android Studio安装ideasmali插件

将我们前面下载的 smalidea-0.03.zip 插件进行本地安装
File -> Settings -> Plugins -> Install plugin from disk 然后选择我们刚刚下载的插件,安装完成之后需要重启Android Studio

反编译app,得到smali文件

这里需要用到的是我们前面下载的 baksmali-2.1.3.jar 工具

java -jar baksmali-2.1.3.jar testSmali.apk -o myapp/src
在当前文件夹下的myapp/src目录下会得到逆向出来的smali文件源码,用于我们后面构建Android Studio工程

从smali文件构建Android Studio工程

File -> New -> Import Project -> 选择刚才myapp目录的位置 -> Create project from existing sources -> 然后一路next
对之前反编译的myapp/src文件夹右键–>Make Directory As —>Source Root

配置远程调试的选项

Run–>Edit Configurations
添加一个remote的调试选项,端口写成8700

debug_apk_step_by_step

配置JDK

File–>Project Structure 我jdk的版本是1.8.0_102

Debug调试

下好断点之后Run–>Debug ‘smali’,这里的Smali是我们之前配置好的remote调试选项,也可以点那个蓝色的小虫子。

debug_apk_step_by_step

可以看到我们逆向出来的smali文件已经停在了MainActivity的Oncreate()函数入口处
这里简单设置了三个断点,一个是上面的Oncreate()函数入口处,一个是按钮点击事件onClick()函数的入口处,还有一个是sink()函数的入口处,根据我们上面贴的代码的逻辑是点击button后是将调用source()之后返回的值传递给sink()函数,我们进去sink()函数debug看一下动态调试的魅力!!!
现在程序是处于等待执行的状态,我们点击Debug窗口下面那个绿色的小箭头Resume Programe (F9),然后没有什么反应,接下来点击模拟器上的click按钮
debug_apk_step_by_step

发现程序停在了我们之前在onClick()函数下断点的位置上,和之前预想一致
debug_apk_step_by_step

继续执行,Resume Programe,然后程序停在了sink()函数里面,这时候我们可以单步step by step去看
debug_apk_step_by_step

可以看到上图中v1寄存器的值为3,正好是我们从source()得到的返回值
整个逆向动态调试Smali的环境就搭建好了,真正考验能力的就是阅读Smali文件和调试程序的功底了。。。

附:吾爱破解Smali基础知识

吾爱破解安卓逆向入门教程–导航帖【持续更新】