【移动安全】Android程序分析入门
2022-8-18 00:2:13 Author: 白帽子(查看原文) 阅读量:23 收藏

声明:Tide安全团队原创文章,转载请声明出处!文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

分析Android程序是开发Android程序的逆过程,要想分析一个Android程序,首先应该了解其开发流程、程序结构、语句分支和解密原理等。

一、编写Android程序

使用Android studio编写Android应用程序

加载完成进入主界面后,点击activity_main.xml进入工程布局界面,然后可以拖动左边的palette内的各种各样的控件,系统就会自动帮我们生成xml代码。
也可以在工程布局文件activity_main.xml中,手动添加或调整输入框的代码信息。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/info"
android:textSize="20sp" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/username" />
<EditText
android:id="@+id/edit_username"
android:hint="@string/hint_username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:ems="10" >
</EditText>
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sn" />
<EditText
android:id="@+id/edit_sn"
android:hint="@string/hint_sn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:ems="10" >
</EditText>
</LinearLayout>

<Button
android:id="@+id/button_register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_gravity="right"
android:text="@string/register" />

</LinearLayout>

开发Android程序时,values目录下的strings.xml文件存放的字符串会在gen//R.java文件的string类中所标识。

<resources>
<string name="app_name">TideCrackme</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">Crackme0201</string>
<string name="info">Tide-Android演示</string>
<string name="username">用户名:</string>
<string name="sn">注册码:</string>
<string name="register">注 册</string>
<string name="hint_username">请输入用户名</string>
<string name="hint_sn">请输入注册码</string>
<string name="unregister">程序未注册</string>
<string name="registered">程序已注册</string>
<string name="unsuccessed">无效用户名或注册码</string>
<string name="successed">恭喜您!注册成功</string>
</resources>

编写MainActivity类的代码
打开MainActivity.java文件,如下

package com.example.textdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

}
}

然后我们在其添加两个方法。
checkcode() 该方法主要用于计算用户名和注册码是否匹配;
OnCreate() 该方法主要用于添加注册按钮点击事件的监听器。

package com.droider.tideandroid;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.support.v7.app.AppCompatActivity;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MainActivity extends AppCompatActivity {
private EditText edit_userName;
private EditText edit_sn;
private Button btn_register;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle(R.string.unregister);
edit_userName = (EditText) findViewById(R.id.edit_username);
edit_sn = (EditText) findViewById(R.id.edit_sn);
btn_register = (Button) findViewById(R.id.button_register);
btn_register.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
if (!checkSN(edit_userName.getText().toString().trim(),
edit_sn.getText().toString().trim())) {
Toast.makeText(MainActivity.this,
R.string.unsuccessed, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this,
R.string.successed, Toast.LENGTH_SHORT).show();
btn_register.setEnabled(false);
setTitle(R.string.registered);
}
}
});
}

private boolean checkcode(String userName, String sn) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(userName.getBytes());
byte[] bytes = digest.digest();
String hexstr = toHexString(bytes, "");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hexstr.length(); i += 2) {
sb.append(hexstr.charAt(i));
}
String userSN = sb.toString();
//Log.d("crackme", hexstr);
//Log.d("crackme", userSN);
if (!userSN.equalsIgnoreCase(sn))
return false;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return false;
}
return true;
}

private static String toHexString(byte[] bytes, String separator) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xFF & b);
if(hex.length() == 1){
hexString.append('0');
}
hexString.append(hex).append(separator);
}
return hexString.toString();
}
}

checkcode()方法
先利用md5算法计算出用户名字符串的散列值,然后将计算结果转换为长度为32位的16进制字符串,然后将字符串中的所有奇数位进行重新组合生成新的字符串,该字符串就是最终的注册码。最后将该字符串与传入的注册码进行比较,如果二者相同,表示注册码是正确的;两者不同,表示注册码为错误。
OnCreate()方法
如果用户名与注册码匹配,就弹出注册成功的提示;如果用户名与注册码不匹配,则弹出用户名与注册码无效的提示。

MainActivity.java与activity_main.xml建立联系

