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

  见证奇迹的时刻到了:

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

Android开发学习之路--逆向分析反编译的更多相关文章

  1. Android开发学习之路-RecyclerView滑动删除和拖动排序

    Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...

  2. Android开发学习之路--MAC下Android Studio开发环境搭建

    自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...

  3. Android开发学习之路--基于vitamio的视频播放器(二)

      终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...

  4. Android开发学习之路--Android Studio cmake编译ffmpeg

      最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...

  5. Android开发学习之路--网络编程之xml、json

    一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...

  6. Android开发学习之路--Activity之初体验

    环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...

  7. Android开发学习之路--Android系统架构初探

    环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...

  8. 我的大学Android开发学习之路——从开始到微信/支付宝/抖音Offer

    前言 笔者2016年高考考入华中科技大学计算机科学与技术专业. 2017年底(大二寒假)拿到今日头条(字节跳动)深圳研发中心Android开发实习生Offer,在深圳研发中心实习至2018年3月. 2 ...

  9. Android开发学习之路-记一次CSDN公开课

    今天的CSDN公开课Android事件处理重难点快速掌握中老师讲到一个概念我觉得不正确. 原话是这样的:点击事件可以通过事件监听和回调两种方法实现. 我一听到之后我的表情是这样的: 这跟我学的看的都不 ...

随机推荐

  1. [LeetCode] Minimum ASCII Delete Sum for Two Strings 两个字符串的最小ASCII删除和

    Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal. ...

  2. promise 的基本概念 和如何解决js中的异步编程问题 对 promis 的 then all ctch 的分析 和 await async 的理解

    * promise承诺 * 解决js中异步编程的问题 * * 异步-同步 * 阻塞-无阻塞 * * 同步和异步的区别? 异步;同步 指的是被请求者 解析:被请求者(该事情的处理者)在处理完事情的时候的 ...

  3. mybatis学习一

    1:ORM概念    ORM(OBJECT-RELATIONSHIP MAPPING) 即对象关系映射,是一种思想,实质是将数据库中的数据用对象的形式表现出来    JPA(JAVA PERSISIT ...

  4. 在iview的Table中添加Select(render)

    首先对Render进行分析,在iview官方的文档中,找到了table插入Button的例子: { title: 'Action', key: 'action', width: 150, align: ...

  5. [SDOI 2015]约数个数和

    Description  设d(x)为x的约数个数,给定N.M,求 $\sum^N_{i=1}\sum^M_{j=1}d(ij)$ Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试 ...

  6. [Tjoi 2013]松鼠聚会

    3170: [Tjoi 2013]松鼠聚会 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1318  Solved: 664[Submit][Stat ...

  7. [HNOI2015]接水果

    题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本. 首先有 ...

  8. permu(变态考试题)

    题目描述 给定一个严格递增的序列T,求有多少个T的排列S满足:∑min(T[i],S[i])=k 输入输出格式 输入格式: 第一行两个数n,k 第二行n个数,表示T 输出格式: 一个正整数表示答案,答 ...

  9. [Codeforces]862F - Mahmoud and Ehab and the final stage

    题目大意:n个字符串,支持修改一个位置上的字符串和查询一个区间的子区间中长度乘LCP的最大值,输入字符数和询问数不超过10^5. 做法:求出相邻的LCP长度,区间LCP等于区间最小值,查询分几种情况考 ...

  10. hdu 5887 搜索+剪枝

    Herbs Gathering Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...