[原创] MFC逆向之CrackMe Level3 过反调试 + 写注册机(一)
2023-3-19 04:33:20 Author: bbs.pediy.com(查看原文) 阅读量:15 收藏

前言

各位大佬早上好,我是你们好朋友旺仔,今天我来分享一下,过反调试的方法以及使用IDA还原代码 + 写注册机的过程

由于内容太多,我准备分为两个帖子写,这个帖子主要是写IDA还原代码,下一个帖子是写反调试的分析以及过反调试和异常

这个CrackMe Level3是一个朋友发我的,我也不知道他在哪里弄的,我感觉挺好玩的,对反调试 异常 以及代码还原的学习有一些帮助

准备环节

反调试原文链接:https://blog.csdn.net/liuhaidon1992/article/details/103888101

C++异常处理的学习笔记:https://bbs.kanxue.com/thread-273332.htm

IDA如何使用:https://www.jianshu.com/p/ee0fcf93c8e7

调试器:X64和OD

反编译工具:IDA

PE工具:Detect It Easy

反调试插件:OD的StrongOD和虫子的ScyllaHide

ARK工具:Pchunter

MFC工具:MfcSpy

初步分析

第一步先看看PE文件 工具说:软件是使用2008的MFC写的

上面得知是MFC写的软件,我们可以利用MFC的RTTI(动态类型识别)获取信息,如果不是MFC写的,我们可以运行一下软件看看,有没有报错信息

当然这个软件,输入注册码点击注册没有任何提示

打开调试器附加看看字符串,结果软件会闪退,无法附加,估计是有反调试,遇到这种情况我有三种方式可以解决

第一种:静态分析解决反调试

第二种:使用ARK工具把软件暂停,然后在用调试器附加

第三种:直接使用调试器打开程序,让软件断到入口或者系统断点

我们先使用第二种吧,因为X32调试器对中文字符串的支持不是很好,我没有装插件,所以先用OD看看字符串吧

验证函数中有一个异常 OD的反汇编已经出现了BUG,可以使用删除分析,我这里就不删除了 直接打开IDA F5吧

可以看到IDA的F5也被废掉了的

虽然不能F5 但是我们可以很清晰的看到,两个GetWindowText 估计一个是获取用户名 一个是获取密码

至于为什么不能F5 是因为有一个jmp [ebx + 0xXXX] 这样的代码 IDA在解析代码的时候 它不知道跳到那里去 导致无法解析

我们直接把这个Jmp [ebp + var_8A8] 给nop掉就好了 然后删除这个函数的分析 接着在把这个函数分析为代码即可

我们可以向上看,在OD反汇编的时候也是到这个位置 反汇编失败了

下面是IDA F5后的代码 很明显 这IDA还原出来的根本不能用,这是需要我们进行修理变量的,另外我们给它重新命名一下

