分析Android程序四要素: Android程序开发流程、程序结构、语句分支、解密原理。
一、编写Crackme
1.新建一个Android工程,需要有MainActivity
2.res\values\strings.xml 增加 info、username、hint_username、sn、hini_sn、register、registered、unsuccessed、successed 这几个字段
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Crackme0201</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="info">Android程序破解演示程序</string>
<string name="username">用户名:</string>
<string name="hint_username">请输入用户名</string>
<string name="sn">注册码:</string>
<string name="hint_sn">请输入注册码</string>
<string name="register">注册</string>
<string name="registered">程序已注册</string>
<string name="unsuccessed">无效用户名或注册码</string>
<string name="successed">恭喜您!注册成功</string>
</resources>
3.res\layout\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="20dp"/>
<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"/>"
</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"/>"
</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>
布局效果如图:
4.MainActivity 编码 (在OnCreate中为按钮 "注册" 绑定OnClick事件,取出EditText中的用户名和注册码,调用checkSN传入用户名计算出16位的注册码与输入的注册码比较
一致(忽略大小写)则提示注册成功,否则失败! ):
package com.droider.crackme0201;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
private EditText edit_userName;
private EditText edit_sn;
private Button btn_register;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle(R.string.unsuccessed);
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(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
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);
btn_register.setEnabled(false);
setTitle(R.string.registered);
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Incflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private boolean checkSN(String userName,String sn){
try{
if((userName==null) ||
(userName.length()==0))
return false;
if((sn==null) ||
(sn.length()==0))
return false;
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();
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();
}
}
5.编译运行,用户名输入1,输入错误的注册码r,点击注册 提示 无效的用户名或注册码。
二、反编译Android程序
1. 利用ShakaApktool_2.0.0.jar 反编译 crackme02.apk
命令行: java -jar ShakaApktool_2.0.0.jar d crackme02.apk
执行命令成功后,apk同目录中会出现一个同名文件夹 crackme02 ,反编译完成。
crackme02文件夹:
2.利用错误提示"无效用户或注册码"找到引用这个字符串的name
字符串位于crackme02\res\values\strings.xml 中,即它的name为unsuccessed。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="unsuccessed">无效用户名或注册码</string>
</resources>
3.查找字符串的ID
但是代码中引用的为字符串的ID,在编码过程中通过 R.java 来标识字符串的ID,而在反编译得到的代码中 ,ID是在crackme02\res\values\public.xml 中。
从public.xml中找到 ,即字符串 unsuccessed的ID为 0x7f0a0017。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="string" name="unsuccessed" id="0x7f0a0017" />
</resources>
4.查找引用这个ID的smali代码
然后再在 crackme02\smali\com\droider\crackme0201 文件夹中的smali文件中查找引用 0x7f0a0017 的代码。
找到 MainActivity$1.smali 中有一处引用了 0x7f0a0017 ,可以看出在 .line36 调用 checkSN 计算并比较注册码,返回值存在v0寄存器中,
.line37 如果返回值不为0则跳转到 cond_0 (注册成功) 继续执行,否则将顺序执行 .line 38 .line 39 的代码。
在 .line 39 引用了 unsuccessed 的字符串。
.line 36
# invokes: Lcom/droider/crackme0201/MainActivity;->checkSN(Ljava/lang/String;Ljava/lang/String;)Z
invoke-static {v0, v1, v2}, Lcom/droider/crackme0201/MainActivity;->access$2(Lcom/droider/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z
move-result v0
.line 37
if-nez v0, :cond_0
.line 38
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
.line 39
const v1, 0x7f0a0017
.line 38
invoke-static {v0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
move-result-object v0
.line 39
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
.line 46
:goto_0
return-void
三、修改smali代码,实现破解crackme。
1.修改smali代码,将 .line 37 的 if-nez 改为 if-eqz ,即不为0跳转改为为0时跳转。
.line 36
# invokes: Lcom/droider/crackme0201/MainActivity;->checkSN(Ljava/lang/String;Ljava/lang/String;)Z
invoke-static {v0, v1, v2}, Lcom/droider/crackme0201/MainActivity;->access$2(Lcom/droider/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z
move-result v0
.line 37
if-eqz v0, :cond_0
.line 38
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity;
.line 39
const v1, 0x7f0a0017
.line 38
invoke-static {v0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
move-result-object v0
.line 39
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
.line 46
:goto_0
return-void
2.重新编译修改后的代码,在原来执行命令的apk文件的目录执行命令
命令行: java -jar apktool.jar b crackme02 -o crackme02_repack.apk
成功执行后将会得到一个新的apk文件 crackme02_repack.apk。
这里用的是 apktool.jar,也可以使用ShakaApktool。
3.给新的apk签名
命令行:java -jar signapk.jar platform.x509.pem platform.pk8 crackme02_repack.apk crackme02_repack_signed.apk
成功执行后将会得到一个新的apk文件 crackme02_repack_signed.apk
4.安装测试,注册成功了。