MRCTF 2021

MRCTF 2021

谭总:这波是re带飞

RE

real_signIn

cmp

换的表

MRCTF{wElc0Me_t0_MRCTF_2o21!!!}

dynamic_debug

main 很简单

里面的 check 看到了其他的引用 在引用处还有异或

遂动调至此处 果然其代码被更改

去掉一些垃圾指令以后 创建函数就可以F5了

随便分析一下就知道是 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
#include<stdio.h>
#define det 0x9E3779B9

unsigned char cmp[] =
{
0x99, 0xA1, 0x85, 0x55, 0x68, 0x5D, 0x82, 0x7E, 0x39, 0x00,
0x4D, 0x94, 0x43, 0x69, 0x72, 0x71, 0x06, 0x43, 0x51, 0x6A,
0x00, 0xAD, 0x14, 0x4B, 0x3F, 0x0D, 0xD2, 0x64, 0x15, 0xDB,
0x37, 0x9F,
};

unsigned char key[]="cdbgnamie_dyilik";

int main()
{
for(int i=0;i<8;i+=2)
{
int sum=det*32;
unsigned int a=((unsigned int*)cmp)[i],b=((unsigned int*)cmp)[i+1];
for(int j=0;j<32;++j)
{
b -= ((a + sum) ^ (16 * a + ((unsigned int*)key)[1]) ^ ((a >> 5) + ((unsigned int*)key)[0]));
a -= (b + sum) ^ (16 * b + ((unsigned int*)key)[3]) ^ ((b >> 5) + ((unsigned int*)key)[2]);
sum -=det;
}
printf("%.4s%.4s",(char*)&a,(char*)&b);
}
}

MRCTF{Dyn4m1c_d3buG_1s_a_ki11eR}

EzGame

玩出来

条件是只死一次 捡到105个星星 捡到曲奇 找到外星人 回家

如果不开挂的话是完不成的 因为捡曲奇就会死 然后105个星星有的星星太高了

所以至少需要一个能够更改高度的挂

然后跳起来为0 可以跳起来即平地状态搜1 可以搜到一个地址

然后一直锁写1就可以连跳了

然后正常完成任务只死一次就可以getflag了

不过后来研究了一下

在点击getflag的时候

会判断是否吃饼干 是否回家 是否找到外星人 星星是否是105 是否只死1次

然后用一个key去解密 再判断解密后的flag前5位是否是MRCTF 如果不是就说你作弊

有一个关键就是捡星星的时候key会被改变

于是逆出了算法

然后用转换过一次的key再转换104次即得到正确的key

再把其他条件hack一下 点击getflag就可以得到flag了

补一张连跳的图

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

void fun(unsigned long long& key)
{
char v13;
int v11;
long long v10,v12;
unsigned long long v14;
for(int i=0;i<4;++i)
{
v10 = 0;
v11 = 0;
v12 = 1;
for(int j=0;j<64;++j)
{
v13 = v11++ & 0x3F;
v14 = v12 & key;
v12 = __ROL8__(v12, 1);
v10 ^= v14 >> v13;
}
key = v10 | (2 * key);
}
}

int main()
{
unsigned long long key=0xEADBEEFAA114514E;

for(int i=0;i<104;++i)
fun(key);
printf("%llx",key);

}//ea8451453bd5b7dd

MRCTF{Ez_G@mE_MaY_Be_Ha3d_4_y0U}

MR_register

这题很有意思

打开主main后,主main创建管道

然后用环境变量 hWrite 保存管道句柄

然后打开子main

然后主main一直等待debug事件

debug事件有下

如果 rip 为 int 3, rip+1 为 0x25 则让 rip+=2

0x25是作者故意写在子main流程的字节 用来标识

如果 rip 为0x25,rip+1 也为0x25 就读一段字节 异或后 写回 rip

这里动调可以知道这里异或的是子main的下面

用ida脚本异或一下即可 再把他的那些没用的0x25 0xC3 nop掉即可反编译

1
2
3
4
5
6
7
8
9
import idc
st = 0x401E1f
i = 0
while st <= 0x401E1f+0x57d:
value = ida_bytes.get_byte(st)
value ^= (i%256)
ida_bytes.patch_byte(st, value)
st += 1
i += 1

