首页
社区
课程
招聘
[原创]看雪CTF-2017秋季赛第三题
2017-10-29 21:15 2860

[原创]看雪CTF-2017秋季赛第三题

2017-10-29 21:15
2860
先看了字符串:
File Monitor - Sysinternals: www.sysinternals.com
Registry Monitor - Sysinternals: www.sysinternals.com
csrss.exe
services.exe
cmd.exe
explorer.exe
windbg.exe
w32dsm89.exe
importrec.exe
lordpe.exe
softice.exe
peid.exe
检测了一堆进程,以这个作为已知条件,很容易找到代码(起始直接看winmain就好)

int __stdcall sub_434EF0(HWND hDlg, int a2, int a3, int a4)
{
  size_t v4; // ST0C_4
  CHAR *v5; // esi
  size_t v6; // eax
  int v8; // [esp+Ch] [ebp-1A40h]
  int i; // [esp+1C4h] [ebp-1888h]
  char v10[1032]; // [esp+1D0h] [ebp-187Ch]
  unsigned __int8 v11[40]; // [esp+5D8h] [ebp-1474h]
  size_t v12; // [esp+600h] [ebp-144Ch]
  _BYTE v13[1032]; // [esp+60Ch] [ebp-1440h]
  char v14; // [esp+A14h] [ebp-1038h]
  char v15; // [esp+A15h] [ebp-1037h]
  char v16; // [esp+E1Ch] [ebp-C30h]
  char v17; // [esp+E1Dh] [ebp-C2Fh]
  CHAR String; // [esp+1224h] [ebp-828h]
  char v19; // [esp+1225h] [ebp-827h]
  UINT v20; // [esp+162Ch] [ebp-420h]
  char v21; // [esp+1638h] [ebp-414h]
  char v22; // [esp+1639h] [ebp-413h]
  int v23; // [esp+1A40h] [ebp-Ch]

  v23 = 0;
  v21 = 0;
  j__memset(&v22, 0, 0x3FFu);
  v8 = a2;
  if ( a2 == 16 )
    ExitProcess(0);
  if ( v8 == WM_INITDIALOG )
  {
    v23 = sub_42D4F1();
    if ( v23 == 1 )
      ExitProcess(0);
    v23 = 0;
    v23 = sub_42E428();
    if ( v23 == 1 )
      ExitProcess(0);
    v23 = 0;
    v23 = sub_42D825();
    if ( v23 == 1 )
      ExitProcess(0);
    sub_42D14F(hDlg, 1);
    return 0;
  }
  if ( v8 != WM_COMMAND )
    return 0;
  v8 = (unsigned __int16)a3;
  if ( (unsigned __int16)a3 == 1002 )
  {
    String = 0;
    j__memset(&v19, 0, 0x3FFu);
    v16 = 0;
    j__memset(&v17, 0, 0x3FFu);
    v20 = GetDlgItemTextA(hDlg, 1001, &String, 1025);
    v14 = 0;
    j__memset(&v15, 0, 0x3FFu);
    base64_decode_42D267((int)&String, 1024, (int)&v16);
    v13[0] = 0;
    j__memset(&v13[1], 0, 0x3FFu);
    base64_decode_42D267((int)&v16, 1024, (int)&v14);
    trans_42D96A(&v14, (int)v13, 1024);
    v12 = 3;
    sm3_42DA78(&v14, 3u, (int)v11);
    for ( i = 0; i < 32; ++i )
      j__sprintf(&v10[2 * i], "%02x", v11[i]);
    v4 = j__strlen(v10);
    v5 = &String + j__strlen(&String);
    v6 = j__strlen(v10);
    // 输入的base64串的后64位与 原始字符串的sm3值相等
    if ( !j__memcmp(v10, &v5[-v6], v4) )
    {
      sub_42D0B4();
      if ( sub_42D9AB((int)byte_49B000, (int)v13) == 1 )
        MessageBoxA(0, "ok", "CrackMe", 0);
    }
  }
  return 1;
}
对话框的窗口回调函数。IDA7.0 还是比较给力,memset这些都失败出来了,6.8的就不行。
//这个流程也不复杂
假设输入的字符串为 string_base64_encode
经过两次解码 string=base64_decode(base64_decode(string_base64_encode))
然后在
trans_42D96A(&v14, (int)v13, 1024);
中做类似摩斯电码解码的操作

