加载中…
个人资料
elecs
elecs
  • 博客等级:
  • 博客积分:0
  • 博客访问:53,406
  • 关注人气:1
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

看雪2016 CTF第四题分析

(2017-04-12 15:10:41)
标签:

看雪

2016

ctf

第四题

分类: 信息安全
之前,做这题时被虚拟机绕进去了,参看HighHand、冰怜泯灭、brichfire等人的分析才算弄明白。今天将详细分析过程列出,算给自己一个温习吧。

1、 首先用IDA搜索GetWindowsText或者GetDlgItemText函数的调用情况,定位到了输入和校验的位置,截图如下。发现字符长度需为0x1E (30个字符)
看雪2016 <wbr>CTF第四题分析
2、只有当sub_401000返回值为1时才提示注册成功。调试跟进函数sub_401000,走几步就进入了虚拟机部分

看雪2016 <wbr>CTF第四题分析

3、我们知道VMP中都有自己的指令解释部分,有其自定义指令与其对应的操作,放在了一张指令派发表(TABLE地址:0x42F8DD)
看雪2016 <wbr>CTF第四题分析

看雪2016 <wbr>CTF第四题分析

依次查看可以定位到vm出口。在退出虚拟机时会把所有寄存器环境恢复。

看雪2016 <wbr>CTF第四题分析

CrackMe加了开源的 WProtect 因此有部分是虚拟机。但是虚拟机部分也不是特别重要。因为一般vm是以函数为单位的,当调用子函数时,会退出当前虚拟机,还原环境,然后调用。正好此程序只是把外层函数给vm了。只需在vm出口等待返回即可。不需要去查看整个虚拟机流程。
注:0x42F779处下断点时,经常会提示错误“设置了单步标志”。解决办法:可以选择将TF标志置为0,再接着执行即可。
看雪2016 <wbr>CTF第四题分析

看雪2016 <wbr>CTF第四题分析
4、略过虚拟机的分析。分析sub_401000函数的返回值,发现仅当ecx值为0x58时能让eax1(注册成功的条件),ecx等于0x30或者0x20会提前结束,其它值能够继续判断下一个字符,因此需要构造路径,中间使该位置的ecx值不为0x300x20
看雪2016 <wbr>CTF第四题分析

5、分析ecx的取值过程

ecx取自内存[ebp-0x370]和内存[ebp-0x170]这两个数据表运算获得,每次取一个字节,偏移量index相同。[ebp-0x370]用数组g_bt1表示,[ebp-0x170]用数组g_bt2表示,ecx计算规则如下:ecx = (g_bt1 [i] ror 2) xor g_bt2 [i];

看雪2016 <wbr>CTF第四题分析

发现内存中数组g_bt1g_bt2是恒定不变的,不受输入影响。
g_bt1[]=ebp-0x370(来自00413798)
0012F540  6E D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9
0012F550  02 18 74 95 1F C2 4D 33 3C F0 3B EC E9 81 4B 9C
0012F560  0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
0012F570  C2 F5 0C DD 42 CC AF B4 D5 E4 86 D3 9A 0B 62 63
0012F580  A7 D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9
0012F590  02 18 74 95 1F C2 4D 33 38 F0 3B EC ED 81 4B 9C
0012F5A0  0B 47 5C AD DD 09 B0 87 57 9B F2 E3 0B 01 92 8D
0012F5B0  C2 F5 0C DD 42 CC AF B4 D5 E4 86 D3 9A 0B 62 63
0012F5C0  A7 D4 1B 69 5F 4E E8 AA 95 F6 AF CE 32 1A 62 D9
0012F5D0  02 18 74 95 1F C2 4D 33 38 F0 3B EC E9 81 4B 9C
0012F5E0  0F 47 5C AD D9 09 B0 87 53 9B F2 E3 0F 01 92 8D
0012F5F0  C2 F5 0C DD 42 CC AF B4 D5 E4 86 D3 9A 0B 62 63
0012F600  A7 D4 1B 69 5B 4E E8 AA 91 F6 AF CE 36 1A 62 D9
0012F610  02 18 74 95 1F C2 4D 33 38 F0 3B EC E9 81 4B 9C
0012F620  0B 47 5C AD DD 09 B0 87 57 9B F2 E3 0B 01 92 8D
0012F630  C6 F5 0C DD 42 CC AF B4 D5 E4 86 D3 9A 0B 62 63