还有一个除零异常

里面有一个 call 扰乱了ida分析 nop 掉即可反编译

这里又是和上面的 读 异或 写 流程很像

动调这里就可以得到异或后的字节 是后面会用到的 cmp

子main流程如下

将 cmp 的内容写入管道 然后引发除零异常 异常处理完后 cmp 已经被修改过了

sub_401CA7 中引发了 int 3 修改了字节码 用上面那个脚本处理过后(并nop掉没用字节)可以比较清晰的反编译

这个函数将两个输入运算后将结果写到 WINDIR\temp\sign 中

而下面那个 sub_40239E 就是 check 函数

初步逆了一下正向算法如下

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
#include<stdio.h>
#include<string.h>
char table[]="ABCDEFGH123456780IJKLMNO+OPQRStu\\vwxyzTUabcdefghVWXYZijklmnopqrs";
char cmp[]="GZ5yExGiGXEx5zGXExGWHlHlExGX5wGVExGZ5wGYExGk5vGjEx5xHl5yExGWGZ5vGWExGXGWHl5yExGYGj5vGkExGiHlGjGZExHl5THlGVEx5zHlGZGYExGWGkHlGXGkExGXGjGVHm5yExGY5yHmGXGjExGjGY5vGYHlEx5vGWGY5xGiExGWGVGZ5zGi5xExGW5vGjGXGiGXExGX5v5wGWGXGXExGZGiGWGY5wGYExGj5T5xGiGYGZEx5wGZ5yHmGiGiExGWGXGZHm5z5wHmExGW5yHmGkHlGj5yExGX5T5zGWGkHm5yExGZ5yGkHmGVGj5wExGk5yGi5vHlGZGWEx5x5v5yGYHl5x5yExGWGZHlGX5zGWHl5wEx";
char mail[1000];//input1
char crit[1000];//input2
char v8[]={0,2,4,6};
char v10[1000];
char v11[1000];
char Buf[1000];
char Des[1000];
unsigned int v21[1000];

int main()
{
unsigned char cha1,cha2,cha3;
int i,j,lv10,lv11,_pos;
for ( i = 0; i < strlen(mail); ++i )
{
cha1 = (mail[i] >> 6) & 1 ^ v8[i % 4];
cha2 = (mail[i] >> 3) & 7;
cha3 = mail[i] & 7;
v10[j] = table[9 * cha1 + cha2];
v10[j + 1] = table[9 * cha2 + cha3];
j += 2;
}
v21[0] = *crit;
v21[1] = crit[1];
sprintf(Buf, "%x#", (unsigned int)v21[0]);
sprintf(&Buf[strlen(Buf)], "%x#", (unsigned int)v21[1]);
for ( i = 2; i < strlen(crit); ++i )
{
v21[i] = v21[i - 2] + v21[i - 1] + crit[i];
sprintf(&Buf[strlen(Buf)], "%x#", (unsigned int)v21[i]);
}
j = 0;
for ( i = 0; i < strlen(Buf); ++i )
{
cha1 = (Buf[i] >> 6) & 1;
cha2 = (Buf[i] >> 3) & 7;
cha3 = Buf[i] & 7;
v11[j] = table[9 * cha1 + cha2];
v11[j + 1] = table[9 * cha2 + cha3];
j += 2;
}
lv11 = strlen(v11);
lv10 = strlen(v10);
strcpy(Des, v10);
Des[strlen(Des)] = '-';
for ( i = lv10 + 1; i <= lv11 + lv10; ++i )
Des[i] = v11[i - lv10 - 1] ^ v10[(i - lv10 - 1) % lv10];
for ( i = 0; Des[i]; ++i )
{
if( Des[i] == '-' )
{
_pos = i;
break;
}
}
j = 0;
for ( i = _pos + 1; i < strlen(Des) ; ++i )
{
Des[i] ^= Des[j % _pos];
if ( Des[i] != cmp[j] )
return 0;
++j;
}
}

仔细看看可以发现mail被异或两次 相当于mail无所谓

