BUUCTF Reverse SimpleRev Writeup

Realknow Lv2

SimpleRev

这道题虽然网上已经有比较多的Writeup了,但是还是想自己写一下,加强一下学习效果🙂


查看main函数,外层就是输入d/D start,q/Q quit的代码实现:

image-20260223220950651

核心是这个Decry函数:

屏幕截图 2026-02-23 222206 屏幕截图 2026-02-23 222230 image-20260223222450983

*注1:*先提一嘴__readfsqword:这是一个栈保护机制,你可以在第15行和第69行看到它,如果这玩意改变了,那么return的异或值就不为0,就说明栈溢出了。总结一句话:这是程序的“防盗报警器”,如果只是逆向分析,可以完全忽略它。

关注最后的比对:textstr2。其中text是可以知道的。第22行join函数实现了key3v9的拼接。其中key3的值是killsv9的值在第19行,是hadow(选中按R将数值转换为字符,又因为是小端序,故取反)。所以text就是killshadow了。

我们也可以算出来key的值:key是由key1src拼接而成。同上,我们可以得到keyADSFKNDCLS

注2:第27行的getchar()是为了“吃掉”上一步输入"d/D"留在缓冲区里的回车符,确保后面的 while 循环能真正等待用户输入 Flag。

注3:Str2显示的.bss:00000000002020A0 key db 80h dup(?)是啥意思?

  • .bss: 这是内存的一个区域,专门存放全局变量静态变量*。*
  • db: 全称 Define Byte,意思是按“字节”为单位定义。
  • 80h: 这是十六进制。转换为十进制是 128
  • dup(?): Duplicate 的缩写。意思是“重复”。? 表示初始值未知(通常在程序运行时才会被填入数据)。

通俗翻译: 程序在内存里申请了一块连续的空地,名字叫 key,大小是 128 个字节。它就像一个能装 128 个字符的空盒子。

第29~34行,

1
2
3
4
5
6
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}

这里是在将key从大写转为小写。

关注核心加密部分:

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
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 'z' )
{
if ( v1 > 64 && v1 <= 'Z' )
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
}
else
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}

读入v1,如果是回车就break;大写和小写字母的加密逻辑一致,都是:

str2[i]=(v139key[v3(mod10)]+97)(mod26)+97str2[i] = (v1 - 39 - key[v3 \pmod{10}] + 97) \pmod{26} + 97

其中 v1 是原始字符,str2[i] 是目标字符(即 “killshadow” 中的一位)。

为什么用 v3 % v5

这是最困惑的地方。在密码学代码中,%(取模运算)通常用于循环遍历固定长度的序列

  • v3: 是一个计数器,它记录了当前用到了密钥的第几个字符。
  • v5: 是密钥 key 的长度(10)。
  • key[v3 % v5]: 这意味着如果你的 Flag 长度是 20,而密钥只有 10 位,当 v3 增加到 10 时,10 % 10 = 0,它会重新回到密钥的第 0 位开始计算。注4:因为前面遍历过(大写转小写),所以此时v3的初值已是10。

我们可以使用正向爆破的手段来得到flag,在这里贴一段网上的脚本:

1
2
3
4
5
6
7
8
9
10
11
text = 'killshadow'
key = 'adsfkndcls'
flag = ''
v5 = len(key)
for i in range(10): #输入十次字符,所以循环十次
for j in range(ord('A'), ord('z')+1): #将所有字母进行尝试,寻找可以使if判断成功的字符
str2 = (j -39 - ord(key[i % v5]) +97) % 26 + 97 #将原本的加密直接照搬过来,这里需要ord(key[i % v5])是因为python不能直接将字符串转成ascii码
if chr(str2) == text[i]: #将算出来的ascii码chr()转成ascii码与text内容比较
flag += chr(j)
break #这里的break作用是如果if判断成功了一次就结束这个内层循环,开始第二个外层循环,如果没有break会输出很多满足条件的别的字符
print(flag)

得到flag:KLDQCUDFZO

当然其实这里有一个问题,那就是如果小写字母和大写字母都满足的情况下,由于大写字母ascii码靠前,有break跳出循环,导致不会输出小写字母的这种情况。所以说其实flag也有别的可能。

  • Title: BUUCTF Reverse SimpleRev Writeup
  • Author: Realknow
  • Created at : 2026-02-23 16:47:38
  • Updated at : 2026-02-23 23:20:59
  • Link: https://realknowtech.github.io/2026/02/23/BUUCTF-Reverse-SimpleRev-Writeup/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
BUUCTF Reverse SimpleRev Writeup