看雪-亦步亦趋 两种解法(动调OD/静调IDA)题解

程序基本逻辑

直接执行exe文件后按照题目输入用户名123456,然后随便输入一个能够辨别的数字我输入的是1111111111然后运行。

提示如下(因为图传有速率限制经可能不用图片或者用小图)

提示词可以作为关键字,基本逻辑应该是

  1. 获取ID
  2. 某些操作
  3. 获取注册码
  4. 某些操作
  5. 验证

解法一动调:本题最快的解法

通过关键字找到对应行,并向上找到关键函数,然后发现调用关键函数前push了两个string猜测可能是相关的。然后在条件调转处下了一个断点(或者上面的那个关键call此处按照程序基本逻辑应该就是获取注册码后进行的比对操作了),然后F9运行,输入

  • 123456
  • 111111111111(随意输入为了好区分)

再按F9运行到断点处发现string1string2被赋值,并且在关键callpush保存了各自的值分别是

  • 894904543(应该就是真正的注册码)
  • 111111111111(我输入的)

因为题目说flag就是用户名为123456的注册码那么这就是答案了即flag{894904543}

解法二静调:注册机

事先说明,这题使用这种解法会比较复杂

IDA打开,对应错误提示按shift+F12搜索关键字找到错误提示的内容点进去,找到对应的主函数,代码如下

INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
  HICON v4; // eax
  int v6; // edx
  unsigned int v7; // esi
  int v8; // eax
  char v9; // cl
  char v10; // bl
  int v11; // edi
  unsigned int v12; // ecx
  char *v13; // esi
  char v14; // al
  char v15; // bl
  int v16; // esi
  unsigned int v17; // edi
  char v18; // al
  char v19; // cl
  int v20; // edi
  unsigned int v21; // ecx
  char *v22; // esi
  char v23; // al
  char v24; // bl
  unsigned int v25; // eax
  _BYTE *v26; // esi
  char v27; // bl
  unsigned int v28; // eax
  int v29; // ebx
  unsigned __int64 v30; // rtt
  int v31; // eax
  int v32; // ebx
  WPARAM v33; // [esp-4h] [ebp-4h]
  int v34; // [esp-4h] [ebp-4h]
  WPARAM v35; // [esp-4h] [ebp-4h]
  int savedregs; // [esp+0h] [ebp+0h]

  switch ( a2 )
  {
    case 0x110u:
      v4 = LoadIconA(hModule, (LPCSTR)0x1F6);
      SendMessageA(hWnd, 0x80u, 0, (LPARAM)v4);
      SendDlgItemMessageA(hWnd, 100, 0xC5u, 0x55u, 0);
      sub_401441(hMem);
      break;
    case 0x111u:
      if ( !HIWORD(a3) )
      {
        if ( (_WORD)a3 == 1004 )
        {
          v33 = a3;
          if ( GetDlgItemTextA(hWnd, 1002, String, 512) <= 3 )
            return MessageBoxA(hWnd, Text, Caption, 0x10u);
          lstrlenA(String);
          v7 = 0;
          v8 = 0;
          do
          {
            v9 = *(_BYTE *)(v7 + v6);
            v10 = byte_406328[v8++] ^ *(_BYTE *)(v7 + v6);
            *(_BYTE *)(v6 + v7) = v10;
            byte_406327[v8] = v9;
            if ( v8 == 5 )
              v8 = 0;
            ++v7;
          }
          while ( v7 < (unsigned int)&savedregs );
          v11 = 0;
          v12 = 0;
          if ( &savedregs )
          {
            do
            {
              v13 = (char *)&savedregs - v12 - 1;
              v14 = v13[v6];
              v15 = v14 ^ byte_40632D[v11++];
              v13[v6] = v15;
              byte_40632C[v11] = v14;
              if ( v11 == 5 )
                v11 = 0;
              ++v12;
            }
            while ( v12 < (unsigned int)&savedregs );
          }
          v16 = 0;
          v17 = 0;
          if ( &savedregs )
          {
            do
            {
              v18 = *(_BYTE *)(v6 + v17);
              v19 = v18 ^ byte_406332[v16++];
              *(_BYTE *)(v6 + v17) = v19;
              byte_406331[v16] = v18;
              if ( v16 == 5 )
                v16 = 0;
              ++v17;
            }
            while ( v17 < (unsigned int)&savedregs );
          }
          v20 = 0;
          v21 = 0;
          if ( &savedregs )
          {
            do
            {
              v22 = (char *)&savedregs - v21 - 1;
              v23 = v22[v6];
              v24 = v23 ^ byte_406337[v20++];
              v22[v6] = v24;
              byte_406336[v20] = v23;
              if ( v20 == 5 )
                v20 = 0;
              ++v21;
            }
            while ( v21 < (unsigned int)&savedregs );
          }
          v25 = 0;
          dword_406345 = 0;
          if ( &savedregs )
          {
            do
            {
              v26 = (char *)&dword_406345 + (v25 & 3);
              v27 = *(_BYTE *)(v6 + v25++) + *v26;
              *v26 = v27;
            }
            while ( v25 < (unsigned int)&savedregs );
          }
          v28 = dword_406345;
          v29 = 0;
          do
          {
            v30 = v28;
            v28 /= 0xAu;
            String1[v29++] = v30 % 0xA + 48;
          }
          while ( v28 );
          v31 = lstrlenA(String1);
          v32 = 0;
          do
            String2[v32++] = byte_406548[v31--];
          while ( v31 );
          lstrcpyA(String1, String2);
          GetDlgItemTextA(hWnd, 100, byte_406949, 512);
          if ( lstrcmpA(byte_406949, String1) )
            MessageBoxA(hWnd, aNopeThatsNotIt, Caption, 0x10u);
          else
            MessageBoxA(hWnd, aYepThatsTheRig, aGoodBoy, 0x40u);
          RtlZeroMemory(String1, 512);
          RtlZeroMemory(String, 512);
          RtlZeroMemory(String2, 512);
          qmemcpy(byte_406327, &unk_406311, 0x16u);
        }
        else
        {
          if ( (_WORD)a3 == 1006 )
          {
            v34 = sub_401441(0);
            ExitProcess(0);
          }
          if ( (_WORD)a3 == 1005 )
          {
            v35 = a3;
            MessageBoxA(hWnd, aCodedByLafarge, aAbout, 0x40u);
          }
        }
      }
      break;
    case 0x10u:
      sub_401441(0);
      EndDialog(hWnd, 0);
      break;
  }
  return 0;
}