尝试解发现table有问题 仔细看了一下才发现 每8位他加了一个空格

后面用的编辑器把#去掉 换成正确的格式 再解掉斐波那契算法即得flag

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

char flag[100]={0};
char table[]="ABCDEFGH 12345678 0IJKLMNO +OPQRStu \\vwxyzTU abcdefgh VWXYZijk lmnopqrs";
char cmp[]="GZ5yExGiGXEx5zGXExGWHlHlExGX5wGVExGZ5wGYExGk5vGjEx5xHl5yExGWGZ5vGWExGXGWHl5yExGYGj5vGkExGiHlGjGZExHl5THlGVEx5zHlGZGYExGWGkHlGXGkExGXGjGVHm5yExGY5yHmGXGjExGjGY5vGYHlEx5vGWGY5xGiExGWGVGZ5zGi5xExGW5vGjGXGiGXExGX5v5wGWGXGXExGZGiGWGY5wGYExGj5T5xGiGYGZEx5wGZ5yHmGiGiExGWGXGZHm5z5wHmExGW5yHmGkHlGj5yExGX5T5zGWGkHm5yExGZ5yGkHmGVGj5wExGk5yGi5vHlGZGWEx5x5v5yGYHl5x5yExGWGZHlGX5zGWHl5wEx";
int a[650];
unsigned char buf[1000];

int main()
{
for (int i = 0;cmp[i];i++)
for (int j = 0;j<65;j++)
if (cmp[i] == table[j])
{
a[i] = j;
break;
}
int k = 0;
for (int i = 0;i<377;i+=2)
{
unsigned char cha1 = (a[i] > 9);
unsigned char cha2 = a[i ] - 9 * cha1;
unsigned char cha3 = a[i+1] - 9 * cha2;
buf[k++]=(cha1<<6)|(cha2<<3)|cha3;
}
//printf("%s",buf);
//4d#52#e2#188#2b0#4b3#7a6#c8d#14a1#218d#36a7#5864#8f80#e843#17827#2609d#3d926#63a38#a13c5#104e5c#1a6252#2ab122#4513b3#6fc534#b4d955#1249eb9#1d9786d#2fe179d#4d7906b#7d5a841#cad38cd#1482e18b#
int tmp[]={0x4d,0x52,0xe2,0x188,0x2b0,0x4b3,0x7a6,0xc8d,0x14a1,0x218d,0x36a7,0x5864,0x8f80,0xe843,0x17827,0x2609d,0x3d926,0x63a38,0xa13c5,0x104e5c,0x1a6252,0x2ab122,0x4513b3,0x6fc534,0xb4d955,0x1249eb9,0x1d9786d,0x2fe179d,0x4d7906b,0x7d5a841,0xcad38cd,0x1482e18b};
flag[0]=0x4d;flag[1]=0x52;
for(int i=2;tmp[i];++i)
flag[i]=tmp[i]-tmp[i-1]-tmp[i-2];
printf("%s",flag);
}

MRCTF{C4n_y0u_d3bug_1t?_n0_wa9!}

MRCheckin

首先第一个框框明文比较 MRCTF

然后切片(6,13]的md5解得为 Andr01d

然后格式 MRCTF{Andr01d+xxx}

又由cmp的数组长度可知xxx长度为25

然后动调出key数组即可

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>

int main()
{
char cmp[]={110, 82, 89, 87, 86, 0x5F, 6, 94, 105, 71, 80, 92, 83, 4, 93, 85, 0x6F, 65, 93, 0x6F, 0x7C, 98, 0x73, 100, 0x76};
char key[100]={0x31,0x31,0x31,0x32,0x35,0x34,0x37,0x30,0x36,0x30,0x35,0x30,0x30,0x34,0x30,0x30,0x30,0x35,0x32,0x30,0x31,0x30,0x30,0x30,0x30,0};
for(int i=0;key[i];++i)
printf("%c",key[i]^cmp[i]);

}

MRCTF{Andr01d_check1n_welc0me_to_MRCTF}

文章作者: Usher
文章链接: https://usher2008.github.io/2021/04/11/MRCTF%202021/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Usher