avatar

CNSS RE wp

CNSS RE

搭建虚拟机

按照题目要求,安装VMware,在虚拟机里直接执行文件即可得到Flag

1
2
root@kali:~/ws$ ./movgcc
cnss{H31l0,1inUx!!}

Baby_string

用IDA加载并按Shift+F12查看字符串即可找到
upload successful

1
.data:00403004 00000019 C cnss{7h1s_iS_wh4t_R3_1s}

Baby_encode

用IDA加载,找到main后进入F5反汇编查看伪代码
upload successful

1
2
3
4
5
6
7
scanf("%s", input);
for ( i = 0; i < strlen(flag); ++i )
flag[i] ^= 7u;
if ( !strcmp(flag, input) )
puts("Oh you got the flag!");
else
puts("What a pity!");

可以看到strcmp函数,相同为1,点击flag进去后发现

1
2
.data:00403004 ; char flag[]
.data:00403004 _flag db 'ditt|7oXj~X`77777777C&z',0 ; DATA XREF: _main+3Do

所以看程序我们只需要异或回去就好

1
2
3
4
5
6
7
8
9
#include<stdio.h>
#include<string>
int main() {
char flag[] = "ditt|7oXj~X`77777777C&z";
for (int i = 0; i < strlen(flag); ++i)
flag[i] ^= 7u;
printf("%s", flag);
}
//cnss{0h_my_g00000000D!}

snake

打开后发现是一个贪吃蛇的游戏,我们放入IDA中进行反编译
upload successful
upload successful
发现了重要的语句

1
2
3
4
5
6
if ( v4 > 265 )
{
v5 = dword_40EDC0;
LOBYTE(v5) = dword_40EDC0 ^ 0xE9;
sub_4032FB(v5);
}

当蛇的长度大于265时,游戏就能通关,把v4和v5的值改成266即可伪装成通关的状态
再看一下sub_4032FB

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
  sub_401500(v7);
sub_401500(Text);
sub_401500(Caption);
sub_401500(Format);
sub_401500(v3);
sub_401500(v2);
system("title 贪吃蛇(控制台版)[当前状态:获胜~!]");
v9 = (unsigned __int8)a1;
v8 = (a1 >> 8) & 255;
dword_40E024 = 0;
v10 = 0;
while ( a1 )
{
aDi585435673491[v10] = a1;
a1 >>= 8;
++v10;
}
sub_401C0A(aHck, (int)aDi585435673491);
if ( !strncmp("cnss", Str2, 4u) )
{
puts(Str2);
MessageBoxA(0, Text, Caption, 0);
system("pause");
exit(0);
}
printf(Format);
MessageBoxA(0, v3, v2, 0);
system("pause");
system("mode con cols=80 lines=23");
return sub_4023DC(49);
}

查看是谁调用了这个地址我们看一下
upload successful
找到内存中snake_realease_v2.exe+EEC4所对应的数据可以看到C4和C8位置
upload successful
由于我们分析了是266时候胜利所以我们直接改成C8位置改成268
upload successful

1
cnss{CE_caN_ju5T_Act_w1lfulLy!!}

Baby_Assembly_Code

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
extern	printf

SECTION .data
name: db 'mrh929', 0
HelloCnss: db 'hello,cnss!', 10, 9 , "---%s", 10,0
str1: db "Let's start reading the assembly code!",10,0

enc: db 0,0,0,7,6,83,83,0,19,11,84,79,7,17,65,5,73,29,14,77,4,4,0,0,2,18,31,6,24,14,13,13,73,12,1,69,68,0,0,0
str2: db "Come on!",10,0
exp1: db "%s%s",0

str3: db "input your answer:",10,0
exp2: db "(%d + %d + %d + %d * %d - (%d >> 1)) ^ %d = ?", 10, "%s" ,0

SECTION .text
global main

alter_str: //将edi指向字符串中的值都和esi指向字符串中的值进行异或一直到遇到0为止
push rbp //将rbp压入栈,保存堆栈
mov rbp, rsp //栈顶和栈底到一个位置
xor ecx, ecx //清空ecx的值

