kxctf第六题

2018kxctf第六题

Posted by gxkyrftx on December 30, 2018

0x01 主函数分析

将可执行文件拖入ida,寻找winmain函数,f5,结果发现里面啥也没。主函数调用空函数…这怎么可能。

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
  sub_401280();
  return 0;
}

void sub_401280()
{
  ;
}

0x02 分析隐藏的逻辑

1.tls回调函数

关于tls函数的作用,前面有过简单分析,程序入口点相关,ctrl+E,寻找程序入口点。 tls

tls中代码逻辑如下

HANDLE __stdcall TlsCallback_0(int a1, int a2, int a3)
{
  HANDLE result; // eax
  int v4; // [esp+4h] [ebp-10h]
  int v5; // [esp+8h] [ebp-Ch]
  int Parameter; // [esp+Ch] [ebp-8h]
  int v7; // [esp+10h] [ebp-4h]

  result = (HANDLE)0xCCCCCCCC;
  v4 = 0xCCCCCCCC;
  v5 = 0xCCCCCCCC;
  Parameter = 0xCCCCCCCC;
  v7 = 0xCCCCCCCC;
  if ( a2 == 1 )
  {
    sub_401D50();
    InitializeCriticalSection(&CriticalSection);//解决同步互斥的一个函数
    sub_401C10();
    result = CreateThread(0, 0, StartAddress, &Parameter, 0, 0);
  }
  return result;
}

我们要关注的主要是函数调用,也就是sub_401D50()和sub_401C10()。

1.1 sub_401D50()函数

函数逻辑

int sub_401D50()
{
  signed int i; // [esp+0h] [ebp-18h]
  int v2; // [esp+Ch] [ebp-Ch]
  int v3; // [esp+10h] [ebp-8h]
  int v4; // [esp+14h] [ebp-4h]

  v3 = 0xCCCCCCCC;
  v4 = 0xCCCCCCCC;
  v2 = 233;
  *(int *)((char *)&v2 + 1) = (char *)off_414014 - (char *)((_BYTE *)off_414018 + 4) - 5;
  sub_4019B0(off_414018);
  for ( i = 0; i < 5; ++i )
    *((_BYTE *)off_414018 + i + 4) = *((_BYTE *)&v2 + i);
  return sub_4019E0(off_414018);
}
长跳指令偏移基本算法:long_jmp_E9_offset = tgt_addr - (long_jmp_E9_addr+5) //length of long_jmp_E9 = 5

结果很明显,*(int *)((char *)&v2 + 1) = (char *)off_414014 - (char *)((_BYTE *)off_414018 + 4) - 5;就是跳转,off_414014和off_414018的内容如下

.data:00414014 off_414014      dd offset sub_401220    ; 
.data:00414018 ; LPVOID off_414018
.data:00414018 off_414018      dd offset sub_401280    ;

就是获取两个sub_401220和sub_401280的偏移,最后保存到off_414018,而且还有一个长跳转,这是一个HOOK操作。通过IDA HEX View直接将401284位置的数据改为E9 97 FF FF FF(jmp指令)看一下HOOK的目的是什么

change

修改后的流程

change1

来看看sub_401220()函数的伪代码

void __noreturn sub_401220()
{
  HINSTANCE hInstance; // ST18_4

  hInstance = GetModuleHandleW(0);
  DialogBoxParamW(hInstance, (LPCWSTR)0x65, 0, DialogFunc, 0);
  exit(0);
}

拿到句柄,创建了DialogBox,看看DialogFunc

BOOL __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
  if ( a2 == 16 )
  {
    DestroyWindow(hWnd);
  }
  else if ( a2 == 273 && a3 == 1002 )
  {
    sub_401040(hWnd);
  }
  return 0;
}

明确一点,一个软件,肯定是要完成功能,因此一定要调用函数,而且是能完成功能的函数,像什么DestroyWindow(hWnd);销毁窗口,这不用看就知道没用,所以关键在有功能的函数。 一步一步往下找,再看看sub_401040