Android手机运行java程序时,MainActivity.java内的setContentView(R.layout.activity_main);这段代码告诉我们:启动一个内容视图为activity_main.xml文件,就会去找activity_main.xml文件,然后Android系统会转换成各种各样的控件,这时候程序上就显示activity_main.xml内所写好的控件了。

二、破解Android程序

破解android程序的流程:
1、分析APP中的错误提示
2、反编译APK文件,生成smali格式的反汇编代码
3、阅读smali文件的代码理解程序运行机制,并对代码进行修改
4、重打包并签名
5、运行测试

1、反编译apk

使用android killer反编译apk
smali目录存放了程序的所有反汇编代码;
res目录中存放的则是程序中的所有资源文件;

2、分析APP中的错误提示

寻找突破口是分析android程序的关键。一般的做法是按程序中的错误提示信息来找到关键代码,因为错误提示代码附近通常就是程序的核心验证代码,可以通过阅读这些代码来理解程序的流程。
一般错误提示属于android程序中的字符串资源,在开发android程序时,这些字符串会被硬编码到源码中,一般引用自res\values下的strings.xms文件。
通过反编译可成功搜索到该错误提示。
反编译APK之后,所有的索引值都保存在了public.xml文件中,“无效用户名或注册码”的字符串名称为”unsuccessed”

R.java文件介绍:gen目录下的R.java文件是编译器自动生成的,它无需开发人员对其进行维护。R.java会自动收录当前应用中所有的资源,并根据这些资源建立对应的ID,包括:布局资源、控件资源、String资源、Drawable资源等。我们可以简单的把R.java理解成是当前Android应用的资源字典。

unsuccessed字符串的id值为0x7f0b002c,然后搜索含有”0x7f0b002c”字符串文件,如下:

public.xml和R$string.smali为程序的资源文件,
MainActivity$1.smali文件进行了调用:
发现line34处调用了checkcode()方法进行注册码合法性检测
checkecode()方法返回布尔类型的值。

move-result v0  //将返回的结果保存到v0寄存器
const/4 v1, 0x0 //4字节常量 v1=0
if-nez v0, :cond_0 //对v0寄存器进行判断,如果其值不为0(条件为真)就跳转到cond_0标号出,反之继续往下执行

如果代码不跳转,会执行如下代码:

const v2, 0x7f0b002c
//将v2寄存器传入 unsuccessed字符串的id值"0x7f0b002c"
invoke-static {v0, v2, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
//调用Toast;->makeText()方法创建字符串,
.line 37 invoke-virtual {v0}, Landroid/widget/Toast;->show()V
//显示结果

如果代码跳转,会执行如下代码:
会弹出注册成功的提示,这里的跳转成功意味着程序注册成功。

3、修改smali文件代码

通过分析得知,if-nez v0, :cond_0是程序破解的关键点。

if-nez是Dalvik指令集中的一个条件跳转指令,与之类似的指令有if-eqz、if-gez、if-lez等。
与if-nez指令功能相反的指令为if-eqz 表示比较结果为0或者相等时跳转。

打开MainActivity$1.smali文件,修改if-nez v0, :cond_0为if-eqz v0, :cond_0 然后保存后退出即可修改成功。

4、重编译APK并签名

使用Android killer可一键进行文件重打包,并自动为该APK文件签名

三、安装测试

将打包好的apk文件,安装在手机或模拟器中,输入任意用户名和注册码,查看程序弹出的提示信息,发现会提示“程序已注册”,意味着破解成功。

四、总结

破解Android程序的大致流程为:
分析发现程序错误提示-反编译-定位程序关键代码-修改代码-重打包签名-测试
本次只是对Android程序分析的基础过程进行了总结,但实际测试过程中,很多APP都进行加壳保护并对代码进行了混淆,使得分析起来相当困难复杂,需要使用动态调式与其它方法进行分析。

E

N

D

guān

zhù

men

Tide安全团队正式成立于2019年1月是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

对安全感兴趣的小伙伴可以关注团队官网: http://www.TideSec.com 或长按二维码关注公众号:


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246296&idx=2&sn=167e62a01e28b7a677986744ac7d2bc3&chksm=82ea5731b59dde279478dbadc1a2c4202021b065762606df064af3b73453b93a87bd0398ce67#rd
如有侵权请联系:admin#unsafe.sh