[TOC]
这篇文章主要介绍几个简单的例子,来逆向分析so,当然有些破解可以在smali里直接修改,但是这里均采用逆向so的方式。
1.将ARM指令转换为Hex时,可以使用工具Arm_Asm,如下图:
2.可以根据ARM参考手册,手动转换。比如add的指令格式如下:
当执行add r0,#1时,Rdn为0,imm8为1,所以二进制为00110000 00000001,转换为16进制就是3001,所以Hex code就是0130
3.百度文库上有人上传了一份整理好的thumb指令集,如果不想到ARM参考手册上找,可以看看这个。
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
v
is
immed
_value
n
is
Rn
m
is
Rm
s
is
Rs
r
is
register
_list
c
is
condition
表一:按指令字母升序排列
0100
0001
01mm
mddd
--
ADC
Rd
,
Rm
0001
110v
vvnn
nddd
--
ADD
Rd
,
Rn
,
#
immed
_3
0011
0ddd
vvvv
vvvv
--
ADD
Rd
,
#
immed
_8
0001
100m
mmnn
nddd
--
ADD
Rd
,
Rn
,
Rm
0100
0100
hhmm
mddd
--
ADD
Rd
,
Rm
h1h2
,
h1
is
msb
for
Rd
,
h2
is
msb
for
Rm
1010
0ddd
vvvv
vvvv
--
ADD
Rd
,
PC
,
#
immed_8
*
4
1010
1ddd
vvvv
vvvv
--
ADD
Rd
,
SP
,
#
immed_8
*
4
1011
0000
0vvv
vvvv
--
ADD
SP
,
#
immed_7
*
4
0100
0000
00mm
mddd
--
AND
Rd
,
Rm
0001
0vvv
vvmm
mddd
--
ASR
Rd
,
Rm
,
#
immed
_5
0100
0001
00ss
sddd
--
ASR
Rd
,
Rs
1101
cccc
vvvv
vvvv
--
Bcc
signed_immed
_8
1110
0vvv
vvvv
vvvv
--
B
signed_immed
_11
0100
0011
10mm
mddd
--
BIC
Rd
,
Rm
1011
1110
vvvv
vvvv
--
BKPT
immed
_8
111h
hvvv
vvvv
vvvv
--
BL
(
X
)
immed
_11
0100
0111
1hmm
mSBZ
--
BLX
Rm
0100
0111
0Hmm
mSBZ
--
BX
Rm
0100
0010
11mm
mnnn
--
CMN
Rn
,
Rm
0010
1nnn
vvvv
vvvv
--
CMP
Rn
,
#
immed
_8
0100
0010
10mm
mnnn
--
CMP
Rn
,
Rm
0100
0101
hhmm
mnnn
--
CMP
Rn
,
Rm
0100
0000
01mm
mddd
--
EOR
Rd
,
Rm
1100
1nnn
rrrr
rrrr
--
LDMIA
Rn
!
,
reg
_list
0110
1vvv
vvnn
nddd
--
LDR
Rd
,
[
Rn
+
#
immed_5
*
4
]
0101
100m
mmnn
nddd
--
LDR
Rd
,
[
Rn
,
Rm
]
0100
1ddd
vvvv
vvvv
--
LDR
Rd
,
[
PC
+
#
immed_5
*
4
]
1001
1ddd
vvvv
vvvv
--
LDR
Rd
,
[
SP
,
#
immed_8
*
4
]
0111
1vvv
vvnn
nmmm
--
LDRB
Rd
,
[
Rn
,
#
immed_5
*
4
]
0101
110m
mmnn
nddd
--
LDRV
Rd
,
[
Rn
,
Rm
]
1000
1vvv
vvnn
nddd
--
LDRH
Rd
,
[
Rn
,
#
immed_5
*
2
]
0101
101m
mmnn
nddd
--
LDRH
Rd
,
[
Rn
,
Rm
]
0101
011m
mmnn
nddd
--
LDRSB
Rd
,
[
Rn
,
Rm
]
0101
111m
mmnn
nddd
--
LDRSH
Rd
,
[
Rn
,
Rm
]
0000
0vvv
vvmm
mnnn
--
LSL
Rd
,
Rm
,
#
immed
_5
0100
0000
10ss
sddd
--
LSL
Rd
,
Rs
0000
1vvv
vvmm
mddd
--
LSR
Rd
,
Rm
,
#
immed
_5
0100
0000
11ss
sddd
--
LSR
Rd
,
Rs
0010
0ddd
vvvv
vvvv
--
MOV
Rd
,
#
immed
_8
0001
1100
00nn
nddd
--
MOV
Rd
,
Rn
0100
0110
hhmm
mddd
--
MOV
Rd
,
Rm
0100
0011
01mm
mddd
--
MUL
Rd
,
Rm
0100
0011
11mm
mddd
--
MVN
Rd
,
Rm
0100
0010
01mm
mddd
--
NEG
Rd
,
Rm
0100
0011
00mm
mddd
--
ORR
Rd
,
Rm
1011
110R
rrrr
rrrr
--
POP
reg
_list
1011
010R
rrrr
rrrr
--
PUSH
reg
_list
0100
0001
11ss
sddd
--
ROR
Rd
,
Rs
0100
0001
10mm
mddd
--
SBC
Rd
,
Rm
1100
0nnn
rrrr
rrrr
--
STMIA
Rn
!
,
reg
_list
0110
0vvv
vvnn
nddd
--
STR
Rd
,
[
Rn
,
#
immed_5
*
4
]
0101
000m
mmnn
nddd
--
STR
Rd
,
[
Rn
,
Rm
]
1001
0ddd
vvvv
vvvv
--
STR
Rd
,
[
SP
,
#
immed_8
*
4
]
0111
0vvv
vvnn
nddd
--
STRB
Rd
,
[
Rn
,
#
immed_5
]
0101
010m
mmnn
nddd
--
STRB
Rd
,
[
Rn
,
Rm
]
1000
0vvv
vvnn
nddd
--
STRH
Rd
,
[
Rn
,
#
immed_5
*
2
]
0101
001m
mmnn
nddd
--
STRH
Rd
,
[
Rn
,
Rm
]
0001
111v
vvnn
nddd
--
SUB
Rd
,
Rn
,
#
immed
_3
0011
1ddd
vvvv
vvvv
--
SUB
Rd
,
#
immed
_8
0001
101m
mmnn
nddd
--
SUB
Rd
,
Rn
,
Rm
1011
0000
1vvv
vvvv
--
SUB
Sp
,
#
immed_7
*
4
1101
1111
vvvv
vvvv
--
SWI
immed
_8
0100
0010
00mm
mnnn
--
TST
Rn
,
Rm
表二:按指令机器码升序排列
0000
0vvv
vvmm
mnnn
--
LSL
Rd
,
Rm
,
#
immed
_5
0000
1vvv
vvmm
mddd
--
LSR
Rd
,
Rm
,
#
immed
_5
0001
0vvv
vvmm
mddd
--
ASR
Rd
,
Rm
,
#
immed
_5
0001
100m
mmnn
nddd
--
ADD
Rd
,
Rn
,
Rm
0001
101m
mmnn
nddd
--
SUB
Rd
,
Rn
,
Rm
0001
110v
vvnn
nddd
--
ADD
Rd
,
Rn
,
#
immed
_3
0001
111v
vvnn
nddd
--
SUB
Rd
,
Rn
,
#
immed
_3
0001
1100
00nn
nddd
--
MOV
Rd
,
Rn
0010
0ddd
vvvv
vvvv
--
MOV
Rd
,
#
immed
_8
0010
1nnn
vvvv
vvvv
--
CMP
Rn
,
#
immed
_8
0011
0ddd
vvvv
vvvv
--
ADD
Rd
,
#
immed
_8
0011
1ddd
vvvv
vvvv
--
SUB
Rd
,
#
immed
_8
0100
0000
00mm
mddd
--
AND
Rd
,
Rm
0100
0000
01mm
mddd
--
EOR
Rd
,
Rm
0100
0000
10ss
sddd
--
LSL
Rd
,
Rs
0100
0000
11ss
sddd
--
LSR
Rd
,
Rs
0100
0001
00ss
sddd
--
ASR
Rd
,
Rs
0100
0001
01mm
mddd
--
ADC
Rd
,
Rm
0100
0001
10mm
mddd
--
SBC
Rd
,
Rm
0100
0001
11ss
sddd
--
ROR
Rd
,
Rs
0100
0010
00mm
mnnn
--
TST
Rn
,
Rm
0100
0010
01mm
mddd
--
NEG
Rd
,
Rm
0100
0011
00mm
mddd
--
ORR
Rd
,
Rm
0100
0010
10mm
mnnn
--
CMP
Rn
,
Rm
0100
0010
11mm
mnnn
--
CMN
Rn
,
Rm
0100
0011
01mm
mddd
--
MUL
Rd
,
Rm
0100
0011
10mm
mddd
--
BIC
Rd
,
Rm
0100
0011
11mm
mddd
--
MVN
Rd
,
Rm
0100
0100
hhmm
mddd
--
ADD
Rd
,
Rm
h1h2
,
h1
is
msb
for
Rd
,
h2
is
msb
for
Rm
0100
0101
hhmm
mnnn
--
CMP
Rn
,
Rm
0100
0110
hhmm
mddd
--
MOV
Rd
,
Rm
0100
0111
0Hmm
mSBZ
--
BX
Rm
0100
0111
1hmm
mSBZ
--
BLX
Rm
0100
1ddd
vvvv
vvvv
--
LDR
Rd
,
[
PC
+
#
immed_5
*
4
]
0101
000m
mmnn
nddd
--
STR
Rd
,
[
Rn
,
Rm
]
0101
001m
mmnn
nddd
--
STRH
Rd
,
[
Rn
,
Rm
]
0101
010m
mmnn
nddd
--
STRB
Rd
,
[
Rn
,
Rm
]
0101
011m
mmnn
nddd
--
LDRSB
Rd
,
[
Rn
,
Rm
]
0101
100m
mmnn
nddd
--
LDR
Rd
,
[
Rn
,
Rm
]
0101
101m
mmnn
nddd
--
LDRH
Rd
,
[
Rn
,
Rm
]
0101
110m
mmnn
nddd
--
LDRV
Rd
,
[
Rn
,
Rm
]
0101
111m
mmnn
nddd
--
LDRSH
Rd
,
[
Rn
,
Rm
]
0110
0vvv
vvnn
nddd
--
STR
Rd
,
[
Rn
,
#
immed_5
*
4
]
0110
1vvv
vvnn
nddd
--
LDR
Rd
,
[
Rn
+
#
immed_5
*
4
]
0111
1vvv
vvnn
nmmm
--
LDRB
Rd
,
[
Rn
,
#
immed_5
*
4
]
0111
0vvv
vvnn
nddd
--
STRB
Rd
,
[
Rn
,
#
immed_5
]
1000
0vvv
vvnn
nddd
--
STRH
Rd
,
[
Rn
,
#
immed_5
*
2
]
1000
1vvv
vvnn
nddd
--
LDRH
Rd
,
[
Rn
,
#
immed_5
*
2
]
1001
0ddd
vvvv
vvvv
--
STR
Rd
,
[
SP
,
#
immed_8
*
4
]
1001
1ddd
vvvv
vvvv
--
LDR
Rd
,
[
SP
,
#
immed_8
*
4
]
1010
0ddd
vvvv
vvvv
--
ADD
Rd
,
PC
,
#
immed_8
*
4
1010
1ddd
vvvv
vvvv
--
ADD
Rd
,
SP
,
#
immed_8
*
4
1011
0000
0vvv
vvvv
--
ADD
SP
,
#
immed_7
*
4
1011
0000
1vvv
vvvv
--
SUB
Sp
,
#
immed_7
*
4
1011
010R
rrrr
rrrr
--
PUSH
reg
_list
1011
110R
rrrr
rrrr
--
POP
reg
_list
1011
1110
vvvv
vvvv
--
BKPT
immed
_8
1100
0nnn
rrrr
rrrr
--
STMIA
Rn
!
,
reg
_list
1100
1nnn
rrrr
rrrr
--
LDMIA
Rn
!
,
reg
_list
1101
cccc
vvvv
vvvv
--
Bcc
signed_immed
_8
1101
1111
vvvv
vvvv
--
SWI
immed
_8
1110
0vvv
vvvv
vvvv
--
B
signed_immed
_11
111h
hvvv
vvvv
vvvv
--
BL
(
X
)
immed_11
|
先对上篇文章中的switch分支进行逆向。
getSwitch()的代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
JNIEXPORT
jint
JNICALL
Java_com_example_hellojni_GetSwitch_getSwitch
(
JNIEnv
*
,
jclass
,
jint
a
,
jint
b
,
jint
i
)
{
switch
(
i
)
{
case
1
:
return
a
+
b
;
break
;
case
2
:
return
a
-
b
;
break
;
case
3
:
return
a
*
b
;
break
;
case
4
:
return
a
/
b
;
break
;
default
:
return
a
+
b
;
break
;
}
}
|
1.逆向APK后,使用IDA打开libs目录下的libHelloJNI.so,alt+t找到GetSwitch函数
2.当我们传入的参数是4,2,2是,执行的是a – b,所以返回的结果是2
3.现在,我们将a – b改为a + b,就是要返回结果为6,找到i=2的减法分支,计算add ro,r2,r3的hex为D018
4.然后切换到Hex View,修改Hex为D018,再回到图形模式时,可以看到SUBS R0,R2,R3已经更改为ADDS R0,R2,R3了
5.使用010editor,ctrl+g跳转到104A的地址,修改Hex为D018,然后保存。
6.重新编译成APK,运行时,发现结果已经变为6了。
1.正常情况下,购买金币时,是需要付费的,因为使用的是模拟器,所以显示付费失败。
2.从smali中找到相应的so,IDA打开。找到函数GameCoin,先F5看一下,看到关键点GameMain::GameCoin = v3 + v1,就是当前游戏的金币数量,对应的汇编为:ADDS R0,R0,R6
3.我们知道R0是返回值,R6是计算出的当前金币数,所以我们把此处改为ADDS R0,#255,所以每次都会增加255个金币,Hex为FF30
4.修改后,初始金币为10,点击一次后,增加了255个。
这篇文章是从吾爱破解(http://www.52pojie.cn/thread-396463-1-1.html)看到的,作者是adslxyz。
准备工具:
手机连上wifi,与电脑处于同一局域网,然后设置好Fiddler,具体步骤不详述。打开宝宝树孕育6.2.1版本,进入登陆窗口,输入账号密码点登陆,在电脑查看封包。可以得到请求URL:http://www.babytree.com/api/muser/login
1
|
POST:
android_id
=
d77c45cb6182db0e
&
build_serial
=
mmmm
&
local_ts
=
1438682239
&
source_channel
=
pc_pregnancy_tongyong_app_140729
&
longitude
=
122.535462
&
latitude
=
35.582332
&
phone_number
=
buROv8rMh50AdCj7mYdAZwWv9YEpsIR2mClGrO43QK6E5GHko7aEZwtZMT
%
2F2nvyuUCYsFtx
%
2Bkz1kwpthbRlpZecGXjkbyaaeIE4ocYjolDyLXLduydRCjnG
%
2Bk9OEfBr3QCShaiJ2Z
%
2BeDQIeqgTssdlzZqzFfpHzsg3oWpdF3RbMLF3O93RzGWntJgkgvzjzK7BfwvRF8p8o6Se2l7mX7malpYrn35H2kh4mEAaLaBGoj2naCxcSXCn6X6w9za6hofkhogCtB7PpY6pulzY
%
2BOhejwG1jdoCpqRxcctRcsFPWNz72SD8Mmqkq6wRVeLYX5ZIoWNafwEJuPobuRtmvhQzg
%
3D
%
3D
&
version
=
6.2.1
&
imei
=
90000451032112
&
secret
=
b8a310e750c8af5187197c279a7e0241
&
email
=
zLkZURsmEpT8fGCGGNADu2KAZ79zkZI3b
%
2FMBDh5i1vCAbvFmKoGu7L4nBPsOvnzzsnWtuOJipiITnoRQ5w
%
2FNTy
%
2FvEx
%
2BX
%
2FO8Ff8jcU2qBAXTzGgd0x1lZ8PsGuan0944ax5n1douoaTC370Hv4A
%
2F58IhP8rhcQWnO4FzFol8FC
%
2FXQ
%
2Fd1
%
2FoAQOycwdLeBUm0JJ5tRKiaT2nBwD1z7Dn1CShswWCNN2zDCvYgtJ7Ig3JIQipUahL9OaR
%
2FQnzUzk
%
2BmaC
%
2Fm6iRr1q4wkiz9lexJkXFHf1g5VuFqshCNgxdgeDw
%
2BZHuagedfj5MakZD96Lj
%
2BtZtRf6GhaLbY29tSL65EdSgsg
%
3D
%
3D
&
password
=
AhOxbuypr6JYDgpP
%
2Fw
%
2BLhVlfzwusSMOaE0137YBOvOAZkk
%
2BbCHwzfduzqsjasx6zrhZUkZv3aoZ27HDIFAebdgsRS9lh2eeuxXiIxZdkaVPy47UNMdLHo1fFnuUEOCbE144H
%
2FL
%
2B2EeY
%
2BjbE8Ebjg0gXuZ7ziNZ
%
2Bmrgt4
%
2FrN8YbizcbyCx7A
%
2Fue3GD3p58GV3ztnlxWp9d4D1IlMT5V
%
2B8qxi3VDpQMr0Ghi4MaDTkH
%
2FV8fcRSCBqutHTDsEs
%
2F5AfBgksHFT6Dcxl3tpBIIfdgEmU4EZ
%
2BpqLqVxVFLdjPA0MJOhzE36P8NqTPiKEXzTuJcmaaS5qU34d2dHGYP2wc7Dg
%
3D
%
3D
&
client_type
=
android
&
app_id
=
pregnancy
&
mac
=
5c
%
3A51
%
5A57
%
3A55
%
3A26
%
3A4d
&
client_baby_status
=
1
&
bpreg_brithday
=
2100
-
01
-
01
|
分析可知:phone_number
,password
,email
,secret
四个参数为加密,其他参数均为固定或者地理位置信息等参数。所以此次目标就为这4个参数
muser\login
,可知登陆的函数在com.babytree.platform.api.muser
中的login.smali
中。由图可知Login.java的父类是ApiBase,可以猜想,所有Api的请求所公共的函数都会在此类中生成。关键地方找一个就行,本次分析主要还是以动态调试为主.图上可以看到,目标应用的端口为8631.
从图中可以看到,左下角为函数的调用堆栈,从 登陆按钮 按下(OnClick),到api.user.login整个的调用过程。展开右下角的p0,可以看到此时已经生成了3个参数,
说明在断点之前,参数已经生成好了。所以我们按照函数的调用栈,挨个查看调用过的函数。查阅一番后可以发现。Login对象是在LoginActivity中的.method public a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V方法创建的。在new-instance v0, Lcom/babytree/platform/api/muser/Login;处下断点,重新点击登陆按钮,如图:
此时v1.v2.v3寄存器分别出现了加密的结果,由上可知,分别对应phone_number
,password
,email
三个参数。
所以加密的函数应该就在上面。往上下断点,重新来一次观察一下加密的过程。
可知:
1
|
iget
-
object
v0
,
p0
,
Lcom
/
babytree
/
apps
/
pregnancy
/
activity
/
LoginActivity
;
->
u
:
Ljava
/
lang
/
String
;
|
这一句函数调用之后,v0是一串固定字符串。
1
|
invoke
-
static
{
p1
,
v0
}
,
Lcom
/
babytree
/
apps
/
pregnancy
/
h
/
a
;
->
a
(
Ljava
/
lang
/
String
;
Ljava
/
lang
/
String
;
)
Ljava
/
lang
/
String
;
|
调用之后,v1为加密结果。查看a函数:
为RSA加密,所以之前的v0就是RSA的pubKey了。在a函数中下断点,看一看加密的参数是什么:
一切尽收眼底。由此可知phone_number
,password
,email
三个参数的算法。
secret
的算法。 回到AndroidKiller搜索关键字,定位到com/babytree/platform/api/ApiCommonParams;
大致看一下,这个是API通用参数生成的类。且加载了一个so:api_encrypt.so在secret
关键字附近看一下,调用了so的invoke-static {v1, v2}, Lcom/babytree/platform/api/ApiCommonParams;->nativeGetParam(Landroid/content/Context;Ljava/util/List;)Ljava/lang/String;
方法。
我们先在这句上面下个断点,看看传给so函数的是什么参数。断点断下后,看到传递给so的参数是一个List,POST参数中除secret
之外的所有参数。参数知道了,调用点知道了。分析一下so,看看需不需要动态调试。载入ida,找到调用的nativeGetParam函数。大致看一下,发现又调用了java的md5函数,好了,调试so的步骤剩下了。
回到IntelliJ IDEA,在getMD5Str处下断点,继续调试:
很快我们可以知道,p0中就是进行md5的参数啦。
分析一下可知,secret
就是 MD5(时间戳+所有参数排序+&asdf12341dfas!@#$%()(Ujjlasdflasdfj;asjdf23412313kljajsdflasjdflasjdflajsdf;lajsdf2342234sdfsdfffds)
总结:
动态调试apk的方式让我们了解apk运作的过程,通过动静结合的分析方式,快速找到想要的东西~
将关键代码放到so中,在底层获取签名信息并验证。因为获取和验证的方法都封闭在更安全的so库里面,能够起到一定意义上的保护作用。实例如下:
转载自:http://drops.wooyun.org/mobile/10010 原文作者:3xpl0it