[原创]Android_Crackmes03_by_deurus破解小记
题记:破解这个Crackme是为了学习Android平台下的软件逆向技术,我是新手,所以就挑了个简单的进行分析。
先看看软件作者的要求:
Rules for the Crackme03 (Android) by deurus
===========================================
- Valid solutions are:
- Make keygen + tuto and send to crackmes.de
Things to consider:
- Because this crackme need some info of phone, not run in emulator.
- Tested on HTC Magic with Android 2.2 but should be run in another android version.
主要是说,1> 写出keygen;2> 因为需要读取电话的某些信息,因而不能运行于模拟器中。但实际上只是不能运行于Google官方出的模拟器,而可以自定义硬件信息的模拟器是可以正常运行的,比如我们这次使用的天天模拟器。
特别说明:
因为是新手,所以大家如果发现文中有错误的地方,还望批评指正。
一、用到的工具
1. ApkIDE v3.3.3;
2. JEB v2.2.5;
3. 天天模拟器 v1.4.1047;
4. Android Studio v1.5 + DDMS + adb等;
二、静态分析
这里我们先通过静态分析来了解程序需要重点分析的地方在哪里,有没有可能只通过静态分析就可以得到有效的注册信息,以及是否存在爆破点等。
1. 用WinRAR打开该apk文件,发现里面没有lib文件夹,说明没有动态连接库文件需要分析,也说明程序的核心都在classes.dex文件里。
2. 分析过程
1> 在模拟器上安装并运行该Crackme,什么都不输入,直接点击Check按钮,提示“Min 4 chars”,如下图所示:

2> 根据上一步的提示,用ApkIDE打开Crackme03.apk文件,等待分析完毕后搜索"Min 4 chars",并在搜索结果里双击“const-string v23, " Min 4 chars"”,ApkIDE自动定位到HelloAndroid$2.smali文件中合适的位置,如下图所示:

3> 为了使反编译取得更佳的效果,我们使用JEB来反编译该apk,并根据“Min 4 chars”字符串定位到onClick函数,如下图:

对于上图中的
String v10 = HelloAndroid.this.findViewById(2131034116).getText().toString();
和
String v15 = HelloAndroid.this.findViewById(2131034118).getText().toString();
我们把数字2131034116转换为16进制后得到0x7F050004,把数字2131034118转换为16进制后得到0x7F050006。在资源文件的values/public.xml中,我们发现了如下信息:
<public id="0x7f050004" name="txt_name" type="id" /> ...... <public id="0x7f050006" name="txt_serial" type="id" />
因此,我们可以把v10和v15分别重命名为name和serial(鼠标单击v10,然后按键盘上的n键,输入name即可,以下的重命名方法和此一样)。相应地,把v11重命名为nameLen。
一般情况下,我们把循环控制变量命名为i,j,k等,因此,我们在这里把v5重命名为i。
我们还知道getDeviceId()函数用于获取IMEI;getSimSerialNumber()函数用于获取ICCID,因此,我们把v6和v16分别重命名为imei和iccid,v19重命名为imeiSix。
重命名的最终结果如下图:

留意到下面的判断语句:
if(String.valueOf(tmp) + "-" + String.valueOf(((long)(Integer.parseInt(imeiSix) ^ Integer.parseInt(iccid.substring(0, 6))))) + "-" + imeiSix.equals(serial)) {
Toast.makeText(HelloAndroid.this.getApplicationContext(), "God boy", 1).show();
}
else {
Toast.makeText(HelloAndroid.this.getApplicationContext(), "Bad boy ", 1).show();
}我们得知,如果String.valueOf(tmp) + "-" + String.valueOf(((long)(Integer.parseInt(imeiSix) ^ Integer.parseInt(iccid.substring(0, 6))))) + "-" + imeiSix的值等于我们输入的serial,那么显示"God boy"表示成功,不等则显示"Bad boy "表示失败。
4> 根据上面的判断语句,我们返回ApkIDE,在Dalvik指令中寻找判断点的位置。经过仔细分析,发觉下面的语句比较可疑:
.local v14, "serial":Ljava/lang/String;
invoke-virtual {v14, v15}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v22
if-eqz v22, :cond_2如下图:

