VN2022

cm狗

用jadx软件载入这个apk文件

判断flag的函数

image-20220215141557259

判断的函数

image-20220215141619535

loadDexClass()的这个函数

image-20220215141712322

这里copyfile了一个dex的文件,这里copyFile()函数里面应该还有一系列的操作,一系列操作的结果最后写入了创建的一个文件里面

找到这个文件,并且找到这个函数

image-20220219205934006

该函数的详情

image-20220219210128389

主要的代码

image-20220219210151167

image-20220219210531948

注意这个地方是每次取1024出来,外层有个while(true)的循环的操作

得到的这个文件名称是 classes.dex

image-20220219213108277

对文件进行操作的脚本:

1
2
3
4
5
6
7
v0 = 'vn2022'
with open("D:\\re\\buuctf\\VN\\3\\ooo",'rb') as f1:
c = f1.read()
with open("D:\\re\\buuctf\\VN\\3\\out",'wb') as f2:
for i in range(len(c)):
f2.write((c[i]^ord(v0[i %1024 % len(v0)])).to_bytes(1,byteorder="little",signed=False))
print("OK")

将得到的这个文件命名为class.dex,这是一个dex文件,首先使用dex2jar 将 dex文件转变成jar文件,然后使用jd-gui打开这个得到的jar文件,注意使用dex2jar得到jar文件的时候,需要将dex文件放入到dex2jar的文件夹目录之下

得到的这个文件之中

加密函数 (xxtea加密)

image-20220219223231506

在hcheck这个函数之中,首先给了一堆数字,然后将输入的flag以及xxtea的密钥输入,对输入进行加密,加密的结果就是上面定义的一系列数值

image-20220219223525059

image-20220219223528938