int __thiscall sub_401810(CWnd *this)
{
  CWnd *DlgItem; // eax
  CWnd *v2; // eax
  int result; // eax
  char *v4; // [esp+10h] [ebp-8C8h]
  CHAR *v5; // [esp+14h] [ebp-8C4h]
  unsigned int v6; // [esp+18h] [ebp-8C0h]
  unsigned __int8 v8; // [esp+21h] [ebp-8B7h]
  unsigned __int8 v9; // [esp+22h] [ebp-8B6h]
  unsigned __int8 v10; // [esp+23h] [ebp-8B5h]
  int k; // [esp+24h] [ebp-8B4h]
  int j; // [esp+28h] [ebp-8B0h]
  int i; // [esp+2Ch] [ebp-8ACh]
  int v14; // [esp+34h] [ebp-8A4h]
  int v15; // [esp+34h] [ebp-8A4h]
  char v16; // [esp+38h] [ebp-8A0h] BYREF
  int v17[6]; // [esp+39h] [ebp-89Fh] BYREF
  char v18; // [esp+51h] [ebp-887h]
  char v19; // [esp+53h] [ebp-885h]
  char v20; // [esp+54h] [ebp-884h]
  int v21; // [esp+55h] [ebp-883h]
  int v22[5]; // [esp+59h] [ebp-87Fh] BYREF
  char v23; // [esp+6Dh] [ebp-86Bh]
  int v24; // [esp+70h] [ebp-868h]
  char v25; // [esp+74h] [ebp-864h]
  int v26; // [esp+75h] [ebp-863h]
  int v27; // [esp+79h] [ebp-85Fh]
  int v28; // [esp+7Dh] [ebp-85Bh]
  int v29; // [esp+81h] [ebp-857h]
  int v30; // [esp+85h] [ebp-853h]
  int v31; // [esp+89h] [ebp-84Fh]
  char v32; // [esp+8Dh] [ebp-84Bh]
  char v33; // [esp+93h] [ebp-845h]
  char v34; // [esp+94h] [ebp-844h]
  int v35; // [esp+95h] [ebp-843h]
  int v36[5]; // [esp+99h] [ebp-83Fh] BYREF
  char v37; // [esp+ADh] [ebp-82Bh]
  CHAR String[1028]; // [esp+B0h] [ebp-828h] BYREF
  int v39; // [esp+4B4h] [ebp-424h]
  CHAR v40[1028]; // [esp+4B8h] [ebp-420h] BYREF
  CPPEH_RECORD ms_exc; // [esp+8C0h] [ebp-18h]

  MEMORY[0] = 6530;
  ms_exc.registration.TryLevel = -2;
  v33 = sub_402D60();
  v14 = sub_402C60(v33);
  sub_402750(v14);
  v26 = 0;
  v27 = 0;
  v28 = 0;
  v29 = 0;
  v30 = 0;
  v31 = 0;
  v32 = 0;
  v35 = 0;
  memset(v36, 0, sizeof(v36));
  v37 = 0;
  v21 = 0;
  memset(v22, 0, sizeof(v22));
  v23 = 0;
  v25 = 48;
  v34 = 66;
  v20 = 98;
  for ( i = 1; i < 26; ++i )
  {
    *(&v25 + i) = *((_BYTE *)&v24 + i + 3) + 1;
    *(&v34 + i) = *(&v33 + i) + 1;
    *(&v20 + i) = *(&v19 + i) + 1;
  }
  CWnd::UpdateData(this, 1);
  memset(String, 0, 1024);
  memset(v40, 0, 1024);
  ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
  ms_exc.exc_ptr = (EXCEPTION_POINTERS *)String;
  DlgItem = CWnd::GetDlgItem(this, 1000);
  CWnd::GetWindowTextA(DlgItem, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
  ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
  ms_exc.exc_ptr = (EXCEPTION_POINTERS *)v40;
  v2 = CWnd::GetDlgItem(this, 1001);
  result = CWnd::GetWindowTextA(v2, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
  if ( String[7] )
  {
    result = String[8];
    if ( !String[8] && v40[23] && !v40[24] )
    {
      v39 = 16;
      v24 = 32;
      for ( j = 0; j < 8; ++j )
      {
        String[j] ^= j;
        String[j] ^= *(_BYTE *)(j + v39);
        String[j] ^= *(_BYTE *)(j + v24);
      }
      v16 = 0;
      memset(v17, 0, sizeof(v17));
      v18 = 0;
      for ( k = 0; k < 8; ++k )
      {
        v10 = (unsigned __int8)(String[k] & 0xE0) / 32;
        v8 = (String[k] & 0x1C) / 4;
        v9 = String[k] & 3;
        if ( k % 3 == 2 )
        {
          *(&v16 + 3 * k) = *(&v25 + v9);
          *((_BYTE *)v17 + 3 * k) = *((_BYTE *)v36 + v10 + 3);
          *((_BYTE *)v17 + 3 * k + 1) = *((_BYTE *)&v22[2] + v8 + 3);
        }
        if ( k % 3 == 1 )
        {
          *(&v16 + 3 * k) = *((_BYTE *)&v36[2] + v10 + 3);
          *((_BYTE *)v17 + 3 * k) = *((_BYTE *)v22 + v8 + 3);
          *((_BYTE *)v17 + 3 * k + 1) = *(&v25 + v9);
        }
        if ( !(k % 3) )
        {
          *(&v16 + 3 * k) = *((_BYTE *)&v36[2] + v8 + 3);
          *((_BYTE *)v17 + 3 * k) = *((_BYTE *)v22 + v9 + 3);
          *((_BYTE *)v17 + 3 * k + 1) = *(&v25 + v10);
        }
      }
      sub_401790();
      v33 = sub_402D90();
      v15 = sub_402C60(v33);
      sub_4028A0(v15);
      v6 = 24;
      v5 = v40;
      v4 = &v16;
      while ( v6 >= 4 )
      {
        result = (int)v5;
        if ( *(_DWORD *)v4 != *(_DWORD *)v5 )
          return result;
        v6 -= 4;
        v5 += 4;
        v4 += 4;
      }
      return CWnd::MessageBoxA(this, &Text, 0, 0);
    }
  }
  return result;
}

1.根据循环可以知道对应数组大小和数组首地址

2.根据MemSet也可以知道对应数组的大小和数组首地址

3.根据GetWindowsTest也可以知道数组的大小

修正变量要结合代码怎么去操作这个变量的 根据它的逻辑来猜测大小

这是变量重命名后的汇编代码

经过修理后的IDA F5代码 可以看到基本上很清晰了 效果跟源代码应该很接近

int __thiscall sub_401810(CWnd *this)
{
  CWnd *DlgItem; // eax
  CWnd *v2; // eax
  int result; // eax
  char *pFinalKey_1; // [esp+10h] [ebp-8C8h]
  char *pGetWindowTextString1_1; // [esp+14h] [ebp-8C4h]
  unsigned int Var24_1; // [esp+18h] [ebp-8C0h]
  unsigned __int8 Var8_1; // [esp+21h] [ebp-8B7h]
  unsigned __int8 Var9_1; // [esp+22h] [ebp-8B6h]
  unsigned __int8 Var10_1; // [esp+23h] [ebp-8B5h]
  int K; // [esp+24h] [ebp-8B4h]
  int J; // [esp+28h] [ebp-8B0h]
  int I; // [esp+2Ch] [ebp-8ACh]
  int Unknown2_1; // [esp+34h] [ebp-8A4h]
  int unknown1; // [esp+34h] [ebp-8A4h]
  char FinalKey_1[26]; // [esp+38h] [ebp-8A0h] BYREF
  char SecretKey3[28]; // [esp+54h] [ebp-884h] BYREF
  int v19; // [esp+70h] [ebp-868h]
  char SecretKey1[31]; // [esp+74h] [ebp-864h] BYREF
  char Unknown3_1; // [esp+93h] [ebp-845h]
  char SecretKey2[28]; // [esp+94h] [ebp-844h] BYREF
  char GetWindowTextString2_1[1028]; // [esp+B0h] [ebp-828h] BYREF
  int v24; // [esp+4B4h] [ebp-424h]
  char GetWindowTextString1[1028]; // [esp+4B8h] [ebp-420h] BYREF
  CPPEH_RECORD ms_exc; // [esp+8C0h] [ebp-18h]

  MEMORY[0] = 6530;
  ms_exc.registration.TryLevel = -2;
  Unknown3_1 = sub_402D60();
  Unknown2_1 = sub_402C60(&dword_42FA04, Unknown3_1);
  sub_402750(Unknown2_1);
  memset(&SecretKey1[1], 0, 25);
  memset(&SecretKey2[1], 0, 25);
  memset(&SecretKey3[1], 0, 25);
  SecretKey1[0] = 48;
  SecretKey2[0] = 66;
  SecretKey3[0] = 98;
  for ( I = 1; I < 26; ++I )
  {
    SecretKey1[I] = SecretKey1[I - 1] + 1;
    SecretKey2[I] = SecretKey2[I - 1] + 1;
    SecretKey3[I] = SecretKey3[I - 1] + 1;
  }
  CWnd::UpdateData(this, 1);
  memset(GetWindowTextString2_1, 0, 1024);
  memset(GetWindowTextString1, 0, 1024);
  ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
  ms_exc.exc_ptr = (EXCEPTION_POINTERS *)GetWindowTextString2_1;
  DlgItem = CWnd::GetDlgItem(this, 1000);
  CWnd::GetWindowTextA(DlgItem, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
  ms_exc.registration.Next = (struct _EH3_EXCEPTION_REGISTRATION *)1024;
  ms_exc.exc_ptr = (EXCEPTION_POINTERS *)GetWindowTextString1;
  v2 = CWnd::GetDlgItem(this, 1001);
  result = CWnd::GetWindowTextA(v2, (LPSTR)ms_exc.exc_ptr, (int)ms_exc.registration.Next);
  if ( GetWindowTextString2_1[7] )
  {
    result = GetWindowTextString2_1[8];
    if ( !GetWindowTextString2_1[8] && GetWindowTextString1[23] && !GetWindowTextString1[24] )
    {
      v24 = 16;
      v19 = 32;
      for ( J = 0; J < 8; ++J )
      {
        GetWindowTextString2_1[J] ^= J;
        GetWindowTextString2_1[J] ^= *(_BYTE *)(J + v24);
        GetWindowTextString2_1[J] ^= *(_BYTE *)(J + v19);
      }
      memset(FinalKey_1, 0, sizeof(FinalKey_1));
      for ( K = 0; K < 8; ++K )
      {
        Var10_1 = (unsigned __int8)(GetWindowTextString2_1[K] & 0xE0) / 32;
        Var8_1 = (GetWindowTextString2_1[K] & 0x1C) / 4;
        Var9_1 = GetWindowTextString2_1[K] & 3;
        if ( K % 3 == 2 )
        {
          FinalKey_1[3 * K] = SecretKey1[Var9_1];
          FinalKey_1[3 * K + 1] = SecretKey2[Var10_1 + 8];
          FinalKey_1[3 * K + 2] = SecretKey3[Var8_1 + 16];
        }
        if ( K % 3 == 1 )
        {
          FinalKey_1[3 * K] = SecretKey2[Var10_1 + 16];
          FinalKey_1[3 * K + 1] = SecretKey3[Var8_1 + 8];
          FinalKey_1[3 * K + 2] = SecretKey1[Var9_1];
        }
        if ( !(K % 3) )
        {
          FinalKey_1[3 * K] = SecretKey2[Var8_1 + 16];
          FinalKey_1[3 * K + 1] = SecretKey3[Var9_1 + 8];
          FinalKey_1[3 * K + 2] = SecretKey1[Var10_1];
        }
      }
      sub_401790();
      Unknown3_1 = sub_402D90();
      unknown1 = sub_402C60(&dword_42FA04, Unknown3_1);
      sub_4028A0(unknown1);
      Var24_1 = 24;
      pGetWindowTextString1_1 = GetWindowTextString1;
      pFinalKey_1 = FinalKey_1;
      while ( Var24_1 >= 4 )
      {
        result = (int)pGetWindowTextString1_1;
        if ( *(_DWORD *)pFinalKey_1 != *(_DWORD *)pGetWindowTextString1_1 )
          return result;
        Var24_1 -= 4;
        pGetWindowTextString1_1 += 4;
        pFinalKey_1 += 4;
      }
      return CWnd::MessageBoxA(this, &Text, 0, 0);
    }
  }
  return result;
}

我们可以看到上面的代码中还是有两个变量的值是错误的 一个是V24 一个是V19 为什么是错误的呢?

我们可以结合下面的循环代码,下面代码中用到V24 是*(BYTE*)(J+V24);    用到V19 是*(BYTE*)(J+V19);

这很明显是一个数组 大小是8个字节

看下面代码中 V19和V24是来源于AbnormalVariable这个变量的值 + 16和+32的位置的

这也就是下面代码中V19和V24数组的值的来源

最终的C代码 算法还原完成

int main()
{
    char SecretKey1[28];
    char SecretKey2[28];
    char SecretKey3[28];
    memset(SecretKey1,0,sizeof(SecretKey1));
    memset(SecretKey2,0,sizeof(SecretKey2));
    memset(SecretKey3,0,sizeof(SecretKey3));

    SecretKey1[0] = 48;
    SecretKey2[0] = 66;
    SecretKey3[0] = 98;
    for (int I = 1; I < 26; ++I)
    {
        SecretKey1[I] = SecretKey1[I - 1] + 1;
        SecretKey2[I] = SecretKey2[I - 1] + 1;
        SecretKey3[I] = SecretKey3[I - 1] + 1;
    }

    char GetWindowTextString1[1024];
    char GetWindowTextString2_1[1024];

    memset(GetWindowTextString1,0,1024);
    memset(GetWindowTextString2_1, 0, 1024);

    strcpy(GetWindowTextString2_1,"12345678");

    char v24[24] = { 0x00,0x00,0x88,0x85,0xBB,0xF7,0xFF,0xFF,0x0F,0xB6,0x8D,0xBB,0xF7,0xFF,0xFF,0xB8,0x04,0xFA,0x42,0x00,0xE8,0xAD };
    char v19[24] = { 0xB8,0x04,0xFA,0x42,0x00,0xE8,0xAD,0x13,0x00,0x00,0x89,0x85,0x5C,0xF7,0xFF,0xFF,0x8B,0x95,0x5C,0xF7,0xFF,0xFF };
    for (int J = 0; J < 8; ++J)
    {
        GetWindowTextString2_1[J] ^= J;
        GetWindowTextString2_1[J] ^= v24[J];
        GetWindowTextString2_1[J] ^= v19[J];
    }

    char FinalKey_1[26];
    memset(FinalKey_1,0,sizeof(FinalKey_1));
    for (int K = 0; K < 8; ++K)
    {
        char Var10_1 = (unsigned __int8)(GetWindowTextString2_1[K] & 0xE0) / 32;
        char Var8_1 = (GetWindowTextString2_1[K] & 0x1C) / 4;
        char Var9_1 = GetWindowTextString2_1[K] & 3;
        if (K % 3 == 2)
        {
            FinalKey_1[3 * K] = SecretKey1[Var9_1];
            FinalKey_1[3 * K + 1] = SecretKey2[Var10_1 + 8];
            FinalKey_1[3 * K + 2] = SecretKey3[Var8_1 + 16];
        }
        if (K % 3 == 1)
        {
            FinalKey_1[3 * K] = SecretKey2[Var10_1 + 16];
            FinalKey_1[3 * K + 1] = SecretKey3[Var8_1 + 8];
            FinalKey_1[3 * K + 2] = SecretKey1[Var9_1];
        }
        if (!(K % 3))
        {
            FinalKey_1[3 * K] = SecretKey2[Var8_1 + 16];
            FinalKey_1[3 * K + 1] = SecretKey3[Var9_1 + 8];
            FinalKey_1[3 * K + 2] = SecretKey1[Var10_1];
        }
    }

    //FinalKey_1 = 0x00aff3f8 "Tk4So33LrVj7Vl20KuRm3Xn3"
    system("pause");
    return 0;
}
用户名:12345678
注册码:Tk4So33LrVj7Vl20KuRm3Xn3

Windows开发不完全指南

最后于 7小时前 被旺仔_小可爱编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-276536.htm
如有侵权请联系:admin#unsafe.sh