当前位置: 移动技术网 > 网络运营>安全>加解密 > 简单Android CrackMe分析2

简单Android CrackMe分析2

2018年03月10日  | 移动技术网网络运营  | 我要评论

这是在网上找到的一个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运行截图如下:

把注册码输入到CrackMe进行注册,提示成功:

 


 

四、相关资源

 

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

 

CrackMe / Keygen下载:

 

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

 

 

Copyed From 程序人生 

Home Page:http://www.programlife.net 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网