Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android开发学习之路--逆向分析反编译

$
0
0

  一般情况下我们想要了解别人的app怎么实现这个动画,这个效果的时候,总是会想到反编译一下,看下布局,看下代码实现。对,这对于有经验的玩家确实手到擒来了,但是初学者,根本就不知道怎么反编译,怎么看代码,甚至不知道什么是反编译。那就学一下吧。


简单写一个app

  先简单写个app用作后面的反编译,当然可以直接拿现有的比较成熟的app,但是没有源码我们没办法好好比较了。好了,比较简单就直接上代码了,这里用了下databinding,具体以后也会写文章具体讲解databinding的。

xml界面代码:

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data class="MainDataBinding">
    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello Decompilation:"
            android:textSize="20sp" />

        <EditText
            android:id="@+id/et_account"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:hint="@string/account"/>

        <EditText
            android:id="@+id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:hint="@string/password"/>

        <Button
            android:id="@+id/bt_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@string/login"
            android:textAllCaps="false" />
    </LinearLayout>
</layout>

java代码:

package com.jared.decompilationstudy;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.jared.decompilationstudy.databinding.MainDataBinding;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private MainDataBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);

        binding.btLogin.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bt_login:
                if (checkInfo()) {
                    Toast.makeText(MainActivity.this, getResources().getString(R.string.login_ok),Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, getResources().getString(R.string.login_failure),Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

    private boolean checkInfo() {
        if (!"admin".equals(binding.etAccount.getText().toString()))
            return false;
        if (!"123456".equals(binding.etPassword.getText().toString()))
            return false;
        return true;
    }
}

  其实主要实现就是一个简单的登录界面,判断用户名为admin,密码为123456才会显示登录成功。后面也会通过反编译之后重新打包破解之。那就继续吧。


Apktool工具–反编译资源

  apktool工具是反编译资源用的,当然你也可以把apk的后缀名改为zip,然后解压文件,但是直接解压出来的文件只有图片资源可用,其他的都是乱码,为了查看layout等资源,所以我们就需要apktool工具了。
  下载地址:http://ibotpeaches.github.io/Apktool/install/

  apktool工具主要有三个文件,分别是aapt,apktool,apktools.jar。以mac为例,将三个文件拷贝到/usr/local/bin/目录下,必要的情况下设置可执行权限。之后在终端可以执行apktool,有如下信息表示ok。

Apktool v2.1.1 - a tool for reengineering Android apk files
with smali v2.1.2 and baksmali v2.1.1
Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
Updated by Connor Tumbleson <connor.tumbleson@gmail.com>

usage: apktool
 -advance,--advanced   prints advance information.
 -version,--version    prints the version then exits
usage: apktool if|install-framework [options] <framework.apk>
 -p,--frame-path <dir>   Stores framework files into <dir>.
 -t,--tag <tag>          Tag frameworks using <tag>.
usage: apktool d[ecode] [options] <file_apk>
 -f,--force              Force delete destination directory.
 -o,--output <dir>       The name of folder that gets written. Default is apk.out
 -p,--frame-path <dir>   Uses framework files located in <dir>.
 -r,--no-res             Do not decode resources.
 -s,--no-src             Do not decode sources.
 -t,--frame-tag <tag>    Uses framework files tagged by <tag>.
usage: apktool b[uild] [options] <app_path>
 -f,--force-all          Skip changes detection and build all files.
 -o,--output <dir>       The name of apk that gets written. Default is dist/name.apk
 -p,--frame-path <dir>   Uses framework files located in <dir>.

For additional info, see: http://ibotpeaches.github.io/Apktool/
For smali/baksmali info, see: https://github.com/JesusFreke/smali

  至于没有成功的,这里也不讲解了,相信google会给你答案。

  接着我们开始反编译资源了。先把之前的android代码打包成decompilation.apk。执行如下命令:

apktool d decompilation.apk
I: Using Apktool 2.1.1 on decompilation.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /Users/jared/Library/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

  有时候会出现问题类似如下:

Exception in thread "main" brut.androlib.err.UndefinedResObject: resource spec: 0x01010462

  原因可能你的apktool版本很老,下载最新的,还有就是需要删除下/Users/用户名/Library/apktool/framework/1.apk

  反编译成功后,会在同级目录下生成decompilation,cd进入decompilation目录,ls查看内容如下,有AndroidManifest.xml文件,res下就是我们需要的资源文件了,smali就是Dalvik的一些指令代码,之后有机会再学习学习。

->decompilation ls
AndroidManifest.xml original            smali
apktool.yml         res                 unknown

  我们看下AndroidManifest.xml的内容:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jared.decompilationstudy" platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
       <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme ="@style/AppTheme">
           <activity android:name="com.jared.decompilationstudy.MainActivity">
               <intent-filter>
                   <action android:name="android.intent.action.MAIN"/>
                   <category android:name="android.intent.category.LAUNCHER"/>
               </intent-filter>
           </activity>
      </application>
  </manifest>

  在对比下源码Manifest.xml的内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jared.decompilationstudy">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

  基本上保持了一致,这里我们没法看java源码,只是资源,那可以看源码吗?答案是肯定的,接着学习吧。


dex2jar&jd-gui工具–反编译源码

  dex2jar是把dex文件反编译为jar文件,jd-gui是将jar文件转换为java代码。
  dex2jar下载地址:http://sourceforge.net/projects/dex2jar/files/
  jd-gui下载地址:http://jd.benow.ca/

  dex2jar下载后是一个目录,内容如下:

➜  dex2jar-2.0 ls
classes-dex2jar.jar            d2j-jar2jasmin.bat
classes.dex                    d2j-jar2jasmin.sh
d2j-baksmali.bat               d2j-jasmin2jar.bat
d2j-baksmali.sh                d2j-jasmin2jar.sh
d2j-dex-recompute-checksum.bat d2j-smali.bat
d2j-dex-recompute-checksum.sh  d2j-smali.sh
d2j-dex2jar.bat                d2j-std-apk.bat
d2j-dex2jar.sh                 d2j-std-apk.sh
d2j-dex2smali.bat              d2j_invoke.bat
d2j-dex2smali.sh               d2j_invoke.sh
d2j-jar2dex.bat                lib
d2j-jar2dex.sh

  这里我们需要的是d2j-dex2jar.sh脚本。至于jd-gui的话,就是安装好就行了,和一般的ide差不多的。

  下面就开始反编译源码了。首先需要把decompilation.apk改为decompilation.zip,然后解压缩得到classes.dex文件。然后把classes.dex拷贝到dex2jar目录下:

➜  dex2jar-2.0 cp ../apk/decompilation2/classes.dex .
➜  dex2jar-2.0 ls
classes-dex2jar.jar            d2j-jar2jasmin.bat
classes.dex                    d2j-jar2jasmin.sh
d2j-baksmali.bat               d2j-jasmin2jar.bat
d2j-baksmali.sh                d2j-jasmin2jar.sh
d2j-dex-recompute-checksum.bat d2j-smali.bat
d2j-dex-recompute-checksum.sh  d2j-smali.sh
d2j-dex2jar.bat                d2j-std-apk.bat
d2j-dex2jar.sh                 d2j-std-apk.sh
d2j-dex2smali.bat              d2j_invoke.bat
d2j-dex2smali.sh               d2j_invoke.sh
d2j-jar2dex.bat                lib
d2j-jar2dex.sh

  开始反编译了,执行如下所示:

➜  dex2jar-2.0 ./d2j-dex2jar.sh classes.dex --force
dex2jar classes.dex -> ./classes-dex2jar.jar
➜  dex2jar-2.0 ls
classes-dex2jar.jar

  执行完后就生成了classes-dex2jar.jar文件。接着我们用jd-gui看下源码,打开jd-gui软件,打开classes-dex2jar.jar文件如下所示:

  这个时候你可能会非常爽,可以看到源码了,当然也会fuck,辛辛苦苦写的代码就这样被盗了。其实一般app都会做混淆的,看得不是那么容易的,这里没有做混淆就很直白了。好了,基本上一个app的反编译分析也基本上到此结束了。


破解apk,重新打包

  这里仅当做技术学习,毕竟别人也是辛辛苦苦写的代码,好了,继续吧。
  上面已经反编译了资源,我们回到decompilation目录下,这里有smali目录,主要是一个davik指令的代码。

decompilation ls
AndroidManifest.xml original            smali
apktool.yml         res                 unknown
➜  decompilation  cd smali
➜  smali ls
android com
➜  smali cd comcom ls
android jared
➜  com cd jared/decompilationstudy/
➜  decompilationstudy ls
BR.smali           R$bool.smali       R$integer.smali    R$styleable.smali
BuildConfig.smali  R$color.smali      R$layout.smali     R.smali
MainActivity.smali R$dimen.smali      R$mipmap.smali     databinding
R$anim.smali       R$drawable.smali   R$string.smali
R$attr.smali       R$id.smali         R$style.smali

  那我们要怎么破解呢?逐个击破吧,先看“登录成功”和“登录失败”,我们已“登录成功”为破解的开始吧:

➜  decompilation grep -nr "登录成功" res
res/values/strings.xml:39:    <string name="login_ok">登录成功</string>

  可以得知这个string的name为login_ok。然后我们继续查找这个login_ok怎么来的?

➜  decompilationstudy grep -nr 'login_ok' .
./R$string.smali:88:.field public static final login_ok:I = 0x7f060024
➜  decompilationstudy grep -nr '0x7f060024' .
./MainActivity.smali:117:    const v1, 0x7f060024
./R$string.smali:88:.field public static final login_ok:I = 0x7f060024

  可以得知login的I=0X7F060024,然后查找这个得到两个地方调用,一个是MainActivity.smali,后一个是string本身。显然我们的这个是在MainActivity.smali的第117行调用了。那我们继续去看看吧:

  这里需要一点汇编基础才能看的懂代码了。

# virtual methods
.method public onClick(Landroid/view/View;)V
    .locals 3
    .param p1, "view"    # Landroid/view/View;

    .prologue
    const/4 v2, 0x0

    .line 25
    invoke-virtual {p1}, Landroid/view/View;->getId()I

    move-result v0

    packed-switch v0, :pswitch_data_0

    .line 34
    :goto_0
    return-void

    .line 27
    :pswitch_0
    invoke-direct {p0}, Lcom/jared/decompilationstudy/MainActivity;->checkInfo()Z

    move-result v0

    if-eqz v0, :cond_0

    .line 28
    invoke-virtual {p0}, Lcom/jared/decompilationstudy/MainActivity;->getResources()Landroid/content/res/Resources;

    move-result-object v0

    const v1, 0x7f060024

    invoke-virtual {v0, v1}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String;

    move-result-object v0

    invoke-static {p0, v0, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v0

    invoke-virtual {v0}, Landroid/widget/Toast;->show()V

    goto :goto_0

    .line 30
    :cond_0
    invoke-virtual {p0}, Lcom/jared/decompilationstudy/MainActivity;->getResources()Landroid/content/res/Resources;

    move-result-object v0

    const v1, 0x7f060023

    invoke-virtual {v0, v1}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String;

    move-result-object v0

    invoke-static {p0, v0, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v0

    invoke-virtual {v0}, Landroid/widget/Toast;->show()V

    goto :goto_0

    .line 25
    nop

    :pswitch_data_0
    .packed-switch 0x7f0b005a
        :pswitch_0
    .end packed-switch
.end method

  可以看出来这是一个onclick方法,有一个checkInfo方法,看这行代码,if-eqz v0, :cond_0,意思是v0为0就跳转到cond_0。很显然cond_0就是登陆失败了,也就是checkInfo返回了true和false分别跳转到对应的方法中。寻着这个,我们看下checkInfo的代码:

.method private checkInfo()Z
    .locals 3

    .prologue
    const/4 v0, 0x0

    .line 37
    const-string v1, "admin"

    iget-object v2, p0, Lcom/jared/decompilationstudy/MainActivity;->binding:Lcom/jared/decompilationstudy/databinding/MainDataBinding;

    iget-object v2, v2, Lcom/jared/decompilationstudy/databinding/MainDataBinding;->etAccount:Landroid/widget/EditText;

    invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v2

    invoke-virtual {v2}, Ljava/lang/Object;->toString()Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v1

    if-nez v1, :cond_1

    .line 41
    :cond_0
    :goto_0
    return v0

    .line 39
    :cond_1
    const-string v1, "123456"

    iget-object v2, p0, Lcom/jared/decompilationstudy/MainActivity;->binding:Lcom/jared/decompilationstudy/databinding/MainDataBinding;

    iget-object v2, v2, Lcom/jared/decompilationstudy/databinding/MainDataBinding;->etPassword:Landroid/widget/EditText;

    invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v2

    invoke-virtual {v2}, Ljava/lang/Object;->toString()Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v1

    if-eqz v1, :cond_0

    .line 41
    const/4 v0, 0x1

    goto :goto_0
.end method

  首先赋值v0为0x0,const/4 v0, 0x0,接着看下代码:const-string v1, “admin”,很明显是常量赋值,接着往下看:if-nez v1, :cond_1,如果结果不为0就跳转到cond_1,继续看:const-string v1, “123456”。也是常量赋值123456,然后结果为0跳转到cond_0,否则执行,const/4 v0, 0x1,goto :goto_0,就是v0赋值为1,跳转到goto_0。

  综上分析,可以得出主要的关键点是v0寄存器了,checkInfo返回的值为v0,那么如果我们把v0的初始值赋值为0x1,那么不就永远返回true了,不管什么账号登录都是ok的了。修改28行代码为:const/4 v0, 0x1。不容易啊,终于改好了,那么接着我们看看是不是如我们所愿呢?
  修改完了代码,我们把修改好的代码打包吧,执行命令如下:

➜  apk apktool b decompilation -o decompilation2.apk
I: Using Apktool 2.1.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...

   打包完的代码是没有签名的,没办法在手机上安装的,那么接下来我们开始重签名吧。

➜  apk keytool -genkey -v -keystore Android.keystore -alias android.keystore -keyalg RSA -validity 20000
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
  [Unknown]:  1
您的组织单位名称是什么?
  [Unknown]:  1
您的组织名称是什么?
  [Unknown]:  1
您所在的城市或区域名称是什么?
  [Unknown]:  1
您所在的省/市/自治区名称是什么?
  [Unknown]:  1
该单位的双字母国家/地区代码是什么?
  [Unknown]:  1
CN=1, OU=1, O=1, L=1, ST=1, C=1是否正确?
  [否]:  y

正在为以下对象生成 2,048 位RSA密钥对和自签名证书 (SHA256withRSA) (有效期为 20,000 天):
         CN=1, OU=1, O=1, L=1, ST=1, C=1
输入 <android.keystore> 的密钥口令
        (如果和密钥库口令相同, 按回车):
[正在存储Android.keystore]

  这里偷懒了,就随便填写了内容,接着用jarsigner签名:

➜  jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore Android.keystore -storepass 123456 decompilation2.apk Android.keystore
   正在添加: META-INF/MANIFEST.MF
   正在添加: META-INF/ANDROID_.SF
   正在添加: META-INF/ANDROID_.RSA
   …………
    正在签名: com/android/databinding/library/baseAdapters/com.android.databinding.library.baseAdapters-br.bin
  正在签名: com/android/databinding/library/baseAdapters/com.android.databinding.library.baseAdapters-layoutinfo.bin
  正在签名: com/android/databinding/library/baseAdapters/com.android.databinding.library.baseAdapters-setter_store.bin
jar 已签名。

警告:
未提供 -tsa 或 -tsacert, 此 jar 没有时间戳。如果没有时间戳, 则在签名者证书的到期日期 (2071-05-29) 或以后的任何撤销日期之后, 用户可能无法验证此 jar。

  大工搞成,接着安装到手机上通过adb install decompilation2.apk。

  见证奇迹的时刻到了:

  破解成功了,你也可以试试。

作者:eastmoon502136 发表于2016/8/25 11:29:06 原文链接
阅读:221 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>