int __cdecl sub_401040(HWND hDlg)
{
  void *v1; // ST08_4
  signed int i; // [esp+Ch] [ebp-24h]
  signed int v4; // [esp+10h] [ebp-20h]
  CHAR String; // [esp+18h] [ebp-18h]
  int v6; // [esp+19h] [ebp-17h]
  int v7; // [esp+1Dh] [ebp-13h]
  int v8; // [esp+21h] [ebp-Fh]
  int v9; // [esp+25h] [ebp-Bh]
  __int16 v10; // [esp+29h] [ebp-7h]
  char v11; // [esp+2Bh] [ebp-5h]

  strcpy(Text, "try again!");
  strcpy(Caption, "fail");
  String = 0;
  v6 = 0;
  v7 = 0;
  v8 = 0;
  v9 = 0;
  v10 = 0;
  v11 = 0;
  GetDlgItemTextA(hDlg, 1001, &String, 20);//获取输入元素个数
  v4 = 0;
  for ( i = 0; i < 20; ++i )
    v4 += *(&String + i);
  if ( v4 > 0 && v4 < 0x1024 )
  {
    v1 = malloc(v4);
    sub_401020();
    j___free_base(v1);
  }
  return MessageBoxA(0, Text, Caption, 0);
}

整体的逻辑,判断校验和在不在0-0x1024之间,不再的话弹窗“失败”,所以重点肯定在成功的里面,进入sub_401020()

void sub_401020()
{
  ;
}

额,完蛋,又是空的,不可能。再看看

1.2 sub_401C10()函数

然后就回退,回退但最初的tls回调函数,里面有两个函数调用,刚才分析了一个,还有一个,看看这个。基本逻辑如下

int sub_401C10()
{
  void *v0; // edx
  signed int j; // [esp+0h] [ebp-Ch]
  signed int i; // [esp+8h] [ebp-4h]

  sub_4018D0(0xCCCCCCCC, 0xCCCCCCCC, 0xCCCCCCCC);
  lpAddress = v0;
  sub_4019B0(v0);
  for ( i = 0; i < 5; ++i )
    byte_4147DC[i] = *((_BYTE *)lpAddress + i + 32);
  *(_DWORD *)&byte_414028[1] = (char *)sub_401A10 - (char *)((_BYTE *)lpAddress + 32) - 5;
  for ( j = 0; j < 5; ++j )
    *((_BYTE *)lpAddress + j + 32) = byte_414028[j];
  return sub_4019E0(lpAddress);
}

这个跟上一个的逻辑很像啊,相同的粗看,不同的重点看。最明显的上面三行差别很大,多了两个函数,而且v0也就是edx,这个参数传进了两个for循环,一个一个看吧。 首先sub_4018D0,伪代码如下

int sub_4018D0()
{
  int v0; // edx
 
  sub_4018F5(0x3BD696F4);
  return sub_401928(v0, 0x925DF53F);
}

查看sub_4018F5代码

int __stdcall sub_4018F5(int a1)
{
  int v1; // ecx
  _DWORD *v2; // esi
  int v3; // ST08_4
  int v4; // ST04_4
  int v5; // eax
  int result; // eax
  int v7; // edx

  v1 = a1;
  v2 = *(_DWORD **)(*(_DWORD *)(__readfsdword(0x30u) + 0xC) + 0x1C);
  do
  {
    v2 = (_DWORD *)*v2;
    v3 = v1;
    v4 = v2[6];// 获取dll名称;
    v5 = sub_401810(v2[6]);//计算名称的长度
    result = sub_4017B0(v4, 2 * v5);//计算一个校验和
    v1 = v3;
  }
  while ( result != v3 );//校验和与传入参数不等的时候,一直不停的校验
  v7 = v2[2];//相等获取dll基址
  return result;
}

看看v2是啥 readfsdword的微软官方解释为:

Microsoft Specific
Read memory from a location specified by an offset relative to the  beginning of the FS segment.

也就是从相对于FS段开头的偏移量指定的位置读取内存。FS段寄存器指向当前的TEB结构,在TEB编译0x30处是PEB指针,通过这个指针即可获得PED的地址。 所以,根据指定一个值获得动态库基址。将DLL基址和目标函数值传入sub_401928()函数

下面看sub_401928

int __stdcall sub_401928(int a1, int a2)
{
  _DWORD *v2; // esi
  int i; // eax
  int v4; // esi
  int v5; // eax
  int v7; // [esp-8h] [ebp-14h]
  int v8; // [esp+0h] [ebp-Ch]
  int v9; // [esp+4h] [ebp-8h]
  int v10; // [esp+8h] [ebp-4h]

  v2 = (_DWORD *)(a1 + *(_DWORD *)(a1 + *(_DWORD *)(a1 + 60) + 120));
  v10 = v2[7];
  v9 = v2[8];
  v8 = v2[9];
  for ( i = 0; ; i = v7 + 1 )
  {
    v4 = a1 + *(_DWORD *)(a1 + v9 + 4 * i);
    v7 = i;
    v5 = sub_401870(a1 + *(_DWORD *)(a1 + v9 + 4 * i));
    if ( sub_4017B0(v4, v5) == a2 )
      break;
  }
  return a1 + *(_DWORD *)(a1 + v10 + 4 * *(unsigned __int16 *)(a1 + v8 + 2 * v7));
}

