最近事情比较多,题都没怎么看就跑路了
下面是我出的送分题
Basicasm
硬怼汇编,分奇偶处理,输出16进制,逆回去就可以了
1 | flag{d0_y0u_know_x86-64_a5m?} |
Ezreverse1
简单的花指令加迷宫题,好心的出题人在每个花指令的地方都patch
了很多个nop
,虽然这么做毫无意义
把简单的花指令patch
掉
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
很显然这是一个7*7的迷宫,虽然数都很大很复杂,但是只比较奇偶,所以只要看最后一位就行了,就是一个很简单的迷宫
1 | flag{kkkkkklljjjjljjllkkkkhkklll} |
Ezreverse2
简单的jar2exe
,动态调试等解密完直接dump
内存得到jar
文件,反编译就可以了
1 | flag{j4r2eXe_I5_@_p0wer7ul_To0l!} |
easyre
这题只上了校内赛道,改编自
b01lersCTF-2020
的一道题,觉得题目很不错拿来给校内做一做
main
函数很简单
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
可以看到使if
判断为真就可以了
sub_1340()
函数纯粹用来输出,可以不看,下面几个函数观察一下可以看的出来,sub_14B0()
用来输入,sub_1100()
用来对输入进行处理,sub_11C0()
用来判断
每一个部分都很长,其实实现的功能很简单,是因为整型数组内存连续存放,所以被当作了很大的数据来处理了,看不明白的话建议动态调试
输入之后数据被存放到了v4
里,然后进行处理
1 | unsigned __int64 __fastcall sub_1100(__int64 a1) |
只是进行了简单的异或,只不过是一遍生成一边和输入异或,不过这些数据和输入无关,可以先算出来,至于v4 = v5 + ((unsigned __int64)(0x8080808080808081LL * (unsigned __int128)(unsigned __int64)v5 >> 64) >> 7);
这一步的计算,其实仔细看看就会发现根本没有意义
接下来进行判断
1 | __int64 __fastcall sub_11C0(_BYTE **a1) |
关键其实只有一个相等的判断,不过两个数组每次索引值增加的不一样,每四位进行一次比较就好了
1 | target = [0x45, 0xE6, 0xD0, 0x4A, 0x4F, 0xC3, 0x7E, 0xAA, 0x45, 0xFC, 0x42, 0xB2, 0x41, 0xB5, 0x01, 0xB4, 0x52, 0x7D, |
直接输出flag
1 | flag{7here_i5_no_d0ubt_th47_re_pr0b1em5_4re_e4sy_7o_s0lve} |
下面是其他师傅的题目
re1
混淆了一下,函数的功能很简单,调试一下就出来了
你好sao啊
很显然换表base64,解一下就出来了
Anti-IDA
出题人给hint说不要用IDA,但我在这之前已经用IDA做完了……
其实只需要调整一下堆栈平衡,patch一些花指令,这题并没有什么难度,但是这么些个逻辑运算真的恶心
首先从main函数走到了sub_401E10
,所有的操作都在这里处理
1 | length0 = strlen(commandline_input); |
commandline_input
可以不管它,先是输入了flag然后获取了长度,然后走第一个判断
1 | input0[length_input] = 10; |
用到的判断是一个永真判断,所以默认执行第一个函数,初始化target1
为奇数
1 | int sub_401870() |
下面的几个逻辑判断发现也是永真,执行到这一步
1 | if ( alwaystrue1(SLODWORD(v46), SHIDWORD(v46)) == 1 ) |
初始化了一个tttt
的数组,然后又执行了一个函数
1 | int (*sub_401A10())() |
但是这里的处理显然是假的,后面返回的函数又对这个数组进行了重新的赋值,真正的赋值部分在下面
1 | void *sub_4015F0() |
然后又是一个永真的判断
1 | v1 = sqrt(v46); |
输入进行了几步处理,然后把输入的字符16进制转成字符串存储起来,长度翻倍,最后执行了sub_401073()
函数,跳到了这个位置
1 | int (*sub_4017A0())() |
return
了一个偏移量,发现事情并不单纯,应该和上面一样又套了一层
1 | void *__usercall sub_401680@<eax>(int a1@<ebp>) |
处理看起来不是很简单,但是和输入没什么关系,直接动态调试dump
出来就行了,没什么难度
接着进行了一个逆序
1 | reverse((int)input_step2, 2 * length_input);// 第四步 |
接下来的几个逻辑判断判断一下就不难发现最后走的是这一部分的处理
1 | else // 第五步 |
sub_401019(v16, i)
的内容看一下也很容易出来
1 | int __cdecl sub_401CD0(char a1, int a2) |
简单的两层异或,中间套了一个函数,传的是固定参数,调试很容易出来,不过这个函数也不复杂,可以直接分析出来
1 | size_t __cdecl sub_401820(int a1) |
求得是这一部分的长度
1 | .data:0042EA34 ; char *off_42EA34 |
但第一部分是数据,肯定不是整个的长度,一定有\0
让strlen
截断
1 | .rdata:0042C01C unk_42C01C db 58h ; X ; DATA XREF: .data:off_42EA34↓o |
发现长度为2,并且在.rdata
段,程序运行过程中没有修改,检验一下查一查交叉引用发现果然没有,回到原来的位置,sub_40101E
的作用很简单,就是*2
,所以返回的值也可以写成
1 | return a2 % 4 ^ target1[a2 % 6] ^ a1 |
下面再稍微判断一下,永远走的是这个处理
1 | if ( v25 >= pow(v40 / 2, 2) ) // 第六步 |
然后四位变五位
1 | for ( i = 0; i < 2 * length_input; ++i )// 第七步 |
几步处理之后继续16进制转字符串
1 | length2 = strlen(&str0); |
最后进行比较
1 | if ( !strcmp( |
整个过程没有什么特别复杂的处理,逆起来也没什么难度
1 | finaltarget = [54, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, |
最后的flag
1 | NNPPUUCTF{S0_M4NY_BUG5!} |
Comments