欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

简单Android CrackMe分析2

程序员文章站 2022-04-22 11:39:13
这是在网上找到的一个Android CrackMe,属于比较简单的类型,只是使用了ProGuard进行处理。   一、switch结构   在分析这个C...

这是在网上找到的一个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分析2

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

 

简单Android CrackMe分析2

 

四、相关资源

 

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