根据函数eaquals比较两个字符串的值是否相等推测v14为程序经过一系列计算后得到的真serial,而v15为输入的假serial。
为了验证我们的推测是否正确,我们修改HelloAndroid$2.smali文件,在其314行下面插入下面的Dalvik指令,以在DDMS(通过Windows中对sdk/tools/monitor.bat批处理脚本运行)输出v14和v15的值:
const-string v2, "=====v14====="
invoke-static {v2, v14}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
const-string v2, "=====v15====="
invoke-static {v2, v15}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
从而得到

在ApkIDE中保存修改后的内容,并点击菜单“编译”=>“编译生成Apk”,等待输出窗口提示成功编译并生成文件ApkIDE_Crackme03.apk后,把该apk文件安装到模拟器。
在运行程序前,我们在电脑上打开控制台,并输入adb connect 127.0.0.1:6555,然后运行DDMS,接着在模拟器中运行Crackme程序。
输入name为phrank,serial为112233后,点击Check按钮得到提示“Bad boy”,切换到DDMS查看输出的信息,能够发现有类似下面的两行信息:
08-23 14:42:01.378: V/=====v14=====(5122): 433116-591824-299725 08-23 14:42:01.378: V/=====v15=====(5122): 112233
显然,433116-591824-299725应该是真正的serial。重新把它输入到serial后点击Check按钮,提示”God Boy”,如下图:

上面说明我们的推测是正确的。
5> 根据上面的分析,修改源程序319行的指令if-eqz v22, :cond_2为if-nez v22, :cond_2是不是就可以达到爆破的目的呢?
但如果name继续为phrank,serail继续为433116-591824-299725呢?又或者serial为空呢?:D
6> 题外话
根据”Bad boy”推测这个”God boy”可能是笔误,应该为”Good boy”。
上面通过静态分析的方法我们得到了一组有效的注册信息,但并没有详细分析serial是怎么一步一步计算出来的;程序界面上的HardwareID 01和HardwareID 02我们可能已经推测出它们是硬件信息,但为什么要显示出来呢?在下一部分我们通过动态分析的方法来认识这个问题。
三、动态分析
本想用JEB直接调试的,但它总是提示“Could not attach to target”,所以,还是用Android Studio来调试,虽然稍微麻烦些。
使用Android Studio如何调试smali代码可以参考http://www.52pojie.cn/thread-505077-1-1.html和http://www.cnblogs.com/lanrenxinxin/p/4891424.html 。
1. 在ApkIDE中,打开AndroidManifest.xml文件,给<application android:icon="@drawable/icon">添加android:debuggable="true",得到:
<application android:icon="@drawable/icon" android:debuggable="true">
完整的AndroidManifest.xml文件内容如下:
<?xml version="1.0" encoding="utf-8" standalone="no"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.crackme03"> <application android:icon="@drawable/icon" android:debuggable="true"> <activity android:label="@string/app_name" android:name="com.example.helloandroid.HelloAndroid"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:label="@string/app_name2" android:name="com.example.helloandroid.prueba2"> <intent-filter> <action android:name="android.intent.action.MAIN"/> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> </manifest>
保存后重新编译生成ApkIDE_Crackme03.apk文件并将其安装到模拟器。
2. 打开天天模拟器,并确认adb connect 127.0.0.1:6555正常工作着,然后在控制台执行命令
adb shell am start -D -n com.example.crackme03/com.example.helloandroid.HelloAndroid,目标程序会被启动并挂起,模拟器中会弹出等待调试的界面。其中,com.example.crackme03为package名,来源于AndroidManifest.xml文件中的<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.crackme03">,而com.example.helloandroid.HelloAndroid为Activity的名字,来源于AndroidManifest.xml文件中<activity android:label="@string/app_name" android:name="com.example.helloandroid.HelloAndroid"> android:name的值。
3. 用DDMS查看进程信息,得知目标程序的PID为3233,如下图:

4. 关闭DDMS,并在控制台中执行端口转发命令adb forward tcp:8700 jdwp:3233。其中3233就是上面通过DDMS得知的PID,其他照抄即可。
如果没有关闭DDMS而执行命令的话可能会得到错误提示error: cannot bind listener: cannot bind to 127.0.0.1:8700: Only one usage of each socket address (protocol/network address/port) is normally permitted. (10048),关闭DDMS后重新执行端口转发即可。
5. 把ApkIDE反编译结果的smali文件夹从ApkIDE的Work\com.example.crackme03拷贝到我们的分析目录(也可以不拷贝,直接使用),打开Android Studio,通过Import project(Eclipse ADT, Gradle, etc.)导入上面拷贝的smali目录的路径。
在Make Directory As/Sources root后,在Project Struct中设置java版本,如下图:

然后点击Run=>Edit configurations...菜单,设置添加Remote和设置端口8700,如下图:

6. 打开HelloAndroid$2.smali文件,定位到onClick,并在合适的地方下断点,Alt+Shift+F10运行,如下图所示:

根据需要,可以在界面的右下方的Watches中添加需要监视的变量,如v10等。
7. 分析过程
# virtual methods
.method public onClick(Landroid/view/View;)V
.locals 25
.param p1, "v" # Landroid/view/View;
.prologue
.line 75
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
const v23, 0x7f050004
invoke-virtual/range {v22 .. v23}, Lcom/example/helloandroid/HelloAndroid;->findViewById(I)Landroid/view/View;
move-result-object v9
check-cast v9, Landroid/widget/EditText;
.line 76
.local v9, "name2":Landroid/widget/EditText;
invoke-virtual {v9}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
move-result-object v22
invoke-interface/range {v22 .. v22}, Landroid/text/Editable;->toString()Ljava/lang/String;
move-result-object v10 // 这行得到name: phrank
.line 77
.local v10, "name3":Ljava/lang/String;
invoke-virtual {v10}, Ljava/lang/String;->length()I
move-result v11 // 得到name的长度nameLen: 6
.line 78
.local v11, "name3lenght":I
const-string v12, ""
.line 79
.local v12, "name4":Ljava/lang/String;
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
const v23, 0x7f050006
invoke-virtual/range {v22 .. v23}, Lcom/example/helloandroid/HelloAndroid;->findViewById(I)Landroid/view/View;
move-result-object v21
check-cast v21, Landroid/widget/EditText;
.line 80
.local v21, "txtserial":Landroid/widget/EditText;
invoke-virtual/range {v21 .. v21}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
move-result-object v22
invoke-interface/range {v22 .. v22}, Landroid/text/Editable;->toString()Ljava/lang/String;
move-result-object v15 // 得到假serial: 112233
.line 84
.local v15, "serial_entered":Ljava/lang/String;
const/16 v22, 0x4
move v0, v11
move/from16 v1, v22
if-ge v0, v1, :cond_0 // nameLen 是否大于等于4
.line 86
:try_start_0
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
invoke-virtual/range {v22 .. v22}, Lcom/example/helloandroid/HelloAndroid;->getApplicationContext()Landroid/content/Context;
move-result-object v22
.line 87
const-string v23, "Min 4 chars"
const/16 v24, 0x1
.line 86
invoke-static/range {v22 .. v24}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v13
.line 88
.local v13, "notificacionToast":Landroid/widget/Toast;
invoke-virtual {v13}, Landroid/widget/Toast;->show()V
.line 129
:goto_0
return-void
.line 92
.end local v13 # "notificacionToast":Landroid/widget/Toast;
:cond_0
const/4 v5, 0x0
.local v5, "i":I
:goto_1
invoke-virtual {v10}, Ljava/lang/String;->length()I
move-result v22
move v0, v5
move/from16 v1, v22
if-lt v0, v1, :cond_1
.line 98
const/16 v22, 0x0 // 循环完毕后跳转到这里
const/16 v23, 0x5
move-object v0, v12
move/from16 v1, v22
move/from16 v2, v23
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->substring(II)Ljava/lang/String; // 取tmp的前5个字符,v0[0:5] = "11210"
move-result-object v12
.line 99
invoke-static {v12}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I // 把上面的"11210"转换为整数类型,得11210
move-result v22
const v23, 0x6b016
xor-int v22, v22, v23 // 上面的11210和0x6b016(十进制的438294)异或得:11210 xor 438294 = 433116
invoke-static/range {v22 .. v22}, Ljava/lang/String;->valueOf(I)Ljava/lang/String; // 把433116转换为字符串,得"433116"
move-result-object v12
.line 100
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
const-string v23, "phone"
invoke-virtual/range {v22 .. v23}, Lcom/example/helloandroid/HelloAndroid;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;
move-result-object v8
check-cast v8, Landroid/telephony/TelephonyManager;
.line 101
.local v8, "mTelephonyMgr":Landroid/telephony/TelephonyManager;
invoke-virtual {v8}, Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; // 得到imei: "299725335498632"
move-result-object v6
.line 102
.local v6, "imei2":Ljava/lang/String;
invoke-virtual {v8}, Landroid/telephony/TelephonyManager;->getSimSerialNumber()Ljava/lang/String; // 得到iccid: "89014103211118516789"
move-result-object v16
.line 103
.local v16, "simsn":Ljava/lang/String;
const-wide/16 v17, 0x0
.line 104
.local v17, "temp01":J
const/16 v22, 0x0
const/16 v23, 0x6
move-object v0, v6
move/from16 v1, v22
move/from16 v2, v23
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->substring(II)Ljava/lang/String; // 取imei的前6个字符,得
// 到imeiSix(就是上面的temp01,我们这
// 里使用我们自己的变量名): "299725"
move-result-object v19
.line 105
.local v19, "temp02":Ljava/lang/String;
const/16 v22, 0x0
const/16 v23, 0x6
move-object/from16 v0, v16
move/from16 v1, v22
move/from16 v2, v23
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->substring(II)Ljava/lang/String; // 取iccid的前6个字符,得iccidSix: "890141"
// 对应于我们使用JEB反编译后的 iccid.substring(0, 6)
move-result-object v20
.line 106
.local v20, "temp03":Ljava/lang/String;
invoke-static/range {v19 .. v19}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I // 把imeiSix字符串转换为整型,得299725
move-result v22
invoke-static/range {v20 .. v20}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I // 把iccidSix转换为整型,得890141
move-result v23
xor-int v22, v22, v23 // 299725 xor 890141 = 591824
move/from16 v0, v22
int-to-long v0, v0
move-wide/from16 v17, v0
.line 107
new-instance v22, Ljava/lang/StringBuilder;
invoke-static {v12}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v23
invoke-direct/range {v22 .. v23}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
const-string v23, "-"
invoke-virtual/range {v22 .. v23}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v22 // 得到"433116-"
invoke-static/range {v17 .. v18}, Ljava/lang/String;->valueOf(J)Ljava/lang/String;
move-result-object v23
invoke-virtual/range {v22 .. v23}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v22
const-string v23, "-"
invoke-virtual/range {v22 .. v23}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v22 // 得到"433116-591824-"
move-object/from16 v0, v22
move-object/from16 v1, v19
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v22 // 得到"433116-591824-299725"
invoke-virtual/range {v22 .. v22}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v14 // 这个v14就是我们在静态分析中通过DDMS输出的"433116-591824-299725"
.line 109
.local v14, "serial":Ljava/lang/String;
invoke-virtual {v14, v15}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z // 在这里比较v14和v15的值是否相等,也就是真serial和假serial作比较
move-result v22 // true or false
if-eqz v22, :cond_2 // 根据true or false进行跳转
.line 111
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
invoke-virtual/range {v22 .. v22}, Lcom/example/helloandroid/HelloAndroid;->getApplicationContext()Landroid/content/Context;
move-result-object v22
.line 112
const-string v23, "God boy"
const/16 v24, 0x1
.line 111
invoke-static/range {v22 .. v24}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v13
.line 113
.restart local v13 # "notificacionToast":Landroid/widget/Toast;
invoke-virtual {v13}, Landroid/widget/Toast;->show()V
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
goto/16 :goto_0
.line 123
.end local v5 # "i":I
.end local v6 # "imei2":Ljava/lang/String;
.end local v8 # "mTelephonyMgr":Landroid/telephony/TelephonyManager;
.end local v13 # "notificacionToast":Landroid/widget/Toast;
.end local v14 # "serial":Ljava/lang/String;
.end local v16 # "simsn":Ljava/lang/String;
.end local v17 # "temp01":J
.end local v19 # "temp02":Ljava/lang/String;
.end local v20 # "temp03":Ljava/lang/String;
:catch_0
move-exception v22
move-object/from16 v4, v22
.line 125
.local v4, "ex":Ljava/lang/Exception;
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
invoke-virtual/range {v22 .. v22}, Lcom/example/helloandroid/HelloAndroid;->getApplicationContext()Landroid/content/Context;
move-result-object v22
.line 126
const-string v23, "Another Error Ocurred :("
const/16 v24, 0x1
.line 125
invoke-static/range {v22 .. v24}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v13
.line 127
.restart local v13 # "notificacionToast":Landroid/widget/Toast;
invoke-virtual {v13}, Landroid/widget/Toast;->show()V
goto/16 :goto_0
.line 93
.end local v4 # "ex":Ljava/lang/Exception;
.end local v13 # "notificacionToast":Landroid/widget/Toast;
.restart local v5 # "i":I
:cond_1
:try_start_1
invoke-virtual {v10, v5}, Ljava/lang/String;->charAt(I)C // name.charAt(i): 'p'(112), 'h'(104), 'r'(114), 'a'(97), 'n'(110), 'k'(107)
move-result v3
.line 94
.local v3, "c":C
move v7, v3
.line 95
.local v7, "j":I
new-instance v22, Ljava/lang/StringBuilder;
invoke-static {v12}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v23
invoke-direct/range {v22 .. v23}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
move-object/from16 v0, v22
move v1, v7
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v22 // tmp: "112", "112104", "112104114", "11210411497", "11210411497110", "11210411497110107"
invoke-virtual/range {v22 .. v22}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v12
.line 92
add-int/lit8 v5, v5, 0x1
goto/16 :goto_1
.line 116
.end local v3 # "c":C
.end local v7 # "j":I
.restart local v6 # "imei2":Ljava/lang/String;
.restart local v8 # "mTelephonyMgr":Landroid/telephony/TelephonyManager;
.restart local v14 # "serial":Ljava/lang/String;
.restart local v16 # "simsn":Ljava/lang/String;
.restart local v17 # "temp01":J
.restart local v19 # "temp02":Ljava/lang/String;
.restart local v20 # "temp03":Ljava/lang/String;
:cond_2
move-object/from16 v0, p0 // 因为我们输入的假serial不正确,所以cond_2跳转到这里
iget-object v0, v0, Lcom/example/helloandroid/HelloAndroid$2;->this$0:Lcom/example/helloandroid/HelloAndroid;
move-object/from16 v22, v0
invoke-virtual/range {v22 .. v22}, Lcom/example/helloandroid/HelloAndroid;->getApplicationContext()Landroid/content/Context;
move-result-object v22
.line 117
const-string v23, "Bad boy " // 要被提示的serial错误的字符串
const/16 v24, 0x1
.line 116
invoke-static/range {v22 .. v24}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v13
.line 118
.restart local v13 # "notificacionToast":Landroid/widget/Toast;
invoke-virtual {v13}, Landroid/widget/Toast;->show()V
:try_end_1
.catch Ljava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_0
goto/16 :goto_0
.end method
调试结束之前的Android Studio界面如下图:

