新建一个AndroID工程,名称为Crackme0201
吧
主界面添加用户名和注册码输入框
<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:app="http://schemas.androID.com/apk/res-auto" xmlns:tools="http://schemas.androID.com/tools" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:orIEntation="vertical" tools:context=".MainActivity" androID:ID="@+ID/linearLayout"> <TextVIEw androID:ID="@+ID/textVIEw" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:text="@string/top_string" androID:textAlignment="center" androID:gravity="center_horizontal" /> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:orIEntation="horizontal"> <TextVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@string/username"/> <EditText androID:ID="@+ID/edit_username" androID:layout_wIDth="0dp" androID:layout_height="wrap_content" androID:ems="10" androID:layout_marginRight="10dp" androID:layout_marginleft="10dp" androID:layout_weight="1" androID:hint="@string/hint_username"/> </linearLayout> <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" androID:orIEntation="horizontal"> <TextVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@string/regCode"/> <EditText androID:ID="@+ID/edit_regCode" androID:layout_wIDth="0dp" androID:layout_height="wrap_content" androID:ems="10" androID:hint="@string/hint_regCode" androID:layout_marginleft="10dp" androID:layout_marginRight="10dp" androID:layout_weight="1" /> </linearLayout> <button androID:ID="@+ID/button_register" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:text="@string/btn_register" androID:layout_marginRight="10dp" androID:layout_gravity="right"/></linearLayout>
然后,编写MainActivity
类的代码,添加一个checkSN()
方法
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(); String hexstr = toHexString(bytes, ""); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < hexstr.length(); i+=2) { stringBuilder.append(hexstr.charat(i)); } String userSN = stringBuilder.toString(); //Log.d("crackme", hexstr); //Log.d("crackme", userSN); if (!userSN.equalsIgnoreCase(sn)) { return false; } } catch (NoSuchAlgorithmException e) { e.printstacktrace(); return false; } return true; }
该方法计算用户名与注册码是否匹配,用MD5计算用户名的散列值,转换为hex串,128bit转换为32位十六进制串,再取32位中的奇数位,拼成16位,即为注册码。
最后判断传入的注册码与计算得出的注册码是否匹配。
在MainActivity
中的onCreate()
方法中添加注册按钮点击事件
@OverrIDe protected 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_regCode); btn_register = (button) findVIEwByID(R.ID.button_register); btn_register.setonClickListener(new VIEw.OnClickListener() { @OverrIDe 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.register); } } }); }
如果匹配,则d出注册成功,如果不匹配,则d出注册无效的提示
编译APK文件
有两种编译方式,使用命令行和AS的Build菜单
命令行gradlew是基于task来编译项目的,在命令行下执行gradlew tasks
会列出所有支持的task,我们主要关注编译使用的Build tasks和安装APK时用的Install tasks
生成DeBUG调试版本的APK:./gradlew assembleDeBUG
,会在Crackme0201/app/build/outputs/apk
目录下生成app-deBUG.apk文件
安装DeBUG版本APK:./gradlew installDeBUG
,会安装到与系统相连的设备/模拟器
adb devices
,列出当前连接的设备,包括模拟器
直接点击run app
会自动编译安装
只编译不安装点make project
破解第一个AndroID程序
以Crackme0201为例
通常方法是,使用ApkTool
反编译APK文件,生成smali格式的反汇编代码,通过阅读smali代码理解程序运行机制,找到突破口进行修改,再使用ApkTool
工具重新编译为APK并签名,进行测试
上述过程循环,直到完全破解
实际中还可以用IDA Pro
直接分析APK文件,使用dex2jar
和jd-gui
配合进行Java源码级分析
下载ApkTool工具,添加路径到PATH环境变量,命令行直接使用
apktool d ./apk-deBUG.apk -o outdir
会在outdir
文件夹下生成反编译文件,smali
目录下是程序的所有反汇编代码,res
目录下是程序所有资源文件,子目录的文件结构与开发源码一致。
找到突破口,一般是错误提示信息,属于字符串资源
APK打包时,string.xml
文件被加密存储为resources.arsc
文件,反编译会解密
string.xml
中的所有字符串资源都在gen/R.java
文件中的String类中标识,每个字符串都有int类型的索引值,对应关系在同目录下的public.xml
文件中
找到unsuccessed
字符串和对应索引值
但是搜索文件夹下没有与索引值相关的信息
接下来生成Release版本APK文件
用AS进行签名生成app-release.apk,再用apktool进行反编译
找到unsuccessed
对应的索引值,全局搜索,在MainActivity.smali
中找到了索引值
在.line 33
处调用checkSN()
(但是这里是access
,没有#备注,不知道为啥) .line 33 invoke-static {p1, v0, v1}, Lorg/nuaa/crackme0201/MainActivity;->access
0Boolean
0(Lorg/nuaa/crackme0201/MainActivity;Ljava/lang/String;Ljava/lang/String;)Z move-result p1 const/4 v0, 0x0 if-nez p1, :cond_0
move-result p1
意思是,返回const/4 v0, 0x0if-nez p1, :cond_0
类型值(哪里看出来的?)
返回结果保存在p1
.line 35 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this.line 35
:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b0032 invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 36 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V goto :goto_0
忽略掉第二行,对寄存器p1进行判断,其值不为0,即条件为真则跳转cond_0处
iget-object
继续往下看,不跳转会执行
MainActivity
在-this
处事要MainActivity
synthetic
指令获取const v1, 0x7f0b0032
的实例的引用,Toast;->maketoast()
是内部类 .line 38 :cond_0 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->this
中的一个,line 32
:Lorg/nuaa/crackme0201/MainActivity; const v1, 0x7f0b002f invoke-static {p1, v1, v0}, LandroID/Widget/Toast;->makeText(LandroID/content/Context;II)LandroID/Widget/Toast; move-result-object p1 .line 39 invoke-virtual {p1}, LandroID/Widget/Toast;->show()V .line 40 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-nez p1, :cond_0
:Lorg/nuaa/crackme0201/MainActivity; iget-object p1, p1, Lorg/nuaa/crackme0201/MainActivity;->btn_register:LandroID/Widget/button; invoke-virtual {p1, v0}, LandroID/Widget/button;->setEnabled(Z)V .line 41 iget-object p1, p0, Lorg/nuaa/crackme0201/MainActivity;->thisif-eqz
:Lorg/nuaa/crackme0201/MainActivity; const v0, 0x7f0b002c invoke-virtual {p1, v0}, Lorg/nuaa/crackme0201/MainActivity;->setTitle(I)V :goto_0 return-voIDif-eqz
字段,存储父类MainActivity的引用
if-nez
该指令是将v1寄存器传入0x7f0b0032,也就是unsuccessed的ID值,接下来调用if-eqz p1, :cond_0
如果跳转cond_0,则执行如下指令
apktool b outdir_rel
这里的代码是注册成功时候的逻辑
修改smali文件代码通过上述分析,outdir_rel/dist
处的代码,apksigner sign --ks [xxx.jks] --out [xxx.apk] [signed.apk]
是跳转关键点
这是Dalvik指令集的一个条件跳转指令(和汇编差不多),应该是not equal zero,不等于0的意思,相反的指令是apksigner
,表示等于0时跳转
就用--ks
代替--out
,保存修改
adb uninstall org.nuaa.crackme0201
重新编译APK并签名修改好以后,使用apktool重新编译打包
adb install signed.apk
在AndroID-Crack-Tool
目录下生成apk文件
要安装还需要签名,之前签名的时候生成了证书,我们可以用命令行来对apk进行签名
AndroID Killer
是AndroID的build-tool中自带的,后接数字证书存储路径,后接签名后apk输出路径,最后是原apk路径
在Terminal下,模拟器开启的情况下,推出原应用,输入命令卸载
再安装签名后的apk
安装成功,测试随意的用户名+注册码都可以验证成功。
总结
一般流程
反编译 -> 分析 -> 修改 -> 回编译 -> 签名
集成工具:MAC下,windows下
总结以上是内存溢出为你收集整理的<<Android软件安全权威指南>>笔记1 第一个Android程序分析全部内容,希望文章能够帮你解决<<Android软件安全权威指南>>笔记1 第一个Android程序分析所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)