label:
mov al, [edi+ecx] //将edi+ecx地址中的值放到al中
test al, al //进行与的操作,改变ZF标志位(结果为0,ZF 变成1),判断al是否为0
je quit //ZF标志位看上个语句的结果,ZF变成了1,就跳转到quit
mov bl, [esi+ecx] //将esi+ecx中地址的值放到bl中
xor al, bl //将al与bl中的值进行异或
mov [edi+ecx], al //al中的值放入edi+ecx的地址中
add ecx, 1 //ecx进行+1
jmp label //跳转到label

quit:
leave //还原堆栈
ret //还原函数

main: ;主函数从这里开始
push rbp //把rbp压入堆栈,保存堆栈
mov rbp, rsp //栈顶和栈底到一个位置
;以上两行不需翻译

mov rdi, HelloCnss //将HelloCnss的地址放入rdi中
mov rsi, name //将name的地址放入rsi中
call printf //调用printf地址的函数,输出HelloCnss与name

mov rdi, str1 //将str1的地址放入rdi中
mov rsi, enc //将enc的地址,放入rsi中
call alter_str //调用alter_str地址的函数,用enc编码str1

mov rdi, exp1 //将exp1的地址放入rdi
mov rsi, str1 //将str1的地址放入rsi
mov rdx, str2 //将str2的地址放入rdx
call printf //调用printf函数,用exp1来格式化输出str1与str2

; 翻译至此(包括alter_str)可得到 60% 的分数

mov rdi, exp2 //将exp2的地址放入rdi
mov rsi, 1 //将1放入rsi中
mov rdx, 2 //将2放入rdx中
mov rcx, 3 //将3放入rcx中
mov r8, 4 //将4放入r8中
mov r9, 5 //将5放入r9中
push str3 //把str3放入堆栈
push 7 //7放入堆栈
push 6 //6放入堆栈

mov rax, 0 //rax清空
call printf //用exp2来格式化输出输出(1 + 2 + 3 + 4 * 5 - (6 >> 1)) ^ 7 = ? ,10,str3,0

; 翻译至此(包括alter_str)
; 并回答问题:
; 就linux下的C语言函数的传参方式而言,x86与x64下有什么区别?
//64位linux下前6个参数用寄存器传入,后面的所有参数栈传入
//32位linux,都用栈传入
//_stdcall和_cdecl用push堆栈传参,而_stdcall来说push恢复堆栈的时候在调用函数里面,从而节省了代码的占用空间,_cdecl是在正常的调用函数外面进行了堆栈的恢复,_fastcall尽可能用mov寄存器传参提升了运算的速度,因为寄存器的运算速度很快,顺序从右到左
; 可得到 100% 的分数

sub rsp, 10 //rsp-10,栈顶提升提供大小为10的空间
mov rax, 0 //将rax清空
mov rdi, 2 //将2放入rdi中
mov rsi, rsp //把rsp的值放入rsi,将栈顶的值保存到rsi中
mov rdx, 10 //将10放入rdx中
syscall //通过syscall来调用sys_read读取用户输入

; 以上部分要求准确答出该语句所调用的函数,不能用C语言的其他函数代替

; 翻译至此(包括alter_str)可获得 120% 的分数

; prepare for fixing input
lea rcx, [rsi] //把rsi地址中的值放入rcx中
add rcx, rax //rcs+rax

; 以上两行不用翻译

push 0x000a //把10压入堆栈
mov rdx, 0x2174686769722074 //!thgirt
push rdx //rdx压入堆栈
mov rdx, 0x6f6e207369207325 //on si s%
push rdx //rdx压入堆栈
mov rax, rsp //%s is not right!

mov rdx, 0x000a21746867 //!thg
push rdx
mov rdx, 0x6972207369207325 //ir si s%
push rdx
mov rbx, rsp //同上%s is right!

mov dl, [rsi] //将rsi中的值放入dl中
cmp dl, 0x31 //与1进行比较
jne eпd //如果输入的不是1,则跳到end
mov dl, [rsi+1] //rsi地址加1的值放入dl中
cmp dl, 0x36 //与6进行比较
jne eпd //如果输入的不是6,则跳到end
mov dl, [rsi+2] //rsi地址加2的值放入dl中
cmp dl, 0xa //将dl与0xa(16)进行比较
jne end //如果输入的不是16,则跳到end

mov rdi, rbx //rbx放入rdi,rdi指向字符串%s is right!
jmp stop //跳转到stop

end:
mov rdi, rax //rdi指向字符串%s is not right!

