破解第一个程序----分析APK文件
反编译APK成功后,在outdir目录下会生成一系列目录与文件。
smali:程序所有的反汇编代码;
res:程序中所有的资源文件;
如何寻找突破口是分析程序的关键。错误提示一般是指引关键代码的风向标,错误提示附近一般是核心验证代码,我们需要阅读这些代码来理解软件的注册流程。
错误提示是安卓中的字符串资源:
1:硬编码到源码中;
2:引用自“res\values"目录下的string.xml文件。
apk文件在打包时,string.xml文件中的字符串被加密存储在resources.arsc文件保存到APK程序包中,APK被反编译之后这个文件也被解密出来。
eg:编写一个安卓程序:
功能:计算用户名与注册码是否匹配。
计算方法:使用MD5算法计算这个用户名字符串的hash,将计算所得的结果转换为长度为32位的十六进制字符串,然后取字符串的所有奇数位重新组合生成新的字符串。此字符串就是最终的注册码。将它与传入的字符串相比较,如果想等,即正确。运行结果如下图:
开始分析:
当程序运行错误时:会弹出,无效的用户名或注册码,以此线索来寻找关键代码。打开"res\values\string.xml"文件:
<?xml version="1.0" encoding="UTF-8"?> -<resources> <string name="app_name">Crackme0201</string> <string name="menu_settings">Settings</string> <string name="title_activity_main">Crackme0201</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>
string.xml中的字符串资源在"gen/<packagename>/R.java"文件中的string类中被标识,每一个字符串都有唯一的Int类型索引值,apktool反编译之后,所有索引值在public.xml文件中。
<?xml version="1.0" encoding="UTF-8"?> -<resources> <public id="0x7f020001" name="ic_launcher" type="drawable"/> <public id="0x7f020000" name="ic_action_search" type="drawable"/> <public id="0x7f030000" name="activity_main" type="layout"/> <public id="0x7f040000" name="padding_small" type="dimen"/> <public id="0x7f040001" name="padding_medium" type="dimen"/> <public id="0x7f040002" name="padding_large" type="dimen"/> <public id="0x7f050000" name="app_name" type="string"/> <public id="0x7f050001" name="menu_settings" type="string"/> <public id="0x7f050002" name="title_activity_main" type="string"/> <public id="0x7f050003" name="info" type="string"/> <public id="0x7f050004" name="username" type="string"/> <public id="0x7f050005" name="sn" type="string"/> <public id="0x7f050006" name="register" type="string"/> <public id="0x7f050007" name="hint_username" type="string"/> <public id="0x7f050008" name="hint_sn" type="string"/> <public id="0x7f050009" name="unregister" type="string"/> <public id="0x7f05000a" name="registered" type="string"/> <public id="0x7f05000b" name="unsuccessed" type="string"/> <public id="0x7f05000c" name="successed" type="string"/> <public id="0x7f060000" name="AppTheme" type="style"/> <public id="0x7f070000" name="activity_main" type="menu"/> <public id="0x7f080000" name="textView1" type="id"/> <public id="0x7f080001" name="edit_username" type="id"/> <public id="0x7f080002" name="edit_sn" type="id"/> <public id="0x7f080003" name="button_register" type="id"/> <public id="0x7f080004" name="menu_settings" type="id"/> </resources>
可知:unsuccessed的值为0x7f05000b;
接下来在smali目录中搜索含有内容为0X7F05000b的文件:【注:在文件下面的组织里面的--文件夹和搜索选项中--搜索---始终搜索文件名和内容】即可搜索。(有点问题,有时候搜不出来。正在钻研中。)
最后发现:只有MainActivity$1.smali文件一处被调用。
.class Lcom/droider/crackme0201/MainActivity$1; .super Ljava/lang/Object; .source "MainActivity.java" # interfaces .implements Landroid/view/View$OnClickListener; # annotations .annotation system Ldalvik/annotation/EnclosingMethod; value = Lcom/droider/crackme0201/MainActivity;->onCreate(Landroid/os/Bundle;)V .end annotation .annotation system Ldalvik/annotation/InnerClass; accessFlags = 0x0 name = null .end annotation # instance fields .field final synthetic this$0:Lcom/droider/crackme0201/MainActivity; # direct methods .method constructor <init>(Lcom/droider/crackme0201/MainActivity;)V .locals 0 .parameter .prologue .line 1 iput-object p1, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity; .line 29 invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method # virtual methods .method public onClick(Landroid/view/View;)V .locals 4 .parameter "v" .prologue const/4 v3, 0x0 .line 32 iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity; iget-object v1, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity; #getter for: Lcom/droider/crackme0201/MainActivity;->edit_userName:Landroid/widget/EditText; invoke-static {v1}, Lcom/droider/crackme0201/MainActivity;->access$0(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/EditText; move-result-object v1 invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable; move-result-object v1 invoke-interface {v1}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v1 invoke-virtual {v1}, Ljava/lang/String;->trim()Ljava/lang/String; move-result-object v1 .line 33 iget-object v2, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity; #getter for: Lcom/droider/crackme0201/MainActivity;->edit_sn:Landroid/widget/EditText; invoke-static {v2}, Lcom/droider/crackme0201/MainActivity;->access$1(Lcom/droider/crackme0201/MainActivity;)Landroid/widget/EditText; move-result-object v2 invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable; move-result-object v2 invoke-interface {v2}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v2 invoke-virtual {v2}, Ljava/lang/String;->trim()Ljava/lang/String; move-result-object v2 .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 .line 33 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, 0x7f05000b .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;
#使用iget-object指令获取MainActivity实例的引用。 .line 38 const v1, 0x7f05000c .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, 0x7f05000a invoke-virtual {v0, v1}, Lcom/droider/crackme0201/MainActivity;->setTitle(I)V goto :goto_0 .end method
修改Smali文件代码:
if-nez v0, :cond_0 #如果结果不为0,则跳转到cond_0处。修改为:
if-eqz v0, :cond_0 #如果结果为0或等于0,则跳转到cond_0处。
然后对修改后的文件进行重新编译打包成apk文件。
在cmd中输入:apktool b outdir
编译生成的apk没有签名,还不能安装测试。接下里需要用signapk.jar工具对apk文件进行签名。(见后文)
安装测试:
由于AVD太慢,所以在手机设备上运行;转换成手机模式:window---OpenPerspective---DDOS,双击即可。
在命令行下:adb install signed.apk
安装完后,随便输入注册码,就会出现:恭喜您,注册成功字样!