去网上找一个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
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9 //固定的一个常量
#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]) //v是要加密的两个元素的数组
{ //n为数组的长度
uint32_t y, z, sum; //无符号整型
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n; //固定的得出轮数
sum = 0;
z = v[n-1];
do
{
sum += DELTA; //每次进行叠加
e = (sum >> 2) & 3; //固定运算
for (p=0; p<n-1; p++)
{
y = v[p+1];
v[p] += MX;
z = v[p];
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
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()
{
int8_t a[]= {68, 39, -92, 108, -82, -18, 72, -55, 74, -56, 38, 11, 60, 84, 97, -40, 87, 71, 99, -82, 120, 104, 47, -71, -58, -57, 0, 33, 42, 38, -44, -39, -60, 113, -2, 92, -75, 118, -77, 50, -121, 43, 32, -106 };
uint32_t *v = (uint32_t *)a;
uint32_t *k= (uint32_t *)"H4pPY_VNCTF!!OvO";
int n= 11;
btea(v, -n, k);
printf("decrypto result:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10]);
printf("result:%s",v);
return 0;
}
VNCTF{93ee7688-f216-42cb-a5c2-191ff4e412ba}

image-20220219230318245

所以最后的flag 是 VNCTF{93ee7688-f216-42cb-a5c2-191ff4e412ba}

BabyMaze

这道题如果直接使用employee6 解pyc得到代码是不行的,因为在这个代码里面有花指令,

方法一:直接使用pycda中的得到bytecode的底层的代码

看见迷宫,拼一下迷宫,按照w代表向上走,s代表向下走,a代表向左走,d代表向右走的方式走迷宫

python的pyc文件的反编译pycdc和pycdas这两个文件,pycdc反编译为python的代码,pycdas这个文件会将pyc文件编译成字节码的形式 pyc反编译

pyc的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Bytecode object */
typedef struct {
PyObject_HEAD
int co_argcount; /* Code Block的位置参数个数,比如说一个函数的位置参数个数*/
int co_nlocals; /* Code Block中局部变量的个数,包括其中位置参数的个数 */
int co_stacksize; /* 执行该段Code Block需要的栈空间 */
int co_flags; /* CO_..., see below */
PyObject *co_code; /* Code Block编译所得的字节码指令序列。以PyStingObjet的形式存在 */
PyObject *co_consts; /* PyTupleObject对象,保存CodeBlock中的所常量 */
PyObject *co_names; /* PyTupleObject对象,保存CodeBlock中的所有符号 */
PyObject *co_varnames; /* Code Block中的局部变量名集合 */
PyObject *co_freevars; /* Python实现闭包需要用的东西 */
PyObject *co_cellvars; /* Code Block中内部嵌套函数所引用的局部变量名集合 */
/* The rest doesn't count for hash/cmp */
PyObject *co_filename; /* Code Block所对应的.py文件的完整路径 */
PyObject *co_name; /* Code Block的名字,通常是函数名或类名 */
int co_firstlineno; /* Code Block在对应的.py文件中起始行 */
PyObject *co_lnotab; /* 字节码指令与.py文件中source code行号的对应关系,以PyStringObject的形式存在 */
void *co_zombieframe; /* for optimization only (see frameobject.c) */
} PyCodeObject;

得到:走出的迷宫的结果

ssssddssaassddddwwwwddwwddddddwwddddddssddwwddddddddssssaawwaassaassaassddssaassaawwwwwwaaaaaaaassaassddddwwddssddssssaassddssssaaaaaawwddwwaawwwwaassssssssssssddddssddssddddddddwwaaaaaawwwwddssddwwwwwwwwddssddssssssssddddss

将上面的解md5(32小写)得到最后的结果 VNCTF{801f190737434100e7d2790bd5b0732e}

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
                                   1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
68 BUILD_LIST 31 1 5 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1
132 BUILD_LIST 31 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 0 1
196 BUILD_LIST 31 1 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1
260 BUILD_LIST 31 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 0 1 0 1 0 1
324 BUILD_LIST 31 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1
388 BUILD_LIST 31 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1
452 BUILD_LIST 31 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 1
516 BUILD_LIST 31 1 0 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1
580 BUILD_LIST 31 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 1 0 1 0 0 0 1
644 BUILD_LIST 31 1 1 1 1 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 0 1 1 1 0 1 0 1 1 1 0 1
708 BUILD_LIST 31 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1
772 BUILD_LIST 31 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 0 1 0 1 1 1
836 BUILD_LIST 31 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1
900 BUILD_LIST 31 1 0 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1
964 BUILD_LIST 31 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1
1028 BUILD_LIST 31 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1
1092 BUILD_LIST 31 1 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1
1156 BUILD_LIST 31 1 0 1 0 1 1 1 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 1 0 1 1 1 0 1 1 1
1220 BUILD_LIST 31 1 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 0 1
1284 BUILD_LIST 31 1 0 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 1
1348 BUILD_LIST 31 1 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1412 BUILD_LIST 31 1 0 1 0 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1
1476 BUILD_LIST 31 1 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 1 0 1 0 1
1540 BUILD_LIST 31 1 0 1 1 1 0 1 0 1 0 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
1604 BUILD_LIST 31 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 1 0 1
1668 BUILD_LIST 31 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 0 1
1732 BUILD_LIST 31 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1
1796 BUILD_LIST 31 1 0 1 1 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1
1860 BUILD_LIST 31 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 7 1
1924 BUILD_LIST 31 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

VNCTF{801f190737434100e7d2790bd5b0732e}

时空飞行

运行一下这个程序,因为不能直接运行,所以使用IDA调试运行,所以现在我们需要找到日期和符来歌(根据运行之中的提示语句得到)

image-20220221120512364

变化之后的日期:

1
0FD07C452h, 0EC90A488h, 68D33CD1h, 96F64587h

对输入的日期进行加密的过程

image-20220221122556035

v5[v3] = sub_401A3B(v5[i + 3] ^ v5[i + 2] ^ (unsigned int)v5[i + 1] ^ dword_405040[i]) ^ v4;

1
2
3
4
5
v5[4] = sub_401A3B(v5[3] ^ v5[2] ^ (unsigned int)v5[1] ^ dword_405040[0]) ^ v5[0];
v5[5] = sub_401A3B(v5[4] ^ v5[3] ^ (unsigned int)v5[2] ^ dword_405040[1]) ^ v5[1];
... ...
v5[34] = sub_401A3B(v5[33] ^ v5[32] ^ (unsigned int)v5[31] ^ dword_405040[2]) ^ v5[30];
v5[35] = sub_401A3B(v5[34] ^ v5[33] ^ (unsigned int)v5[32] ^ dword_405040[3]) ^ v5[31];

sub_401A3B()这个函数的作用

image-20220221123230352

a1和a1向左移13位的结果和a1向右移9位的结果做异或运算,

逆向分析的脚本:

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
#include <stdio.h>
unsigned int CalcRound(unsigned int key){
return key ^((((key&0xffffffff)<<13)|((key&0xffffffff)>>(32-13)))^(((key&0xffffffff)<<(32-9))|((key&0xffffffff)>>9)));
}

int main()
{
unsigned long CK[32] = {
0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};
unsigned int result[] = {0x0FD07C452, 0x0EC90A488, 0x68D33CD1, 0x96F64587};
unsigned int re_result[36];
for(int i=32;i<36;i++){
re_result[i] =result[i-32];
}

for(int i=31;i>=0;i--){
re_result[i] = CalcRound(re_result[i+1]^re_result[i+2]^re_result[i+3]^CK[i])^re_result[i+4];
}
unsigned long tem[4] = {0xA3B1BAC6,0x56AA3350,0x677D9197,0xB27022DC};
for(int i=0;i<=3;i++){
re_result[i]=re_result[i]^tem[i];
}
unsigned char *data = (unsigned char *)re_result; //因为日期的表示是8个数字,每个数字是用8位来代表
for(int i=1;i<=2;i++){
for(int j=1;j<=4;j++){
printf("%c",data[i*4 - j]);
}
}

return 0;
}
20211205

image-20220221133215573

将得到的日期放入之后,就要进行符来歌的输入

image-20220221133557404

最后需要对比的元素是

1
0x1ebe,0x1e8e,0x1e44,0x1e39,0x1e5b,0x1e8,0x1e36,0x1e8f,0x1edd,0x1e5e,0x1e94,0x1eb5,0x1e1,0x1e70,0x1eab,0x1e63,0x1ebb,0x1e72,0x1e50,0x1e13,0x1e5d,0x1e25,0x1e16,0x1e78

在上面函数 之中对这个数组中的元素做了一定的运算,下面得到的就是运算加密之后的数组

image-20220221170531828

所以需要比较的数组是

1
0x25,0x15,0xdf,0xa2,0xc0,0x93,0xad,0x14,0x46,0xc5,0xf,0x2e,0x9a,0xeb,0x30,0xf8,0x20,0xe9,0xcb,0x88,0xc6,0xbe,0x8d,0xe3

image-20220221183335046

用爆破的方式得到上面的解,发现这里某些会有多个解所以需要使用递归的方式,深度优先算法解得所有的解

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
obj = [ 0x25,0x15,0xdf,0xa2,0xc0,0x93,0xad,0x14,0x46,0xc5,0xf,0x2e,0x9a,0xeb,0x30,0xf8,0x20,0xe9,0xcb,0x88,0xc6,0xbe,0x8d,0xe3]
i=0
flags=[]
flag = [0]*24
flag[23] = 0xe3
def DFS(deep):
global flags
global flag
if deep ==0:
flags.append(flag.copy())
return
else:
for j in range(1,0xff):
tem = j^((j % 0x12+ flag[deep] + 0x05) ^ 0x41)
if tem == obj[deep-1]:
flag[deep-1]=j
DFS(deep-1)

if __name__ == '__main__':
DFS(23)
for i in range(len(flags)):
print(flags[i])
[20, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[21, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[22, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[23, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[28, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[29, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[30, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[31, 105, 41, 173, 62, 178, 75, 159, 182, 170, 33, 91, 46, 230, 57, 64, 234, 134, 33, 151, 82, 198, 52, 227]
[128, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[129, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[130, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[131, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[136, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[137, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[138, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[139, 221, 127, 219, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[72, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[73, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[74, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[75, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[80, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[81, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[82, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[83, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[88, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[89, 39, 107, 223, 48, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[184, 211, 117, 221, 52, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[185, 211, 117, 221, 52, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[186, 211, 117, 221, 52, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]
[187, 211, 117, 221, 52, 160, 93, 169, 240, 236, 97, 35, 54, 232, 45, 78, 236, 134, 33, 151, 82, 198, 52, 227]

image-20220221183354391

通过这个我们可以获得 a1[60] a1[61] a1[62] a1[63] a1[64] a1[65] 5个数

对输入的每32个字节的操作:将32位数 F1F1 F2F2 F3F3 F4F4 排列为 F4F4 F3F3 F2F2 F1F1

image-20220221184405201

将所有输入的字符进行上面的运算,然后重新赋值给a1 则可以知道数组之中有6个元素

image-20220221184743758

然后就需要进行一系列的运算,

image-20220221185657924

当v5不是6的倍数的时候

当i不是6的倍数的时候:v5[7] = v5[1]^v5[6] v5[i] = v5[i-6] ^ v5[i-1]

当i是6的倍数的时候:v5[i] = v5[i-6] ^ T(v5[i-1],v3) v3代表的是第几个6的倍数

T函数的讲解

image-20220221190435689

其中的第一个函数:将a1分解成了4个元素的数组0x F0F0 F1F1 F2F2 F3F3

image-20220221190819733

其中的第二个函数:将数组中的元素循环右移一位 ;例如 a=[a0,a1,a2,a3] 转化成 a=[a1,a2,a3,a0]

[F3F3,F2F2,F1F1,F0F0] [F2F2,F1F1,F0F0,F3F3]

image-20220221191008054

其中第三个函数:整合上面的数组之中的元素 0x F3F3 F0F0 F1F1 F2F2

image-20220221191329845

所以这个函数的作用是 将 0x F0F0 F1F1 F2F2 F3F3 转化成 0x F3F3 F0F0 F1F1 F2F2 循环左移动了8位 然后再和对应那个6的倍数作为脚标找到数组对应的元素

作为索引对象的数组是:

1
2
[0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000,
0x36000000]

每组得到了6个数字,将这六个元素作为66个元素之中的后六位元素

逆向:

当i不是6的倍数时: v5[i-6] = v5[i]^v5[i-1]

当i是6的倍数时: v5[i-6] = v5[i]^ T(v5[i-1],v3) # v3 = i%6 T是将数循环左移动了8位,再和对应的数字进行异或运算

逆向得到的66个元素的前6个元素,将前6个元素转化为字符输出

所有运算的脚本:

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
obj = [ 0x25,0x15,0xdf,0xa2,0xc0,0x93,0xad,0x14,0x46,0xc5,0xf,0x2e,0x9a,0xeb,0x30,0xf8,0x20,0xe9,0xcb,0x88,0xc6,0xbe,0x8d,0xe3]
inner_index = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000,
0x36000000]
i=0
flags=[]
flag = [0]*24
flag[23] = 0xe3
# 用递归 ,深度遍历的方法得到所有的解
def DFS(deep):
global flags
global flag
if deep ==0:
flags.append(flag.copy())
return
else:
for j in range(1,0xff):
tem = j^((j % 0x12+ flag[deep] + 0x05) ^ 0x41)
if tem == obj[deep-1]:
flag[deep-1]=j
DFS(deep-1)

# 位移运算
def rol8(num):
return ((num&0xffffff)<<8) |((num&0xff000000)>>(32-8))

# 位移加上 异或运算
def T(num,i):
num = rol8(num)
return num^inner_index[i-1]

# 根据是否是6的倍数进行的不同的异或运算
def multiple(final_flag):
for i in range(65,5,-1):
if i%6 !=0:
final_flag[i-6] = final_flag[i]^final_flag[i-1]
else:
final_flag[i - 6] = final_flag[i] ^ T(final_flag[i - 1], i//6)
return final_flag

# 将得到的数组进行输出
def print_flag(final_flag):
for i in range(len(final_flag)):
for j in range(4):
print(chr((final_flag[i] & 0xff000000)>>(32-8)),end="")
final_flag[i] = final_flag[i] << 8
print()

if __name__ == '__main__':
DFS(23)
re_flags = []
for j in range(len(flags)):
flag = flags[j]
tem = []
# 将每4个8位数组合成 一个16进制的32位数
for i in range(0, len(flag), 4):
tem.append(int(
"0x" + hex(flag[i + 3])[2:].zfill(2) + hex(flag[i + 2])[2:].zfill(2) + hex(flag[i + 1])[2:].zfill(2) + hex(
flag[i])[2:].zfill(2),16))
re_flags.append(tem.copy()) # re_flags 里面将24个元素每4个一组转化为一个数

# 异或运算的逆向
final_flag = [0]*66
re_flags2 = []
for i in range(len(re_flags)):
final_flag = [0]*66
for j in range(6): #将已知的5个值放入其中
final_flag[60+j] = re_flags[i][j]

# 利用这5个元素推理出所有的数字
final_flag = multiple(final_flag)
tem = []
for j in range(6):
tem.append(final_flag[j])
print(tem)
re_flags2.append(tem.copy())
for i in range(len(re_flags2)):
print_flag(re_flags2[i])

在所有的输出之中找到flag VNCTF{TimeFlightMachine}

cmgo!

将这个exe文件拖入ida之中,从函数名称的窗口之中就可以看到这里进行了程序的无符号化,使得函数的名称不能够直观的得到,所以函数的入口难以找到,无符号golang逆向技巧,使用IDAGolangHelper这一脚本,打开脚本文件(文件-脚本文件)找到这个脚本文件夹,选中rename function之后选择go版本,所以将这个exe拖进IDA7.6之中,找到程序入口main.main

image-20220221232254810

找到初始化虚拟机的地方和opcode

image-20220222081345526

第二个函数 mov

image-20220222084558720

第三个函数 mov

image-20220222084616048

image-20220222084743697

第四个函数 mov

image-20220222084926787

第五个函数 push

image-20220222085210002

第六个函数 和push类似的样子

image-20220222085400565

第七个函数 pop

image-20220222085437971

第八个函数 add

image-20220222085652595

第九个函数 sub

image-20220222085733426

第十个函数 div

image-20220222085838161

第十一个函数 mul

image-20220222085928057

第十二个函数 xor

image-20220222090005356

第十三个函数 jmp

image-20220222090054005

第十四个函数 jmp(多了一个判断条件)当等于的时候跳转

image-20220222090501248

第十五个函数 jnp

image-20220222090557647

第十六个函数 jlp 当小于的时候跳转

image-20220222090652596

第十七个函数 jhp 当大于的时候跳转

image-20220222090801331

第十八个函数 scanf

image-20220222091046004

第十九个函数 print

image-20220222091135371

第二十个函数 quit

image-20220222091303160

初始化虚拟机并且载入opcode之后,要运行这个虚拟机,所以查看虚拟机的运行的函数,用流程图的方式查看函数的逻辑,找到逻辑之中的循环,找到其中的关键代码

image-20220222120534330

进行动调得到函数的对应关系,动态调试得到的对应关系,

image-20220223001010055

跟踪到[rbx+rdi*8+1008h]这个里面,通过按D将db的数据转化成dq

image-20220223001105287

通过他们的顺序,就能知道code 和 函数的对应关系

opcode的 0-16对应函数1-17

97 98 99对应函数18 19 20

将opcode得到

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
mov r0 0X57
pushchar(r0)
mov r0 0X65
pushchar(r0)
mov r0 0X6C
pushchar(r0)
mov r0 0X63
pushchar(r0)
mov r0 0X6F
pushchar(r0)
mov r0 0X6D
pushchar(r0)
mov r0 0X65
pushchar(r0)
mov r0 0X20
pushchar(r0)
mov r0 0X74
pushchar(r0)
mov r0 0X6F
pushchar(r0)
mov r0 0X20
pushchar(r0)
mov r0 0X56
pushchar(r0)
mov r0 0X4E
pushchar(r0)
mov r0 0X43
pushchar(r0)
mov r0 0X54
pushchar(r0)
mov r0 0X46
pushchar(r0)
mov r0 0X32
pushchar(r0)
mov r0 0X30
pushchar(r0)
mov r0 0X32
pushchar(r0)
mov r0 0X32
pushchar(r0)
mov r0 0X21
pushchar(r0)
mov r0 0XA
pushchar(r0)
mov r0 0X69
pushchar(r0)
mov r0 0X6E
pushchar(r0)
mov r0 0X70
pushchar(r0)
mov r0 0X75
pushchar(r0)
mov r0 0X74
pushchar(r0)
mov r0 0X20
pushchar(r0)
mov r0 0X66
pushchar(r0)
mov r0 0X6C
pushchar(r0)
mov r0 0X61
pushchar(r0)
mov r0 0X67
pushchar(r0)
mov r0 0X3A
pushchar(r0)
mov r0 0XA
pushchar(r0)


mov r19 0X49
mov r3 0X0
mov r1 0X2B
mov r2 0X1
scanf(r0)
push r0
sub r1 r2
if r1 != r3:quit
mov r0 0X0
push r0
nop
nop

pop r0
mov r5 0X100
mul r0 r5
mov r6 r0
pop r0
add r6 r0
mov r0 r6
mul r0 r5
mov r6 r0
pop r0
add r6 r0
mov r0 r6
mul r0 r5
mov r6 r0
pop r0
add r6 r0
nop

pop r0
mov r5 0X100
mul r0 r5
mov r7 r0
pop r0
add r7 r0
mov r0 r7
mul r0 r5
mov r7 r0
pop r0
add r7 r0
mov r0 r7
mul r0 r5
mov r7 r0
pop r0
add r7 r0
nop

pop r0
mov r5 0X100
mul r0 r5
mov r8 r0
pop r0
add r8 r0
mov r0 r8
mul r0 r5
mov r8 r0
pop r0
add r8 r0
mov r0 r8
mul r0 r5
mov r8 r0
pop r0
add r8 r0
nop

pop r0
mov r5 0X100 //进行位移运算 向左移动8位
mul r0 r5
mov r9 r0
pop r0
add r9 r0
mov r0 r9
mul r0 r5
mov r9 r0
pop r0
add r9 r0
mov r0 r9
mul r0 r5
mov r9 r0
pop r0
add r9 r0
nop

pop r0
mov r5 0X100 //进行位移运算 向左移动8位
mul r0 r5
mov r10 r0
pop r0
add r10 r0
mov r0 r10
mul r0 r5
mov r10 r0
pop r0
add r10 r0
mov r0 r10
mul r0 r5
mov r10 r0
pop r0
add r10 r0
nop

pop r0
mov r5 0X100 //进行位移运算 向左移动8位
mul r0 r5
mov r11 r0
pop r0
add r11 r0
mov r0 r11
mul r0 r5
mov r11 r0
pop r0
add r11 r0
mov r0 r11
mul r0 r5
mov r11 r0
pop r0
add r11 r0
nop

pop r0
mov r5 0X100 //进行位移运算 向左移动8位
mul r0 r5
mov r12 r0
pop r0
add r12 r0
mov r0 r12
mul r0 r5
mov r12 r0
pop r0
add r12 r0
mov r0 r12
mul r0 r5
mov r12 r0
pop r0
add r12 r0
nop

pop r0
mov r5 0X100 //进行位移运算 向左移动8位
mul r0 r5
mov r13 r0
pop r0
add r13 r0
mov r0 r13
mul r0 r5
mov r13 r0
pop r0
add r13 r0
mov r0 r13
mul r0 r5
mov r13 r0
pop r0
add r13 r0
nop

pop r0
mov r5 0X100
mul r0 r5
mov r14 r0
pop r0
add r14 r0
mov r0 r14
mul r0 r5
mov r14 r0
pop r0
add r14 r0
mov r0 r14
mul r0 r5
mov r14 r0
pop r0
add r14 r0
nop

pop r0
mov r5 0X100
mul r0 r5
mov r15 r0
pop r0
add r15 r0
mov r0 r15
mul r0 r5
mov r15 r0
pop r0
add r15 r0
mov r0 r15
mul r0 r5
mov r15 r0
pop r0
add r15 r0
nop

pop r0
mov r5 0X100
mul r0 r5
mov r16 r0
pop r0
add r16 r0
mov r0 r16
mul r0 r5
mov r16 r0
pop r0
add r16 r0
mov r0 r16
mul r0 r5
mov r16 r0
pop r0
add r16 r0
nop

push r6
push r7
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
push r16
//第一次加密和比较
pop r1
pop r2
mov r20 0X11C
mov r0 0X154
jmp r0
mov r0 0XE8D1D5DF
mov r19 0X183
mov r20 0X153
if r1 != r0:quit // 0XE8D1D5DF 数据的比较
mov r0 0XF5E3C114
if r2 != r0:quit // 0XF5E3C114
// 第二次加密和比较
pop r1
pop r2
mov r20 0X127
mov r0 0X154
jmp r0
mov r0 0X228EC216
mov r19 0X183
mov r20 0X153
if r1 != r0:quit // 0X228EC216 数据的比较
mov r0 0X89D45A61
if r2 != r0:quit // 0X89D45A61
// 第三次加密和比较
pop r1
pop r2
mov r20 0X132
mov r0 0X154
jmp r0
mov r0 0X655B8F69
mov r19 0X183
mov r20 0X153
if r1 != r0:quit // 0X655B8F69 数据的比较
mov r0 0X2484A07A
if r2 != r0:quit // 0X2484A07A
// 第四次加密和比较
pop r1
pop r2
mov r20 0X13D
mov r0 0X154
jmp r0
mov r0 0XD9E5E7F8
mov r19 0X183
mov r20 0X153
if r1 != r0:quit // 0XD9E5E7F8 数据的比较
mov r0 0X3A441532
if r2 != r0:quit // 0X3A441532
// 第五次加密和比较
pop r1
pop r2
mov r20 0X148
mov r0 0X154
jmp r0
mov r0 0X91AB7E88
mov r19 0X183
mov r20 0X153
if r1 != r0:quit // 0X91AB7E88
mov r0 0X69FC64BC
if r2 != r0:quit // 0X69FC64BC
pop r1
mov r0 0X7D3765
if r1 != r0:quit // 0X7D3765
mov r0 0X189
jmp r0
quit!

mov r3 0X9E3779B9 //tea函数开始的地方 delta
mov r4 0X95C4C //k0
mov r5 0X871D //k1
mov r6 0X1A7B7 //k2
mov r7 0X12C7C7 //k3
mov r8 0X0
mov r17 0X10 //用于位移的一堆数字
mov r18 0X20
mov r19 0X160
mov r10 0X0
mov r11 0X20
mov r12 0X1
add r8 r3
mov r0 r2
mul r0 r17
add r0 r4
mov r14 r0
mov r0 r2
add r0 r8
mov r15 r0
mov r0 r2
div r0 r18
add r0 r5
mov r16 r0
mov r0 r14
xor r0 r15
xor r0 r16
add r1 r0
mov r0 r1
mul r0 r17
add r0 r6
mov r14 r0
mov r0 r1
add r0 r8
mov r15 r0
mov r0 r1
div r0 r18
add r0 r7
mov r16 r0
mov r0 r14
xor r0 r15
xor r0 r16
add r2 r0
sub r11 r12
if r11 != r10:quit
jmp r20
nop

mov r0 0X6E
pushchar(r0)
mov r0 0X6F
pushchar(r0)
jmp r20
nop

mov r0 0X79
pushchar(r0)
mov r0 0X65
pushchar(r0)
mov r0 0X73
pushchar(r0)
jmp r20
nop
nop
nop
nop
nop
v[2]={0XE8D1D5DF,0XF5E3C114,0X228EC216,0X89D45A61,0X655B8F69,0X2484A07A,0XD9E5E7F8,0X3A441532,0X91AB7E88,0X69FC64BC,0X7D3765}

网上的TEA的加密代码:

1
2
3
4
5
6
7
8
9
10
11
void encrypt (uint32_t v[2], const uint32_t k[4]) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x9E3779B9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}

网上的TEA的解密代码:

1
2
3
4
5
6
7
8
9
10
11
void decrypt (uint32_t v[2], const uint32_t k[4]) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up; sum is 32*delta */
uint32_t delta=0x9E3779B9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}

这道题目之中的TEA解密的代码:

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
#include <bits/stdc++.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include<windows.h>
using namespace std;

void Decrypt(unsigned long* EntryData,unsigned long* Key) {
//分别加密数组中的前四个字节与后4个字节,4个字节为一组每次加密两组
unsigned long x = EntryData[0];
unsigned long y = EntryData[1];

unsigned long sum = 0xC6EF3720;
unsigned long delta = 0x9E3779B9;
sum = delta << 5; //注意这里,sum = 32轮之后的黄金分割值. 因为我们要反序解密.
//总共加密32轮 那么反序也解密32轮
for (int i = 0; i < 32; i++) {
// 先将y解开 然后参与运算在解x
y -= ((x << 4) + Key[2]) ^ (x + sum) ^ ((x >> 5) + Key[3]);
x -= ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1]);
sum -= delta;
}
//最后加密的结果重新写入到数组中
EntryData[0] = x;
EntryData[1] = y;
}

int main() {
//通过对比元素的代码处将其分为6组,每两个是一组,只有前10个进行了加密的操作
unsigned long t1[3] = {0XE8D1D5DF,0XF5E3C114,0x0};
unsigned long t2[3] = {0X228EC216,0X89D45A61,0x0};
unsigned long t3[3] = {0X655B8F69,0X2484A07A,0x0};
unsigned long t4[3] = {0XD9E5E7F8,0X3A441532,0x0};
unsigned long t5[3] = {0X91AB7E88,0X69FC64BC,0x0};
unsigned long t6[2] = {0X7D3765,0x0};
//printf("待加密的数值 = %s\r\n", (char*)Data);

unsigned long key[4] = { 0X95C4C,0X871D,0X1A7B7,0X12C7C7 };

// 分析代码我们可以得到一共加密了5次,所以数组之中只有10个分组进行了加密的操作
Decrypt(t1, key);
Decrypt(t2, key);
Decrypt(t3, key);
Decrypt(t4, key);
Decrypt(t5, key);

printf("解密后的数值 = %s%s%s%s%s%s\r\n", (char*)t1,(char*)t2,(char*)t3,(char*)t4,(char*)t5,(char *)t6);
//system("pause");
return 0;
}
VNCTF{ecd63ae5-8945-4ac4-b5a5-34fc3ade81e7}