hgame week2 WP

week2的题更加难绷啊,感觉大脑被论剑了(呜呜呜

RE

Signin

主要是过掉反调试

1.xxtea加密的key由一段程序的硬编码计算而来

image

而软件断点的原理是用一条软件断点指令替代断点地址所在位置的操作码字节,也就是说通过软件断点得到的key是错误的

2.硬件断点的检测

image

这里的qword_1400BB880即为delta,Dr寄存器是调试寄存器,储存我们的硬件断点,也就是说delta为0时才是正常状态

过掉反调试,标准xxtea,直接解密即可

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define delta 0
#define DELTA 0
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 11;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 11;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}



int main()
{
unsigned int v[] = {0x3050EA23, 0x47514C00, 0x2B769CEE, 0x1794E6D5, 0xB3E42BED, 0x61D536CB, 0x7CA0C2C0, 0x5ED767FE,
0xC579E0AF};
unsigned int key[4] = {0x97A25FB5, 0xE1756DBA, 0xA143464A, 0x5A8F284F};

btea(v,-9,key);
for(int i=0;i<9;i++)
{
printf("%c%c%c%c",*((char*)&v[i]+0),*((char*)&v[i]+1),*((char*)&v[i]+2),*((char*)&v[i]+3));
}
return 0;
}

Mysterious signals

Java层很简单没什么东西

image

b是类似签名(最后发现是把密文16进制转为字符串)的东西 c函数是字符串解密函数

c函数就不分析了,用不到

通过动态注册找到b函数,发现是几层加密叠叠乐

key0的生成,这里注意反调试

image

image

进入主加密函数,这里的v14其实就是aes加密的sbox,作为key1

image

image

紧接着又生成了一个key2

image

第一步加密用key1(sbox)进行换表

image

第二步加密用key2进行变异tea加密

image

第三步把16进制转换为字符串

image

exp写的是依托,见谅

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
#include <stdio.h>
#include <stdint.h>

void encrypt (uint32_t *v,uint32_t *k ){
uint32_t v0=v[0],v1=v[1],sum=0,i;
uint32_t delta=0x9e3779b9;
for(i=0;i<32;i++){
v0 += (v1>>3)^(v1*4)^(sum+k[2*i])^v1;
v1 += (v0>>5)^(v0*16)^(sum+k[(2*i+1)])^v0;
sum += delta;
}
// printf("\n%X\n",sum);
v[0]=v0;v[1]=v1;
}



void decrypt (uint32_t *v,uint32_t *k){
uint32_t delta=0x9e3779b9;
uint32_t v0=v[0],v1=v[1],sum=0xC6EF3720,i;

for (i = 0 ; i < 32 ; i++){
sum-=delta;
int idx = 31 - i;
v1-=(v0>>5)^(v0*16)^(sum+k[(2*idx+1)])^v0;
v0-=(v1>>3)^(v1*4)^(sum+k[2*idx])^v1;
}
v[0]=v0;v[1]=v1;
}

int main()
{
uint32_t k[]={0x75277321,0x76702174,0x72257326,0x7075217c,0x2500456,0x1075603,0x5520451,0x702560b,0x13411547,0x10164712,0x14431540,0x1613471a,0x20722674,0x23257421,0x27702673,0x25207429,0x75277321,0x76702174,0x72257326,0x7075217c,0x5520556,0x80d5b07,0x105c0d59,0x16106317,0x19451747,0x1e22511a,0x2a572750,0x342f6132,0x29782974,0x3837832d,0x488e418b,0x524a9b4d,0x75277321,0x76702174,0x72257326,0x7075217c,0x8540656,0xf13600b,0x1b661661,0x251e7023,0x1f491947,0x2c2e5b22,0x406b3960,0x524b7b4a,0x327e2c74,0x4d499239,0x69ac5ca3,0x7f74c271,0x75277321,0x76702174,0x72257326,0x7075217c,0xb560756,0x1619650f,0x26701f69,0x342c7d2f,0x254d1b47,0x3a3a652a,0x567f4b70,0x70679562,0x3b842f74,0x625ba145,0x8aca77bb,0xac9ee995},v1[]={0xd61f184b,0xa952b8f8,0x774a3ae2,0x90f6e576,0x1a34715b,0xa594f1f8,0x90d207db,0x4055262d,0xc9e12213,0xd0ea9a1,0x9c807686,0x62474808};



for (int i = 0; i < 6 ;i++) {
decrypt(&v1[2*i],k);
printf("0x%X,0x%X,",v1[2*i],v1[2*i+1]);
}
return 0;
}
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
import struct
table = list("0123456789abcdef")
enc = list("4b181fd6f8b852a9e23a4a7776e5f6905b71341af8f194a5db07d2902d2655401322e1c9a1a90e0d8676809c08484762")

