攻防世界reverse题学习笔记(1)

该文章已生成可运行项目,

未来方向二进制,根据同学和老师引导先刷reverse逆向类的题,真的好难,通过发博客,回来也想看这篇博客复习并掌握一些技巧

insanity

先查壳

发现是32位,放入IDA,找到main函数

按F5查看源代码

跟进strs,即可看到flag

也可以直接放入WinHex搜索flag即可

game

打开是这样

题目是指输入不同数字使其变亮(由黑变白),当所有图形全部变白时,就可以获得flag

我本来是想输入数字寻找规律,结果从1输到8,到8时直接出来flag

the flag is zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}

正经解题

open-sourse

打开后发现是一个内容为C语言的文件,那就分析代码,我放到了VS Code里面

第一个if语句,如果argc不等于4,输出what?并调用exit(1)终止程序;

第二个if语句,将argv[1]转换为无符号整数并赋值给first,如果first不等于0xcafe,就输出you are wrong, sorry并调用exit(2)终止程序;

第三个if语句,将argv[2]转换为无符号整数并赋值给second,如果second取余5等于3并且取余17不等于8,就输出you are wrong, sorry并调用exit(3)终止程序;

第四个if语句,将h4cky0和argv[3]进行比较,如果不相等,就输出so close, dude!并调用 exit(4) 终止程序;

最后是计算hash的算法,输出Get your key: 并以十六进制输出hash的值,那么这个就是flag;

由此可得重要的是hash的计算,前面的可以删去,并把first,second以及argv[3]进行相应赋值,这里second取25就行,代码如下:

运行即可得出flag

easyRE1

在IDA(64)上打开后在main函数里按F5即可获得flag

lucknum

这道题没有什么技巧,也是同样思路,在main函数里按F5即可获得flag

 

1000Click

用IDA打开

在此目录下查找到含有flag的

打开发现有很多flag

但是有一个与其他不同,这个就是真正的flag

Hello,CTF

打开后尝试一下,发现不行

拖进PE里,发现是32位

放进IDA(32bit)

如图,观察代码,将字符串“437261636b4d654a757374466f7246756e”复制给v13,接下来v10被初始化为0。输入的内容作为v9,判断v9的长度>0x11则结束循环退出,因此要求输入内容长度<=0x11,也就是<=17。

然后接下来的for循环将v9的值赋值给v4,遇到空字符则退出,然后将v4的值以16进制形式写入Buffer中,再将Buffer接到v10后面,而由于v10初始化为全0,故V10的内容就是Buffer的内容。
随即比较v10与v13的值,相等则输出"Success"。也就是需要v10=“437261636b4d654a757374466f7246756e”,这是16进制,可以将其转换为ASCII码,即可得到flag

re1

打开后

查壳

32bit

扔进IDA,查看伪码

v3为真则flag判断正确,v3是v5和v9的比较,v9是输入,那么flag就是v5了

跟踪得到十六进制

将其转化为字符串 

很明显是倒序的,正序后拼接起来即为flag

DUTCTF{We1c0met0DUTCTF}

simple-unpack

先查壳,发现有壳,脱壳

放入ida

查看main函数

点flag即可

xxxorrr

看到题目想着和异或有关,这道题有些麻烦,就参照别人的

先查壳,放入ida里,进入main函数

第六行这里声明了一个名为 v6 的 64 位无符号整数变量。注释 [rsp+38h] [rbp-8h] 是一种在反汇编代码或者调试相关代码时常见的表示方式,用于指示该变量在栈帧中的位置,其中 rsp 是栈指针寄存器,rbp 是基址指针寄存器。这个注释说明变量 v6 在相对于栈指针偏移量为 0x38 字节的位置(从 rsp 角度看),同时相对于基址指针偏移量为 -8 字节的位置(从 rbp 角度看)。在函数的栈帧布局中,通过这样的标记便于调试人员或者分析代码的人清楚变量的存储位置信息。

第七行v6,用于__readfsqword(0x28u)反调试,

sub_916函数,a2,a3是sub_A90函数的参数

for循环里是一个将输入s与已经被定义好的s1相互异或的算法,点进去可以看到s1

接下来分析sub_916函数

if语句中将s1和s2进行比较,如果相等的话,估计会给出一些提示,说明从s1和s2着手

目前得到就是在main函数中对输入s异或(或者说是对s1加密)后要和s2相同

分析s1和s2