整理下上面的分析过程:
1> 在循环中把name的每个字符转换为对应的整数形式的字符串"11210411497110107",并在串接后取其前5个字符,得到tmp: "11210",转换为整型后得11210;
2> 把11210和常数438294进行异或:11210 xor 438294 = 433116
3> 得到imei"299725335498632"和iccid"89014103211118516789";
4> 取imei的前6个字符得imeiSix: "299725";
5> 取iccid的前6个字符,得iccidSix: "890141";
6> imeiSix和iccidSix的整型形式相异或:299725 xor 890141 = 591824;
7> 使用"-"串接得到"433116-591824-299725"
分析的时候可多次在Watches窗口中添加vxx变量以观察寄存器的值。在JEB官方对调试功能的介绍一文(地址是https://www.pnfsoftware.com/blog/jeb-android-debuggers/)中得知JEB提供了Hover功能(我自己瞎取的名字),就是当鼠标放置于寄存器、变量、类名等上时显示相关的信息,包括寄存器和变量的值,这个简直是太方便了。目前调试功能还处于Beta版,不好用,让我们耐心地等待稳定版吧。
四、编写注册机
通过静态分析和动态分析,我们已经知道了serial的生成过程,为了验证我们的分析是否正确,我们编写一个注册机,代码如下:
import java.util.Scanner;
public class keygen {
public static void main(String[] args) throws Exception {
System.out.print("Enter HardwareID 01:\t");
Scanner scan = new Scanner(System.in);
String imei = scan.nextLine();
System.out.print("Enter HardwareID 02:\t");
String iccid = scan.nextLine();
System.out.print("Enter your name:\t");
String name = scan.nextLine();
if (name.length() < 4) {
System.out.println("Min 4 chars\n");
return;
}
String tmp = "";
for (int i=0; i<name.length(); i++) {
tmp = String.valueOf(tmp) + (int)(name.charAt(i));
}
tmp = String.valueOf(Integer.parseInt(tmp.substring(0, 5)) ^ 438294);
String imeiSix = imei.substring(0, 6);
String serial = String.valueOf(tmp) + "-" + String.valueOf(((long)(Integer.parseInt(imeiSix) ^ Integer.parseInt(iccid.substring(0, 6))))) + "-" + imeiSix;
System.out.println("Your serial:\t\t" + serial);
}
}
编译运行后可以试试输入不同的name而产生不同的serial,如另外一组:
Enter HardwareID 01: 299725335498632
Enter HardwareID 02: 89014103211118516789
Enter your name: moon
Your serial: 432777-591824-299725
但可以发现当name为phrank和phrankmoon时生成的注册码是一样的,究其原因在于这一行:
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->substring(II)Ljava/lang/String; // 取tmp的前5个字符,v0[0:5] = "11210"也就是说,tmp只取了name各字符转换为整型后并串接的字符串中的前5个字符"11210",因此,只有字母p和字母h真正参与了计算。
现在简单说一下程序界面上的HardwareID 01和HardwareID 02的作用,我推测是为了提供给他人,以方便他人检验注册机生成的serial是否正确。
五、后记
这个Crackme的算法简单,单纯的静态分析已经足够,动态分析主要是为了学习调试的方法。
还有一个就是感谢ApkIDE、Android Studio,JEB和天天模拟器的开发者以及背后工具的开发者,特别感谢currwin、maxking和scz对JEB所做的工作。
原Crackme在这里: Android_Crackmes03_by_deurus.zip
