安洵杯2023-wp
ez_cpp
正向逻辑:
1 | def reverse_bit(num, bit): |
逆向逻辑
其中第一个加密用可见字符输入得到可见字符输出来逆,因此可能产生一个字符对应多个,所以筛掉看上去不像的就好了。
1 | def reverse_bit(num, bit): |
其他师傅WP的思路
具体可看 👉 2023 安洵杯SYCTF writeup by Arr3stY0u
由于整体的加密过程是单个字符的加密,因此可以通过爆破一个个字符来得到flag。
方法:patch掉部分代码,使exitcode为符合条件的字符数。
原代码:

patch后的代码:

爆破脚本:
1 | import string |
3D_Maze
了解逻辑,获取迷宫后手动走走(注意:z的顺序是有要求的):
1 | maze = [ |
ezr3
脱壳
加了壳,但是特征码被覆盖了,改回UPX,然后脱壳:



其中 ‘#’ 的数据:

程序逻辑


主要函数为v(), p()。
v():更改一些数据。

p():处理输入然后比对。
- 处理输入:
input[i] = (input[i] >> 4) || (input[i] << 4)input[i] = input[i] ^ input[len(input) - i]

脚本
1 | auth32 = [ |
snake
一个贪吃蛇游戏,死后会生成一个flag,只有蛇到361长度生成的flag才是真的flag(比赛的时候一直不知道这题要干嘛),看了其他师傅的wp才知道。
程序主逻辑
由于获取flag的过程与游戏关系不大,所以游戏部分逻辑不阐述。
main函数中,初始化蛇,身子用链表来实现,每节身子有它的位置x、y以及其他信息。
蛇的身子长度一开始为3,可通过动调得到。

获取随机数序列,在之后的果实生成以及加密会用到:

在程序游戏每轮的进行中,都会更新分数以及其它细节:

当蛇吃到果实时,就会进入if语句,最后CxxThrowException抛出一个异常,具体处理在汇编代码处可以看出:

抛出异常执行的地方应该是0x401E92处,将前面跳到0x401F92的跳转指令改成跳转至0x401E92处,可以获取反汇编代码:

程序更新最后将会被生成md5的数据为目前的蛇的各节点的第五个数据。
对数据进行一次RC4加密。
倒转蛇节点的数据,即将加密后的
will_be_md5反转放入蛇的节点。(dword_C1DEA4的下一个地址就是will_be_md5)新增一个蛇节点
node,其node[5]为index_data,同时更新index_data(最开始为0x92)为(index_data - 1) ^ (final_node[5] - 1),其中final_node为蛇最后一个节点(不包括新增的)。
新的果实的生成:
由于dowrd_4BDEA4当蛇吃到果实时为0,因此当走果实生成的条件分支时,蛇的位置和果实的位置一定相同,因此会走第一个条件分支:由随机数序列的一个数作为种子来生成果实位置。

RC4_for_data的逻辑
整体流程就是RC4。
RC4的key是以蛇的位置作为种子,连续生成的随机数,而由于只有蛇吃到果实才能进入这个函数,因此也是果实的位置。

先将RC4加密异或的值存储下来,然后在下面异或数据,异或的处理无论走哪个条件分支都将数据与上面保存的异或数据进行异或。

flag生成
获取数据,生成md5:

脚本
贴一个其他师傅的脚本:
1 |
|
babyThread
逻辑
流程很明显,就是加密和比对,主要是判断生成密钥的字符串是哪一个。
加密过程:

主过程:

三个密钥(三个只有一个是被用到的):
所以直接三个都试一下。
脚本
1 |
|
gowhere
用IDAGolang脚本还原符号、函数名称。
大概的逻辑是输入30个字符的字符串,进行三轮三个函数加密然后与密文比对。
函数一逻辑
逻辑:input = (input + enc1_key) ^ 0x17

主要是寻找enc1_key的变化。
enc_key1初始为9。
每次进入main_enc1()时,enc_key1会加一:

每次进入main_enc2()时,enc_key1也会加一:

每次进入main_enc3()时,enc_key1也会加一:

函数二逻辑

函数三逻辑

主逻辑
三个加密的代码片段都弄懂了,但调用的顺序却没有,程序的具体逻辑我还没有弄懂。
分别为三个加密的代码片段走过且每次加密只走一次的代码下条件断点,打印一些信息:

可以知晓加密流程:enc1-2-3-2-3-1-2-3-2-3
脚本
1 | def dec1(data, key): |




