学习破解一个Android程序

news/2024/12/27 13:56:08/文章来源:https://www.cnblogs.com/abyssdawn/p/18635580

首先编写一个android测试程序

功能:校验用户名和注册码,成功则弹出注册成功提示

以下仅给出关键部分的代码

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical"><TextViewandroid:id="@+id/textView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@string/info"android:textSize="20dp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/username" /><EditTextandroid:id="@+id/edit_username"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_weight="1"android:ems="10"android:hint="@string/hint_username"></EditText></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/sn" /><EditTextandroid:id="@+id/edit_sn"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_weight="1"android:ems="10"android:hint="@string/hint_sn"></EditText></LinearLayout><Buttonandroid:id="@+id/button_register"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right"android:layout_marginRight="10dp"android:text="@string/register" /></LinearLayout>

java/com/example/myapplication/MainActivity.java

package com.example.myapplication;import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class MainActivity extends Activity {private EditText edit_userName;private EditText edit_sn;private Button btn_register;@Overridepublic 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);  //模拟程序已注册}}});}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.activity_main, menu);return true;}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对用户名进行HashString 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();}}

2024-07-22T06:35:25.png

运行没有问题之后,通过AndroidStudio编译成apk文件

开始破解程序

破解 Android 程序通常的方法是将 apk 文件利用 ApkTool 反编译,生成 Smali 格式的反汇编代码,然后阅读 Smali 文件的代码来理解程序的运行机制,找到程序的突破口进行修改,最后使用 ApkTool 重新编译生成 apk 文件并签名,最后运行测试,如此循环,直至程序被成功破解。

使用apk-tool反编译apk程序

下载地址:https://down.52pojie.cn/Tools/Android_Tools/apktool_2.9.3.jar

执行

java -jar apktool_2.9.3.jar d -f app-debug.apk -o output

2024-07-22T06:45:41.png

smail目录下存放了程序的所有反汇编代码,res目录下则是程序的所有资源文件

先通过res\values\string.xml定位程序的错误信息

<resources>
...<string name="unsuccessed">无效用户名或注册码</string>
...
</resources>

再通过同目录下的public.xml找到name="unsuccessed"的id

<public type="string" name="unsuccessed" id="0x7f1000a5" />