g_bt2 []=ebp-0x170(来自00413898)

0012F740  EE 05 F6 6A E7 A2 0B 9B 54 8C DA 82 BD B6 A8 46
0012F750  B1 36 2D 55 F7 81 63 FC 3F 0C FE 0B 4B 50 E2 17
0012F760  F2 E1 27 5B 46 73 1C D0 E5 D7 8D C9 F2 70 94 53
0012F770  81 4C 32 46 A0 02 DB 1C 45 09 91 C4 96 F2 A8 E8
0012F780  D9 05 F6 6B E7 A2 0A 9B 54 8C DA 82 BD B7 A9 46
0012F790  B0 36 2D 54 F7 81 63 FC 3E 0C FE 0B 4B 50 E3 17
0012F7A0  F2 E0 26 5A 47 73 1C D1 E5 D6 8C C8 F2 70 95 53
0012F7B0  80 4C 33 47 A0 02 DB 1C 44 08 91 C4 96 F2 A9 E8
0012F7C0  D9 04 F6 6A E7 A2 0A 9B 55 8C DB 83 BC B6 A9 46
0012F7D0  B0 37 2D 55 F7 81 63 FD 3E 0D FE 0B 4A 50 E3 17
0012F7E0  F3 E0 27 5B 46 73 1D D0 E4 D7 8C C8 F3 70 95 53
0012F7F0  80 4C 33 47 A0 03 DB 1D 45 08 91 C4 96 F2 A9 E8
0012F800  D9 04 F6 6A E6 A3 0A 9A 54 8C DB 82 BC B7 A9 46
0012F810  B0 37 2C 54 F6 81 62 FD 3E 0D FE 0A 4A 50 E2 17
0012F820  F2 E1 27 5B 47 72 1C D0 E5 D7 8C C9 F2 70 94 53
0012F830  81 4D 33 47 A0 03 DB 1C 44 08 91 C5 97 9A A8 E8

这样我们可以生成一张解密后的地图g_bt3[i]= (g_bt1[i] ror 2) xor g_bt2[i]。搜索数组中满足成功的位置和不可触碰的位置,C为起点,X为终点。黑块为墙(0x30),白块为其可以行走的路线

看雪2016 <wbr>CTF第四题分析

BYTE g_bt3[0x100]取值为

43 30 30 30 30 31 31 31 31 31 31 31 31 30 30 30
31 30 30 30 30 31 30 30 30 30 30 30 31 30 30 30
31 30 30 30 30 31 30 31 31 31 31 31 31 30 30 30
31 31 31 31 30 31 30 31 30 30 30 30 30 30 30 30
30 30 30 31 30 31 30 31 31 31 31 31 31 31 31 30
30 30 30 31 30 31 30 30 30 30 30 30 30 30 31 30
30 31 31 31 30 31 30 30 30 30 30 30 30 30 31 30
30 31 30 30 30 31 30 31 31 31 30 30 30 30 31 30
30 31 30 30 30 31 30 31 30 31 30 30 30 30 31 30
30 31 30 30 30 31 30 31 30 31 30 30 30 30 31 30
30 31 30 30 30 31 31 31 30 31 30 30 30 30 31 30
30 31 30 30 30 30 30 30 30 31 30 30 30 30 31 30
30 31 30 30 30 30 30 30 30 31 30 31 31 31 31 30
30 31 31 31 31 31 31 31 30 31 30 31 30 30 30 30
30 30 30 30 30 30 30 31 30 31 30 31 30 30 30 30
30 30 30 30 30 30 30 31 31 31 30 31 31 58 30 30

写程序计算走法,如上图中红色箭头所示。

BYTE g_bt1[] = "\x6E\xD4\x1B\x69\x5F\x4E\xE8\xAA\x95\xF6\xAF\xCE\x32\x1A\x62\xD9\x02\x18\x74\x95\x1F\xC2\x4D\x33\x3C\xF0\x3B\xEC\xE9\x81\x4B\x9C\x0F\x47\x5C\xAD\xD9\x09\xB0\x87\x53\x9B\xF2\xE3\x0F\x01\x92\x8D\xC2\xF5\x0C\xDD\x42\xCC\xAF\xB4\xD5\xE4\x86\xD3\x9A\x0B\x62\x63\xA7\xD4\x1B\x69\x5F\x4E\xE8\xAA\x95\xF6\xAF\xCE\x32\x1A\x62\xD9\x02\x18\x74\x95\x1F\xC2\x4D\x33\x38\xF0\x3B\xEC\xED\x81\x4B\x9C\x0B\x47\x5C\xAD\xDD\x09\xB0\x87\x57\x9B\xF2\xE3\x0B\x01\x92\x8D\xC2\xF5\x0C\xDD\x42\xCC\xAF\xB4\xD5\xE4\x86\xD3\x9A\x0B\x62\x63\xA7\xD4\x1B\x69\x5F\x4E\xE8\xAA\x95\xF6\xAF\xCE\x32\x1A\x62\xD9\x02\x18\x74\x95\x1F\xC2\x4D\x33\x38\xF0\x3B\xEC\xE9\x81\x4B\x9C\x0F\x47\x5C\xAD\xD9\x09\xB0\x87\x53\x9B\xF2\xE3\x0F\x01\x92\x8D\xC2\xF5\x0C\xDD\x42\xCC\xAF\xB4\xD5\xE4\x86\xD3\x9A\x0B\x62\x63\xA7\xD4\x1B\x69\x5B\x4E\xE8\xAA\x91\xF6\xAF\xCE\x36\x1A\x62\xD9\x02\x18\x74\x95\x1F\xC2\x4D\x33\x38\xF0\x3B\xEC\xE9\x81\x4B\x9C\x0B\x47\x5C\xAD\xDD\x09\xB0\x87\x57\x9B\xF2\xE3\x0B\x01\x92\x8D\xC6\xF5\x0C\xDD\x42\xCC\xAF\xB4\xD5\xE4\x86\xD3\x9A\x0B\x62\x63";

 BYTE g_bt2[] = "\xD8\x05\xF6\x6A\xE7\xA2\x0B\x9B\x54\x8C\xDA\x82\xBD\xB6\xA8\x46\xB1\x36\x2D\x55\xF7\x81\x63\xFC\x3F\x0C\xFE\x0B\x4B\x50\xE2\x17\xF2\xE1\x27\x5B\x46\x73\x1C\xD0\xE5\xD7\x8D\xC9\xF2\x70\x94\x53\x81\x4C\x32\x46\xA0\x02\xDB\x1C\x45\x09\x91\xC4\x96\xF2\xA8\xE8\xD9\x05\xF6\x6B\xE7\xA2\x0A\x9B\x54\x8C\xDA\x82\xBD\xB7\xA9\x46\xB0\x36\x2D\x54\xF7\x81\x63\xFC\x3E\x0C\xFE\x0B\x4B\x50\xE3\x17\xF2\xE0\x26\x5A\x47\x73\x1C\xD1\xE5\xD6\x8C\xC8\xF2\x70\x95\x53\x80\x4C\x33\x47\xA0\x02\xDB\x1C\x44\x08\x91\xC4\x96\xF2\xA9\xE8\xD9\x04\xF6\x6A\xE7\xA2\x0A\x9B\x55\x8C\xDB\x83\xBC\xB6\xA9\x46\xB0\x37\x2D\x55\xF7\x81\x63\xFD\x3E\x0D\xFE\x0B\x4A\x50\xE3\x17\xF3\xE0\x27\x5B\x46\x73\x1D\xD0\xE4\xD7\x8C\xC8\xF3\x70\x95\x53\x80\x4C\x33\x47\xA0\x03\xDB\x1D\x45\x08\x91\xC4\x96\xF2\xA9\xE8\xD9\x04\xF6\x6A\xE6\xA3\x0A\x9A\x54\x8C\xDB\x82\xBC\xB7\xA9\x46\xB0\x37\x2C\x54\xF6\x81\x62\xFD\x3E\x0D\xFE\x0A\x4A\x50\xE2\x17\xF2\xE1\x27\x5B\x47\x72\x1C\xD0\xE5\xD7\x8C\xC9\xF2\x70\x94\x53\x81\x4D\x33\x47\xA0\x03\xDB\x1C\x44\x08\x91\xC5\x97\x9A\xA8\xE8";

 BOOL IsRun(int x, int y)
{
  if (x < 0 || x >= 0x10)
  {
    return FALSE;
  }
  if (y < 0 || y >= 0x10)
  {
    return FALSE;
  }
  if (g_bt3[x*0x10+y] == 0x31){

    return TRUE;

  }

  return FALSE;

}

 

