buuctf reverse题解1

1.[GXYCTF2019]luck_guy

elf文件,64位,目测是输入什么lucky number

不管他,直接ida f5大法
主要的函数就是getflag这个函数

设置了随机数,然后switch,本地测试基本不可能走1-5分支
目测一下,比较合理的流程就是451
4为f2赋值
5改f2的值
1拼接f1和f2
其中f1在data段,有字符串数据,f2在bss未初始化全局变量段
然后就走这个流程,一开始脑子抽了,指针那个地方读错了然后就怎么也找不到
结合汇编来读,局部变量s其实不是一个long long类型的数据而是一个字符数组

然后本地写个脚本一运行就出来了
附上本地调试的py脚本

flag = "7F666F6067756369"

listFlag = []

for i in range(len(flag)):
if i % 2 == 0 and i != 0:
listFlag.append(flag[i-2:i])

listFlag.append('69')

print(listFlag)
listFlag.reverse()
for i in range(len(listFlag)):
listFlag[i] = '0x' + listFlag[i]
# listFlag[i] = int(listFlag[i],16)
# print(listFlag)

for i in range(len(listFlag)):
listFlag[i] = int(listFlag[i],base = 16)
print(listFlag)
if i % 2 == 1:
listFlag[i] = listFlag[i] - 2
else:
listFlag[i] = listFlag[i] - 1

print(listFlag)



for i in range(len(listFlag)):
listFlag[i] = chr(listFlag[i])

print("".join(listFlag))

最后得到flag,但是是GXY前缀,换上ctf前缀交上才对
flag{{do_not_hate_me}

2.[BJDCTF2020]JustRE

签到题

感觉像个win32的小程序

搜字符串大法

发现格式化字符串

交上就对了555

3.[FlareOn4]login

下载下来发现是html文件

document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}

发现一段js代码
分析发现核心逻辑主要是这一句,只要是字母就被正则匹配到,然后执行下面这个
String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
大概就是判断字符是否小于Z,如果是第一个括号的值就变成Z否则变成z(注意此时c的值没有发生改变),然后第二个小括号是将c本身+13(注意此时c的值已经发生了改变)
再判断两个值的大小关系,如果前者大就不改变,如果后者大就-26(即相当于+13后-26,本质是原本的字符-13)
字母一共26个,如果本身变成z后大于本身+13,那么字母一定会小于n

有了这个逻辑写解密脚本就可以了,遍历加密后的字符串,如果当前字符属于a-m,那么+13,如果属于n-z,那么-13即可

flag = "PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"
flag = list(flag)

flaglist = ""
for i in range(len(flag)):
if (flag[i] <= 'Z' and flag[i] >= 'A') or (flag[i] >= 'a' and flag[i] <= 'z'):
if (flag[i] <= 'M' and flag[i] >= 'A') or (flag[i] <= 'm' and flag[i] >= 'a'):
flag[i] = chr(ord(flag[i]) + 13)
else:
flag[i] = chr(ord(flag[i]) - 13)

print("".join(flag))

flag{ClientSideLoginsAreEasy@flare-on.com}

看了别的师傅的wp才发现这是一种加密为ROT13加密,学到了

套用ROT13到一段文字上仅仅只需要检查字元字母顺序并取代它在13位之后的对应字母,有需要超过时则重新绕回26英文字母开 头即可。A换成N、B换成O、依此类推到M换成Z,然后序列反转:N换成A、O换成B、最后Z换成M。只有这些出现在英文字母里头的字元受影响;数字、符 号、空白字元以及所有其他字元都不变。因为只有在英文字母表里头只有26个,并且26=2×13,ROT13函数是它自己的逆反: [1]

对任何字元x:ROT13(ROT13(x))=ROT26(x)=x。
直接找个在线解密工具梭出来也可以

4.[GUET-CTF2019]re

是个ELF文件,先checksec一下

发现存在UPX壳
直接用工具脱壳
./upx -d ./re
然后进入ida分析,核心逻辑很简单,求出每一位flag然后转换成字符就可以,但是有坑点