int __cdecl sub_435DE0(char *a1, int a2, int a3)
{
  int result; // eax
  char v4; // [esp+D3h] [ebp-4Dh]
  int v5; // [esp+DCh] [ebp-44h]
  int v6; // [esp+E8h] [ebp-38h]
  int v7; // [esp+F4h] [ebp-2Ch]
  int v8_i; // [esp+100h] [ebp-20h]
  void *v9_buf_9; // [esp+10Ch] [ebp-14h]
  int v10; // [esp+118h] [ebp-8h]

  v10 = 0;
  v9_buf_9 = sub_42DE38();
  v8_i = 0;
  v7 = 0;
  v6 = 0;
  v4 = 42;
  j__memset(v9_buf_9, '*', 8u);
  v5 = j__strlen(a1);
  while ( 1 )
  {
    result = v8_i;
    if ( v8_i >= v5 )
      break;
    if ( v6 > a3 )
      return j__printf("the string buffer is too little\n");
    v10 = 0;                                    // 一堆反调试
    // // 一堆反调试
    v10 = sub_42D23F();
    if ( v10 == 1 )
      sub_42E086();
    v10 = sub_42D334();
    if ( v10 == 1 )
      sub_42E086();
    v10 = 0;
    v10 = sub_42DD66();
    if ( v10 == 1 )
      sub_42E086();
    v10 = 0;
    v10 = sub_42D92E();
    if ( v10 == 1 )
      sub_42E086();
    v10 = 0;
    v10 = sub_42DF7D();
    if ( v10 == 1 )
      sub_42E086();
    v10 = 0;
    v10 = sub_42E31F();
    if ( v10 == 1 )
      sub_42E086();
    if ( a1[v8_i] == ' ' || a1[v8_i] == '/' )
    {
      if ( a1[v8_i] != ' ' || a1[v8_i - 1] == '/' )
      {
        if ( a1[v8_i] == '/' )
          *(_BYTE *)(v6++ + a2) = ' ';
      }
      else
      {
        if ( j_trans_char_435D00((int)v9_buf_9, (int)&v4) != 1 || v7 >= 5 )
        {
          if ( j_trans_num_435AA0((int)v9_buf_9, (int)&v4) == 1 )
          {
            *(_BYTE *)(v6++ + a2) = v4;
          }
          else if ( sub_42E414((int)v9_buf_9, (int)&v4) == 1 )// 转换特殊符号
          {
            *(_BYTE *)(v6++ + a2) = v4;
          }
          else
          {
            j__printf("error !\n");
          }
        }
        else
        {
          *(_BYTE *)(v6++ + a2) = v4;
        }
        v7 = 0;
        j__memset(v9_buf_9, 42, 8u);
      }
    }
    else
    {
      *((_BYTE *)v9_buf_9 + v7++) = a1[v8_i];
    }
    ++v8_i;
  }
  return result;
}
三个函数分别转换数字 字母 特殊符号  两个 电码之间以 空格字符或者/分割。

sm3_42DA78(&v14, 3u, (int)v11);

这个函数根据下边函数里的初始值很容易搜到是国密算法sm3
int __cdecl sub_436700(_DWORD *a1)
{
  int result; // eax

  *a1 = 0;
  a1[1] = 0;
  a1[2] = 0x7380166F;
  a1[3] = 0x4914B2B9;
  a1[4] = 0x172442D7;
  a1[5] = 0xDA8A0600;
  a1[6] = 0xA96F30BC;
  a1[7] = 0x163138AA;
  a1[8] = 0xE38DEE4D;
  a1[9] = 0xB0FB0E4E;
  if ( sub_42DA7D() == 1 )
    sub_42E086();
  sub_42D389();
  if ( sub_42D807() == 1 )
    sub_42E086();
  result = sub_42D39D();
  if ( result == 1 )
    sub_42E086();
  return result;
}
主要是计算解码后的字符串的sm3 值。 string_sm3 =sm3(string);
    for ( i = 0; i < 32; ++i )
      j__sprintf(&v10[2 * i], "%02x", v11[i]);
    v4 = j__strlen(v10);
    v5 = &String + j__strlen(&String);
    v6 = j__strlen(v10);
    // 输入的base64串的后64位与 原始字符串的sm3值相等
    if ( !j__memcmp(v10, &v5[-v6], v4) )