该函数返回目标函数地址,目标函数为:GetDlgItemTextA()也就是即sub_00401A10

TLS函数分析完成, 函数 401A10 为真正的check函数,其调用 sub_401290函数,如果 sub_401290函数返回1,则起再次计算输入sn的hash值,并判断此hash值是否等于0x5634D252

2.判定函数

2.1 sub_401A10()函数

伪代码:

int __usercall sub_401A10@<eax>(int a1@<ecx>, int a2@<ebp>, int a3@<esi>)
{
  unsigned int v3; // et0
  int v4; // eax
  _BYTE *v5; // ecx
  unsigned int v7; // [esp-24h] [ebp-24h]
  int v8; // [esp-18h] [ebp-18h]

  v8 = a1;
  v3 = __readeflags();
  v7 = v3;
  dword_414800 = a3;
  dword_4147EC = *(_DWORD *)(a2 + 20);
  dword_4147F0 = malloc(dword_4147EC);
  dword_4147F4 = a2 + 16;
  memmove(dword_4147F0, *(const void **)(a2 + 16), dword_4147EC);//获取输入内容
  *(_DWORD *)(a2 - 16) = dword_4147F0;
  *(_DWORD *)(a2 - 20) = *(_DWORD *)(a2 - 16) + 1;
  do
    *(_BYTE *)(a2 - 21) = *(_BYTE *)(*(_DWORD *)(a2 - 16))++;
  while ( *(_BYTE *)(a2 - 21) );
  *(_DWORD *)(a2 - 28) = *(_DWORD *)(a2 - 16) - *(_DWORD *)(a2 - 20);
  if ( sub_401290((int)dword_4147F0, *(_DWORD *)(a2 - 28)) )//第一层检验
  {
    v4 = sub_401870(dword_4147F0);
    if ( sub_4017B0((int)dword_4147F0, v4) == 1446302290 )//第二层检验
    {
      v5 = off_414030;
      *(_WORD *)off_414030 = 0;
      v5[2] = 0;
      for ( *(_WORD *)(a2 - 4) = 0; *(signed __int16 *)(a2 - 4) < 8; ++*(_WORD *)(a2 - 4) )
        *((char *)off_414030 - *(signed __int16 *)(a2 - 4)) = (39 - *(unsigned __int16 *)(a2 - 4)) ^ byte_41401C[7 - *(signed __int16 *)(a2 - 4)];
      *(_WORD *)off_414034 = 0;
      for ( *(_WORD *)(a2 - 8) = 0; *(signed __int16 *)(a2 - 8) < 3; ++*(_WORD *)(a2 - 8) )
        *((char *)off_414034 - *(signed __int16 *)(a2 - 8)) = (34 - *(unsigned __int16 *)(a2 - 8)) ^ byte_414024[2 - *(signed __int16 *)(a2 - 8)];
    }
  }
  j___free_base(dword_4147F0);
  dword_4147F8 = (int (__thiscall *)(_DWORD))((char *)lpAddress + 32);
  sub_4019B0(lpAddress);
  for ( *(_DWORD *)(a2 - 12) = 0; *(_DWORD *)(a2 - 12) < 5; ++*(_DWORD *)(a2 - 12) )
    *((_BYTE *)lpAddress + *(_DWORD *)(a2 - 12) + 32) = byte_4147DC[*(_DWORD *)(a2 - 12)];
  sub_4019E0(lpAddress);
  dword_4147FC = 1;
  __writeeflags(v7);
  return dword_4147F8(v8);
}

2.2 sub_401290()函数

第一层检验

bool __cdecl sub_401290(int a1, int a2)
{
  byte_4147D0[0] = 4;
  byte_4147D0[1] = 1;
  byte_4147D0[2] = 3;
  byte_4147D0[3] = 7;
  byte_4147D0[4] = 2;
  byte_4147D0[5] = 5;
  byte_4147D0[6] = 8;
  byte_4147D0[7] = 6;
  byte_4147D0[8] = 0;
  return sub_4015B0(a1, a2);
}