通过该id可以到smail目录搜索,在output\smali_classes3\com\example\myapplication\MainActivity$1.smali搜索到一处结果

   91      iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;92  93:     const v2, 0x7f1000a594  95      invoke-static {v0, v2, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;

onclick方法


# virtual methods
.method public onClick(Landroid/view/View;)V.locals 3.param p1, "v"    # Landroid/view/View;.line 31iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;invoke-static {v0}, Lcom/example/myapplication/MainActivity;->access$000(Lcom/example/myapplication/MainActivity;)Landroid/widget/EditText;move-result-object v1invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;move-result-object v1invoke-virtual {v1}, Ljava/lang/Object;->toString()Ljava/lang/String;move-result-object v1invoke-virtual {v1}, Ljava/lang/String;->trim()Ljava/lang/String;move-result-object v1iget-object v2, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;.line 32invoke-static {v2}, Lcom/example/myapplication/MainActivity;->access$100(Lcom/example/myapplication/MainActivity;)Landroid/widget/EditText;move-result-object v2invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;move-result-object v2invoke-virtual {v2}, Ljava/lang/Object;->toString()Ljava/lang/String;move-result-object v2invoke-virtual {v2}, Ljava/lang/String;->trim()Ljava/lang/String;move-result-object v2.line 31invoke-static {v0, v1, v2}, Lcom/example/myapplication/MainActivity;->access$200(Lcom/example/myapplication/MainActivity;Ljava/lang/String;Ljava/lang/String;)Zmove-result v0const/4 v1, 0x0if-nez v0, :cond_0 # 关键条件判断.line 33iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;const v2, 0x7f1000a5invoke-static {v0, v2, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;move-result-object v0.line 34invoke-virtual {v0}, Landroid/widget/Toast;->show()Vgoto :goto_0.line 36:cond_0iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;const v2, 0x7f1000a2invoke-static {v0, v2, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;move-result-object v0.line 37invoke-virtual {v0}, Landroid/widget/Toast;->show()V.line 38iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;invoke-static {v0}, Lcom/example/myapplication/MainActivity;->access$300(Lcom/example/myapplication/MainActivity;)Landroid/widget/Button;move-result-object v0invoke-virtual {v0, v1}, Landroid/widget/Button;->setEnabled(Z)V.line 39iget-object v0, p0, Lcom/example/myapplication/MainActivity$1;->this$0:Lcom/example/myapplication/MainActivity;const v1, 0x7f100099invoke-virtual {v0, v1}, Lcom/example/myapplication/MainActivity;->setTitle(I)V.line 41:goto_0return-void
.end method

经过分析,if-nez v0, :cond_0判断决定了校验是否通过,这句代码的意思是:如果v0不为0则跳转到cond_0,也就是注册失败的分支。

破解方法:将if-nez改为if-eqz也就是等于则为真

修改后保存,执行如下代码重新编译

java -jar apktool_2.9.3.jar b output

编译后需要对apk进行签名,这里我本地生成了一个测试签名

keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore

然后用360加固助手的工具包进行签名。

接下来可以通过adb命令安装和启动

adb install app-debug_sign.apk
adb shell am start -n com.example.myapplication/.MainActivity

2024-07-22T08:00:54.png

至此破解就算完成了。

使用IDA pro破解

因为使用apktool每次都需要重新编译,很花时间,idapro提供了快速测试的方法。

下载地址:https://down.52pojie.cn/Tools/Disassemblers/IDA_Pro_v8.3_Portable.zip

用压缩包工具打开app-debug.apk,提取出classes.dex文件,通过ida pro打开

注意:如果classes.dex没有可能在classes3.dex

按照先找错误信息的思路,按alt+t打开文本搜索功能,搜索:0x7f1000a5

2024-07-22T08:35:50.png

定位到关键代码(按空格可以切换视图)

2024-07-23T01:31:23.png

可以看到分支判断位于CODE:000006BE,将光标放在if-nez处,点击hex-view,修改if-nez的字节码

39 00 0F 00改为38 00 0F 00

然后关闭ida pro,不需要保存到database

c32asm打开classes3.dex定位到000006BE,将39改为38,保存退出

2024-07-23T01:41:13.png

这里由于修改了dex文件,会导致dex文件在验证计算checksum会失败,从而导致程序安装失败,因此需要重新计算checksum值

用Dexfixer将classes3.dex文件checksum值修复

2024-07-23T01:51:10.png

将修复好的classes3.dex,重新拉入apk

aapt r app-debug.apk classes3.dex
aapt a app-debug.apk classes3.dex

删除META-INT(可使用winrar工具),并重新签名apk即可破解成功。

image

参考:《Android软件安全与逆向分析》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/859829.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DP1363F是一款高度集成的非接触读写芯片,高性能、多协议NFC读卡IC

DP1363F是一款高度集成的非接触读写芯片,集强大的多协议支持、最高射频输出功率,以及突破性技术低功耗卡片检测等优势于一身,满足市场对更高集成度、更小外壳和互操作性的需求,适用于银行、电子政务、交通、移动支付等众多基础设施应用。 DP1363F支持下列操作模式: •读写模…

ASP.NET Core 中的速率限制中间件的使用

简介 在ASP.NET Core中,速率限制中间件是用来控制客户端对Web API或MVC应用程序发出请求的速率,以防止服务器过载和提高安全性。 下面是 AddRateLimiter 的一些基本用法: 1. 注册服务 在 Startup.cs 或 Program.cs 中,需要注册 AddRateLimiter 服务。这可以通过以下代码完成…

java 使用HttpClient发送@RequestBody类型的请求(解决中文乱码)

如何使用java发送参数以@RequestBody类型接收到的请求。如下使用java的HttpClient写一个样例 依赖<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.10</version> </dep…

再战博客园美化(五)

再战系列更新到几来着。。。 (看一眼)哦,原来更新到五了! 这一次,我直接来个连接到本机,在线调试!好了,似了。 https是什么东西,我有吗? csdn启动真的假的,自签名还能过系统,这是拿了管理员权限? https://xiaoshen.blog.csdn.net/article/details/135893188我就知…

CDS标准视图:设备信息 I_EquipmentData

视图名称:I_EquipmentData 视图类型:基础视图 视图代码:点击查看代码 @EndUserText.label: Equipment Data @VDM.viewType: #COMPOSITE @AbapCatalog.sqlViewName: IEQUIPMENTATTR @AbapCatalog.compiler.compareFilter: true @ClientHandling.algorithm: #SESSION_VARIABLE…

四款简洁又好用的日记app推荐

以前使用纸质的笔记本来写日记,但是最近几年再也没有写过日记了,最近又想要开始写日记,发现用日记本app会更加简单方便。 打开手机就能给直接记录,除了记录文字,还可以保存图片、语音、视频等,更加简单便捷! 1、念念手帐 优点:画风可爱,很适合喜欢可爱风格的女生。可以…

polarctf-crypto困难难度wp整理(截止至2024.12)

分段解密 加密脚本如下: import sysdef abc(First): First = c_uint32(First)return Firstdef enflag(i, j):a = 32tt = 0x9e3779b9b = [0,0]First = abc(i[0])Second = abc(i[1])add = abc(0)add=add.valuewhile(a>0):add += ttFirst.value += ( Second.value << 4…

我的第一篇博客!学习Markdown

Markdown学习 标题 (#[空格]+标题名字) 二级标题##[空格]+标题名字 三级标题###[空格]+标题名字 以此类推,最高六级; 字体 Hello,World! (两边加两个星号"**"为设置粗体); Hello,World! (两边加一个星号"*"为设置斜体); Hello,World! (两边加三个星号“**…

Python包管理不再头疼:uv工具快速上手

Python 包管理生态中存在多种工具,如 pip、pip-tools、poetry、conda 等,各自具备一定功能。 而今天介绍的uv 是 Astral 公司推出的一款基于 Rust 编写的 Python 包管理工具,旨在成为 “Python 的 Cargo”。 它提供了快速、可靠且易用的包管理体验,在性能、兼容性和功能上都…

博客园markdown及配图自动上传配置

解决自己使用markdown写博客,上传到博客园上面,需要一张一张重新上传图片的问题博客园markdown与配图自动上传配置(mac版 m芯片)引言 安装配置工具 尾注前言 目的:因为自己使用markdown写博客,但是在博客园上面发布的时候,发现本地写好的markdown文件,上传到博客园上面…

【unity】学习制作类银河恶魔城游戏-4-

制作攻击计数器给全部攻击动作应用帧事件但是理想情况下应该是,短间隔时间内连续点击鼠标才能连击,加入连击计时器编辑代码修补“桶子” 解决攻击时移动的问题解决冲刺时攻击的问题解决无方向键输入时原地冲刺的问题解决空中攻击无法掉落的问题继承“inheritance”(继承)是…

React 移动端框架

1. Material-UI Material-UI 组件是独立工作的。 它们是自我支持的,并只要注入而且仅注入它们需要显示的样式。 他们不依赖任何全局的样式表,如 normalize.css。Material-UI首先是移动开发的,我们首先为移动设备编写代码,然后根据需要使用CSS媒体查询扩展组件。 要确保所有…