2.分析Android程序

分析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.安装测试,注册成功了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值