由于刚踏入Android逆向分析领域,因此有许多的不懂,所以得不断地去学习。
因为是入门,我又有一定的Android应用开发基础,所以先从一些简单工程的反编译开始入手,先了解一些反编译所用到的工具。
上一次写博客,记录了反编译工具dex2jar和jd-gui 的使用,这次继续记录另一款比较强大的反编译工具APKTool。
该工具的使用方法很简单,只需要在dos控制台里输入apktool d xxx.apk 则可以在当前文件夹生成一个装有该APK的smali文件夹。
通过记事本可打开smali文件,可以看到一套以smali语法写的编码,实际上就是android虚拟机Dalvik的汇编语言。
算了,本人语言表达技术不好,还是直接上图清晰明了:
第一步,编写一个简单的应用工程,并打包APK,用于下面的反编译(直接用上一次博客中用到的登录界面应用):
MainActivity.java:
public class MainActivity extends Activity {
private final String ACCOUNT="samuel";
private final String PASSWORD="123456";
private EditText etAccount, etPassword;
private Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etAccount=(EditText)findViewById(R.id.et_account);
etPassword=(EditText)findViewById(R.id.et_password);
btnLogin=(Button)findViewById(R.id.btn_login);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(isOK(etAccount.getText().toString(), etPassword.getText().toString())){
Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MainActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
}
}
});
}
private boolean isOK(String account, String password){
if(account.equals(ACCOUNT) && password.equals(PASSWORD))
return true;
else
return false;
}
}
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="帐号:"/>
<EditText
android:id="@+id/et_account"
android:layout_width="100dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码:"/>
<EditText
android:id="@+id/et_password"
android:layout_width="100dp"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="登录"/>
</LinearLayout>
</RelativeLayout>
第二步,下载APKTool,下面底部附有下载连接:
第三步,dos下cd到APKTool的位置,再输入apktool d login.apk,回车进入反编译,完了后在当前文件夹下生成login文件夹,该文件夹里有smali文件:
第四步,用记事本打开login->smali中的LoginActivity.smali:
.class public Lcom/samuelzhan/logintest/LoginActivity;
.super Landroid/app/Activity;
# instance fields
.field private final a:Ljava/lang/String;
.field private final b:Ljava/lang/String;
.field private c:Landroid/widget/EditText;
.field private d:Landroid/widget/EditText;
.field private e:Landroid/widget/Button;
# direct methods
.method public constructor <init>()V
.locals 1
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
const-string v0, "samuel"
iput-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->a:Ljava/lang/String;
const-string v0, "123456"
iput-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->b:Ljava/lang/String;
return-void
.end method
.method static synthetic a(Lcom/samuelzhan/logintest/LoginActivity;)Landroid/widget/EditText;
.locals 1
iget-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->c:Landroid/widget/EditText;
return-object v0
.end method
.method static synthetic a(Lcom/samuelzhan/logintest/LoginActivity;Ljava/lang/String;Ljava/lang/String;)Z
.locals 1
invoke-direct {p0, p1, p2}, Lcom/samuelzhan/logintest/LoginActivity;->a(Ljava/lang/String;Ljava/lang/String;)Z
move-result v0
return v0
.end method
.method private a(Ljava/lang/String;Ljava/lang/String;)Z
.locals 1
const-string v0, "samuel"
invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_0
const-string v0, "123456"
invoke-virtual {p2, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-eqz v0, :cond_0
const/4 v0, 0x1
:goto_0
return v0
:cond_0
const/4 v0, 0x0
goto :goto_0
.end method
.method static synthetic b(Lcom/samuelzhan/logintest/LoginActivity;)Landroid/widget/EditText;
.locals 1
iget-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->d:Landroid/widget/EditText;
return-object v0
.end method
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
.locals 2
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
const v0, 0x7f040019
invoke-virtual {p0, v0}, Lcom/samuelzhan/logintest/LoginActivity;->setContentView(I)V
const v0, 0x7f0c0050
invoke-virtual {p0, v0}, Lcom/samuelzhan/logintest/LoginActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/EditText;
iput-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->c:Landroid/widget/EditText;
const v0, 0x7f0c0051
invoke-virtual {p0, v0}, Lcom/samuelzhan/logintest/LoginActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/EditText;
iput-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->d:Landroid/widget/EditText;
const v0, 0x7f0c0052
invoke-virtual {p0, v0}, Lcom/samuelzhan/logintest/LoginActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/Button;
iput-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->e:Landroid/widget/Button;
iget-object v0, p0, Lcom/samuelzhan/logintest/LoginActivity;->e:Landroid/widget/Button;
new-instance v1, Lcom/samuelzhan/logintest/a;
invoke-direct {v1, p0}, Lcom/samuelzhan/logintest/a;-><init>(Lcom/samuelzhan/logintest/LoginActivity;)V
invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
return-void
.end method
对比一下MainActivity.java里的源代码,其实Dalvik汇编语言还是比较好理解的。
第五步,修改smali文件,让登录系统无论帐号密码是否正确,均可以实现登录成功,即破解:
修改返回值,让它只返回0x1,即true
第六步,重新打包:
在dos下,cd到apktool.jar当前的位置,输入apktool b <刚刚反编译生成文件夹的路径>
反编译后和打包后的login文件夹会多出两个文件夹,dist里有打包后的apk
第七步,因为反编译后签名被破坏,需要重新签名才能在手机上安装:
签名工具很多,我这里使用的是Auto-sign,下面附加下载
用记事本打开批处理文件Sign.bat,并修改
修改后保存,并运行Sign.bat文件
多出一个已签名的APK,至此反编译修改smali文件重新打包的工作基本已完成,现在验证一下,是否输入任意的帐号密码也能显示登录成功。
效果图:
本来输入的帐号密码应该是 samuel 123456,现在随便输入都可以显示“登录成功”提示字符,说明破解成功了~
工具下载:APKTool & Auto-sign