关注两次获取输入框的函数调用,可以发现主要是获取用户名后对用户名进行加密生成了注册码,然后第二次获取的是你输入的注册码(byte_406949)和正确的(即String1)比较来验证。

因此关键的就是这个String1是怎么加密用户名得到的了

结合汇编代码

DB定义字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加1

DW定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2

DD定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4

.data:00406328 ; char byte_406328[4]
.data:00406328 byte_406328     db 0AAh                 ; DATA XREF: DialogFunc+CB↑r
.data:00406329                 db  89h
.data:0040632A                 db 0C4h
.data:0040632B                 db 0FEh
.data:0040632C ; char byte_40632C[]
.data:0040632C byte_40632C     db 46h                  ; DATA XREF: DialogFunc+103↑w
.data:0040632D ; char byte_40632D[4]
.data:0040632D byte_40632D     db 78h                  ; DATA XREF: DialogFunc:loc_4011A3↑r
.data:0040632E                 db 0F0h
.data:0040632F                 db 0D0h
.data:00406330                 db    3
.data:00406331 ; char byte_406331[]
.data:00406331 byte_406331     db 0E7h                 ; DATA XREF: DialogFunc+12C↑w
.data:00406332 ; char byte_406332[4]
.data:00406332 byte_406332     db 0F7h                 ; DATA XREF: DialogFunc+120↑r
.data:00406333                 db 0FDh
.data:00406334                 db 0F4h
.data:00406335                 db 0E7h
.data:00406336 ; char byte_406336[]
.data:00406336 byte_406336     db 0B9h                 ; DATA XREF: DialogFunc+15A↑w
.data:00406337 ; char byte_406337[6]
.data:00406337 byte_406337     db 0B5h                 ; DATA XREF: DialogFunc:loc_4011FA↑r
.data:00406338                 db  1Bh
.data:00406339                 db 0C9h
.data:0040633A                 db  50h ; P
.data:0040633B                 db  73h ; s
.data:0040633C                 db    0

可以得到一些数组被赋予的初始值

char byte_406328[] = {0xaa,0x89,0xc4,0xfe};
char byte_40632C[] = {0x46};
char byte_40632D[] = {0x78,0xf0,0xd0,0x03};
char byte_406331[] = {0xe7};
char byte_406332[] = {0xf7,0xfd,0xf4,0xe7};
char byte_406336[] = {0xb9};
char byte_406337[] = {0xb5,0x1b,0xc9,0x50,0x73,0x00};

关键加密部分解读如下

  if ( GetDlgItemTextA(hWnd, 1002, String, 512) <= 3 )
    return MessageBoxA(hWnd, Text, Caption, 0x10u);
  lstrlenA(String);//String为输入的注册码
  v7 = 0;
  v8 = 0;
  do
  {
    v9 = *(_BYTE *)(v7 + v6);
    v10 = byte_406328[v8++] ^ *(_BYTE *)(v7 + v6);
    *(_BYTE *)(v6 + v7) = v10;
    byte_406327[v8] = v9;
    if ( v8 == 5 )
      v8 = 0;
    ++v7;
  }
  while ( v7 < (unsigned int)&savedregs );
  v11 = 0;
  v12 = 0;
  if ( &savedregs )
  {
    do
    {
      v13 = (char *)&savedregs - v12 - 1;
      v14 = v13[v6];
      v15 = v14 ^ byte_40632D[v11++];
      v13[v6] = v15;
      byte_40632C[v11] = v14;
      if ( v11 == 5 )
        v11 = 0;
      ++v12;
    }
    while ( v12 < (unsigned int)&savedregs );
  }
  v16 = 0;
  v17 = 0;
  if ( &savedregs )
  {
    do
    {
      v18 = *(_BYTE *)(v6 + v17);
      v19 = v18 ^ byte_406332[v16++];
      *(_BYTE *)(v6 + v17) = v19;
      byte_406331[v16] = v18;
      if ( v16 == 5 )
        v16 = 0;
      ++v17;
    }
    while ( v17 < (unsigned int)&savedregs );
  }
  v20 = 0;
  v21 = 0;
  if ( &savedregs )
  {
    do
    {
      v22 = (char *)&savedregs - v21 - 1;
      v23 = v22[v6];
      v24 = v23 ^ byte_406337[v20++];
      v22[v6] = v24;
      byte_406336[v20] = v23;
      if ( v20 == 5 )
        v20 = 0;
      ++v21;
    }
    while ( v21 < (unsigned int)&savedregs );
  }
  v25 = 0;
  dword_406345 = 0;
  if ( &savedregs )
  {
    do
    {
      v26 = (char *)&dword_406345 + (v25 & 3);
      v27 = *(_BYTE *)(v6 + v25++) + *v26;
      *v26 = v27;
    }
    while ( v25 < (unsigned int)&savedregs );
  }
  v28 = dword_406345;
  v29 = 0;
  do
  {
    v30 = v28;
    v28 /= 0xAu;
    String1[v29++] = v30 % 0xA + 48;
  }
  while ( v28 );
  v31 = lstrlenA(String1);
  v32 = 0;
  do
    String2[v32++] = byte_406548[v31--];
  //String2就是答案
  while ( v31 );
  lstrcpyA(String1, String2);
  //String1就是答案
Last modification:January 23, 2021
如果觉得我的文章对你有用,请随意赞赏。咖啡(12RMB)进度+100%,一块巧克力(1RMB)进度+6%。
(赞赏请备注你的名称哦!后台记录中来自理工小菜狗)