这里的s1和main中的一样,如果按照顺序结构,应该先执行sub_916话术,再异或,但是这样的话,s1和s2就不相等了,这该怎么办呢?

转向未分析的函数sub_A90函数

这个函数很简洁,去搜了一下atexit(),它的作用是当程序正常时,调用其中被传入的函数,表明了sub_916函数在程序执行结束后才执行

那么现在的顺序就是先异或加密,然后程序终止,再比较s1和s2

本以为到这就可以了,后来试了几下发现不行,难道还有其它加密?这时想到了交叉引用(对于关键字符串要注意交叉引用!)

选中s1按x键

果然,在sub_84A()函数中对s1进行了加密,但这个和异或那个顺序是什么?到现在也不太理解,斟酌了一下只能先sub_84A()函数,再异或,最后比较

s2处理

点进去s2的字符发现不统一,那就先把其后带h的和前面的都提取出来改成十六进制弄在一起

s2=0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46,0x17, 0x46, 0x54, 0x5A, 0x59, 0x59, 0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C, 0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE, 0x00

写脚本,借阅别人的先

s2 = [0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46,0x17, 0x46, 0x54, 0x5A,0x59, 0x59,0x1F, 0x48, 0x32, 0x5B,
      0x6B, 0x7C,0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE, 0x00]
s1 = 'qasxcytgsasxcvrefghnrfghnjedfgbhn'
flag = ''
for i in range(0, 33):
    #先对s1解密1,再拿s2和解密1后的s1进行解密2,得到的字符就是flag的一部分
    #同时注意python的字符不能直接当ASCII码来用
    flag += chr(s2[i] ^ ord(s1[i]) ^ (2 * i + 65) )
print(flag)

maze

先查壳

是64位,放入IDA,进入主函数,等一下,怎么发现有一串特殊字符串

莫非这是一道迷宫题?

我就先按照我的猜想分析函数 

 先看到数字很敏感(125,79,111,46,48),把它们转换成对应字符,125和前面连接是flag的格式

而剩下四个分别对应O,o,.,0,这些就是控制上下左右来走迷宫的,但是是怎么对应的呢?这里不太懂,就看了看别人的博客:逆向工程与迷宫解析:攻防世界挑战解析-CSDN博客 ,终于知道+4的是左右移,不加4的是上下移

而v10对应的函数是左右移,v9对应的是上下移

继续看sub_400650、sub_400660、sub_400670、sub_400680、sub_400690、asc_601060函数,看着挺难受,但其实很简单

这四个函数就是描述的是迷宫格为8*8,如果你没有越出迷宫边界,就继续玩,如果你越出了,就不用玩了

而-1代表的是上移和左移,+1代表的是下移和右移

并且O左移 o右移 .上移 0下移

这里sub_400690点进去分析后的(__int64)asc_601060如图是一串字符串,后来知道了是迷宫的图,sub_400690函数里传入v9的有符号高双字r15寄存器,和v9底双字的r14寄存器,然后运算表达式result = *(unsigned __int8 )(a1 + a2 + 8LL * a3); 就是在asc_601060字符串数组内取字符而已

根据前面相继得出的结论,可以画出迷宫图并且得出路线


 

所以flag就是 nctf{o0oo00O000oooo..OO}

easyre-xctf

先查壳,发现是64位且有壳

用upx脱壳  放入ida中

观察main函数,没有发现一点有关flag的线索

尝试着按shift+F12查找字符串,发现第一行像是flag——的一部分

说明还有一部分,继续寻找

发现函数名里又一个part1函数,发现两个十六进制汇编语言

可以用十六进制转换为字符串工具转换,记住不要带h

 转换完后发现这是倒序的,需要把它正向,并和前面那一段拼接起来

得到flag{UPX_4nd_0n3_4nd_tw0}

ereere

发现没有main函数,按Shif+F12找到了flag线索

按F5进行反编译,发现失败了,后来更换了版本才成功(应该是某些版本缺少模块)

 分析代码:输入flag,第14行有sub_400A40函数,点进去发现很长看不懂,给豆包分析说是带并发控制的格式化输入函数,就是将输入的赋给v3。

继续看有个sub_41A1E0函数,点进去还得给豆包,是优化的字符串长度计算,重点是下面那个

 

sub_4009DC函数,发现是算法

  

先调用了sub_400864函数,跟进分析。初始化S盒,为0-255,

