TGSTC_2020_qualify

2020 腾讯游戏安全技术竞赛 初赛-Android-Writeup

java层和libcrackme.so中逻辑很简单,调用libgoodluck.so中的Tell_me_the_key检查输入字符串

Tell_me_the_key函数逆向:

构造输入为’1’,与调试结果匹配,验证快速幂算法,以及大数存储形式

1
2
3
4
5
6
7
8
9
10
11
import binascii
str_n = '+\x1B\x89=]\x04\xF0\x06A\xCA\x0B\x9EJ\xBC\xEDxp0f\xE3\x02\xB4^\x09\xBB\xCE\xDA.\x0F\xDF\xFE\xD5\x8A\xD1\x1Eu;\x03\xA9p\x1D\xD20\xB4Uq]!ze\xCFIb\x07"\xBBeC\xEDOQ()\x84\x8B\x132\x83\x94a\xB6\x03\x97N\xCA%\xE9\xAA_&\x8Da\xC2?\xEAU\xB5m\xBA\xBC\xA4K|\x89\xDEM\xA9\x0Ea\x91\xB4\x99\x05\xDD\xE6)l\x15w\x8F\xE5\xD4B\x13\x03o\xDEWI\xAE}\x0Ed\xDC\xEB\xD5\x10\xEF'
n = int(binascii.b2a_hex(str_n[::-1]), 16)

print hex(n)
# 0xef10d5ebdc640e7dae4957de6f031342d4e58f77156c29e6dd0599b491610ea94dde897c4ba4bcba6db555ea3fc2618d265faae925ca4e9703b661948332138b842928514fed4365bb22076249cf657a215d7155b430d21d70a9033b751ed18ad5fedf0f2edacebb095eb402e366307078edbc4a9e0bca4106f0045d3d891b2bL


m = ord('1')
print hex(m ** 0x10001 % n)
# 0xbc974acaa76d023f45e7a0b0d4071bffa333be9be468f55dc11a5a005cfef7fbadbf046d9b8c093d840ab6d06027c505eacdb0a62aa76ed6d33f6c993772cef3f4160d27407666f64006f8f7a7276f2fa0fb23927c9eab8c4296e16af8ce542853f9ddd6b58525de878e6a584625db190720ee862e9894ec471dda04c6f617d5L

image-20200403145108524

image-20200403145123453

1
2
exp_mod(&c2, &m, &e2, &n);
exp_mod(&c1, &m, &e1, &n);

n为1024bit,通过共模攻击,恢复m。

恢复C1:

sub_CC76D294函数:算法结构类似AES。

与普通AES加密算法不同的是:roundkey的生成过程、SBOX使用时异或了0x6B、ciphertext出来的时候异或了0x27。