一开始是想逐步求解的,但是发现这也太反人类了,直接用正则提取吧
脚本如下

import re

flag = [
166163712//1629056
,731332800//6771600
,357245568//3682944
,1074393000//10431000
,489211344//3977328
,518971936//5138336
,406741500//7532250
,294236496//5551632
,177305856//3409728
,650683500//13013670
,298351053//6088797
,386348487//7884663
,438258597//8944053
]

text = """
if ( 5198490 * flag[14] != 249527520 )
return 0LL;
if ( 4544518 * flag[15] != 445362764 )
return 0LL;
if ( 3645600 * flag[17] != 174988800 )
return 0LL;
if ( 10115280 * flag[16] != 981182160 )
return 0LL;
if ( 9667504 * flag[18] != 493042704 )
return 0LL;
if ( 5364450 * flag[19] != 257493600 )
return 0LL;
if ( 13464540 * flag[20] != 767478780 )
return 0LL;
if ( 5488432 * flag[21] != 312840624 )
return 0LL;
if ( 14479500 * flag[22] != 1404511500 )
return 0LL;
if ( 6451830 * flag[23] != 316139670 )
return 0LL;
if ( 6252576 * flag[24] != 619005024 )
return 0LL;
if ( 7763364 * flag[25] != 372641472 )
return 0LL;
if ( 7327320 * flag[26] != 373693320 )
return 0LL;
if ( 8741520 * flag[27] != 498266640 )
return 0LL;
if ( 8871876 * flag[28] != 452465676 )
return 0LL;
if ( 4086720 * flag[29] != 208422720 )
return 0LL;
if ( 9374400 * flag[30] == 515592000 )
return 5759124 * flag[31] == 719890500;
return 0LL;
}
"""

# 匹配至少三次数字才有效
pattern = re.compile(r'\d{3,}')
flagRe = pattern.findall(text)
print(flagRe)

smallList = []
bigList = []
flagAppend = []

for i in range(0,len(flagRe),2):
smallList.append(int(flagRe[i]))

for i in range(1,len(flagRe),2):
bigList.append(int(flagRe[i]))

print(len(smallList))
print(len(bigList))