利用密钥(aFlag1233213211[]值为flag{123321321123badbeef012})对 S 盒进行 256 次置换,每次置换将 S [j] 与 S [(S [j]+j + 密钥字节) % 256] 交换。这样分析就是RC4算法了

再回到DC函数,有sub_41A6E0函数,跟进,跟之前的很像。给豆包发现果然是计算字符串长度的,剩下的函数没啥用,继续回到主函数向下看

 有个sub_400550()函数,这是一个变种base64算法,区别在于字符表是自定义的

如下

 

再回到主函数,第18行是将字符和v5作比较,相同就是对的,而v5就是flag加密后的东西

那么大致逆向分析思路就是将base64进行换表之后把ScDZC1cNDZaxnh/2eW1UdqaCiJ0ijRIExlvVEgP43rpxoxbYePBhpwHDPJ==

进行base64解密 然后再进行RC4解密就能得到flag

脚本(大佬的)

这个简单但是得有相应的库

import base64
from Crypto.Cipher import ARC4
 
key = "ScDZC1cNDZaxnh/2eW1UdqaCiJ0ijRIExlvVEgP43rpxoxbYePBhpwHDPJ=="
string1 = "ZYXWVUTSRQPONMLKJIHGFEDCBAabcdefghijklmnopqrstuvwxyz/+9876543210"  # string1表示更换后的码表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  # string2表示原始码表
Str = base64.b64decode(key.translate(str.maketrans(string1, string2)))  # Base64解密,bytes型
 
aFlag1233213211 = "flag{123321321123badbeef012}"
flag = ARC4.new(bytes(aFlag1233213211, encoding='utf-8')).decrypt(Str)
print(flag)

这个是手动的RC4解密,不需要库,但是太难了

import base64
 
flag = ""
key = "ScDZC1cNDZaxnh/2eW1UdqaCiJ0ijRIExlvVEgP43rpxoxbYePBhpwHDPJ=="
string1 = "ZYXWVUTSRQPONMLKJIHGFEDCBAabcdefghijklmnopqrstuvwxyz/+9876543210"  # string1表示更换后的码表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  # string2表示原始码表
print("Base64还原: ", key.translate(str.maketrans(string1, string2)))   # 将 key 还原成初始码表加密后的结果
Str = base64.b64decode(key.translate(str.maketrans(string1, string2)))  # Base64解密,bytes型
print("Base64解密: ", Str)
 
byte_4A0860 = []
aFlag1233213211 = "flag{123321321123badbeef012}"
 
# rc4_init
for i in range(256):
    byte_4A0860.append(i)
v4 = 0
v3 = 0
for j in range(256):
    v5 = byte_4A0860[j]
    v3 = (v5 + v3 + ord(aFlag1233213211[int(j % len(aFlag1233213211))])) & 0xff
    byte_4A0860[j] = byte_4A0860[v3]
    byte_4A0860[v3] = v5
    v4 = v4 + 1
    if v4 >= len(aFlag1233213211):
        v4 = 0
 
print("s_box:", end=" ")
for j in range(256):
    print(byte_4A0860[j], end=" ")
print()
 
# rc4_crypt
v4 = 0
v3 = 0
for k in range(len(Str)):
    v3 = (v3 + 1) & 0xff
    v4 = (byte_4A0860[v3] + v4) & 0xff
    v5 = byte_4A0860[v3]
    byte_4A0860[v3] = byte_4A0860[v4]
    byte_4A0860[v4] = v5
    flag += chr(Str[k] ^ byte_4A0860[(byte_4A0860[v3] + byte_4A0860[v4]) & 0xff])
 
print(flag)

toddler_regs

打开后发现有main_0函数,点进去j_stage_wrapper()函数

在stage_2_real函数里发现了flag,想着不会这么就结束吧

没错,在flag中间还缺少了几段,因为发现了strcat函数,它是将字符串team[g_team_idx]和teamjnu[g_teamjnu_idx]数组分别拼接到flag的末尾

接下来就是找数组对应字符串内容,就要找其索引,stage_2里有g_teamjnu_idx的,stage_1里有g_team_idx的

跟进相应数组找索引对应的字符串。

 

 team[64][7] 表示有 64 个字符串(行),每个字符串最多存储 6 个字符 + 1 个终止符\0。如果用代码的话得注意0不是结束的标志,需要算到字符串里

根据以上规则可以找到team  Xp0int

teamjnu Xp0intJNU

还可以动态调试

flag{Xp0int_1s_n1c3_but_Xp0intJNU_is_we1rd}  

 

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值