zoukankan      html  css  js  c++  java
  • Android开发学习之路--逆向分析反编译

      一般情况下我们想要了解别人的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。

      见证奇迹的时刻到了:

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

  • 相关阅读:
    C SHARP.net 中DataSet.Fill实现不很理想,摸索中
    java程序代码 Exchenge.java
    java中的BREAK和CONTINUE语句的应用
    C++ 和 Java 中的变参
    BS程序代码与安全与基本攻击/防御模式
    MySql与Java的时间类型
    Ant 阅读笔记
    进度,效率,与个人事务管理 Personal Task 1.0
    Struts 实现的I18N
    解决站点关键数据,状态数据,无须持久化数据的一些思路
  • 原文地址:https://www.cnblogs.com/wuyida/p/6299937.html
Copyright © 2011-2022 走看看