有个数组,是413725860

bool __cdecl sub_4015B0(int a1, int a2)
{
  int i; // [esp+0h] [ebp-Ch]
  int v4; // [esp+8h] [ebp-4h]

  v4 = 0xCCCCCCCC;
  if ( a2 % 2 )
    return 0;
  for ( i = 0; i < a2; i += 2 )
  {
    if ( *(_BYTE *)(i + a1) == 'w' )
      v4 = 0;
    if ( *(_BYTE *)(i + a1) == 'd' )
      v4 = 1;
    if ( *(_BYTE *)(i + a1) == 's' )
      v4 = 2;
    if ( *(_BYTE *)(i + a1) == 'a' )
      v4 = 3;
    if ( !sub_401380(v4, *(char *)(i + a1 + 1) - 48) )
      return 0;
  }
  return byte_4147D0[0] == 1
      && byte_4147D0[1] == 2
      && byte_4147D0[2] == 3
      && byte_4147D0[3] == 4
      && byte_4147D0[4] == 5
      && byte_4147D0[5] == 6
      && byte_4147D0[6] == 7
      && byte_4147D0[7] == 8
      && !byte_4147D0[8];
}

对传入的序列号进行了判断输入w->0,d->1,s->2 a->3,传入的值经过一个变换,会变为123456780


char __cdecl sub_401380(int a1, int a2)
{
  char result; // al
  signed int i; // [esp+8h] [ebp-8h]
  signed int v4; // [esp+Ch] [ebp-4h]

  if ( !a2 )
    return 0;
  v4 = 0;
LABEL_4:
  if ( v4 >= 3 )
    return 0;
  for ( i = 0; ; ++i )
  {
    if ( i >= 3 )
    {
      ++v4;
      goto LABEL_4;
    }
    if ( byte_4147D0[3 * v4 + i] == a2 )
      break;
LABEL_6:
    ;
  }
  switch ( a1 )
  {
    case 0:
      if ( v4 )
      {
        if ( byte_4147D0[3 * (v4 - 1) + i] )
        {
          result = 0;
        }
        else
        {
          byte_4147D0[3 * (v4 - 1) + i] = byte_4147D0[3 * v4 + i];
          byte_4147D0[3 * v4 + i] = 0;
          result = 1;
        }
      }
      else
      {
        result = 0;
      }
      break;
    case 1:
      if ( i == 2 )
      {
        result = 0;
      }
      else if ( byte_4147D1[3 * v4 + i] )
      {
        result = 0;
      }
      else
      {
        byte_4147D1[3 * v4 + i] = byte_4147D0[3 * v4 + i];
        byte_4147D0[3 * v4 + i] = 0;
        result = 1;
      }
      break;
    case 2:
      if ( v4 == 2 )
      {
        result = 0;
      }
      else if ( byte_4147D0[3 * (v4 + 1) + i] )
      {
        result = 0;
      }
      else
      {
        byte_4147D0[3 * (v4 + 1) + i] = byte_4147D0[3 * v4 + i];
        byte_4147D0[3 * v4 + i] = 0;
        result = 1;
      }
      break;
    case 3:
      if ( i )
      {
        if ( byte_4147CF[3 * v4 + i] )
        {
          result = 0;
        }
        else
        {
          byte_4147CF[3 * v4 + i] = byte_4147D0[3 * v4 + i];
          byte_4147D0[3 * v4 + i] = 0;
          result = 1;
        }
      }
      else
      {
        result = 0;
      }
      break;
    default:
      goto LABEL_6;
  }
  return result;
}

对传入的参数在0 1 2 3,进行了判断,有w a s d 我们就就可以初步的推断是对表中的元素进行了移动。就可以初步的推断是对表中的元素进行了移动。大佬提示,是九宫格。

3.求解

总结起来,就是

4   1   3
7   2   5   d6
8   6   0
    
4   1   3
7   2   5   d8
8   0   6  
    
4   1   3
7   2   5   s7
0   8   6
    
4   1   3
0   2   5   s4
7   8   6
    
0   1   3
4   2   5   a1
7   8   6
    
1   0   3
4   2   5   w2
7   8   6
    
1   2   3
4   0   5   a5
7   8   6
    
1   2   3
4   5   0   w6
7   8   6
    
1   2   3
4   5   6  
7   8   0

序列号为d6d8s7s4a1w2a5w6,结果如图

4

完结


本文访问量: