首页 > Android安全 > 简单Android CrackMe分析2

简单Android CrackMe分析2

作者:代码疯子
博客:Http://Www.ProgramLife.Net/ 求关注,求勾搭!

这是在网上找到的一个Android CrackMe,属于比较简单的类型,只是使用了ProGuard进行处理。

一、switch结构

在分析这个CrackMe之前,先说一下JD-GUI对switch结构的支持问题,知道这个BUG的存在就好了。JD-GUI反编译出来的switch语句可读性很差,所以最好结合一下smali代码看一下分支的走向。我们先来自己写一段switch代码,体会一下这个BUG。编译下面一段代码:

Button btnTest = (Button)findViewById(R.id.btnTest);
final EditText editInput = (EditText)findViewById(R.id.editText);
btnTest.setOnClickListener(new OnClickListener() {
	public void onClick(View v) {
		int n = Integer.parseInt(editInput.getText().toString());
		String strText;
		switch (n) {
		case 0:
			strText = "AAAA";
			break;
		case 1:
			strText = "BBBB";
			break;
		case 2:
			strText = "CCCC";
			break;
		default:
			strText = "DEFAULT";
			break;
		}
		Toast.makeText(getApplicationContext(), strText, Toast.LENGTH_LONG).show();
	}
});

把classes.dex转成jar包,然后用JD-GUI查看,已然不一样了:

public void onClick(View paramView)
{
    String str;
    switch (Integer.parseInt(this.val$editInput.getText().toString()))
    {
    default:
      str = "DEFAULT";
    case 0:
    case 1:
    case 2:
    }
    while (true)
    {
      Toast.makeText(this.this$0.getApplicationContext(), str, 1).show();
      return;
      str = "AAAA";
      continue;
      str = "BBBB";
      continue;
      str = "CCCC";
    }
}

对于刚接触的人来说,确实不怎么好看。不过凭经验可以理解为case 0对应AAAA,case 1对应BBBB,case 2对应CCCC,default对应DEFAULT,然后执行Toast的代码后返回。

看看smali代码的结构:

packed-switch v0, :pswitch_data_0   # v0为switch参数
.line 47
const-string v1, "DEFAULT"           # default值
# 省略N多代码
:pswitch_data_0
.packed-switch 0x0
    :pswitch_0
    :pswitch_1
    :pswitch_2
.end packed-switch

上面的代码可以这样解释:执行完第一句代码之后来到.packed-switch处检查v0的值,因为我们原始的检查范围是0~2,所以这里指定初值为0×0,有三个分支,分别对应0、1、2,如果检查到相等,则跳转到相应的分支,如果没有那么再跳回去,也就是default分支了。
当然,如果我们在switch中检查的值不是连续的,那么.packed-switch就有一点点变化了,比如:

sparse-switch v0, :sswitch_data_0 # v0为switch参数
.line 36
const-string v1, "DEFAULT"         # default值
 
:sswitch_data_0
.sparse-switch
    0x4d2 -> :sswitch_0            # 分支1
    0x929 -> :sswitch_1            # 分支2
    0xd80 -> :sswitch_2            # 分支3
.end sparse-switch

发现关键字从packed-switch变成了sparse-switch了,分支结构是具体的值对应一个分支。关于switch将介绍到这里了,主要是让大家知道JD-GUI怎么看switch的代码。

其实不只是switch,有时候JD-GUI的代码并不是很好看,这时候就得结合smali代码一起分析。

二、CrackMe分析

下面开始解剖这个CrackMe,先使用ApkTool GUI反编译apk文件,查看AndroidManifest.xml可以知道MainActivity类为Main。接着用解压缩软件从APK包中提取出classes.dex并将其转换为jar包,就可以使用JD-GUI查看Java代码了,看到里面很多a、b、c之类的方法名和类名,就应该知道这个被ProGuard处理过了,不过不要紧,代码还是能看的。

2.1 类b代码分析

可以先从Main类的代码开始看,看到里面使用到了a、b、c,这里我们先看类b的代码。类b提供了一个公共的构造函数b,一个私有的成员函数b以及一个公有成员函数a。私有方法b通过TelephonyManager获取设备相关的一些信息,以及通过PackageManager获取自身的签名(com.lohan.crackme1),然后把这些字符串串接起来。

类b的方法a为调用方法b获取字符串,然后通过SharedPreferences.Editor将这个字符串值存储到键machine_id,也就是所谓的机器码了。

经过上面的分析,类b对外提供方法a,功能就是生成机器码并存储到系统中,对应的键为machine_id。

2.2 类c代码分析

类c提供的方法比较多,下面一个一个的分析他们的作用。
1. public c(Context paramContext)
构造函数,同时定义两个字符串:
b = “f0d412b5530e1f9841aab434d989cc77″;
c = “4ec407446b872351e613111339daae9″;

2. public static boolean b()
通过getPackageManager获取自身的签名,如果签名与构造函数中的两个字符串b或者c任意一个相等,那么返回false,否则返回true。