求逆SBOX:

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
def get_inv_SBOX():
SBOX = [
0x08, 0x17, 0x1C, 0x10, 0x99, 0x00, 0x04, 0xAE, 0x5B, 0x6A,
0x0C, 0x40, 0x95, 0xBC, 0xC0, 0x1D, 0xA1, 0xE9, 0xA2, 0x16,
0x91, 0x32, 0x2C, 0x9B, 0xC6, 0xBF, 0xC9, 0xC4, 0xF7, 0xCF,
0x19, 0xAB, 0xDC, 0x96, 0xF8, 0x4D, 0x5D, 0x54, 0x9C, 0xA7,
0x5F, 0xCE, 0x8E, 0x9A, 0x1A, 0xB3, 0x5A, 0x7E, 0x6F, 0xAC,
0x48, 0xA8, 0x73, 0xFD, 0x6E, 0xF1, 0x6C, 0x79, 0xEB, 0x89,
0x80, 0x4C, 0xD9, 0x1E, 0x62, 0xE8, 0x47, 0x71, 0x70, 0x05,
0x31, 0xCB, 0x39, 0x50, 0xBD, 0xD8, 0x42, 0x88, 0x44, 0xEF,
0x38, 0xBA, 0x6B, 0x86, 0x4B, 0x97, 0xDA, 0x30, 0x01, 0xA0,
0xD5, 0x52, 0x21, 0x27, 0x33, 0xA4, 0xBB, 0x84, 0xC1, 0x90,
0x28, 0x26, 0x58, 0xEE, 0x2E, 0x92, 0x69, 0x14, 0x3B, 0x57,
0xF4, 0xC3, 0x3A, 0xC8, 0x2B, 0xE4, 0xF9, 0xF6, 0x53, 0x9E,
0xD7, 0xDD, 0xB1, 0x4A, 0x7B, 0x94, 0x98, 0xB9, 0xA6, 0x67,
0x78, 0x87, 0x34, 0xFC, 0x2F, 0x7C, 0xAF, 0xCC, 0x15, 0x56,
0x0F, 0x36, 0x72, 0x18, 0x0B, 0xEA, 0x24, 0xB7, 0x49, 0x41,
0xFB, 0xE3, 0x2D, 0x85, 0xD3, 0x7F, 0xB5, 0x35, 0x60, 0xB0,
0x8B, 0x59, 0x51, 0x61, 0x22, 0x6D, 0x4F, 0x37, 0xA9, 0xB8,
0xC7, 0x09, 0xFA, 0xFE, 0x8F, 0x12, 0x8C, 0xA3, 0x5C, 0x06,
0xE6, 0xBE, 0x25, 0xC2, 0x07, 0x3D, 0x9F, 0x81, 0x0E, 0x11,
0xC5, 0x63, 0xD1, 0x13, 0x4E, 0x45, 0x77, 0xCD, 0xDF, 0xAD,
0x83, 0xB6, 0x1F, 0x74, 0x20, 0xD6, 0xE0, 0xE1, 0x1B, 0x55,
0xDE, 0x0D, 0x23, 0x68, 0x9D, 0x65, 0x0A, 0x5E, 0x3C, 0xD2,
0xED, 0xAA, 0x76, 0xF5, 0x8A, 0x93, 0xF3, 0x7A, 0x02, 0xB2,
0xE5, 0xFF, 0xF0, 0x75, 0xEC, 0x82, 0xA5, 0x3E, 0x43, 0xB4,
0xE7, 0xCA, 0xE2, 0x66, 0xD4, 0x8D, 0x29, 0x03, 0x2A, 0xF2,
0x46, 0x64, 0xDB, 0x3F, 0xD0, 0x7D
]

inv_SBOX = [0] * 256

for i in range(len(SBOX)):
SBOX[i] = SBOX[i] ^ 0x6B
print SBOX
print len(SBOX)
for i in range(16):
for j in range(16):
v = SBOX[i*16+j]
vj = v & 0xF
vi = (v >> 4) & 0xF
inv_SBOX[vi*16+vj] = i*16+j
print inv_SBOX

AES解密(详见aes-128.cpp),得C1:

1
2
3
v20_m = [
148, 232, 71, 16, 202, 146, 147, 126, 248, 93, 20, 224, 110, 74, 65, 190, 159, 224, 185, 57, 9, 98, 161, 10, 189, 138, 40, 68, 77, 87, 54, 75, 16, 213, 137, 221, 43, 6, 74, 172, 201, 95, 28, 22, 146, 83, 146, 74, 170, 113, 84, 13, 230, 87, 63, 107, 93, 60, 131, 195, 26, 210, 129, 145, 243, 27, 202, 9, 181, 6, 65, 237, 160, 70, 240, 215, 177, 198, 241, 114, 9, 202, 150, 219, 173, 61, 113, 117, 202, 37, 248, 248, 45, 30, 19, 27, 120, 246, 42, 224, 97, 8, 32, 252, 199, 227, 228, 230, 130, 181, 206, 25, 78, 3, 230, 231, 95, 27, 169, 143, 112, 124, 51, 103, 167, 33, 0, 4,
]

恢复C2:

sub_CC76EDC0、sub_CC76EA98函数:

控制流平坦化,动态调试,sub_CC76EDC0比较好分析,4byte4byte传入sub_CC76EA98。

sub_CC76EA98分析:

根据0x61C88647,推断出为类TEA加密,动态调试发现轮数没有32轮

image-20200403170203356

验证了一下,动态调出来的值为TEA中间变量,确认算法为8轮TEA。key为n的前4个uint32。(代码详见tea.cpp)

image-20200403170508746

解密得C2

1
2
3
v19_m = [
0, 147, 17, 173, 243, 118, 147, 244, 122, 83, 213, 121, 46, 14, 171, 80, 254, 107, 131, 122, 113, 140, 6, 122, 240, 173, 55, 19, 170, 0, 215, 16, 250, 59, 136, 42, 214, 84, 98, 102, 248, 200, 32, 55, 199, 65, 142, 240, 144, 155, 146, 251, 42, 26, 180, 117, 106, 52, 135, 141, 82, 50, 176, 10, 20, 5, 179, 193, 221, 26, 242, 25, 19, 65, 102, 211, 0, 152, 99, 181, 229, 49, 16, 57, 156, 197, 171, 154, 11, 140, 177, 67, 104, 127, 100, 59, 150, 37, 36, 139, 160, 172, 25, 163, 91, 0, 28, 185, 252, 32, 151, 37, 238, 2, 178, 146, 39, 230, 100, 26, 218, 52, 36, 247, 111, 155, 3, 108,
]

共模攻击:

C1最低位为止,爆破可出

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
import gmpy2
import binascii
import libnum
import string

# C2
v19_m = [
0, 147, 17, 173, 243, 118, 147, 244, 122, 83, 213, 121, 46, 14, 171, 80, 254, 107, 131, 122, 113, 140, 6, 122, 240, 173, 55, 19, 170, 0, 215, 16, 250, 59, 136, 42, 214, 84, 98, 102, 248, 200, 32, 55, 199, 65, 142, 240, 144, 155, 146, 251, 42, 26, 180, 117, 106, 52, 135, 141, 82, 50, 176, 10, 20, 5, 179, 193, 221, 26, 242, 25, 19, 65, 102, 211, 0, 152, 99, 181, 229, 49, 16, 57, 156, 197, 171, 154, 11, 140, 177, 67, 104, 127, 100, 59, 150, 37, 36, 139, 160, 172, 25, 163, 91, 0, 28, 185, 252, 32, 151, 37, 238, 2, 178, 146, 39, 230, 100, 26, 218, 52, 36, 247, 111, 155, 3, 108,
]

# C1
v20_m = [
148, 232, 71, 16, 202, 146, 147, 126, 248, 93, 20, 224, 110, 74, 65, 190, 159, 224, 185, 57, 9, 98, 161, 10, 189, 138, 40, 68, 77, 87, 54, 75, 16, 213, 137, 221, 43, 6, 74, 172, 201, 95, 28, 22, 146, 83, 146, 74, 170, 113, 84, 13, 230, 87, 63, 107, 93, 60, 131, 195, 26, 210, 129, 145, 243, 27, 202, 9, 181, 6, 65, 237, 160, 70, 240, 215, 177, 198, 241, 114, 9, 202, 150, 219, 173, 61, 113, 117, 202, 37, 248, 248, 45, 30, 19, 27, 120, 246, 42, 224, 97, 8, 32, 252, 199, 227, 228, 230, 130, 181, 206, 25, 78, 3, 230, 231, 95, 27, 169, 143, 112, 124, 51, 103, 167, 33, 0, 4,
]

for _ in range(256):
v19_m[0] = _

c1_s = ""
for i in v20_m:
c1_s += chr(i)
c1 = int(binascii.b2a_hex(c1_s[::-1]), 16)
c2_s = ""
for i in v19_m:
c2_s += chr(i)
c2 = int(binascii.b2a_hex(c2_s[::-1]), 16)

str_n = '+\x1B\x89=]\x04\xF0\x06A\xCA\x0B\x9EJ\xBC\xEDxp0f\xE3\x02\xB4^\x09\xBB\xCE\xDA.\x0F\xDF\xFE\xD5\x8A\xD1\x1Eu;\x03\xA9p\x1D\xD20\xB4Uq]!ze\xCFIb\x07"\xBBeC\xEDOQ()\x84\x8B\x132\x83\x94a\xB6\x03\x97N\xCA%\xE9\xAA_&\x8Da\xC2?\xEAU\xB5m\xBA\xBC\xA4K|\x89\xDEM\xA9\x0Ea\x91\xB4\x99\x05\xDD\xE6)l\x15w\x8F\xE5\xD4B\x13\x03o\xDEWI\xAE}\x0Ed\xDC\xEB\xD5\x10\xEF'
n = int(binascii.b2a_hex(str_n[::-1]), 16)

e1 = 65537
e2 = 17
s = gmpy2.gcdext(e1, e2)
s1 = s[1]
s2 = -s[2]

c2 = gmpy2.invert(c2, n)
m = (pow(c1,s1,n) * pow(c2 , s2 , n)) % n

# print m
# print libnum.n2s(m)
ss = libnum.n2s(m)
if ss.isalnum():
print ss
f = 1
for c in ss:
if not c in string.printable:
f = 0
break
if f == 1:
pass
print ss
# Tencent_GSLab_Sec_2020

得flag:

1
Tencent_GSLab_Sec_2020