首页
社区
课程
招聘
[原创]2017CTF第二题_lelfei攻击手法
2017-6-5 08:45 3788

[原创]2017CTF第二题_lelfei攻击手法

2017-6-5 08:45
3788

在xp里调试运行上传的攻击脚本,然后在00401257这个地址(push "WELL DONE!")下普通断点,等待程序跑到这个地址断下来就穷举出结果

说明:这个攻击脚本是在原来程序上,直接修改exe内容,通过汇编代码修改程序流程,来达到优化后穷举的目的,

主要分2大步骤,第一步:修改exe使得程序可以自行穷举,第二步:优化使得穷举变快,其中优化是关键


优化的地方:

1)nop掉GetTickCount,跟GetTickCount相关的保存结果,和结果保存后,将来再进行运算的代码都可以nop掉,因为分析代码得出结论GetTickCount并没有什么影响到注册码的判断

2)sub_401A60里的从(00401A8B到00401AA8)的这个循环可以被优化掉,完全是废代码,此处费代码会在循环里调用sub_4019E0,此处浪费时间非常多,将循环优化掉后,就完全不计算GetTickCount相关的代码了,IDA分析如下:

int __thiscall sub_401A60(void *this)
{
  void *v1; // esi@1
  signed int v2; // eax@1
  char *v3; // edi@1
  char *v4; // ecx@1
  int v5; // edx@2
  signed int v6; // ebp@3
  int v7; // ebx@4
  int v8; // eax@4
  int result; // eax@5
  int v10; // ecx@5
  signed int v11; // edx@5
  int v12; // esi@6

  v1 = this;
  v2 = 0;
  v3 = (char *)this + 4104;
  v4 = (char *)this + 4104;
  do
  {
    v4 += 4;
    v5 = v2++ & 0x3FF;
    *((_DWORD *)v4 - 1) = v5;
  }
  while ( v2 < 1024 );
  v6 = 1024;

//下面紧接着的这个循环可以完全干掉,毫无用处

  do
  {
    v7 = *(_DWORD *)v3;
    v8 = sub_4019E0(v1);
    *(_DWORD *)v3 = *((_DWORD *)v1 + v8 + 1026);
    v3 += 4;
    --v6;
    *((_DWORD *)v1 + v8 + 1026) = v7;
  }
  while ( v6 );
  result = (int)((char *)v1 + 8);
  v10 = (int)((char *)v1 + 8196);
  v11 = 1024;
  do
  {
    v12 = *(_DWORD *)v10;
    v10 -= 4;
    *(_DWORD *)result = v12;
    result += 4;
    --v11;
  }
  while ( v11 );
  return result;
}

上面的代码,第二处也就是中间的循环可以完全干掉,节约大量穷举的时间

3)在00401168处,修改jnz跳转的地址到准备开始下次穷举的位置,此处用IDA分析是

while ( 1 )
  {
    CObj5_CObj5_szKey(&obj5_2, szKey);
    LOBYTE(v23) = 1;
    v8 = sub_401840((int)&obj5_2, (int)&obj5, (int)&obj5_2);
    v9 = sub_401730(v8, (int)&obj5, 9) + v8;
    nullsub_1();
    if ( v9 || sub_4013A0(&obj5) % 2 != 1 )
      goto LABEL_16;
    v10 = sub_4013A0(&obj5);
    v11 = sub_4013B0((int)&obj5, v10 >> 1);
    v12 = sub_4013B0((int)&obj5_2, 0);
    v13 = &obj5_2;
    if ( v11 == v12 )
      break;
LABEL_17:
    LOBYTE(v23) = 0;
    CObj5_Destructor(v13);
    if ( v9 )
    {
      printf("WRONG KEY...\n");
      goto LABEL_19;
    }
  }
  v14 = sub_4013A0(&obj5_2) - 1;
  v15 = 1 - sub_4013A0(&obj5_2);
  v16 = sub_4013A0(&obj5);
  v17 = sub_4013E0(&obj5, (int)&obj5_2, v16 + v15, 1, v14, 0);
  v18 = sub_4013A0(&obj5_2);
  if ( sub_4013E0(&obj5, (int)&obj5_2, 0, 1, v18 - 1, 1) + v17 )
  {
    v9 = 0;
LABEL_16:
    v13 = &obj5_2;
    goto LABEL_17;
  }

上面分析代码里,经过调试发现如果sub_4013A0(&obj5) % 2 != 1,while(1)循环还将继续尝试运算,这个模2等不等于1的判断总是失败,也就是总是等于0,导致while(1)循环无故循环了很多次,此处将goto LABEL_16;改成跳转jnz到将注册码自加1,直接尝试下一个注册码的地方,减少大量浪费时间的循环操作

主要做了上面3处优化


在上面3处优化的基础上,让程序可以自己进行穷举尝试,然后调试运行,在push "WELL DONE!"处下断点等待结果,对注册码自加1的代码如下:

004012A1  |>  8038 31            cmp     byte ptr [eax], 31
004012A4  |. |7D 03              jge     short 004012A9
004012A6  |. |C600 31            mov     byte ptr [eax], 31
004012A9  |> |8038 39            cmp     byte ptr [eax], 39
004012AC  |. |7C 06              jl      short 004012B4
004012AE  |. |C600 31            ||mov     byte ptr [eax], 31
004012B1  |. |40                 inc     eax
004012B2  |.^\EB ED              jmp     short 004012A1
004012B4  |>  FE00               inc     byte ptr [eax]
004012B6  |.  BC 34BE1200        mov     esp, 12BE34
004012BB  \.^ E9 93FDFFFF        jmp     00401053
004012C0  /$  56                 push    esi
004012C1  |.  8BF1               mov     esi, ecx
004012C3  |.  C706 C8804000      mov     dword ptr [esi], 004080C8



最后还附上了自己还原的大部分代码,还原的代码可以供参考,某些函数发现可以优化就优化了




[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞1
打赏
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  CCkicker   +1.00 2017/06/06
最新回复 (1)
雪    币: 598
活跃值: (282)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
Fpc 4 2017-6-8 09:10
2
0
工作量有些大,修改优化后穷举速度会快不少吧
游客
登录 | 注册 方可回帖
返回