接着是比较string_sm3 是否等于输入的string_base64_encode的后64位时候相等。
原来以为是要hash碰撞,后来发现根本不用,只需要把sm3的值贴在base64的后边即可。
最后的验证:
sub_42D9AB((int)byte_49B000, (int)v13) == 1
char __cdecl sub_435400(int a1, _BYTE *a2_input)
{
  int v2; // ecx
  int v4; // [esp+10Ch] [ebp-14h]
  int v5; // [esp+118h] [ebp-8h]

  v5 = 0;
  v4 = 0;
  if ( sub_42E27F(v2) == 1 )
    sub_42E086();
  if ( sub_42E162() == 1 )
    sub_42E086();
  if ( sub_42D4F6() == 1 )
    sub_42E086();
  if ( sub_42DA41() == 1 )
    sub_42E086();
  if ( sub_42D096() == 1 )
    sub_42E086();
  if ( sub_42E45A() == 1 )
    sub_42E086();
  if ( sub_42D203() == 1 )
    sub_42E086();
  while ( *a2_input != ' ' )
  {
    if ( v5 != 8 || v4 != 3 )
    {
      if ( *a2_input == 'z' )
      {
        if ( v4 + 1 >= 10 )
          return 0;
        if ( !*(_DWORD *)(a1 + 0x28 * (v4 + 1) + 4 * v5) )
          ++v4;
      }
      if ( *a2_input == 'l' && v5 + 1 < 10 )
      {
        if ( *(_DWORD *)(a1 + 40 * v4 + 4 * (v5 + 1)) )
          return 0;
        *(_DWORD *)(a1 + 40 * v4 + 4 * v5++) = 4;
      }
      if ( *a2_input == 'q' && v4 - 1 >= 0 )
      {
        if ( *(_DWORD *)(a1 + 40 * (v4 - 1) + 4 * v5) )
          return 0;
        *(_DWORD *)(a1 + 40 * v4-- + 4 * v5) = 4;
      }
      if ( *a2_input == 'p' && v5 - 1 >= 0 )
      {
        if ( *(_DWORD *)(a1 + 40 * v4 + 4 * (v5 - 1)) )
          return 0;
        --v5;
      }
    }
    ++a2_input;
  }
  return 1;
}
这个应该是一个迷宫类似的东西,通过zlqp操作最后能走出吧。。。。。等着看大佬的分析。
但是仔细看过这段代码之后,发现这个验证逻辑有漏洞,如果
trans_42D96A(&v14, (int)v13, 1024);
转换之后的字符中没有zlqp的话,只有1234这样的数值时也会成功。
自己构造的字符串:
TGk0dExTMGdMaTR1TFMwZ0xTMHRMUzBnTGkwdExTMGdMUzB0TFMwZ0xpNHVMUzBnTGk0dUxTMGdMaTB0TFMwZ0xpNHVMUzBnTGk0dExTMGdMUzB0TFMwZ0xTMHRMUzBn4cfba0a0c4b5039049dc3f6801f07d54df35ead01cbb31247cc56793a2155168

其他一些笔记:
sub_434010  check_crc
0-9
0  ----- 
1  .----
2  ..---
3  ...--
4  ....-
5  .....
6  -....
7  --...
8  ---..
9  ----.

字母
a .-**
b -...
-.-.
-..*
.***
..-.
--.*
....


0049B2A0  2E 2D 2A 2A 2D 2E 2E 2E  2D 2E 2D 2E 2D 2E 2E 2A  .-** -... -.-. -..*    a  b c d
0049B2B0  2E 2A 2A 2A 2E 2E 2D 2E  2D 2D 2E 2A 2E 2E 2E 2E  .*** ..-. --.* ....    e f g h
0049B2C0  2E 2E 2A 2A 2E 2D 2D 2D  2D 2E 2D 2A 2E 2D 2E 2E  ..** .--- -.-* .-..
0049B2D0  2D 2D 2A 2A 2D 2E 2A 2A  2D 2D 2D 2A 2E 2D 2D 2E  --** -.** ---* .--.
0049B2E0  2D 2D 2E 2D 2E 2D 2E 2A  2E 2E 2E 2A 2D 2A 2A 2A  --.- .-.* ...* -***
0049B2F0  2E 2E 2D 2A 2E 2E 2E 2D  2E 2D 2D 2A 2D 2E 2E 2D  ..-* ...- .--* -..-  y z
0049B300  2D 2E 2D 2D 2D 2D 2E 2E  00 00 00 00 00 00 00 00  -.-- --.. .... ....
zlqp

z  --..
l .-..
p .--.
q --.-

od脚本,用于过滤检测到的反调试:
mov [435e90],#eb#   //jmp
mov [435ea8],#eb#
mov [4313f4],#9090#  //nop
mov [4367bf],#eb#
mov [435481],#eb#





[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 1140
活跃值: (102)
能力值: ( LV4,RANK:48 )
在线值:
发帖
回帖
粉丝
大只狼 2017-11-13 22:20
2
0
问下大佬,  “”      这个函数根据下边函数里的初始值很容易搜到是国密算法sm3    “”  这个怎么搜到知道是sm3的
雪    币: 3003
活跃值: (464)
能力值: ( LV15,RANK:1395 )
在线值:
发帖
回帖
粉丝
lacoucou 12 2017-11-14 18:38
3
0
搜  0x7380166F  这个值,baidu第一条就是。再对比下输入输出就能确定了。
游客
登录 | 注册 方可回帖
返回