ctfshow:真的是签到
真的是签到
可以开始下一套题,内部赛,不知道是个什么玩意,看看就是了
0x00 压缩包伪加密
发现是个之前没见过的新题型,这个zip解压的时候里面的exe是加了密码的,首先要想办法解决掉这个密码:

这里发现这个flag是0,说明加密对应的位置bit0=0,说明是没加密的,但是解压找我要密码是啥意思?再看一下核心目录记录区

这里可以看到对应的flag是09,转成二进制就是1001,这里的bit0=1,那这玩意到底有没有被加密啊,一般来说,这个传统的zip加密,这个最开始的文件头末尾是有个加密头的,但是我好像没找到,然后这个压缩也是最普通的压缩:

猜测是伪加密,回去把核心目录区的flag改一下试试:0009改成0000,再解压发现畅通无阻,至此得到exe
0x01 查壳
拿到exe的第一时间还是查壳,这次好像遇到新壳了,我服了,又得找新的脱壳工具,而且section看到了两个壳,我滴妈,先进一步确认一下

在OptionalHeader里面可以找到这个程序的入口,我们可以看到是先用的aspack,所以我们脱壳先找这玩意的脱壳工具

这个小工具也是成功发现了一层壳,我们直接脱壳,这个壳需要运行stub,然后捕获跳到OEP的位置,再存Dump……令人难过的是,这个工具没有帮我解开,所以我准备自己手动脱壳
0x02 手动脱壳
这道题套了两层壳,小工具解不开,只能使用ESP定律下断点慢慢找了,我们用dbg打开运行到用户代码,位置和我们读PE文件的结果一致

pushad是典型的壳开头,将寄存器的快照压入栈,接着我们要找对应的popad,将当前ESP存入的地址转到内存窗口,下一个硬件访问断点

下完断点直接运行,就能找到壳的尾部popad,再往后步过几步,理论上就是入口了,但是后面套了两层,还有一层魔改UPX,所以我们还要手动再脱一次,一样的操作。

不过有一点要注意,我们要知道pushad对应操作码为60,popad对应为61,在步进到下一个壳的时候,要注意这个操作码

这里这个dbg把这个机器码读错了,这里这个60理论上该读成pushad的,但是这里显示错误了,可能是题目挖的坑吧,我们用同样的方法脱壳

这里dbg是吧popad正常读出来了,我们继续往下可以知道最后是跳转到401280,里面就是正常的程序了,你看,前面多经典的栈帧啊!
0x03 Scylla Dump
现在这个程序在内存里已经复原出来了,接下来就是要捕获(Dump)当前内存里面的程序,恢复导出表什么的,反正我们把当前dbg中脱壳好的进程,按顺序点:
- IAT Autosearch:自动找API导出表
- Get Imports:获取导入表
- Dump:捕获当前进程内存中的程序

这样子会生成一个dump后的PE文件,后面再点击Fix Dump用我们的Import修复刚刚dump出来的PE文件,得到最终得到一个SCY版,打开后,start的内容就是我们一开始在dbg看到的内容,至此脱壳成功

0x04 读伪代码找flag啦
我们从这个start一步步找函数,最后定位到一个疑似主函数的sub_4012F0:
c
int sub_4012F0()
{
_DWORD v1[23]; // [esp+30h] [ebp-168h] BYREF
size_t i; // [esp+8Ch] [ebp-10Ch]
char Str[264]; // [esp+90h] [ebp-108h] BYREF
sub_401990();
sub_401510();
scanf("%s", Str);
if ( strlen(Str) == 18 )
{
i = strlen(Str) - 1;
memcpy(v1, dword_402000, 0x48u);
while ( 1 )
{
Str[i] ^= i;
if ( !i )
break;
--i;
}
strrev(Str);
for ( i = 0; i != 18; ++i )
{
if ( Str[i] != v1[i] )
{
printf("%s\n", aTryAgain);
return 0;
}
}
printf("%s\n", aYouWin);
}
else
{
printf("%s\n", aTryAgain);
}
return 0;
}
阅读一下,首先scanf前面两个函数应该不用管,只管Str就好了,第一个条件就是字符串长度为18,后面的操作是异或,属于对称操作,我们只要一点点解开逆向出来就好了
c
memcpy(v1, dword_402000, 0x48u);
这里有一句比较重要的语句,这个v1是后面用来和下标异或后的输入对比的内容,但是dword_402000这里没告诉我们是啥,跳转查一下

找到以下数据,但是不确定是什么编码,那我干脆直接用十六进制处理得了
6Ch, 2Fh, 30h, 31h, 32h, 33h, 0FFFFFFB6h, 0FFFFFFBFh
0FFFFFFA0h, 0FFFFFFCFh, 7Ch, 71h, 6Ah, 6Ch, 70h, 64h
75h, 63h
刚好18个,我们写个脚本:
python
v1 = [0x6C, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFFFFFFB6, 0xFFFFFFBF, 0xFFFFFFA0, 0xFFFFFFCF, 0x7C, 0x71, 0x6A, 0x6C, 0x70, 0x64, 0x75, 0x63]
v1.reverse()
for i in range(18):
v1[i] = v1[i] ^ i
print(chr(v1[i]), end='')
然后发现chr转不了,说是出了范围,但是把索引换成0,1,2一个个试,发现开头是正确的,可能中间的字符不是简单的ASCII,最后翻别人题解发现是GBK编码,不知道要怎么认出来,最终脚本如下:
python
v1 = [0x6C, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFFFFFFB6, 0xFFFFFFBF, 0xFFFFFFA0, 0xFFFFFFCF, 0x7C, 0x71, 0x6A, 0x6C, 0x70, 0x64, 0x75, 0x63]
v1.reverse()
for i in range(18):
v1[i] = v1[i] ^ i
print(bytes(x & 0xff for x in v1).decode('gbk'))
得到:ctfshow{签到?????}
评论