BOOL IsOK(int x, int y)

{

  if (x < 0 || x >= 0x10)

  {

    return FALSE;

  }

  if (y < 0 || y >= 0x10)

  {

    return FALSE;

  }

  if (g_bt3[x*0x10+y] == 0x58){

    return TRUE;

  }

  return FALSE;

}

 

void GetPath(BYTE pResult[])

{

  int i = 0;

 

  int x = 0;

  int y = 0;

  int n = 0;

  int nCount = 0;

  BYTE btResult = 0;

  BOOL bIsOK = FALSE;

  i = 1;

  while(1){

    if (IsOK(x+1, y)){

      n = 2;

      bIsOK = TRUE;

    }

    else if (IsOK(x-1, y)){

      n = 0;

      bIsOK = TRUE;

    }

    else if (IsOK(x, y+1)){

      n = 1;

      bIsOK = TRUE;

    }

    else if (IsOK(x, y-1)){

      n = 3;

      bIsOK = TRUE;

    }

    g_bt2[x*0x10+y] = g_bt2[x*0x10+y] ^ 0x80;

    if (IsRun(x+1, y)){

      n = 2;

      x++;

    }

    else if (IsRun(x-1, y)){

      n = 0;

      x--;

    }

    else if (IsRun(x, y+1)){

      n = 1;

      y++;

    }

    else if (IsRun(x, y-1)){

      n = 3;

      y--;

    }

    g_bt2[x*0x10+y] = g_bt2[x*0x10+y] ^ 0xD;

    g_bt3[x*0x10+y] = 0x0;

    i++;

    nCount++;

    switch(i){

    case 2:

      btResult = n << 6;

      break;

    case 3:

      btResult = btResult + (n << 4);

      break;

    case 4:

      btResult = btResult + (n << 2);

      break;

    case 5:{

      btResult = btResult + n;

      *pResult = btResult;

      pResult++;

      printf("X ", btResult);

      i = 1;

      }

      break;

    }

    if (bIsOK == TRUE){

      break;

    }

  }

 

}

输出路径N= A9 5A BE AA A5 55 A5 00 00 FA BC 00 00 15 55 AF FE 95 55 AA AA FE A5

6、分析发现,数组索引值edx受到输入控制。eax起始为0,之后以+1-1+0x10,  -0x10的形式在原有基础上变化(关键代码如下图)。问题转化成迷宫路径搜索的形式。原作者的意图是只能取表中正向偏移的位置(这样答案就唯一了,从第8个字符开始,每个字符控制走4步,长度限制了最多走23*4=92步),但是由于实现限制不严格,导致可以走到负区域,相当于走了一条捷径。
看雪2016 <wbr>CTF第四题分析

EDX分别右移6420位,然后and 3得到2比特作为+1-1+0x10-0x10的控制条件。
以上的汇编代码还原为C,其实就是利用switch...case移动路线:( x为行,y为列)
case 0: x--;
case 1: y++;
case 2: x++;
case 3: y--;

7、现在我们重新返回,分析下数组索引值edx的取值过程。
7.1 通过对输入内容下硬件断点,来到MD5计算部分。先取出序列号的前7位,进行MD5计算(sub_004014F0实现)。
  假设输入的SN前七位为1111111,则地址0x12F4E8的内容为7FA8282AD93047A4D6FE6111C93B308A

看雪2016 <wbr>CTF第四题分析

接下来,为了后面的计算方便,将16MD5扩展为23位,即后续添加7MD5值,将它们拷贝到0012F640

0012F640  7F A8 28 2A D9 30 47 A4 D6 FE 61 11 C9 3B 30 8A  ?*?Gぶ?0
0012F650  7F A8 28 2A D9 30 47                             ?*?G..

7.2 将序列号的后23位与密钥序列key=44 AD 5C CC 12 90 73 8D 47 81 E3 89 84 9C DF F9 47 6A B6 9E 11 30 27 进行异或加密。
下图给出SN23位为4zSLvWKv4*T2g,0,BVTTTTT的情况。

看雪2016 <wbr>CTF第四题分析

7.37.1MD57.2中的结果进行异或。
看雪2016 <wbr>CTF第四题分析

最终得到,第6步中0x40124e位置的edx = MD5(前七位SN) xor key xor (23SN)=路径NCrackme程序依照edx的值来寻路,判断注册是否成功。

到此为止,知道了加密及验证算法,需要还原出序列号。
方案一:输入SN23位,求出前7位。
方案二:输入SN7位,求出后23位。

         针对方案一,中间涉及由MD5逆查原文的过程不太容易成功,因此选第二种方案。假设I2表示后23SNI1表示前7SN,由N=MD5(I1) xor key xor I2得出
I2=MD5(I1) xor key xor N
         由于I1I2未知,所以需要枚举一下I1的取值,且需要满足I1I2都为可见字符。

#include
#include
static int check(unsigned char *i2)
{
int i;
for (i = 0; i < 23; i++)
{
if (i2[i] < ' ')
return -1;

if (i2[i] > 0x7e)
return -1;
}

return 0;
}

 

int main(void)

{

unsigned char y1[23] = {};

MD5_CTX md;

int i,k;

unsigned char path[] = {"\xA9\x5A\xBE\xAA\xA5\x55\xA5\x00\x00\xFA\xBC\x00\x00\x15\x55\xAF\xFE\x95\x55\xAA\xAA\xFE\xA5"};

unsigned char key[] = {"\x44\xAD\x5C\xCC\x12\x90\x73\x8D\x47\x81\xE3\x89\x84\x9C\xDF\xF9\x47\x6A\xB6\x9E\x11\x30\x27"};

 

for (i = 0; i < 23; i++)

{

y1[i] = path[i] ^ key[i];

}

 

char I1[8] = { "0000000" };

unsigned char i1[23] = { 0 };

unsigned char I2[32] = { 0 };

 

for (i = 0; i <= 0xFFFFFFF; i++)

{

sprintf(I1, "x", i);

 

MD5_Init(&md);

MD5_Update(&md, I1, 7);

MD5_Final(i1, &md);

 

i1[0x10] = i1[0];

i1[0x11] = i1[1];

i1[0x12] = i1[2];

i1[0x13] = i1[3];

i1[0x14] = i1[4];

i1[0x15] = i1[5];

i1[0x16] = i1[6];

for (k = 0; k < 23; k++)

{

I2[k] = i1[k] ^ y1[k]; // 简化为 y1 = path^key

}

if (check(I2) == 0)

{

printf("sn=%s%s\n", I1, I2);

}

}

printf("\n");

printf(" ok.\n");

return 0;

 

}

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有