Decompetition v2.0 baby-c writeup

2022逆向挑战赛中一道简单的C逆向,由于作者能力有限先写了这一篇writeup,其他的还在弄懂中。。

比赛官网:https://decompetition.io/

github:https://github.com/decompetition/challenges-2021

baby-c

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
; This is the disassembly you're trying to reproduce.
; It uses Intel syntax (mov dst, src).

; 一些初始化操作,把某个变量的值赋为1。
main:
endbr64
push rbp
mov rbp, rsp
push rbx
sub rsp, 0x18
mov [rbp-0x15], 1
; 调用getc函数,将获取到的输入与-1比较,若相等则跳转到block7。
block1:
mov rax, [stdin]
mov rdi, rax
call getc@plt.sec
mov [rbp-0x14], eax
cmp [rbp-0x14], -1
je block7
; 调用__ctype_b_loc函数,把结果放在rax中,将获取到的输入进行一系列赋值运算。
; 然后eax与0x2000做与运算,测试eax,若相等则跳转到block4。
block2:
call __ctype_b_loc@plt.sec
mov rax, [rax]
mov edx, [rbp-0x14]
movsxd rdx, edx
add rdx, rdx
add rax, rdx
movzx eax, [rax]
movzx eax, ax
and eax, 0x2000
test eax, eax
je block4
; 这里可以看到stdout和我们之前的输入,调用putc,将某个变量赋值为1,跳转到block1。
block3:
mov rdx, [stdout]
mov eax, [rbp-0x14]
mov rsi, rdx
mov edi, eax
call putc@plt.sec
mov [rbp-0x15], 1
jmp block1
; 比较某个变量与0,若相等跳转到block6。
block4:
cmp [rbp-0x15], 0
je block6
; 调用了toupper函数,将某个变量赋值为0,跳转到block1。
block5:
mov rbx, [stdout]
mov eax, [rbp-0x14]
mov edi, eax
call toupper@plt.sec
mov rsi, rbx
mov edi, eax
call putc@plt.sec
mov [rbp-0x15], 0
jmp block1
; 调用了tolower函数,调用了putc,跳转到block1。
block6:
mov rbx, [stdout]
mov eax, [rbp-0x14]
mov edi, eax
call tolower@plt.sec
mov rsi, rbx
mov edi, eax
call putc@plt.sec
jmp block1
; return 0
block7:
mov eax, 0
add rsp, 0x18
pop rbx
pop rbp
ret

写完注释感觉好一些了,对自己的逆向燃起了一丢丢希望。

一共用到了两个变量[rbp-0x15] [rbp-0x14]

补充知识__ctype_b_loc 函数

这个函数定义在 ctype.h标准库 ,参考这篇文章

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
v3 = __ctype_b_loc();
printf("isalnum %d\n", (*v3)[s] & 8);
v4 = __ctype_b_loc();
printf("isalpha %d\n", (*v4)[s] & 0x400);
v5 = __ctype_b_loc();
printf("iscntrl %d\n", (*v5)[s] & 2);
v6 = __ctype_b_loc();
printf("isdigit %d\n", (*v6)[s] & 0x800);
v7 = __ctype_b_loc();
printf("isgraph %d\n", (*v7)[s] & 0x8000);
v8 = __ctype_b_loc();
printf("islower %d\n", (*v8)[s] & 0x200);
v9 = __ctype_b_loc();
printf("isprint %d\n", (*v9)[s] & 0x4000);
v10 = __ctype_b_loc();
printf("ispunct %d\n", (*v10)[s] & 4);
v11 = __ctype_b_loc();
printf("isspace %d\n", (*v11)[s] & 0x2000);
v12 = __ctype_b_loc();
printf("isupper %d\n", (*v12)[s] & 0x100);
v13 = __ctype_b_loc();
printf("isxdigit %d\n", (*v13)[s] & 0x1000);
v14 = __ctype_b_loc();
printf("isblank %d\n", (*v14)[s] & 1);
return 0;

我们可以看到0x2000是isspace,可以使用它来实现。

运行一下:

1
2
3
4
5
fr3y@ubuntu:~$ ./baby-c
abcd789uiuiuiu
Abcd789uiuiuiu
DE890absdUUUUU
De890absduuuuu

功能就是把首字母大写,其余小写。

不过刚刚作为一个废物发现自己并不能直接对着汇编写源码,所以用IDA反汇编了给出的可执行文件,稍微修改了一下。

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

int main(void)

{
char v7;
int input;

v7 = 1;
while ( 1 )
{
input = getc(stdin);
if ( input == -1 )
break;
if ( ((*__ctype_b_loc())[input] & 0x2000) != 0 )
{
putc(input, stdout);
v7 = 1;
}
else
{
if ( v7 )
{
putc(toupper(input), stdout);
v7 = 0;
}
else
{
putc(tolower(input), stdout);
}
}
}
return 0;
}

运行结果:

Source Score Weight Total
Test Cases 100% 20% 20%
ASM Diff 100% 60% 60%
Perfect Match Bonus 100% 20% 20%
Latest Submission 100%

IDA真的牛,我不如IDA…T_T

对于这道题我同样试了下ghidra,发现结果不如IDA清楚。。(

贴下这道题的源码:

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

int main() {
char cap = 1;

while(1) {
int c = getc(stdin);
if(c == EOF) break;

if(isspace(c)) {
putc(c, stdout);
cap = 1;
}
else if(cap) {
putc(toupper(c), stdout);
cap = 0;
}
else {
putc(tolower(c), stdout);
}
}

return 0;
}

确实用了isspace…

我确实是一个逆向小菜鸡。。。