GACTF2020 - Wannaflag
题目链接
十分传统的一个winapi逆向。最近也很少见了,对新人有难度但有趣。
本篇的一些细节之处省略截图,但我会把步骤写明白。若复现不出来可评论或私信。
程序拿到手还有点大,443kb
同时附带了一个flag.bin 进去,打开发现是可读的一串字符串。很显然是被加密过了。
查pe,32位无壳 vs编译(这里我用的exeinfo)
ida一打开就定位到了WinMain。这里很容易看出来其只调用了两个自定义函数 – 背景音乐(1b20),消息处理(2280)
进消息处理函数(函数偏移2280)检查
首先开头两个case是没什么营养的,重点放在东西很多的case 5
首先这里通过GetWindowsTextA获取了我们的输入串,还去了一个长度。然后厦门进行了一连串的不可名状的变换。不过这里取的元素十分清晰。我们先看一下这个Paint结构体的定义
额,简单的说就是查不到啥。不过如果这些东西要作为解题步骤的一部分的话,极有可能是固定值。先看看后面
首先MessageBox(orz)哪里应该就是我们的正确跳转。因为底下调用的偏移1f20处调用了flag.bin,解密并创建了flag.txt文件夹。但是这里的解密流程十分混沌,至少我静态解不出来。不过外部的流程很清晰。因此这里应该就是两种解法:动调直接调用1f20,或者静态解密解出key。我估摸着动调不太能直接解,因为Paint结构体包含了我们输入的字符串。
而简单分析一下程序流程:先将v57数组和byte42ae0进行异或并左移一位(注意是循环左移),而后这个数组要与底下的这个78d8进行比较,是不是很简单呢?
但是问题来力,这个v57是什么?
经过简单的调用分析和推理(猜)可知,这里应该是我们的输入串。不过上方又有一个看起来非常痛苦的操作,其满足条件是输入串长度大于等于0x40。
但是这里,我们或者通过字符串长度推理,或者通过动调都可以发现 – 这个条件是满足不了的。因此我们只需要对这个变换和check进行逆向就好啦。
exp:
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include<string.h>
#include<tchar.h>
//#+OXJ xnB1 QWT4 9cnW cGFB ZOjn yfZo ZV1m 7+/v /29t f2c=
int ror(unsigned int a,unsigned int b)
{
unsigned char c = a,d = 0;
for(int i = 0; i < b; i++)
{
c=(c>>1)|(c<<7);
}
return c;
}
int main()
{
// printf("%x\n",0xae>>1);
unsigned char key[0x1f] = {0x4e,0xae,0x61,0xba,0xe4,0x2b,0x55,0xaa,0x59,0xfc,0x4d,0x2,0x17,0x6b,0x13,0xa1,0x41,0xfe,0x35,0xb,0xb4,0xb,0x52,0x2f,0x46,0xcc,0x35,0x82,0xe5,0x88,0x50};
unsigned char ca[] = "ANNAWGALFYBKVIAHMXTFCAACLAAAAYK";
unsigned char v38[] = {1,2,6,24,120,720,5760};
for(unsigned int i = 0; i <= 0x1e; i++)
{
printf("%x ",ror(key[i],i));
key[i] = (ror(key[i],i))^ca[i];
//printf("%d ",key[i]);
}
printf("\n");
for(int l = 0; l < 7; l++)
{
for(int i = 0; i <= 0x1e; i++)
{
printf("%c",key[i]^v38[4]);
}
printf("\n");
}
}
再一跑,欸嘿,flag。