-
-
[原创]看雪CTF.TSRC 2018 团队赛_第6题WP
-
2018-12-12 13:55
5579
-
[原创]看雪CTF.TSRC 2018 团队赛_第6题WP
程序拿到手之后,在Win10以及Win7_x86都没有跑起来,后来在Win7_x64中可以正常运行
明修栈道
关键函数的定位好像很简单,看导入表发现GetDlgItemTextA和MessageBoxA这样的敏感函数,下断后就来了
程序的流程看起来简单清晰,小菜就喜欢这种简单粗暴的
仔细一看,好像有“猫腻”,没找到注册成功的提示
看起来只有那个sub_401020()函数有可疑成分,跟进去一看,是个“空壳”。其实WinMain也是一个“空壳”
猜测是哪里进行了代码修改之类的,对那两个全局错误提示的字符串参考引用,发现都是落在了sub_401040()这个函数内部。这就很头大了。。。
暗度陈仓
突然间在IDA中Ctrl+E,看到了TLS回调,故事开始了…
TLS回调中使用SMC技术,对代码进行修改
其实关键是修改user32.GetDlgItemTextA,看看它修改前后的汇编代码变化效果
修改前的GetDlgItemTextA:
修改后的GetDlgItemTextA:
很显然“猫腻”就出现在这个jmp指令上
这也就是,之前F8步过GetDlgItemTextA函数时,所有的萎缩操作都完成了,给我们看到的只有“try again!”
熟悉的八数码
跟进jmp那个跳转,来到下面这一坨,关键是那个sub_401290()
接着跟进,看到初始化一个状态数组
bool __cdecl sub_401290(int key, int key_len)
{
g_ary[0] = 4;
g_ary[1] = 1;
g_ary[2] = 3;
g_ary[3] = 7;
g_ary[4] = 2;
g_ary[5] = 5;
g_ary[6] = 8;
g_ary[7] = 6;
g_ary[8] = 0;
return sub_4015B0(key, key_len);
}
跟进sub_4015B0(key, key_len),其实看到下面这个函数就应该联想的“八数码问题”,因为之前手撸过【吾爱破解2016安全挑战赛-第一题】,不过可能当时脑子太累了,和稀泥了,没看出来
bool __cdecl sub_4015B0(int key, int key_len)
{
int i; // [esp+0h] [ebp-Ch]
int v4; // [esp+8h] [ebp-4h]
v4 = 0xCCCCCCCC;
if (key_len % 2) // 说明 key_len 是偶数
return 0;
for (i = 0; i < key_len; i += 2)
{
if (*(_BYTE *)(i + key) == 'w') // 4个移动方向
v4 = 0;
if (*(_BYTE *)(i + key) == 'd')
v4 = 1;
if (*(_BYTE *)(i + key) == 's')
v4 = 2;
if (*(_BYTE *)(i + key) == 'a')
v4 = 3;
if (!sub_401380(v4, *(char *)(i + key + 1) - '0'))
return 0;
}
return g_ary[0] == 1 // 验证成功的条件
&& g_ary[1] == 2
&& g_ary[2] == 3
&& g_ary[3] == 4
&& g_ary[4] == 5
&& g_ary[5] == 6
&& g_ary[6] == 7
&& g_ary[7] == 8
&& !g_ary[8];
}
接着跟进sub_401380()函数,这时F5就看起来有些吃力了
还是手撸吧,老是F5容易营养不良
其实逻辑很简单了,说来真惭愧,撸成下面这样才突然想起来是“八数码问题”,记性真是差的够呛
int sub_401380(int nDirect, int nNum)
{
if (nNum == 0)
{
return 0;
}
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
if (g_byteAry[i][j] != nNum)
{
continue;
}
switch (nDirect)
{
case 0: //上w
if (i == 0 || g_byteAry[i - 1][j] != 0)
{
return FALSE;
}
g_byteAry[i - 1][j] = g_byteAry[i][j];
g_byteAry[i][j] = 0;
return TRUE;
case 1: //右d
if (j == 2 || g_byteAry[i][j + 1] != 0)
{
return FALSE;
}
g_byteAry[i][j + 1] = g_byteAry[i][j];
g_byteAry[i][j] = 0;
return TRUE;
case 2: //下s
if (i == 2 || g_byteAry[i + 1][j] != 0)
{
return FALSE;
}
g_byteAry[i + 1][j] = g_byteAry[i][j];
g_byteAry[i][j] = 0;
return TRUE;
case 3: //左a
if (j == 0 || g_byteAry[i][j - 1] != 0)
{
return FALSE;
}
g_byteAry[i][j - 1] = g_byteAry[i][j];
g_byteAry[i][j] = 0;
return TRUE;
default:
}
}
}
return FALSE;
}
八数码初始状态和目标状态,移动规则只能是“0”和它挨着的元素交换
逆推Key,其实知道这是“八数码问题”基本上就解决了,去网上A一段别人的代码
此处推荐:https://blog.csdn.net/GuangHEultimate/article/details/51377269
八数码存在多解的,此处得到的是最小步数
由于八数码存在多解,CM还有一些其他的约束条件,当时就拿着上面的求解结果去了
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2018-12-12 14:12
被DCO编辑
,原因: