掏出你的小手机Writeup

Posted by Pwnhub on 2017-06-12

各位胖友,大家好!今天是困乏的周一,又到了「胖哈勃电台」时间。(啊,熟悉的开场白~

上周末胖哥给各位胖友上了一道安卓题开胃,这道题依然来自 @alset 胖友的倾情奉献,掌声致谢~

话不多说,上 Writeup ,本期 Writeup 来自强无敌的 @spine ,除了要膜一下 @spine 胖友,还要膜一下一个多小时就 AK 了比赛的 @LeadroyaL 胖友…为什么这么强…

接下来,Writeup Time!


解压完apk就只看到了一个so,直接打开ida,程序的主要逻辑在 android_main
不难发现程序 zlib 从自己的 data 段自解码了一个文件出来,但 data 段先做了一个运算

1.png

稍微逆一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import zlib
f=open("decode.txt",'r').read().decode('hex')
length = 0x41BE8
data=list(f)
for sharktimes in range(0,90):
if sharktimes%10 == 9:
index = sharktimes/10
gap = length/10
start = index*gap
end = (index+1)*gap
for i in range(start,end):
data[i] = chr(ord(data[i])^sharktimes)
if sharktimes == 89:
for i in range(end,length):
data[i] = chr(ord(data[i])^sharktimes)
result = ''.join(data)
c = zlib.decompress(result)

c 就是 dex 文件了

打开 jeb 分析 dex 文件,发现了一个 onClick ,大概也只有这个了吧233

1
2
3
4
5
6
7
8
9
10
public void onClick(View key) {
if(Arrays.equals(MainActivity.a(this.a.getText().toString(), this.c.getString(2131099683)), //two_fish
MainActivity.i()))
{
Toast.makeText(this.b, this.c.getString(2131099685), 1).show(); //yes
}
else {
Toast.makeText(this.b, this.c.getString(2131099682), 1).show(); //no
}
}

这有几个字符串是从资源里来的
用 apktools 解析 asrc 出来的 xml 文件 (/res/values/strings.xml)

1
2
3
4
<string name="no">"NO~ You don't get me~ T_T"</string>
<string name="two_fish">I have a male fish and a female fish.</string>
<string name="what_else">What else?</string>
<string name="yes">Yes! You got me! :)</string>

索引在 /res/values/public.xml ,id 和 name 对上然后去 strings.xml 里找就行

MainActivity.i:

1
[3, -91, 61, -29, -87, -21, 101, -57, 27, -19, -27, -10, -61, 15, -85, 12, -32, -102, 106, 92, -39, 100, -122, 112, 18, -100, -55, 44, 60, -118, -97, 127, 76, 113, -35, 68, -17, -33, -93, -62, -57, -84, 36, -96, 97, -15, 20, 69]

很明显 MainActivity.i 是结果,但做了啥运算呢?接着看:

1
2
3
static byte[] a(String arg1, String arg2) {
return MainActivity.b(arg1, arg2);
}

调用`MainActivity.b

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
private static byte[] b(String input, String two_fish) {
byte[] v0_1;
try {
BufferedInputStream flag = new BufferedInputStream(new ByteArrayInputStream(input.getBytes()));
byte[] buffer = new byte[16];
ArrayList arr = new ArrayList();
Object unknow = a.a(two_fish.getBytes());
while(flag.read(buffer, 0, 16) != -1) {
arr.add(a.a(buffer, 0, unknow));
buffer = new byte[16];
}
ByteBuffer result = ByteBuffer.allocate(arr.size() * 16);
Object[] arr = arr.toArray();
int len = arr.length;
int i;
for(i = 0; i < len; ++i) {
result.put(arr[i]);
}
v0_1 = result.array();
}
catch(Exception v0) {
v0_1 = new byte[1];
}
return v0_1;
}

调用 a.a:

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
a.a(byte[])
public static Object a(byte[] key) {
Object v0_3;
int v1 = 32;
int v0 = 0;
Class v2 = a.class;
__monitor_enter(v2);
//输入two_fish 37字节
//截断后 32 字节
try {
if(key.length > v1 || key.length % 8 != 0) {
if(key.length <= v1) {
v1 = key.length;
}
if((v1 & 7) > 0) {
v0 = 1;
}
byte[] v0_2 = new byte[v0 * 8 + v1];
System.arraycopy(key, 0, v0_2, 0, v1);
key = v0_2;
}
//调用a.b(byte[])
v0_3 = a.b(key);
}
catch(Throwable v0_1) {
__monitor_exit(v2);
throw v0_1;
}
__monitor_exit(v2);
return v0_3;
}

就是做了个简单的32字节截断,然后送到 a.b

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
a.b(byte[])
private static Object b(byte[] arg19) {
Object[] v1_2;
int[] v13;
int v9_1;
int v8_1;
int[] v11_1;
int v12;
int v2;
int v3;
int v1_1;
int v5;
int v4;
Class v6 = a.class;
__monitor_enter(v6);
if(arg19 != null) {
goto label_9;
}
try {
throw new InvalidKeyException("Empty key");
label_9:
v4 = arg19.length;
//v4 32字节
if(v4 != 8 && v4 != 16 && v4 != 24 && v4 != 32) {
throw new InvalidKeyException("Incorrect key length");
}
int v7 = v4 / 8;
v5 = 40;
int[] v8 = new int[4];
int[] v9 = new int[4];
int[] v10 = new int[4];
v1_1 = 0;
v3 = 0;
v2 = v7 - 1;
label_33:
//str2int
if(v3 < 4 && v1_1 < v4) {
int v11 = v1_1 + 1;
v12 = v11 + 1;
v1_1 = arg19[v1_1] & 255 | (arg19[v11] & 255) << 8;
v11 = v12 + 1;
v1_1 |= (arg19[v12] & 255) << 16;
v12 = v11 + 1;
v8[v3] = v1_1 | (arg19[v11] & 255) << 24;
v1_1 = v12 + 1;
v11 = arg19[v12] & 255;
v12 = v1_1 + 1;
v1_1 = (arg19[v1_1] & 255) << 8 | v11;
v11 = v12 + 1;
v12 = (arg19[v12] & 255) << 16 | v1_1;
v1_1 = v11 + 1;
v9[v3] = (arg19[v11] & 255) << 24 | v12;
//v8[0],v9[0]都是str转int,v10不清楚干的啥,先放放
v10[v2] = a.a(v8[v3], v9[v3]);
++v3;
--v2;
goto label_33;
}
return v1;
}
v11_1 = new int[v5];
v1_1 = 0;
v2 = 0;
label_84:
// 这里我github搜索了0x2020202 key,20轮不明所以的循环,大概就是子密钥了
if(v2 < 20) {
v3 = a.a(v7, v1_1, v8);
v4 = a.a(v7, 16843009 + v1_1, v9);
v4 = v4 >>> 24 | v4 << 8;
v3 += v4;
v11_1[v2 * 2] = v3;
v3 += v4;
v11_1[v2 * 2 + 1] = v3 >>> 23 | v3 << 9;
++v2;
v1_1 += 33686018;
goto label_84;
}

出来个twofish算法,很好,这个很明显了,猜测做了一次 twofish 加密
于是。。。

1
pip install twofish
1
2
from twofish import Twofish
T = Twofish(b'I have a male fish and a female ')

2.png

getflag