【buuctf】DontEatMe
0x01反调试
拿到Ntdll
的模块句柄,从句柄中查找ZwSetInformationThread
的地址,然后调用这个函数去检查当前线程是否处于Debug
模式下
1 | ModuleHandleA = GetModuleHandleA("Ntdll"); |
关于ZwSetInformationThread
的类型:
1 | typedef NTSTATUS(NTAPI* pZwSetInformationThread)( |
重点查看这个线程信息类型,是一个枚举类型,根据传入参数调用特定函数
1 | typedef enum _THREADINFOCLASS { |
这里传入17,刚好调用ThreadHideFromDebugger
,检测到调试就会将该线程强制分离出调试器,造成反调试。修改方法直接将push 的17 patch成0就好了。
修改后便能正常调试。
0x02 走迷宫
一开始往下翻就发现是一个迷宫,中间有一大段加密,看不懂先不管。反正关键肯定是走迷宫,迷宫地图是在运行中生成的,这时候处理完反调试就能跑出地图了。
跑到进行走迷宫逻辑前:
1 | while ( 1 ) |
查看这个dword_10053A8数组,是一个int256大小的数组,点进去把数组内容shiftE出来,弄到python处理一下,有1024个01数字,但是int类型4个一组打印出来。
打印出来后发现只有01数,没有明显终点起点,于是再查看一下代码,发现v37记录着上下,v39记录左右移动。当v37 == 4 ,v39 == 9 ,且移动次数小于17次时,到达终点,于是终点坐标是(9 , 4)
再看成v39和v37的初始值,知道起点坐标为(5 , A),假设终点为$,起点为#
1 | map = [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, |
读图法,得到迷宫路径为ddddwwwaaawwwddd
到这里可以合理猜测,输入的flag经过某种算法加密后,得到迷宫路径的正确答案,接下来就分析一下中间的算法是什么东西。
0x03 BLOWFISH
解出迷宫后再回来看看中间那里,猜测是什么算法(迫真猜测),用FindCrypt插件看看(下载地址)
去查查BLOWFISH是什么东西(摘抄)。
BLOWFISH加密解析
blowfish加密算法是一种对称的分组加密算法,对Plaintext进行分组,每组长度为64bit,而该加密的密钥为变长密钥,32bit-448bit都可以当作密钥进行加解密处理
密钥可以定义为Key数组,该数组的长度为[1, 14]
存在两个数组pBox和sBox,pBox为18个32位子密钥组成,sBox为4*256个32位子密钥组成,一般情况下,是用Π的小数点的16进制
BLOWFISH加密过程
- 传入明文和密钥K,密钥K的长度为
32bit-448bit
,密钥K数组的单位为32bit,所以得到K = [K1, K2, ... , Kn]
,其中1≤n≤14
- 初始化子密钥,子密钥分为1个pBox和4个sBox,pBox的长度为18,sBox的长度为256,一般情况下,该子密钥的初始化为Π的16进制
- 子密钥预处理pBox:这里密钥数组K的长度最大只有14,所以存在轮换使用密钥的情况,假设密钥数组K的长度为14,首先是pBox与密钥数组K进行异或:
pBox[0] ^ K[0]
pBox[1] ^ K[1]
- …
pBox[13] ^ K[13]
pBox[14] ^ K[0]
- pBox变换:产生一个64bit全0的数据,然后调用BlowFish的主加密函数进行加密,得到一个64bit的数据替换pBox的数据,总共进行9轮,得到一个新的pBox
- sBox变换:此时沿用pBox中产生的leftpart和rightpart,再次通过BlowFish进行主加密函数进行加密,每次将处理后的leftpart和rightpart进行替换,替换得到新的sBox
- Plaintext加密:首先是对明文进行填充,若不足8个字节则填充至8个字节,填充为8的整数倍,然后分为前4字节与后4字节传入主加密函数进行加密,在主加密函数中进行加密得到最终的密文
主加密函数,总共为8轮加密,每轮都会从pBox中拿取64bit的数据分成2*32bit异或给leftpart和rightpart,然后再分别异或从sBox中查表得到的值
分析程序
1 | srand(0xDEADBEEF); |
这段代码用一个srand指定时间戳,然后用rand生成伪随机数来产生密钥,但是在调试后发现实际上的密钥并非如此,查了地址才发现,6671,13569等值紧随其后对生成的key进行了覆盖,把实际的密钥转化为16进制dump出来。
1 | 00 0F 1A 01 35 3A 3B 20 |
用cyberchef网站的BLOWFISHFrom_Base64(‘HNO4klm6ij9n%2BJ2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo%3DILaUpPBC5’,false,false/disabled)&input=ZGRkZHd3d2FhYXd3d2RkZA)加密
包裹flag提交,发现错误,后来查资料才知道会填充多余的8位空格。删去最后8个字节得到答案
flag{db824ef8605c5235b4bbacfa2ff8e087}