yangyunhui

功不唐捐,玉汝于成

Android逆向学习笔记(六)

Android逆向学习笔记(六)

《安卓逆向这档事》三、初识smali,vip终结者

目录


smali及其语法

数据类型

Java类型 Smali表示 说明
byte B 字节型
char C 字符型
short S 短整型
int I 整型
long J 长整型
float F 单精度浮点
double D 双精度浮点
boolean Z 布尔型
void V 无返回值

常量

const/4 v0,0x1 4位常量:v0=1
const/16 v0,0x100 16位常量:v0=256
const v0, 0x12345678 32位常量
const-wide/16 v0, 0x100 宽(64位)常量
const-string v0, “hello” 字符串常量
const-class v0, Ljava/lang/String; 类常量

算数运算指令

add-int v0, v1, v2 v0 = v1 + v2 (int)
sub-int v0, v1, v2 v0 = v1 - v2
mul-int v0, v1, v2 v0 = v1 * v2
div-int v0, v1, v2 v0 = v1 / v2
rem-int v0, v1, v2 v0 = v1 % v2

条件控制指令

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 获取寄存器数据

寄存器

在smali里的所有操作都必须经过寄存器来进行:

本地寄存器 用v开头数字结尾的符号来表示,如v0、 v1、v2。

参数寄存器 则使用p开头数字结尾的符号来表示,如p0、p1、p2。

特别注意的是,p0不一定是函数中的第一个参数,在非static函数中,p0代指“this”,p1表示函数的第一个 参数,p2代表函数中的第二个参数。而在static函数中p0才对应第一个参数(因为Java的static方法中没有this方法)

demo3作业

首先下载核心破解,下载好之后会有弹窗显示权限问题,点击弹窗

下载好这个软件之后,即使我们的签名不一致也可以重新安装。

打开这个demo看看关键词是什么

可以看到是请先充值大会员

在开始破解之前,我们需要知道代码的逻辑是什么

将apk文件拖入jadx中查看java源与smali代码

逻辑很简单:

  1. 当金币数量小于10时,提醒先获得10个硬币
  2. 当不是会员时提醒先充值会员

(smali码太长不再展示,由于jadx无法加注释,看unicode码很难受,我就使用了Androidkiller来进行反编译,这样得到的unicode可以看见中文,比较方便)

破解逻辑:

  1. 修改跳转条件,使得能执行会员逻辑
  2. 使用goto直接跳转
  3. 修改寄存器内的值

修改跳转条件

用MT管理器打开demo后,定位到我们要改的地方(可以直接搜索:大会员)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  const/4 p5, 0x1

const/16 v0, 0xa

if-ge p0, v0, :cond_15

.line 34
move-object p0, p1

check-cast p0, Landroid/content/Context;

const-string v0, "请先获取10个硬币哦"

check-cast v0, Ljava/lang/CharSequence;

invoke-static {p0, v0, p5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object p0

invoke-virtual {p0}, Landroid/widget/Toast;->show()V

.line 36
:cond_15
invoke-virtual {p1}, Lcom/zj/wuaipojie/ui/ChallengeSecond;->isvip()Z

move-result p0

if-eqz p0, :cond_43

.line 37
check-cast p1, Landroid/content/Context;

const-string p0, "当前已经是大会员了哦!"

check-cast p0, Ljava/lang/CharSequence;

invoke-static {p1, p0, p5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object p0

invoke-virtual {p0}, Landroid/widget/Toast;->show()V

const p0, 0x7f0d0018

.line 38
invoke-virtual {p2, p0}, Landroid/widget/ImageView;->setImageResource(I)V

const p0, 0x7f0d0008

.line 39
invoke-virtual {p3, p0}, Landroid/widget/ImageView;->setImageResource(I)V

const p0, 0x7f0d000a

.line 40
invoke-virtual {p4, p0}, Landroid/widget/ImageView;->setImageResource(I)V

.line 41
sget-object p0, Lcom/zj/wuaipojie/util/SPUtils;->INSTANCE:Lcom/zj/wuaipojie/util/SPUtils;

const/4 p2, 0x2

const-string p3, "level"

invoke-virtual {p0, p1, p3, p2}, Lcom/zj/wuaipojie/util/SPUtils;->saveInt(Landroid/content/Context;Ljava/lang/String;I)V

goto :goto_50

.line 44
:cond_43
check-cast p1, Landroid/content/Context;

const-string p0, "请先充值大会员哦!"

check-cast p0, Ljava/lang/CharSequence;

invoke-static {p1, p0, p5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object p0

invoke-virtual {p0}, Landroid/widget/Toast;->show()V

:goto_50
return p5
.end method

代码如上

这一段就是先判断金币够不够10,如果够10的话,跳转到cond_15(下一个if语句),那么要想改变这个逻辑,使得如果不够也要跳转,就将if-ge 改成if-le。

这段就是判断p0是否等于0,如果等于0的话跳转到cond_43(请充值会员这里),如果想直接往下运行,不进行跳转,可以直接将这个跳转语句注释掉(往前加#即可)。

使用goto直接跳转

我们可以直接跳过中间的if判断,直接进行goto跳转

加完goto之后,直接改变了我们的控制流

改完之后,保存,成功!

修改寄存器的值

到这里其实根据修改寄存器的值也能明显知道怎么修改,我们会跳转就是因为不满足条件,那我们直接将寄存器改了让它满足条件不就好了?

首先是这里,我想让它跳转到cond_15,但是必须满足p0>=v0才可以,这里的v0=10,那直接将v0改成0,不管p0等于多少都是大于等于的。

代码向下执行时,会进入isvip()这个函数,将返回值赋给p0,这里的move-result:

当你调用一个方法后(使用 invoke-* 指令),该方法的返回值会存储在一个特殊的寄存器中,你需要使用 move-result 或 move-result-wide 将其移动到指定的寄存器中才能使用。

再往下看,如果po=0就跳转到cond_43,这里同样不能跳转,那就要修改isvip()的返回值,我们跟进isvip()这个函数。

这里的返回值是v0,我们将v0改成1就可以了。保存,成功!

流程可以简单总结一下:

  1. 根据关键词查到相关函数。
  2. 修改函数逻辑(改变执行流或者改变寄存器值)