plain3 = [0x3CEF8545,0xAA9A214D,0x18969A4D,0x23D8C712,0xD81223C3,0xAAC30418,0x230712D8,0x7EFD812,0x23180433,0xAAC34343,0x63FF04EF,0x63636363]

char_list = []
for num in plain3:
byte1 = (num >> 24) & 0xFF
byte2 = (num >> 16) & 0xFF
byte3 = (num >> 8) & 0xFF
byte4 = num & 0xFF

print(hex(byte4))
print(hex(byte3))
print(hex(byte2))
print(hex(byte1))



for i in range(len(enc1)//2):
for j in range(256):
if enc1[2*i] == table[(j>>4)&0xf] and enc1[2*i+1] == table[j&0xf]:
print(j,end=',')
print()

# v2 = v3++;
# *(a2 + v2) = ((*(result + 36) >> (8 * j)) ^ *(result + k)) + i * j * k;
key1 = []

key0=list("e7c10e42b7a68e14")
key = 0x11223344
v3 = 0
for i in range(4):
for j in range(4):
for k in range(16):
key1.append((((key>>(8*j))^ord(key0[k]))+i*j*k)&0xff)
# print(key1)



plain1=[]

for i in range(48):
v9 = 0;
v8 = 0;
for j in range(16):
if enc[i] == table[j]:
v9 = j
break
for j in range(16):
if enc[i+1] == table[j]:
v8 = j
break
plain1.append((v8 & 0xF) + 16 * v9)

# for i in range(12):
# print(hex(struct.unpack('>I', bytes(plain1)[4*i:4*i+4])[0]),end=',')

print()
# print(plain1)




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


for i in v10:
for j in range(256):
if i == sbox[j]:
print(chr(j),end='')
break

Fast and frustrating

aot逆向,相当逆天,死去的线性代数又开始攻击我了

image主逻辑所在位置

image

这里是第一次验证,验证了当前语言所属国家名称(这个api好像在win11 aot下有问题 无法返回正常值,不使用aot编译就能得到不清楚什么问题,而且好像没有缩写是vt的国家吧???)

image

这里取出了一个字符串进行base64解码,gunzip和反序列化操作得到一个Constrs对象

image

字符串来自Resouces类下的get函数

image

image

但是这个字符串并未初始化,进字符串搜索

image

成功找到

image

给了一个27*27的矩阵还有一个向量

image

这里的一大坨我是没看懂,不过我猜可能是输入变成向量和矩阵相乘等于给出的向量

z3解出结果,key为CompressedEmbeddedResources,按理来说,输入key应该就会得到flag,但是报错了?!!

image

在这里其实存在两种语言,只有vt这个语言才能正常进行解密

虽然我们绕过了最开始的语言检测,但是系统识别到的语言没有改变,也就不会加载FastAndFrustrating.Resources.vt.resources对应的正确数据,所以我们应该找到一种方法绕过这种机制

翻看net的源码发现

512f0425f5170496820a9fde15d2a74b

通过修改ResFileExtension即可走正常的执行流

f10d7af3842546f4426e672ae726dc66

这里注意修改0xd那里,这是字符串长度

e99a7cf74382b727c57105eee48ddf91

Nop'd

这个更是烧脑

image

main函数fork了一个子进程,让子进程执行游戏,父进程ptrace子进程,这样就没办法对子线程进行调试了

image

image

构造函数指定了main函数终止后的函数,

image

这个函数进行了一系列复杂的操作,很难看懂,不过他使用ptrace读取和修改了数据,那我们就可以使用LD_PRELOAD这一特性对其读写内容进行监听

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#define PTRACE_TRACEME 0
#define PTRACE_PEEKTEXT 1
#define PTRACE_PEEKDATA 2
#define PTRACE_PEEKUSR 3
#define PTRACE_POKETEXT 4
#define PTRACE_POKEDATA 5
#define PTRACE_POKEUSR 6
#define PTRACE_CONT 7
#define PTRACE_KILL 8
#define PTRACE_SINGLESTEP 9
#define PTRACE_GETREGS 12
#define PTRACE_SETREGS 13
#define PTRACE_GETXTREGS 18
#define PTRACE_SETXTREGS 19
#define PTRACE_GETHBPREGS 20
#define PTRACE_SETHBPREGS 21
#define PTRACE_GETFDPIC 22
# define R15 0
# define R14 1
# define R13 2
# define R12 3
# define RBP 4
# define RBX 5
# define R11 6
# define R10 7
# define R9 8
# define R8 9
# define RAX 10
# define RCX 11
# define RDX 12
# define RSI 13
# define RDI 14
# define ORIG_RAX 15
# define RIP 16
# define CS 17
# define EFLAGS 18
# define RSP 19
# define SS 20
# define FS_BASE 21
# define GS_BASE 22
# define DS 23
# define ES 24
# define FS 25
# define GS 26
void AppendDataToFile(char* msg)
{
FILE* fp = fopen("./1.txt", "a+");
if (fp==0)
{
printf("can't open log file\n");
return;
}
fseek(fp, 0, SEEK_END);
fwrite(msg, strlen(msg), 1, fp);
fclose(fp);


}


long ptrace(int request, int pid, void *addr, void *data) {
char msg[100]={0};
unsigned long addr1 = 0x555555554000+0x5100;
long int (*orig_ptrace)(int request, int pid, void *addr, void *data);
orig_ptrace = dlsym((void *) -1, "ptrace");
long int result = orig_ptrace(request, pid, addr, data);
// sprintf(msg,"pid -> %d\n",pid);
// AppendDataToFile(msg);
// sprintf(msg,"request -> %d\n",request);
// AppendDataToFile(msg);
if (request == PTRACE_SETREGS){
unsigned long rip = *((unsigned long*)data + 16)- 0x555555554000;
sprintf(msg,"SETREGS: rip: 0x%lx\n", rip);
AppendDataToFile(msg);
unsigned long r12 = *((unsigned long*)data + 3)- 0x555555554000;
sprintf(msg,"SETREGS: r12: 0x%lx\n", r12);
AppendDataToFile(msg);
unsigned long rax = *((unsigned long*)data + 10);
sprintf(msg,"SETREGS: rax: %ld\n", rax);
//sprintf(msg,"SETREGS: rip: 0x%lx\n", rip);
} else if (request == PTRACE_POKETEXT|| request == PTRACE_POKEDATA){
sprintf(msg,"POKETEXT: (addr , data) = (0x%lx , 0x%lx)\n", (unsigned long)addr- 0x555555554000, (unsigned long)data);
} else if (request == PTRACE_PEEKTEXT|| request == PTRACE_PEEKDATA) {
sprintf(msg,"PEEKTEXT: (addr , data) = (0x%lx , 0x%lx)\n", (unsigned long)addr- 0x555555554000, (unsigned long)result);
}
if (strlen(msg)!=0) {
AppendDataToFile(msg);
}
return result;
}



int memcmp(const void *str1, const void *str2, size_t n)
{
char msg[100]={0};
sprintf(msg,"memcmp -> %lX -- %lX-- len -> %d\n",*(unsigned long *)str1,*(unsigned long *)str2,n);
AppendDataToFile(msg);
int (*orig_memcmp)(const void *str1, const void *str2, size_t n);
orig_memcmp = dlsym((void *) -1, "memcmp");
//orig_memcmp(str1,str2,n)
return 0;
}

这里直接使用pwntools把地址随机化关闭

运行即可得到日志

分析日志我们发现,launcher从syscall旁边读取数据,经过运算得到opcode,然后取出game中保存的数据进行操作

image

观察几个加密函数

image

发现一处简单的运算,还原后发现是chacha20加密的特征

既然这样,那我们就可以直接取出最后一次生成的矩阵直接用来加密

image

不过在launcher中我们并未找到最后一步的加密,难道在game中吗??

image

果然,这里的rax指向我们的输入,下面跟生成的矩阵和一个值异或

这里的值其实就是image

可以将rax打印出来验证

取出值解密即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
key = [0x4a,0x69,0x5b,0x2a,0xf3,0x87,0x56,0x46,0xf3,0x7,0x74,0xc6,0x65,0x73,0xd6,0x16,0x45,0xfe,0xd9,0x98,0x3,0x76,0xb3,0x6d,0x50,0xe0,0x96,0xf7,0x4c,0xbc,0xb0,0xa4,0xea,0xf2,0xdc,0x93,0xd8,0x38,0x8,0xa7,0x23,0xde,0x6b,0x3b,0x87,0x84,0x6e,0xd1,0x4,0x4d,0xc3,0x2a,0x56,0x3f,0xee,0x8,0xa3,0xd8,0x76,0xe6,0x6b,0xbc,0x48,0xca]

delta = 70

enc = [0x64, 0x6A, 0x50, 0x17, 0x81, 0x7D, 0x6F, 0x1A, 0x87, 0xB1,
0xA4, 0x00, 0x09, 0x03, 0xF8, 0x8D, 0xF8, 0x6B, 0xDF, 0x32,
0x5F, 0x40, 0x90, 0x9C, 0xB8, 0x3D, 0x86, 0x13, 0x26, 0xB7,
0x63, 0xF7, 0x74, 0xE8, 0x53, 0xED, 0x58, 0x20, 0x4F, 0xD9,
0x99, 0x26, 0x21, 0x37, 0xDE, 0x35, 0x76, 0xC8, 0xBC, 0xD0,
0x6E]


for i in range(51):
enc[i]=enc[i]^key[i]^delta
delta=(delta^key[i]^enc[i])
print(chr(enc[i]),end='')