安卓相关环境配置
偶尔更新。。。
JEB(动态调试好用)
JEB动态调试Smali-真机/模拟器(详细,新手必看) 夜步城
JADX官网(静态分析)
https://github.com/skylot/jadx/releases/tag/v1.5.0
雷电模拟器自己去官网下
Magisk 面具
https://www.52pojie.cn/thread-1583586-1-1.html
LSPosed官网
https://github.com/LSPosed/LSPosed/releases/download/v1.8.6/LSPosed-v1.8.6-6712-zygisk-release.zip
MT管理器(吾爱破解论坛里搜)
adb (记得加环境变量)
https://developer.android.google.cn/tools/releases/platform-tools?hl=zh-cn#downloads
android studio(不太行,甚至打不开xml文件,环境难配)
https://developer.android.google.cn/studio?hl=zh-cn
吐槽一下插件套娃:Magisk->LSPosed->核心破解
文章目录
- 安卓相关环境配置
- 1. 模拟器环境配置
- 2. 小概念
- 3. smali语法
- 4. 使用JEB进行动态调试
- 5. debug模式调试
- 参考
1. 模拟器环境配置
雷电模拟器9,设置把root权限开一下,然后重启
磁盘共享(可写入)
将magisk安装目录里的app-debug.apk
拖入模拟器中。
点击magisk->永久记住选择->安装->允许
点击右下角的正方形按钮,然后中间向左拖动退回关掉。
点击magisk->安装->下一步->安装至系统分区->开始
然后重启雷电模拟器,进magisk设置,把Zygisk开启
更多->共享文件->打开电脑文件夹
把下载好的LSPosed.zip文件拖进共享文件夹
magisk点击右下角模块->从本地安装->选择Picture目录下的LSPosed.zip进行安装
重启雷电模拟器,下拉状态栏,打开LSPosed管理器
在LSPosed仓库里下载核心破解(MT管理器改了xml后,apk不用验证签名就可以安装)。
若模拟器没挂代理,则把网址复制一下,在主机访问进行下载,再把安装包拖进模拟器。
模拟器安装核心破解后,需要启动核心破解模块,然后重新启动。
2. 小概念
详见正己佬的视频
apk 全称 Android Package,它相当于一个压缩文件,只要在电脑上将apk后缀改为zip即可解压。
classes.dex是APK运行的主要逻辑。
Dalvik是Google专门为Android设计的一个虚拟机,Dalvik有专属的文件执行格式dex(Dalvik executable)
Art(Android Runtime)相当于Dalvik的升级版,本质与Dalvik无异
安卓四大组件
组件 | 描述 |
---|---|
Activity(活动) | 在应用中的一个Activity可以用来表示一个界面,意思可以理解为“活动”,即一个活动开始,代表 Activity组件启动,活动结束,代表一个Activity的生命周期结束。一个Android应用必须通过Activity来运行和启动,Activity的生命周期交给系统统一管理。 |
Service(服务) | Service它可以在后台执行长时间运行操作而没有用户界面的应用组件,不依赖任何用户界面,例如后台播放音乐,后台下载文件等。 |
Broadcast Receiver(广播接收器) | 一个用于接收广播信息,并做出对应处理的组件。比如我们常见的系统广播:通知时区改变、电量低、用户改变了语言选项等。 |
Content Provider(内容提供者) | 作为应用程序之间唯一的共享数据的途径,Content Provider主要的功能就是存储并检索数据以及向其他应用程序提供访问数据的接口。Android内置的许多数据都是使用Content Provider形式,供开发者调用的(如视频,音频,图片,通讯录等) |
3. smali语法
smali是Dalvik的寄存器语言,smali代码是dex反编译而来的。类似汇编
关键字
名称 | 注释 |
---|---|
.class | 类名 |
.super | 父类名,继承的上级类名名称 |
.source | 源名 |
.field | 变量 |
.method | 方法名 |
.local v0,"bytes":[B | 定义局部变量v0为字节数组,"bytes"是局部变量的名称(调试时有用) |
.register | 寄存器 |
.end method | 方法名的结束 |
public | 公有 |
protected | 半公开,只有同一家人才能用 |
private | 私有,只能自己使用 |
.parameter | 方法参数 |
.prologue | 方法开始 |
.line xxx | 位于第xxx行 |
数据类型对应
smali类型 | java类型 | 注释 |
---|---|---|
V | void | 无返回值 |
Z | boolean | 布尔值类型,返回0或1 |
B | byte | 字节类型,返回字节 |
S | short | 短整数类型,返回数字 |
C | char | 字符类型,返回字符 |
I | int | 整数类型,返回数字 |
J | long (64位 需要2个寄存器存储) | 长整数类型,返回数字 |
F | float | 单浮点类型,返回数字 |
D | double (64位 需要2个寄存器存储) | 双浮点类型,返回数字 |
string | String | 文本类型,返回字符串 |
Lxxx/xxx/xxx | object | 对象类型,返回对象 |
[I | int[] | int数组 |
[[I | int[][] | 二维整型数组 |
[Ljava/lang/String | String对象数组 |
常用指令
关键字 | 注释 |
---|---|
const | 重写整数属性,真假属性内容,只能是数字类型,const/4 为4bit数字 |
const-string | 重写字符串内容 |
const-wide | 重写长整数类型,多用于修改到期时间。 |
return | 返回指令 |
if-eq | 全称equal(a=b),比较寄存器ab内容,相同则跳 |
if-ne | 全称not equal(a!=b),ab内容不相同则跳 |
if-eqz | 全称equal zero(a=0),z即是0的标记,a等于0则跳 |
if-nez | 全称not equal zero(a!=0),a不等于0则跳 |
if-ge | 全称greater equal(a>=b),a大于或等于则跳 |
if-le | 全称little equal(a<=b),a小于或等于则跳 |
goto | 强制跳到指定位置 |
switch | 分支跳转,一般会有多个分支线,并根据指令跳转到适当位置 |
iget | 获取寄存器数据 |
调用方法
invoke-virtual {v0, v1}, Ljava/lang/String;->length()I
//调用`v0`对象的实例方法`length()`,传参为`v1`,返回值类型为`Int`
invoke-super {v0, v1}, Ljava/lang/Object;->toString()Ljava/lang/String;
//v0重写了Object类的toString方法,这里调用了Object类原本的toString方法
invoke-direct {v0}, Lcom/example/MyClass;-><init>()V
//调用实例的私有方法、构造方法或者父类的私有方法
invoke-static {}, Ljava/lang/Math;->random()D
//调用静态方法,由于静态方法属于类而非类的实例,因此调用时不需要对象引用。
invoke-interface {v0}, Ljava/util/List;->size()I
//v0是实现了List接口的对象的引用,调用List接口中的方法size()
invoke-custom {v0, v1}, Ljava/lang/invoke/LambdaMetafactory;->metafactory(...)Ljava/lang/invoke/CallSite;
//用于动态绑定方法调用
结果处理
invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object v0
//将这个返回的字符串对象引用移动到寄存器v0中
move-result v0
//将前一个函数调用返回的基本数据类型的结果移动到指定的寄存器中。
const/4 v1, 0x5
move v0, v1 //将v1中的值0x5移动到v0中
在smali里的所有操作都必须经过寄存器来进行:本地寄存器用v开头数字结尾的符号来表示,如v0、 v1、v2。 参数寄存器则使用p开头数字结尾的符号来表示,如p0、p1、p2。特别注意的是,p0不一定是函数中的第一个参数,在非static函数中,p0代指“this",p1表示函数的第一个 参数,p2代表函数中的第二个参数。而在static函数中p0才对应第一个参数(因为Java的static方法中没有this方法)
4. 使用JEB进行动态调试
想要动态调试,首先得修改debug权限
法一:在AndroidManifest.xml里添加可调试权限
android:debuggable="true"
法二:XappDebug模块hook对应的app
法三:Magisk命令(重启失效)
1. adb shell #adb进入命令行模式
2. su #切换至超级用户
3. magisk resetprop ro.debuggable 1
4. stop;start; #一定要通过该方式重启
自行下载吾爱破解第五课的课后作业
安装配置好JEB
shift+鼠标滚轮 代码横向滚动
Ctrl+B 打断点
Tab 根据java代码找到smali代码所在处
jadx全局搜索无效
后,全局搜索unsuccessed
,追踪onClick
函数
jadx追踪checkSN
private boolean checkSN(String userName, String sn) {
// 如果用户名为空,直接返回false
if(userName == null) {
return false;
}
try {
// 检查用户名非空且SN非空且长度为16
if(userName.length() != 0 && (sn != null && sn.length() == 16)) {
// 获取MD5 MessageDigest实例
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.reset();//注意:reset()在这里实际上是多余的,因为每次new MessageDigest实例时,它都是初始化的
digest.update(userName.getBytes()); // 这里使用默认字符集编码,可能在不同环境中导致不同结果
// 将字节数组转换为十六进制字符串
String hexstr = MainActivity.toHexString(digest.digest(), "");
// 尝试将十六进制字符串转换为字符数组
StringBuilder sb = new StringBuilder();
int i;
for(i = 0; true; i += 2) { // 无限循环,直到i超出hexstr长度
if(i >= hexstr.length()) {
// 这里应该是比较整个转换后的字符串与sn
return sb.toString().equalsIgnoreCase(sn);
}
// 这里直接将单个十六进制字符转换为字符,而不是每两个十六进制字符转换为一个字节的字符
sb.append(((char)hexstr.charAt(i))); //(char)会将十六进制字符转换为ASCII字符
}
}
}
catch(NoSuchAlgorithmException e) {
// MD5算法通常总是可用的,这里的catch块可能永远不会执行
e.printStackTrace();
}
// 如果没有匹配,返回false
return false;
}
JEB反编译一下并找到return sb.toString().equalsIgnoreCase(sn);
的位置,按Tab
键,使左侧smali代码跟踪到该函数位置,然后Ctrl+B
打断点。
雷电模拟器启动apk程序,JEB附加进程调试
程序输入用户名,注册码(必须16位),点击注册
JEB查看v5寄存器的值,实际就是注册的sn码。
a96fa7cd071995a2
5. debug模式调试
- **普通模式:**无法调试界面的 创建,即 onCreate 函数
- **debug 模式:**可以调试 界面的 onCreate 函数,即刚开始创建界面是就开始调试
下载攻防世界android-app-100的附件。
模拟器设置里面,版本号点击7次以上进入开发者模式。
系统->高级->开发者选项->打开USB调试
模拟器MT管理器->安装包提取->查看附件安装包->修改AndroidManifest.xml
,
加上android:debuggable="true"
可以选择不签名,然后虽然校验不通过,但是可以安装(已经核心破解)。
把修改后的安装包通过共享文件夹的方式传到主机。
jadx查看应用程序包名/应用程序入口界面,因为MT没充会员,xml与真实的有出入。
主机开启cmd
adb shell am start -D -n 应用程序包名/应用程序入口界面
adb shell am start -D -n com.example.ctf2/.MainActivity
然后模拟器打开程序,JEB附加调试
然后在输入框输入东西,再点login就会断在断点处。
但是这题IsCorrect函数在运行时加载,没法步入函数,需要IDA打开libadnjni.so才能看到逻辑。
是个字符串比较函数
ef57f3fe3cf603c03890ee588878c0ec
就是注册码
参考
-
吾爱破解安卓逆向入门教程《安卓逆向这档事》一、模拟器环境搭建
-
《安卓逆向这档事》二、初识APK文件结构、双开、汉化、基础修 正己
-
最新Windows安装adb方法 飞⑧风暴
-
JEB动态调试Smali-真机/模拟器(详细,新手必看) 夜步城
-
Smali基础语法总结 bmjoker
-
安卓逆向_5 — jeb 和 AndroidStudio 动态调试 smali 擒贼先擒王