stop:
; Fix the output
sub rcx, 1 //rcx-1
mov byte [rcx], 0 ////字符串末尾添0
; Show the result
xor rax, rax //清空rax
call priпtf //调用printf 输出结果is (not) right!

mov rax, 0 //rax清空
leave //还原堆栈
ret //还原原函数

;翻译全部(包括alter_str)可获得 150% 的分数

<–指针–>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*(unsigned long long*)(a.var1) = 0x01020304aabbccdd
声明a.var1指向八个字节大小的空间,并在其中存储了八个字节的内容,在内存中的顺序是反序
结果为
0xDD,0xCC,0xBB,0xAA,0x4,0x3,0x2,0x1,
char是有符号数,所以需要判断符号位
处理01,02,03,04时候因为二进制最高位为0,要保证表示的数字不变,前面全部补0即可,没有影响
处理aa,bb,cc,dd时候因为二进制最高位为1,计算机中负数用补码表示
结果为
0xFFFFFFDD,0xFFFFFFCC,0xFFFFFFBB,0xFFFFFFAA,0x4,0x3,0x2,0x1,
a.var1 + 8 + i
地址越位
结果为
0xDD,0xCC,0xBB,0xAA,0x4,0x3,0x2,0x1,
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,

Easy_Flat

拖进IDA,看一下主函数
upload successful

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
v8 = 2094104837;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( v8 == -1910856655 )
{
v4 = -1741896534;
if ( v9 <= 25 )
v4 = 437352961;
v8 = v4;
}
if ( v8 != -1741896534 )
break;
v5 = strcmp(s, (const char *)(unsigned int)s2) == 0;
v6 = 1105054524;
if ( v5 )
v6 = 304955634;
v8 = v6;
}
if ( v8 != -1214177781 )
break;
sub_400610(s);
v11 = 0;
v8 = 658032998;
}
if ( v8 != -1113027527 )
break;
v9 = 0;
v8 = -1910856655;
}
if ( v8 != -170736245 )
break;
++v9;
v8 = -1910856655;
}
if ( v8 != 304955634 )
break;
sub_400640(s);
v8 = 658032998;
}
if ( v8 != 437352961 )
break;
s[v9] += v9;
v8 = -170736245;
}
if ( v8 == 658032998 )
break;
if ( v8 == 1105054524 )
{
sub_400610(s);
v8 = 658032998;
}
else if ( v8 == 2094104837 )
{
v3 = -1113027527;
if ( v12 != 26 )
v3 = -1214177781;
v8 = v3;
}
}

看一下strcmp v2的字符串的值
upload successful

1
2
3
4
5
.data:0000000000601050 ; char byte_601050[4]
.data:0000000000601050 byte_601050 db 63h, 6Fh, 75h, 76h, 7Fh, 35h, 68h, 6Dh, 7Dh, 3Eh, 6Dh
.data:0000000000601050 ; DATA XREF: main+266↑o
.data:0000000000601050 db 6Ch, 60h, 2 dup(3Eh), 7Dh, 6Fh, 42h, 65h, 72h, 7Bh
.data:0000000000601050 db 67h, 49h, 78h, 6Ch, 96h, 18h dup(0)

分析程序走到sub_400640();这里就可以成功并且退出程序,我们逆推一下 v8 刚开始需要等于304955634之后v8=658032998 break 出去了往前推v8=-1741896534且v5的strcmp需要将对比相等 v8=-1910856655 可以看出来在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    if ( v8 != 437352961 )
break;
s[v9] += v9;
v8 = -170736245;
}
if ( v8 != -170736245 )
break;
++v9;
v8 = -1910856655;
}
while ( v8 == -1910856655 ){
v4 = -1741896534;
if ( v9 <= 25 )
v4 = 437352961;
v8 = v4;
}

因为这两个的while(1)在正确的输出的前面所以可以找到了规律

1
2
3
4
5
6
7
8
9
10
#include <cstdio>
#include <cstring>
int main()
{
char flag[] = { 0x63,0x6F,0x75,0x76,0x7F,0x35,0x68,0x6D,0x7D,0x3E,0x6D,0x6C,0x60,0x3E,0x3E,0x7D,0x6F,0x42,0x65,0x72,0x7B,0x67,0x49,0x78,0x6C,0x96 };
for (int i = 0;i < sizeof(flag) ;i++)
flag[i] -= i;
printf("%s", flag);
}
//cnss{0bfu5caT10n_1S_gR3aT}

