Tencent Game 2016安全大赛Android基础题
1 收集信息
将apk
安装,查看界面
1 | $ adb install Tencent2016A.apk |
输入用户名和code,然后进行校验
2 分析
拖入JEB,找到关键信息
可以看到使用的是NDK进行name和code的校验,方法在:
将libCheckRegister.so
拖入IDA分析
3 IDA分析libCheckRegister.so
找到对应的方法,f5,反汇编
修改之后的checkRegister方法,可以看到方法调用sub_1634()
是我们处理的关键方法,双击进入,分析如下:
首先判断name
字段长度,如果小于6或者大于20,退出。
接下来对name进行处理:
1 | char *v6; // r7 |
v6取当前数组地址, v7 = (name[i%name_length]) * (i + 20160126)* name_length
然后i++;
*(_DWORD *)v6 += v7
这一条指定中 v6
是一个char指针,而指针*(_DWORD *)
大小为4个字节,这里就是从v6的位置开始读4个字节大小的内存,而这四个大小的内存就是上一次计算的高三个字节(小端序)+一个0。v7
类型位int,所以这个的意思是,当前计算的v7+上一步计算结果的高三个字节。
再之后是调用一个函数对code进行判断,观察到没有和name产生什么联系,暂时不看,继续往下看,又是一个函数,发现是base64解码函数
解码之后存在str中。之后解码之后的code和变化之后的name满足一定的关系,如下:
1 | do |
梳理一下思路:
对name 进行处理,得到
name_change_1
1
2
3
4
5
6
7do
{
v6 = (char *)s + v5;
v7 = v11[v5 % v3] * (v5 + 20160126) * v3;
++v5;
*(_DWORD *)v6 += v7;
}对code进行base64解码,得到一个
str
对name再进行一次处理得到
name_change_2
从
str
得到check_str
check_str
和name
满足一定的关系,返回result(true or false)
4 注册机
1 |
|
base64.h
1
2
3
4
5
std::string base64_encode(unsigned char const*, unsigned int len);
std::string base64_decode(std::string const& s);base64.cpp
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
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
5 总结
第一次分析,而且还对着writeup看,还是磕磕碰碰的,记录一下不足吧
- 在IDA分析的时候,急于一口气看懂所有的汇编码,中间十分焦虑,因为对其中做过的一些地址无关以及栈保护部分过于纠结。
- 对指针不是很熟练,在阅读反汇编之后的伪C的时候,浪费了很多时间,特别是第一次对name做变化时的那个加法,这里需要理解的是内存中的指针并没有类型之分,在某一个地址上的值是固定的,是认为规定了某种类型的指针的单位长度,理解了这一点之后,再来理解这段代码就比较方便了
- 开始的时候自己分析的时候没有修改IDA伪C代码的参数类型,分析起来过于吃力,而在分析过程中,对参数类型进行适当的修改,可能会容易分析太多,比如这一题最后的code和name的关系,如果不修改,会是多个不同的寄存器,寄存器中的值其实就是数组中的某一个位置。
- 不会Android正向开发,实现一个在android上跑的注册机有点困难。