第六届上海市大学生网络安全大赛

2个虚拟机类型的题目。很高兴进了线下,队伍最后第2名/。

909254_NE362J9P4YSEMAR

真正的Babyre

ida开始直接搜索字符串,找到关键函数,简单修复一下栈指针,直接F5。

开始总感觉自己看到的是假的流程,把程序翻了个遍,没发现其它的流程,但是发现了几个反调试,但是调试发现程序都没有使用。。。其中二个:

image-20201115110658656

image-20201114231613090

然后确定这就是一个虚拟机逆向。静态分析一下后直接让程序跑起来。

这里我做的时候和复现的时候,ida识别的竟然不一样,复现分析的更简洁。记得做的时候,每次赋值操作码与数据的时候是使用一个四字节数据来保存的,其中L0BYTE,Byte1,Byte2,HIByte都是不同的含义:

做的时候的一些笔记:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//测试flag。
flag{0123456789012345678901234567890}

//最后比较的数据。
unsigned char ida_chars[] =
{
31, 24, 15, 250, 184, 99, 100, 137, 24, 104,
124, 25, 20, 45, 125, 88, 0, 30, 84, 106,
65, 60, 54, 62, 86, 19, 4, 59, 46, 75,
121, 67, 122, 34, 69, 110, 58, 117, 38, 200,
192, 142
};


LOBYTE:%64 index
BYTE2: 开始与LOBYTE值相同,做运算完的赋值index
BYTE1: xor input[] opcode: 2

3:BYTE1 &= LOBYTE;

然后程序中使用了的一个反调试(时钟检测):简单patch了即可。

image-20201115001246130

乍一看程序流程有点复杂,感觉输入的数据会应该下一步操作码,这个其实就是一个加密操作。

总结一下程序的加密操作(对所有字符串的加密都是这个操作):首先从操作码中取出一个数据op_data ,然后temp = input[i]^op_data,再让op_data &= input[i],input[i] = temp,一直这个循环直到op_data == 0,最后取出input[i+1]进行input[i] ^= input[i+1]。一直这样对所有的input加密2遍,就是使用op_data不同。

使用ida_python导出我们要用的op_data。

按照程序的算法,逆二次即可:第一次解密过程我进行了注释。

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

unsigned char ida_chars[] =
{
31, 24, 15, 250, 184, 99, 100, 137, 24, 104,
124, 25, 20, 45, 125, 88, 0, 30, 84, 106,
65, 60, 54, 62, 86, 19, 4, 59, 46, 75,
121, 67, 122, 34, 69, 110, 58, 117, 38, 200,
192, 142
};

unsigned char a1[] = {9, 2, 7, 8, 6, 9, 11, 10, 4, 11, 12, 4, 7, 7, 10,
4, 3, 9, 4, 6, 3, 12, 11, 13, 4, 10, 7, 7, 2, 6, 6, 12, 9,
14, 7, 8, 11, 13, 14, 12, 4, 7};
unsigned char b[] = {13, 8, 12, 10, 3, 9, 5, 12,
13, 11, 12, 7, 6, 10, 8, 9, 13, 8, 5, 10, 7,
13, 4, 13, 2, 12, 10, 12, 9, 7, 14, 5, 11, 7, 13, 7, 13, 11, 5, 14, 6, 10};

unsigned char c[] = {3, 15, 15, 20, 228, 95, 11, 116, 9, 14, 113, 1, 17, 3, 32, 85, 6, 19, 5, 94, 2, 72, 105, 91, 86, 14, 9, 23, 24, 15, 93, 18, 84, 37, 14, 94, 11, 34, 88, 123, 65, 135};

int main(void)
{

/* int i = 0, j = 0;

ida_chars[41] = 135;
for(j = 40; j >= 0; j--)
{
ida_chars[j] ^= ida_chars[j+1];

for(i = 0; i < 255; i++)
{
unsigned char temp = i, temp1 = i;
unsigned char a = b[j];
while(1)
{
temp1 ^= a;
a &= temp;
a *= 2;
temp = temp1;

if(a == 0)
break;
}
if(temp == ida_chars[j])
{
ida_chars[j] = i;
break;
}
}
}

for(i = 0; i < 42; i++)
printf("%d, ", ida_chars[i]);*/

int i = 0, j = 0;

c[41] = 125;
for(j = 40; j >= 0; j--)
{
c[j] ^= c[j+1];

for(i = 0; i < 255; i++)
{
unsigned char temp = i, temp1 = i;
unsigned char a = a1[j];
while(1)
{
temp1 ^= a;
a &= temp;
a *= 2;
temp = temp1;

if(a == 0)
break;
}
if(temp == c[j])
{
c[j] = i;
break;
}
}
}

for(i = 0; i < 42; i++)
printf("%c", c[i]);
}
//flag{e1750505-7a05-4de9-a333-72ec8cd26a78}

ctfvm

从题目名字可知道是虚拟机逆向。在linux中运行一下,看见字符串信息。

ida中搜索一圈并没有发现任何相关字符串,那就是程序运行时解密出相关字符串。从入口点找到main函数,可以看到464行的代码,分支极其多。。image-20201114224414324

静态分析是不可能了,直接动调。

开始慢慢的调试看哪里解密字符串的,但实在是冗长,直接在while循环下断,然后F9一直跑,直到程序等待我们输入。

随便输入后,开始慢慢跟进熟悉一些函数的功能,发现开始就是在以此取我们输入的字符存入一个大数组中

image-20201114225040904

继续跟踪,分析到我们输入的字符长度要是38,虽然读取了最后的回车符,但是没有使用的。

其中程序中这个内存区域类似就是vm使用的寄存器。

image-20201114230655880

接下来判断我们输入最后一位是不是 ‘}‘ 字符,再把除了flag{ }中的内容复制到与程序最后做比较的字符串的下面,

image-20201114230109551

接着判断我们开始的5位是不是 flag{。

最后就是以此取出我们flag{}中每一位,(0xFB*input[i])&0xff,然后以此与上面的比较字符串比较。解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

unsigned char a[] = {237, 6, 17, 11, 252, 227, 232, 6, 237, 11, 247, 247, 22, 6, 237, 27, 12, 252, 232, 12, 242, 22, 247, 2, 242, 17, 227, 227, 2, 12, 247, 252};


int main(void)
{
unsigned char i = 0, j = 0;

printf("flag{");
for(j = 0; j < 32; j++)
{
for(i = 32; i < 127; i++)
{
if(((i*0xFB)&0xff) == a[j])
printf("%c", i);
}
}
printf("}");
}
//flag{72c149827155b27ad48d6b5f6c99fd54}
-------------本文结束感谢您的阅读-------------