for i in range(len(smallList)):
flagAppend.append(bigList[i] // smallList[i])

print(flagAppend)

flag = flag + flagAppend

for i in range(len(flag)):
flag[i] = chr(flag[i])

print("".join(flag))

后来由于这俩坑点就怎么交也不对,搜了师傅们的wp才发现…
还有一种解法就是用z3约束器来解(学到了学到了

https://arabelatso.github.io/2018/06/14/Z3%20API%20in%20Python/

用这种方法来解也比较方便,先解出来然后再转ascii码

from z3 import *

flag = [0] * 32
for i in range(len(flag)):
flag[i] = Int("flag[" + str(i) + "]")

s = Solver()
s.add(1629056 * flag[0] == 166163712)
s.add(6771600 * flag[1] == 731332800)
s.add(3682944 * flag[2] == 357245568)
s.add(10431000 * flag[3] == 1074393000)
s.add(3977328 * flag[4] == 489211344)
s.add(5138336 * flag[5] == 518971936)
s.add(7532250 * flag[7] == 406741500)
s.add(5551632 * flag[8] == 294236496)
s.add(3409728 * flag[9] == 177305856)
s.add(13013670 * flag[10] == 650683500)
s.add(6088797 * flag[11] == 298351053)
s.add(7884663 * flag[12] == 386348487)
s.add(8944053 * flag[13] == 438258597)
s.add(5198490 * flag[14] == 249527520)
s.add(4544518 * flag[15] == 445362764)
s.add(3645600 * flag[17] == 174988800)
s.add(10115280 * flag[16] == 981182160)
s.add(9667504 * flag[18] == 493042704)
s.add(5364450 * flag[19] == 257493600)
s.add(13464540 * flag[20] == 767478780)
s.add(5488432 * flag[21] == 312840624)
s.add(14479500 * flag[22] == 1404511500)
s.add(6451830 * flag[23] == 316139670)
s.add(6252576 * flag[24] == 619005024)
s.add(7763364 * flag[25] == 372641472)
s.add(7327320 * flag[26] == 373693320)
s.add(8741520 * flag[27] == 498266640)
s.add(8871876 * flag[28] == 452465676)
s.add(4086720 * flag[29] == 208422720)
s.add(9374400 * flag[30] == 515592000)
s.add(5759124 * flag[31] == 719890500)

print(s.check())
print(s.model())

5.[WUSTCTF2020]level1

源码和逻辑都很简单,对flag进行加密,然后还给了output文件,应该就是flag加密后的输出结果

写个解密脚本就行了(1,3,5,7…与1相与为1)

flagReverse = [
0,
198,
232,
816,
200,
1536,
300,
6144,
984,
51200,
570,
92160,
1200,
565248,
756,
1474560,
800,
6291456,
1782,
65536000
]

for i in range(1,len(flagReverse)):
if i % 2 == 1:
flagReverse[i] = flagReverse[i] >> i
else:
flagReverse[i] = flagReverse[i] // i

flagReverse[i] = chr(flagReverse[i])

flag = flagReverse[1:]
print("".join(flag))

6.Youngter-drive

UPX脱壳:https://github.com/upx/upx/releases

先去个壳

main函数中启动了两个子线程

第一个线程对于我们输入的flag进行替换

// positive sp value has been detected, the output may be wrong!
char *__cdecl sub_411940(int source, int target_in_data_1d)
{
char *result; // eax
char v3; // [esp+D3h] [ebp-5h]

v3 = *(_BYTE *)(target_in_data_1d + source);
if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
exit(0);
if ( v3 < 'a' || v3 > 'z' )
{
result = off_418000[0];
// 大写字母 - 38,即27-52
*(_BYTE *)(target_in_data_1d + source) = off_418000[0][*(char *)(target_in_data_1d + source) - 38];
// 将索引映射到另外一张表进行替换
}
else
{
result = off_418000[0];
// 小写字母 - 96,即1 - 26
*(_BYTE *)(target_in_data_1d + source) = off_418000[0][*(char *)(target_in_data_1d + source) - 96];
}
return result;
}

第二个线程只是减去data段的一个计数器,并没有对我们输入的字符串进行操作,所以坑点1就是在我们输入的字符串中,只有偶数位(0,1,3,…29)被进行替换了
替换逻辑比较简单,见上面贴的注释

坑点2就是最后check的时候只检查了29位,最后一位搜的wp是填E(好像在安恒赛的时候填啥都对,然后看了一个师傅的wp觉得应该填Z,因为映射回来正好是NULL

脚本如下:

key = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"
flagRe = "TOiZiZtOrYaToUwPnToBsOaOapsySa"

"""
v3 = *(_BYTE *)(target_in_data_1d + source);
if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
exit(0);
if ( v3 < 'a' || v3 > 'z' )
{
result = off_418000[0];
*(_BYTE *)(target_in_data_1d + source) = off_418000[0][*(char *)(target_in_data_1d + source) - '&'];
}
else
{
result = off_418000[0];
*(_BYTE *)(target_in_data_1d + source) = off_418000[0][*(char *)(target_in_data_1d + source) - '`'];
}
"""

flag = []

i = 0x1d
while i > -1:
if key.find(flagRe[i]):
position = key.find(flagRe[i])
if position <= 26:
flag.append(chr(position + 96))
else:
flag.append(chr(position + 38))
i -= 1

flag.append(flagRe[i])
i -= 1

flag = reversed(flag)
print("".join(flag))
print(len(flag))

大师傅的wp,详细解释了这个程序中反调试的部分,值得阅读:https://hx1997.github.io/2018/07/22/anheng-july-re-youngter-drive/

文章作者: Alex
文章链接: http://example.com/2021/04/07/buuctf-reverse/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alex's blog~