Tencent Game 2016安全大赛Android基础题

Tencent Game 2016安全大赛Android基础题

1 收集信息

apk安装,查看界面

1
$ adb install Tencent2016A.apk

输入用户名和code,然后进行校验

2 分析

拖入JEB,找到关键信息

1585227810870

可以看到使用的是NDK进行name和code的校验,方法在:

1585227861492

1585227885077

libCheckRegister.so拖入IDA分析

3 IDA分析libCheckRegister.so

1585228159692

找到对应的方法,f5,反汇编

1585232005384

修改之后的checkRegister方法,可以看到方法调用sub_1634()是我们处理的关键方法,双击进入,分析如下:

1585229111101

首先判断name字段长度,如果小于6或者大于20,退出。

接下来对name进行处理:

1585229819096

1
2
3
4
5
6
7
8
9
10
11
12
13
char *v6; // r7
int v7; // r3
char arr[20];

j_memset((int)arr, 0, 20); // arr[20]
i = 0;
do {
v6 = &arr[i];
//LDRB
v7 = *(unsigned __int8 *)(current_name + i % name_length) * (i + 20160126) * name_length;
++i;
*(_DWORD *)v6 += v7;
}

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解码函数

1585471557738

解码之后存在str中。之后解码之后的code和变化之后的name满足一定的关系,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
do
{
v10 = str[j]; // 解码后的数组
name_change_2[j] = name_change_1[j] / 10;
check_str[j] = v10;
++j;
}
while ( j != 5 );
result = 0;
if ( check_str[4] + name_change_2[0] == check_str[2]
&& check_str[4] + name_change_2[0] + name_change_2[1] == 2 * check_str[4]
&& name_change_2[2] + check_str[3] == check_str[0]
&& name_change_2[2] + check_str[3] + name_change_2[3] == 2 * check_str[3] )
{
result = (unsigned int)(name_change_2[4] + check_str[1] - 3 * name_change_2[2]) <= 0;
}

梳理一下思路:

  1. 对name 进行处理,得到name_change_1

    1
    2
    3
    4
    5
    6
    7
    do
    {
    v6 = (char *)s + v5;
    v7 = v11[v5 % v3] * (v5 + 20160126) * v3;
    ++v5;
    *(_DWORD *)v6 += v7;
    }
  2. 对code进行base64解码,得到一个str

  3. 对name再进行一次处理得到name_change_2

  4. str得到check_str

  5. check_strname满足一定的关系,返回result(true or false)

4 注册机

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
#include<stdio.h>
#include<windows.h>
#include "base64.h"
#include <string>


int main()
{
const unsigned char name[20] = {0};

char *v2; // r6@1
signed int v3; // r5@1
int result; // r0@2
signed int v5; // r4@3
char *v6; // r7@4
int v7; // r3@4
int v8; // r4@6
int j; // r4@7
int v10; // r1@8
const unsigned char *v11; // [sp+Ch] [bp-464h]@1
int v12[5] = {0}; // [sp+18h] [bp-458h]@7
int v13[5] = {0}; // [sp+2Ch] [bp-444h]@7
int s[5] = {0}; // [sp+40h] [bp-430h]@3
int v15[234] = {0}; // [sp+54h] [bp-41Ch]@5

printf("请输入name:");
scanf_s("%s", name, 20);

// name->s

v11 = name;
v5 = 0;
v3 = strlen((const char*)name);
do
{
v6 = (char *)s + v5;
v7 = v11[v5 % v3] * (v5 + 20160126) * v3;
++v5;
*(DWORD *)v6 += v7;
} while (v5 != 16);

// s->v12
j = 0;
do
{
v12[j] = s[j] / 10;
++j;
} while (j != 5);

// v12->v13
v13[0] = 2 * v12[2] + v12[3];
v13[1] = 3 * v12[2] - v12[4];
v13[2] = 2 * v12[0] + v12[1];
v13[3] = v12[2] + v12[3];
v13[4] = v12[0] + v12[1];

// v13->v15
j = 0;
do
{
v15[j] = v13[j];
++j;
} while (j != 5);

// v15->pwd
std::string encodeStr = base64_encode((unsigned char*)v15, 20);
const char* pwd = encodeStr.c_str();

printf("密码是:%s",pwd);
system("pause");
return 0;
}
  • base64.h

    1
    2
    3
    4
    5
    #pragma once
    #include <string>

    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
    #include "base64.h"
    #include <iostream>

    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看,还是磕磕碰碰的,记录一下不足吧

  1. 在IDA分析的时候,急于一口气看懂所有的汇编码,中间十分焦虑,因为对其中做过的一些地址无关以及栈保护部分过于纠结。
  2. 对指针不是很熟练,在阅读反汇编之后的伪C的时候,浪费了很多时间,特别是第一次对name做变化时的那个加法,这里需要理解的是内存中的指针并没有类型之分,在某一个地址上的值是固定的,是认为规定了某种类型的指针的单位长度,理解了这一点之后,再来理解这段代码就比较方便了
  3. 开始的时候自己分析的时候没有修改IDA伪C代码的参数类型,分析起来过于吃力,而在分析过程中,对参数类型进行适当的修改,可能会容易分析太多,比如这一题最后的code和name的关系,如果不修改,会是多个不同的寄存器,寄存器中的值其实就是数组中的某一个位置。
  4. 不会Android正向开发,实现一个在android上跑的注册机有点困难。