Android反编译学习-使用APKtool破解注册类程序
2015-04-20 17:04
253 查看
#1 机制
破解Android 程序通常的方法是将
apk 文件利用
ApkTool 反编译,生成
Smali 格式的反汇编代码,然后阅读
Smali
文件的代码来理解程序的运行机制,找到程序的突破口进行修改,最后使用
ApkTool 重新编译生成
apk 文件并签名,最后运行测试,如此循环,直至程序被成功破解。
#2 准备工作
APKtool的安装使用请参考笔者上篇博客编写软件Crackme_01用做测试
value文件夹下的String.xml内容如下
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Crackme0201</string> <string name="hello_world">Hello world!</string> <string name="menu_settings">Settings</string> <string name="title_activity_main">crackme02</string> <string name="info">Android程序破解演示实例</string> <string name="username">用户名: </string> <string name="sn">注册码: </string> <string name="register">注 册</string> <string name="hint_username">请输入用户名</string> <string name="hint_sn">请输入16位的注册码</string> <string name="unregister">程序未注册</string> <string name="registered">程序已注册</string> <string name="unsuccessed">无效用户名或注册码</string> <string name="successed">恭喜您!注册成功</string> </resources>
onCreate方法修改如下
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setTitle(R.string.unregister); // 模拟程序未注册 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() { public void onClick(View v) { 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).show(); btn_register.setEnabled(false); setTitle(R.string.registered); // 模拟程序已注册 } } }); }
MainActivity中添加类
private boolean checkSN(String userName, String sn) { try { if ((userName == null) || (userName.length() == 0)) return false; if ((sn == null) || (sn.length() != 16)) return false; MessageDigest digest = MessageDigest.getInstance("MD5"); digest.reset(); digest.update(userName.getBytes()); byte[] bytes = digest.digest(); // 采用MD5对用户名进行Hash String hexstr = bytes2HexString(bytes); // 将计算结果转化成字符串 StringBuilder sb = new StringBuilder(); for (int i = 0; i < hexstr.length(); i += 2) { sb.append(hexstr.charAt(i)); } String userSN = sb.toString(); // 计算出的SN if (!userSN.equalsIgnoreCase(sn)) // 比较注册码是否正确 return false; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return false; } return true; }
在上面的checkSN中用到的bytes2string方法如下
public String bytes2HexString(byte[] b) { byte[] hex ="0123456789ABCDEF".getBytes(); byte[] buff = new byte[2 * b.length]; for (int i = 0; i < b.length; i++) { buff[2 * i] = hex[(b[i] >> 4) & 0x0f]; buff[2 * i + 1] = hex[b[i] & 0x0f]; } return new String(buff); }
布局文件如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/root"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="android程序破解演示实例1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="用户名:" /> <EditText android:id="@+id/edit_username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:ems="10" > </EditText> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="注册码:" /> <EditText android:id="@+id/edit_sn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:ems="10" /> </LinearLayout> <Button android:id="@+id/button_register" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="注册" /> </LinearLayout>
#3 破解过程
反编译apk 文件成功后,会在当前的
outdir 目录下生成一系列目录与文件。其中
smali目录下存放了程序所有的反汇编代码,
res 目录则是程序中所有的资源文件,这些目录的子目录和文件与开发时的源码目录组织结构是一致的。如何寻找突破口是分析一个程序的关键。对于一般的
Android
来说,错误提示信息通常是指引关键代码的风向标,在错误提示附近一般是程序的核心验证代码,分析人员需要阅读
这些代码来理解软件的注册流程。错误提示是
Android 程序中的字符串资源,开发
Android 程序时,这些字符串可能硬编码到源码中,也可能引用自“
res\values”目录下的
strings.xml
文件, apk
文件在打包时,strings.xml
中的字符串被加密存储resources.arsc
文件保存到 apk
程序包中, apk
被成功反编译后这个文件也被解密出来了。
在软件注册失败时会弹出“无效用户名或注册码”,我们以此为线索来寻找关键代码。打开“
res\values\string.xml”文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Crackme0201</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">crackme02</string>
<string name="info">Android程序破解演示实例</string>
<string name="username">用户名:
</string>
<string name="sn">注册码:
</string>
<string name="register">注 册</string>
<string name="hint_username">请输入用户名</string>
<string name="hint_sn">请输入16位的注册码</string>
<string name="unregister">程序未注册</string>
<string name="registered">程序已注册</string>
<string name="unsuccessed">无效用户名或注册码</string>
<string name="successed">恭喜您!注册成功</string>
</resources>
开发
Android 程序时,
String.xml 文件中的所有字符串资源都在“
gen/<packagename>/
R.java”文件的
String
类中被标识,每个字符串都有唯一的 int
类型索引值,使用 Apktool
反
编译
apk 文件后,所有的索引值保存在
string.xml 文件同目录下的
public.xml 文件中。
unsuccessed
的
id 值为
0x7f05000c,在
smali 目录中搜索含有内容为
0x7f05000c 的文件,
最后发现只有
MainActivity$1.smali 文件一处调用,代码如下:
# virtual methods
.method public onClick(Landroid/view/View;)V
.locals 4
.parameter "v"
.prologue
const/4 v3, 0x0
……
.line 32
#calls:
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
if-nez v0, :cond_0
#如果结果不为0,就跳转到cond_0标号处
.line 34
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
this$0:Lcom/droider/crackme0201/MainActivity;
.line 35
const v1, 0x7f05000c# unsuccessed字符串
.line 34
invoke-static {v0, v1, v3}, Landroid/widget/Toast;->
makeText(Landroid/content/Context;II)Landroid/widget/Toast;
move-result-object v0
.line 35
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
.line 42
:goto_0
return-void
.line 37
:cond_0
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
this$0:Lcom/droider/crackme0201/MainActivity;
.line 38
const v1, 0x7f05000d# successed字符串
.line 37
invoke-static {v0, v1, v3}, Landroid/widget/Toast;->
makeText(Landroid/content/Context;II)Landroid/widget/Toast;
move-result-object v0
.line 38
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
.line 39
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
this$0:Lcom/droider/crackme0201/MainActivity;
#getter for: Lcom/droider/crackme0201/MainActivity;->btn_register:Landroid/
widget/Button;
invoke-static {v0}, Lcom/droider/crackme0201/MainActivity;->
access$3(Lcom/droider/crackme0201/MainActivity;)Landroid/widget
/Button;
move-result-object v0
invoke-virtual {v0, v3}, Landroid/widget/Button;->setEnabled(Z)V#设置注册按钮不可用
.line 40
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->
this$0:Lcom/droider/crackme0201/MainActivity;
const v1, 0x7f05000b# registered字符串,模拟注册成功
invoke-virtual {v0, v1}, Lcom/droider/crackme0201/MainActivity;->
setTitle(I)V
goto :goto_0
.end method
Smali
代码中添加的注释使用井号“
#”开头,“
.line 32”行调用了
checkSN()函数进行注册码的合法检查,接着下面有如下两行代码:
move-result v0
if-nez v0, :cond_0
checkSN()函数返回
Boolean
类型的值。这里的第一行代码将返回的结果保存到 v0
寄存器中,第二行代码对
v0
进行判断,如果 v0
的值不为零,即条件为真的情况下,跳转到 cond_0标号处,反之,程序顺利向下执行。
分析可以发现,“
.line 32”行的代码“
if-nez v0, :cond_0”是程序的破解点。
if-nez
是 Dalvik
指令集中的一个条件跳转指令,类似的还有 if-eqz、
if-gez、
if-lez
等。这些指令会在本书第
3 章进行介绍,读者在这里只需要知道,与
if-nez 指令功能相反的指令为if-eqz,表示比较结果为
0
或相等时进行跳转。用任意一款文本编辑器打开
MainActivity$1.smali 文件,将“
.line 32”行的代码“
if-nezv0, :cond_0”修改为“
if-eqz v0, :cond_0”,保存后退出,代码就算修改完成了。修改完
Smali
文件代码后,需要将修改后的文件重新进行编译打包成
apk 文件,签名后安装,随意输入,可以发现注册成功。
相关文章推荐
- APK反编译利器Apktool实现android程序汉化(转http://www.cnmsdn.com/html/201008/1282267450ID7326.html)
- Android学习之三:使用DDMS调试程序
- 使用天乐软件加密狗(JDProtect)保护您的软件,防止程序被跟踪/逆向/反编译/破解
- android 学习笔记(五) 调试相关 5.1 android使用wifi进行程序调试
- 如何使用反编译软件破解android的布局文件
- android 反编译 apktool 的使用 dex2jar
- Android程序apk反编译破解方法
- 学习Android之第五个小程序新浪微博样式(Listview的使用)
- android使用apktool反编译出现Input file (d:\t) was not found or was not readable
- Android学习——在Android中使用OpenCV的第一个程序
- Android-Mac电脑如何进行APK反编译-使用apktool、jd-gui
- 使用apktool 进行android APK 反编译和打包
- Android学习——在Android中使用OpenCV的第一个程序
- (转)Android-Mac电脑如何进行APK反编译-使用apktool、jd-gui
- 使用android-apktool来逆向(反编译)APK包方法介绍
- Android APK反编译 apktool使用教程
- Android-Mac电脑如何进行APK反编译-使用apktool、jd-gui
- 【android学习】onSaveInstanceState使用详解-之解决问题:android程序崩溃后,app异常
- Android-Mac电脑如何进行APK反编译-使用apktool、jd-gui
- 一个 Android程序员的小程序入门学习笔记『二』(template 模版的使用注意)