看雪 2016 CTF 第二十题 点评和解析

回复 星标
更多

看雪 2016 CTF 第二十题 点评和解析

看雪版主&评委

netwind点评

该题反调试和算法都比较精彩。作者运用函数表来隐藏函数调用过程,通过检查线程运行时间、检查调试状态,通过设置 int 3 中断来阻止攻击者逆向分析;算法是一个N(11)阶幻方的算法,根据用户的输入,当 N 行、N 列、2N 条循环斜对角线之和相等的条件下验证成功。

作者简介

网名brichfire,职业看门大叔,爱好密码、数论、登山、下海……看雪 CTF 2016 活动开始才初步学习程序逆向破解,出题之前才恶补反逆向技术。

题目设计思路

1、  通过函数表隐藏调研过程,增加静态分析的难度。

DWORD (*pfunc[8]) (char *);

int (*pfunc4[4]) (void);

2、通过3种反调试手段增加动态分析难度。

超时退出线程

CheckRemoteDebuggerPresent

int 3,调试状态下进入无解的校验函数。

3、验证算法

N(11)阶幻方扣掉对角线的10个数据,该数据由用户输入数据查表覆盖,当N行、N列、2N条循环斜对角线之和相等的条件下验证成功。(出题的时候考虑N取更大值、甚至扣掉更多的数据,但是为了让破解者更加方便,也为了避免多解的情况只取了N为11,抠掉10个数)。

题目破解步骤

1、  将下处反调试的影响去除,nop掉该指令,或者改为or esi, 1,否则进入无解的校验函数。

2、  去除下图中调试超时退出的代码,或者将jle改成 jmp

508613

3、  去除 CheckRemoteDebuggerPresent 反调试退出代码,将第一个 jnz 改为 nop,将第二个jnz 改为 jmp(第二个其实可以不修改)。

508613

4、  去除 int 3 反调试,将 int 3 的指令 0xcc 改为 0x00,原来 exception 触发 0x80000003 异常,修改后触发 0xc0000005 异常,将异常处理的 cmp ecx, 80000003h 修改为cmp ecx, c0000005h。此时去除了 int 3 的反调试。

508613

508613

去除反调试之后调试状态下的 exception 执行后能够执行到如下代码

508613

Call edx 的值取决于 ecx,ecx 来自如下代码:

508613

无反调试情况下 ecx 不应该去参与 or 2 运行,但应该参与 or 1 的运行,可以猜测 ecx 就应该为1,且需要构造输入使 input[1]%4 == 1 成立。ecx为1 时执行到 0x401500 位置的 check 代码。

5、  校验

Sub_0x401500 处的函数是校验代码,校验算法是一个 N 阶幻方,就是将1,2,3,……,N*N这些数填入一个 N*N 的方阵,使得 N 行、N 列、2N 条循环对角线之和相等。本题中 N 取11,并且已经将大部分数据填好,剩下的 Table[i][i] (i从0到9)位置由用户输入查表生成。N 为大于 2 的素数时,N 阶幻方可通过如下公式生成,不知道公式可以由无需填充的右斜对角线计算得到和,再计算需要填充的位置:

Table[i][j] = ((2*i+j+3)%N)*N + (2*i-j+N+1)%N + 1;  ( i, j 选自 0,1,2,……N-1)

校验矩阵如下

508613

标注出来的位置实际应该为:  0x23,0x45,0x67,0x10,0x32,0x54,0x76,0x1f,0x41,0x63

这些值通过 table_410ee0[input[i]] 获得,根据下图数据,推断出 input[i] 依次为:

0x43,0x6d,0x4a,0x46,0x39,0x32,0x6f,0x52,0x63,0x75, 对应字符为:CmJF92oRcu

508613

最后附上去除反调试之后的程序 RmCrackMe 和原程序 CrackMe。

下面选择了分析者loudy的详细分析

第 二十 题的分析走了不少弯路。被作者的设计迷惑,一直在 sub_4011E0 里面转,解一个11X11 的格子,分析之后发现根本无解,纠结好久,刚刚终于发现一个漏掉的地方,如下。

508613

508613

且有一个明显进入异常处理的点

508613

即进入 sub_4011E0、sub_401500、sub_401820、sub_401B40 都有可能。

分析又发现这四个函数结构完全一样,貌似仅有一处不同,如下(按顺序)。

508613

看到下图此处,v2即为填充的11X11矩阵,可以大胆猜测,程序是先将正确结果填好,每到12的整数倍用和输入有关的数据填充。

508613

那么,我们先把do-while循环中的所有判断删掉,得到矩阵数据(记得最后一位填1)。发现sub_4011E0不能满足要求,而sub_401500可以满足要求。即相等关系成立,下图中v47==4,调用函数sub_4010c0,调用线程sub_401020,打印成功信息。

508613

508613

508613

此时,v2 处矩阵数据如下。

508613

因此,和输入有关的填充数据按顺序为:

unsigned char ff[] = {0x23,0x45,0x67,0x10,0x32,0x54,0x76,0x1f,0x41,0x63};

编程查表解出:

unsigned char ee[] = {0x3A,0x5D,0x49,0x5F,0x78,0x51,0x0F,0x55,0x5E,0x12,0x15,0x26,0x58,0x47,0x69,0x6D,

0x3F,0x5C,0x56,0x3D,0x29,0x04,0x77,0x4F,0x52,0x6E,0x2C,0x1B,0x44,0x1C,0x14,0x4C,

0x46,0x03,0x3B,0x25,0x38,0x06,0x7B,0x7E,0x0C,0x24,0x4E,0x3C,0x1E,0x13,0x1D,0x53,

0x40,0x57,0x54,0x6A,0x5B,0x31,0x66,0x27,0x74,0x32,0x2A,0x5A,0x60,0x0A,0x02,0x3E,

0x34,0x08,0x6F,0x23,0x7F,0x30,0x10,0x07,0x64,0x17,0x67,0x05,0x48,0x62,0x7A,0x73,

0x01,0x71,0x1F,0x37,0x18,0x70,0x4B,0x7C,0x2E,0x79,0x2D,0x33,0x20,0x28,0x2B,0x43,

0x22,0x72,0x36,0x41,0x11,0x42,0x6B,0x61,0x59,0x19,0x2F,0x39,0x68,0x45,0x0B,0x76,

0x1A,0x21,0x7D,0x80,0x4D,0x63,0x50,0x16,0x65,0x35,0x6C,0x4A,0x0E,0x75,0x09,0x0D};

unsigned char ff[] = {0x23,0x45,0x67,0x10,0x32,0x54,0x76,0x1f,0x41,0x63};

for(int k1=0;k1<10;k1++)

{

for(int k2=0;k2<128;k2++)

{

if(ee[k2]==ff[k1])

{

printf("%c",k2);

break;

}

}

}

printf("\n");

508613

将“CmJF92oRcu”填入,得到

508613

508613

508613

508613

508613

508613

508613

508613

508613

508613

微博:看雪安全

508613

看雪安全 · 看雪众测

持续关注安全16年,专业为您服务!

快,关注这个公众号,一起涨姿势~

508613

此帖已被锁定,无法回复
新窗口打开 关闭