2023DASCTF七月赛controlflow
0x00 题目分析
题为ControlFlow,即控制流,说明此题可能在控制流上做文章,因此要时刻注意堆栈的内容和变化
0x01 解
先查壳

没壳,拖IDA
可以找到main函数

F5反编译

看起来好像很简单,只是将40字节长的输入与0x401进行异或加密,但是这里并没有与正确密文进行比较,结合控制流有问题的可能性,这个ret可能存在问题
获取main函数入口地址

上x32dbg动调看看

开头几个push(push <consoleapplication1.sub_CA….>)将函数的地址压入了栈中,当main函数执行至ret的时候则返回到这些地址中执行这些函数
随便输入一个40字节长的数进去再跟到ret处,此时的栈内容如下(有三个函数)

ret跟进去后进入第一个函数(相对地址为1220)

这些个函数可能用IDA会比较好看,遂上IDA

函数1220将接受的数据与i(i>=0||i<40)平方进行逐位相加
之后return进1560这个函数(注意输入函数1560的数据是偏移了10位的!)

函数1560将接受的数据与i*(i+1)(i>=0||i<20)进行逐位异或
最后return
根据堆栈,后面应该还有两个函数,遂回到x32dbg查看
运行至函数1220的ret处,发现多了一个函数1290,回看函数1220的汇编(上图),可以发现函数1220执行过程中将函数1290压入了堆栈(这程序流有够乱)

跟进查看函数11A0

看不太懂,IDA内也是没什么结果,应该也是对程序流做了点操作,直接跟到ret处查看

对比上面那个堆栈发现栈中多了15AF和11E4的地址,合理推断是和main函数一样压了两个新的函数地址进堆栈了
跟进查看15AF

似乎不是一个函数,而且在IDA中也没有反汇编出来(那就硬读汇编呗)
通过阅读汇编(痛苦)可以发现此段一直对4370这个地址进行操作,转内存窗口

由汇编看出,执行过程中会将edx的值不断+1,然后存到4370这个地址,并将4370地址的内容与0x28(40)这个立即数进行比较,看来4370这个地址存放的就是循环次数了
单步运行并查看内存,可以发现15AF这一段一直在将43A8中的值减去循环次数(i),那么这个43A8存的是个啥呢
这就要回到main函数去看一看了(参照上面IDA的main函数反汇编)

从main函数我们可以看出,这里存放的就是进行变换后的输入,而后续的所有操作都对43A8这个地址内的数据进行操作,因此43A8里面存的就是加密的结果了
因此这个15AF就是将每一位都减i(i>=0||i<40)
然后再跟进到15AF的ret,下一个跳转到了11E4

看得出来11E4和15AF一样都不是完整的函数,所以要继续看汇编了
这里需要注意imul的意思,参考https://www.felixcloutier.com/x86/
注意次处用到了三个值
IMUL — Signed Multiply
Performs a signed multiplication of two operands. This instruction has three forms, depending on the number of operands.
Three-operand form — This form requires a destination operand (the first operand) and two source operands (the second and the third operands). Here, the first source operand (which can be a general-purpose register or a memory location) is multiplied by the second source operand (an immediate value). The intermediate product (twice the size of the first source operand) is truncated and stored in the destination operand (a general-purpose register).
根据指南,可以知道这个地方是将43A8地址内的值乘3,然后放入eax寄存器中,后面再将eax寄存器中的值放回43A8地址中
因此可以知道11E4这一段的作用是将上一步的结果逐位乘3
跟到11E4的ret处,可以发现ret的位置是1100了(此处借用11A0函数之后的图,实际情况是运行到了下面的1100)

这个1100是一个完整的函数,因此到IDA去看看

这是一个0空间实现两值交换的函数,作用就是将20位的值两两一组进行互换,即123456变为214365
值得注意的是,这个交换函数是对加密过程中的值偏移10之后进行的交换,即前面10位不进行交换,对第11~30个数据进行操作

最后就是1290了,这也是一个完整的函数,而且密文就在其中

最后这个函数1290就是与密文相比较,所以根据以上的所有操作,可以获得这样一张表
| 函数 | 作用 | 备注 |
|---|---|---|
| main | ^=0x401 | 存入函数1220,11A0,1100到栈中 |
| 1220 | +=i*i | 存入函数1290到1100之后,并调用了函数1560 |
| 1560 | ^=i*(i+1) | 从偏移10开始操作,i>=0 || i<20 |
| 11A0 | 存入15AF和11E4这两个非完整函数的段 | |
| 15AF | -=i | |
| 11E4 | *=3 | |
| 1100 | 交换 | 从偏移10开始,对20个数进行两两交换 |
| 1290 | 与密文做比较 |
将1290中的密文dump下来然后写脚本就好了
1 | enc = [3279, 3264, 3324, 3288, 3363, 3345, 3528, 3453, 3498, 3627, 3708, 3675, 3753, 3786, 3930, 3930, 4017, 4173, 4245, 4476, 4989, 4851, 5166, 5148, 4659, 4743, 4596, 5976, 5217, 4650, 6018, 6135, 6417, 6477, 6672, 6891, 7056, 7398, 7650, 7890] |