3. private static String b(String paramString)
通过MessageDigest计算paramString的MD5值。

4. public static int a(String paramString)
jd-gui的代码有点乱,结合smali代码看。还原的代码如下:
可以看出这段代码的功能为计算机器码的MD5,如果与传入的参数一致,那么通过SharedPreferences存入到serial字段中。当然还有调用b方法进行一些判断,自身的签名不能是已知的两个。

public static int a(String paramString) {
    if (b() == false) {
        SharedPreferences localSharedPreferences = 
            PreferenceManager.getDefaultSharedPreferences(a);
        String mId = localSharedPreferences.getString("machine_id", "");
        String idMd5 = b(mId);
        if (idMd5.equals(paramString) == false) {
            return 0;
        }
 
        SharedPreferences.Editor editor1 = localSharedPreferences.edit();
        editor1.putString("serial", paramString);
        editor1.commit();
        return 1;
    }
    return 0;
}

5. public static boolean a()
这个其实就是上面的int a(String paramString)的包装函数,通过SharedPreferences获取serial字段,并传给这个方法,返回相应的返回值。

2.3 类a代码分析

倒计时6秒钟,然后调用类c的a方法(boolean那个),如果返回false的话,就设置TextView内容提示注册。

2.4 类Main代码分析

在OnCreate方法中,先调用b.a()存储机器码,然后调用c.a(),也就是判断是否已经存储了serial,并判断是否能通过算法校验:

    invoke-static {}, Lcom/lohan/crackme1/c;->a()Z
    move-result v0
    if-eqz v0, :cond_0
    :try_start_0
    invoke-direct {p0}, Lcom/lohan/crackme1/Main;->a()V
    :try_end_0
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
    :cond_0
    :goto_0
    return-void

如果不能通过,则什么都不做,如果能通过,则调用自身的方法a()。而该方法中又调用了c.b()方法,如果c.b()返回false,那么就把Button和EditText设置为隐藏(setVisibility(4)),并设置TextView的文本为PRO VERSION!(id=”0x7f040003″),并启用倒计时类a,这样看来,这里就有了两次校验了。

OnClick方法中,将输入的注册码传给c.a(String)方法检查,如果通过则提示Thanks for purchasing!,否则提示Invalid serial!。

经过上面的分析,如果APK自身签名是f0d412b5530e1f9841aab434d989cc77或者4ec407446b872351e613111339daae9,那么即使序列号通过验证,也只是开始的6秒钟显示PRO VERSION!,之后就提示要注册了。不过APK的签名是很长的一串啊,所以这里应该是没有什么影响了。

三、编写Keygen

可以参考类b的方法b,先获取机器码,然后计算MD5值。核心代码如下:

btnKeygen.setOnClickListener(new OnClickListener() {
	public void onClick(View v) {
		TelephonyManager tm = (TelephonyManager)getSystemService("phone");
	    String str1 = tm.getDeviceId();
	    String str2 = tm.getLine1Number();
	    String str3 = tm.getDeviceSoftwareVersion();
	    String str4 = tm.getSimSerialNumber();
	    String str5 = tm.getSubscriberId();
	    String machineId;
	    PackageManager pm = getPackageManager();
	    try {
	    	PackageInfo pkgInfo = pm.getPackageInfo("com.lohan.crackme1", 
	    							PackageManager.GET_SIGNATURES);
	    	String sig = pkgInfo.signatures[0].toCharsString();
	    	machineId = str1 + str2 + str3 + str4 + str5 + sig;
	    	// 机器码
	    	editMachineId.setText(machineId);
	    	// 签名
	    	editSig.setText(sig);
	    	// 注册码
	    	MessageDigest md = MessageDigest.getInstance("MD5");
	        int len = machineId.length();
	        md.update(machineId.getBytes(), 0, len);
	        BigInteger bigInt = new BigInteger(1, md.digest());
	        String serial = bigInt.toString(16);
	        editSerial.setText(serial);
	    } catch (Exception e) {
	    	editMachineId.setText("没有发现安装CrackMe");
	    }
	}
});

KeyGen运行截图如下:
Android CrackMe KeyGen 注册机
把注册码输入到CrackMe进行注册,提示成功:
Android CrackMe注册成功

四、相关资源

Android的CrackMe比较难找,crackmes.de上也只能找到几个而已。本文的CrackMe来源于网上,作者的博客是http://androidcracking.blogspot.com/,上面有一些关于Android逆向相关的文章,有兴趣的朋友可以看一下,自备梯子。

CrackMe / Keygen下载:

http://pan.baidu.com/share/link?shareid=2857217394&uk=369321854

在线阅读本PDF:
http://www.programlife.net/doc/Android_CrackMe_1.pdf


觉得文章还不错?点击此处对作者进行打赏!


本文地址: 程序人生 >> 简单Android CrackMe分析2
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


更多



  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.