Android apk逆向实战
简介
逆向Android apk其实是一个分析Android apk的一个过程,必须了解Android程序开发的流程、结构、语句分支、解密原理等等。
功能
破解一个注册验证程序(自写一个简单的注册验证程序,然后分析它,再破解它)。
步骤
1、编写一个简单的注册验证apk,关键代码如下:
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 = toHexString(bytes, ""); //将计算结果转化成字符串
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hexstr.length(); i += 2) {
sb.append(hexstr.charAt(i));
}
String userSN = sb.toString(); //计算出的SN
//Log.d("crackme", hexstr);
//Log.d("crackme", userSN);
if (!userSN.equalsIgnoreCase(sn)) //比较注册码是否正确
return false;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return false;
}
return true;
}
private static String toHexString(byte[] bytes, String separator) { //转为十六进制
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xFF & b);
if(hex.length() == 1){
hexString.append('0');
}
hexString.append(hex).append(separator);
}
return hexString.toString();
}
这个方法的主要功能是计算用户名与注册码是否匹配。
运行效果如图:
2、分析、破解
下载开源工具ApkTool:http://code.google.com/p/android-apktool/
破解Android apk的方法是将apk文件利用ApkTool反编译,生成Smali格式的反汇编代码,然后分析Smali文件的代码运行机制,找到程序的突破口进程修改,最后使用ApkTool
重新编译生成apk文件并签名。
命令格式如下:
反编译apk:apktool d[ecode] [opts] <file.apl> [<dir>]
编译apk:apktool b[uild] [opts] [<app_path>] [<out_file>]
运行效果如图:
反编译apk文件成功后,会在当前的outdir目录下(默然是apk文件名)生成一系列目录与文件,其中smali目录下存放了程序所有的反汇编代码,res目录则是程序中所有的资源文件,这些目录的子目录和文件与原程序的源码目录结构是一致的。
如何寻找突破口是分析一个程序的关键,一般来说,错误提示信息通常是指引关键代码的风向标,在错误提示附近一般是程序的核心验证码。
错误提示是Android 程序中的字符串资源,这些字符串可能硬编码到源代码中,也可能引用自 “res目录下的string.xml文件,apk打包时,string.xml中的字符串被加密存储为resources.arsc文件保存到apk程序包中。
string.xml 详情如下:
<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>
对应加密文件(public.xml)详情:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="drawable" name="ic_launcher" id="0x7f020001" />
<public type="drawable" name="ic_action_search" id="0x7f020000" />
<public type="layout" name="activity_main" id="0x7f030000" />
<public type="dimen" name="padding_small" id="0x7f040000" />
<public type="dimen" name="padding_medium" id="0x7f040001" />
<public type="dimen" name="padding_large" id="0x7f040002" />
<public type="string" name="app_name" id="0x7f050000" />
<public type="string" name="hello_world" id="0x7f050001" />
<public type="string" name="menu_settings" id="0x7f050002" />
<public type="string" name="title_activity_main" id="0x7f050003" />
<public type="string" name="info" id="0x7f050004" />
<public type="string" name="username" id="0x7f050005" />
<public type="string" name="sn" id="0x7f050006" />
<public type="string" name="register" id="0x7f050007" />
<public type="string" name="hint_username" id="0x7f050008" />
<public type="string" name="hint_sn" id="0x7f050009" />
<public type="string" name="unregister" id="0x7f05000a" />
<public type="string" name="registered" id="0x7f05000b" />
<public type="string" name="unsuccessed" id="0x7f05000c" />
<public type="string" name="successed" id="0x7f05000d" />
<public type="style" name="AppTheme" id="0x7f060000" />
<public type="menu" name="activity_main" id="0x7f070000" />
<public type="id" name="textView1" id="0x7f080000" />
<public type="id" name="edit_username" id="0x7f080001" />
<public type="id" name="edit_sn" id="0x7f080002" />
<public type="id" name="button_register" id="0x7f080003" />
<public type="id" name="menu_settings" id="0x7f080004" />
</resources>
unsuccessed的id值为0x7f05000c,在smali目录中搜索含有内容为0x7f05000c的文件,最后发现只有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 #调用checkSN函数
#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-neqz v0, :cond_0 #关键跳转 程序的破接口 把if-eqz v0 ---->if-eqz v0 即可
.line 34#获取实例的引用
iget-object v0, p0, Lcom/droider/crackme0201/MainActivity$1;->this$0:Lcom/droider/crackme0201/MainActivity; .line 35 字符串压人v1寄存器
const v1, 0x7f05000c注意!!!!!!!!!!!! .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 .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 invoke-virtual {v0, v1}, Lcom/droider/crackme0201/MainActivity;->setTitle(I)V goto :goto_0
.end method
分析完毕,重新编译生成apk文件并签名
签名成功后会在同目录下生成signed.apk文件,如图:
破解完成,放入模拟器运行下,可以了。
小结
通过实战破解一个简单的验证程序,了解Android程序的一般分析与破解流程,但在实际的分析过程中,接触的代码远比这些要复杂得多。
下载
Android apk逆向实战的更多相关文章
- Android apk逆向:反编译,回编译,签名,打包。
Android apk逆向:反编译,回编译,签名,打包流程. 第一步: apk 反编译. 1) 打开命令行窗口,输入java -version, 检测当前java版本,若版本较低, 则下载JAVA S ...
- android apk 逆向中常用工具一览
关于apk 逆向中的一些工具的使用,看了不少文章,也使用过有很长一段时间了,今天对此做一总结: 几种文件之间的转换命令: 1. odex -> smali java -jar baksmali ...
- Android逆向系列文章— Android基础逆向(6)
本文作者:HAI_ 0×00 前言 不知所以然,请看 Android逆向-Android基础逆向(1) Android逆向-Android基础逆向(2) Android逆向-Android基础逆向(2 ...
- Android逆向-Android基础逆向(5)
本文作者:i春秋作家——HAI_ 0×00 前言 不知所以然,请看 Android逆向-Android基础逆向(1)Android逆向-Android基础逆向(2)Android逆向-Android基 ...
- 转: android apk 防止反编译技术(1~5连载)
转: android apk 防止反编译技术 做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习. ...
- android apk 防止反编译技术第一篇-加壳技术
做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习.现在将最近学习成果做一下整理总结.学习的这些成 ...
- Android apk反编译基础(apktoos)图文教程
本文主要介绍了Android apk反编译基础,使用的工具是apktoos,我们将用图文的方式说明apktoos工具的使用方式,你可以参考这个方法反编译其它APK试试看了 很久有写过一个广工图书馆主页 ...
- Android 手势检测实战 打造支持缩放平移的图片预览效果(下)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39480503,本文出自:[张鸿洋的博客] 上一篇已经带大家实现了自由的放大缩小图 ...
- 《Android Studio开发实战 从零基础到App上线》资源下载和内容勘误
转载于:https://blog.csdn.net/aqi00/article/details/73065392 资源下载 下面是<Android Studio开发实战 从零基础到App上线&g ...
随机推荐
- BootStrap 智能表单系列 四 表单布局介绍
表单的布局分为自动布局和自定义布局两种: 自动布局就是根据配置项中第二级配置项中数组的长度来自动使用不同的bootstrap栅格,通过设置autoLayout为true可以实现自动布局 自动以布局就是 ...
- CSS了一个浮动导航条
绝对浏览器窗口定位positio:FIXED: 下拉后出现返回顶部按钮 图片是我们美工给做的.55*55px,中间缝隙3px. css: html,body { height:100%}html,bo ...
- ASP.NET性能监视参数详解
性能监视器- Performance Monitor 性能监视器是Windows自带的系统资源和性能监视工具. 性能监视器能够量化地提供CPU使用率, 内存分配状况, 异常派发情况, 线程调度频率等信 ...
- 注册表缺失导致Windows Server 2008 R2时钟服务W32time不能自启
参照@飘云 http://blog.csdn.net/piaoyunqing/article/details/6323647 的文章. 测试环境中有一台Windows Server 2008 R2的虚 ...
- JavaScript 堆
1.IE中不兼容ajax中data最后一个参数加逗号,其余chrome Firefox均支持. code: $("document ").ready(function() { $( ...
- .NET study collection links
Parameter Binding in ASP.NET Web API http://www.asp.net/web-api/overview/formats-and-model-binding/p ...
- Windows10 上运行Ubuntu Bash
Windows10 上运行Ubuntu Bash 2016年4月6日,Windows 10 Insider Preview 发布的版本 14316,添加了Ubuntu Bash,在Windows上提供 ...
- 操作 IoT 设备内嵌 SQLite
Win10 IoT C#开发 5 - 操作 IoT 设备内嵌 SQLite 数据库 CURD Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本 ...
- 【图文教程】用“iz3d”软件将您的游戏打造为红蓝3D游戏。
iz3d是一款能将普通3D游戏转换为红蓝3D游戏的软件.基本上支持所有游戏,或许没用过的人会认为这只是类似于播放器中的一个小功能,将平面图形做成“伪3D”红蓝效果. 实际上不是的,游戏与平面图的结构不 ...
- (C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单
原文 (C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单 (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windo ...