Mips架构逆向

照着Mips指令翻译

1
//cnss{71me_t0_le4rn_Mips}

辉夜大小姐想要玩游戏

放入IDA之后,找到主函数进入
upload successful

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
if ( strlen(s) == v3 )
{
v6 = 0;
v7 = 0;
while ( v6 <= 80 )
{
if ( !dword_203020[v6] )
{
v5 = v7++;
dword_203020[v6] = s[v5] - 48;
}
++v6;
}
v8 = 1;
for ( i = 0; i <= 8; ++i )
v8 &= sub_F3B(&dword_203020[9 * i]);
for ( j = 0; j <= 8; ++j )
v8 &= sub_FFA(&dword_203020[j]);
for ( k = 0; k <= 2; ++k )
{
for ( l = 0; l <= 2; ++l )
v8 &= sub_10C3(&dword_203020[27 * k + 3 * l]);
}
if ( v8 )
{
puts("don-dayo!");
sub_11A1((__int64)s);
puts(byte_2032C0);
}

dword_203020:

1
2
3
4
5
6
7
.data:0000000000203020 dword_203020    dd 2 dup(0), 7, 3 dup(0), 4, 2 dup(0), 6, 2 dup(0), 3
.data:0000000000203020 ; DATA XREF: sub_127B+21↑o
.data:0000000000203020 ; main+F2↑o ...
.data:0000000000203020 dd 4 dup(0), 8, 5 dup(0), 1, 3 dup(0), 3, 2 dup(0), 9
.data:0000000000203020 dd 4 dup(0), 1, 4 dup(0), 4, 0, 2, 2 dup(0), 8, 0Ah dup(0)
.data:0000000000203020 dd 2, 4 dup(0), 9, 4 dup(0), 6, 4 dup(0), 3, 2 dup(0)
.data:0000000000203020 dd 4, 1Dh dup(0

sub_F3B

1
2
3
4
5
6
7
for ( i = 0; i <= 8; ++i )
++*((_DWORD *)&v4 + *(signed int *)(4LL * i + a1));
for ( j = 1; j <= 9; ++j )
{
if ( *((_DWORD *)&v4 + j) != 1 )
return '\0';
}

进行了行的9个数是否重复

sub_FFA

1
2
3
4
5
6
7
8
9
  for ( i = 0; i <= 8; ++i )
++*((_DWORD *)&v4 + *(signed int *)(36LL * i + a1));
for ( j = 1; j <= 9; ++j )
{
if ( *((_DWORD *)&v4 + j) != 1 )
return '\0';
}
return 1LL;
}

进行了列的9个数是否重复

sub_10C3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  for ( i = 0; i <= 2; ++i )
{
for ( j = 0; j <= 2; ++j )
{
v1 = *(_DWORD *)(4LL * (9 * i + j) + a1);
++*((_DWORD *)&v6 + v1);
}
}
for ( k = 1; k <= 9; ++k )
{
if ( *((_DWORD *)&v6 + k) != 1 )
return 0LL;
}
return 1LL;
}

进行了3×3的9个数是否重复
upload successful
发现自己输入的个数在rax中,与rdx进行了比较cmp函数,可以看出来是输入64个数

进入到内存中看到了81个数后,进行9×9的整理

1
2
3
4
5
6
7
8
9
2, 1, 7, 5, 8, 9, 4, 3, 6
6, 9, 5, 3, 7, 4, 1, 2, 8
4, 8, 3, 2, 6, 1, 9, 5, 7
3, 4, 6, 9, 2, 7, 5, 8, 1
5, 7, 1, 8, 4, 3, 2, 6, 9
8, 2, 9, 1, 5, 6, 3, 7, 4
7, 3, 2, 4, 1, 8, 6, 9, 5
1, 5, 8, 6, 9, 2, 7, 4, 3
9, 6, 4, 7, 3, 5, 8, 1, 2

输入64个空值得出了答案

1
cnss{faN7ast1c_5ud0ku_R3verse!!}

Junk Code

Author: L0x1c
Link: https://l0x1c.github.io/2019/12/18/2019-12-18/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