• 【reverse】新160个CrackMe之116-REM-KeyGenME#10——脱壳、去背景音乐、识别反调试


    依赖

    IDA版本为7.7。

    PETools查看概况

    32位程序,Section: [TDC1], EP: 0x0001E730,有壳且暂时不知道是什么壳。

    这个exe需要手动脱壳、去背景音乐、有反调试,可惜算法较简单,勉强如参考链接1所说,“列入精品软件”。

    作者:hans774882968以及hans774882968以及hans774882968

    本文juejin:https://juejin.cn/post/7139215208513290253/

    本文csdn:https://blog.csdn.net/hans774882968/article/details/126684991

    本文52pojie:https://www.52pojie.cn/thread-1683892-1-1.html

    手动脱壳

    看了参考链接1,知道是upx壳,但upx命令脱壳失败。那么我们尝试手动脱壳,跟着参考链接2操作即可。Sections一般都是可读写的,如果出现了和参考链接2一样需要改Section Header可读写的情况,可用PETools点击Sections按钮去修改。

    去背景音乐

    参考链接1找到音乐播放函数的方法是:单步执行,执行到TDC0:0040108E call sub_4013D9的时候,发现这句指令执行时间比其他语句长,断定这是音乐播放的函数。x64dbg实测执行时间比其他语句时间长,但感受差别比较小,不容易注意到。

    我们用先进的IDA7.7就不需要这么麻烦了。

    1. 看到start函数的sub_4013D9非常显眼,又看到sub_4013D9出现了一句TDC0:0040143D 008 E8 BE 2C 00 00 call waveOutReset,因此sub_4013D9就是播放音乐的函数。
    2. 用IDA还可以这么定位:看imports标签页,能看到waveOutReset等和音频有关的函数,去找交叉引用就行。

    直接把TDC0:0040108E call sub_4013D9patch掉即可。

    分析

    发现有些代码变成了数据,但因为没有标红的代码,不像是插入了花指令。我们在x64dbg中查看汇编代码,发现x64dbg的识别非常成功。因此我们在IDA中不停地强转代码,直到看不到被识别为数据的代码为止。然后右键Create function,成功F5。

    DialogFunc非常滴显眼:

    INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, HDC hdc, LPARAM a4)
    {
      HICON IconA; // eax
      UINT v6; // eax
      int v7; // eax
    
      if ( a2 == 272 )
      {
        SetWindowTextA(hWnd, String);
        IconA = LoadIconA(hInstance, (LPCSTR)0x64);
        SendMessageA(hWnd, 0x80u, 1u, (LPARAM)IconA);
      }
      else
      {
        if ( a2 == 312 || a2 == 307 )
        {
          SetTextColor(hdc, 0xFFFFFFu);
          return SetBkMode(hdc, 16763904);
        }
        if ( a2 != 513 )
        {
          if ( a2 == 0x111 )
          {
            if ( hdc == (HDC)1001 )
            {
              v6 = 5 * GetDlgItemTextA(hWnd, 2001, dword_40622C, 51);
              if ( v6 > 0xFA || v6 < 0x1E )
              {
                SetDlgItemTextA(hWnd, 2002, aNameMustBeBetw);
                return 0;
              }
              dword_40625F = GetDlgItemInt(hWnd, 2002, 0, 0);
              v7 = sub_4012A8(dword_40622C);
              if ( byte_406263 != 1 )
              {
                if ( !v7 )
                {
                  SetDlgItemTextA(hWnd, 2002, aCongratulation);
                  return 0;
                }
                if ( loc_40121C != 0xCC && loc_401223 != 0xCC )
                {
                  SetDlgItemTextA(hWnd, 2002, aSorryThatIsInc);
                  return 0;
                }
              }
              SetDlgItemTextA(hWnd, 2002, aDebuggerDetect);
              return 0;
            }
            if ( hdc == (HDC)1002 )
            {
              MessageBoxA(hWnd, Text, Caption, 0x20u);
              return 0;
            }
            if ( hdc != (HDC)1003 )
              return 0;
          }
          else if ( a2 != 16 )
          {
            return 0;
          }
          EndDialog(hWnd, 0);
          return 0;
        }
        ReleaseCapture();
        SendMessageA(hWnd, 0xA1u, 2u, 0);
      }
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    dword_40622Cdword_40625F分别是你输入的NameSerial

    有反调试哦

    SetDlgItemTextA(hWnd, 2002, aDebuggerDetect);告诉我们全局变量byte_406263是用来反调试的。

    关键函数只有sub_4012A8,里面耦合了一段反调试逻辑。sub_4012A8sp-analysis failed,但因为我们用的是先进的IDA7.7,不用管它直接F5!但反汇编结果仅供参考了。

    void __stdcall sub_4012A8(_BYTE *a1)
    {
      int v1; // eax
      int v2; // ecx
      int v4; // ecx
    
      if ( loc_4012B4 == 0xCC )
        goto LABEL_9;
      v1 = 0;
      v2 = 0;
      if ( loc_4012D4 == 0xCC )
        goto LABEL_9;
      while ( *a1 )
      {
        LOBYTE(v1) = *a1;
        v2 = __ROL4__(v1 + v2, 8);
        ++a1;
      }
      v4 = ((v2 ^ 2) - 80) ^ 0x1337;
      LOWORD(v4) = SystemTime.wYear + v4;
      if ( loc_4012FA == 0xCC )
      {
    LABEL_9:
        byte_406263 = 1;
      }
      else if ( dword_40625F == v4 )
      {
        byte_406263 = 0;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    那算法很明显了,根据你输入的Name算出一个int,要求等于你输入的int类型的Serial。最后还需要注意一个点:dword_40625F = GetDlgItemInt(hWnd, 2002, 0, 0);,第4个参数为0表示返回unsigned int,1表示返回int(参考链接3)。因此我们算出的int类型的Serial要最后额外加一步:转为unsigned int

    代码

    1. 获取系统时间:参考链接4。
    2. 用IDA安装目录下可找到的defs.h,赋能cpp注册机,打出一套组合👊。
    #include 
    #include 
    #include "defs.h"
    using namespace std;
    #define rep(i,a,b) for(int i = (a);i <= (b);++i)
    #define re_(i,a,b) for(int i = (a);i < (b);++i)
    #define dwn(i,a,b) for(int i = (a);i >= (b);--i)
    
    void dbg() {
        puts ("");
    }
    template<typename T, typename... R>void dbg (const T &f, const R &... r) {
        cout << f << " ";
        dbg (r...);
    }
    template<typename Type>inline void read (Type &xx) {
        Type f = 1;
        char ch;
        xx = 0;
        for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
        for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
        xx *= f;
    }
    void read() {}
    template<typename T, typename ...R>void read (T &x, R &...r) {
        read (x);
        read (r...);
    }
    
    int solve (string inpName) {
        int v1 = 0, v2 = 0;
        for (auto c : inpName) {
            LOBYTE (v1) = c;
            v2 = __ROL4__ (v1 + v2, 8);
        }
        int v4 = ( (v2 ^ 2) - 80) ^ 0x1337;
        SYSTEMTIME st;
        GetSystemTime (&st);
        LOWORD (v4) = st.wYear + v4;
        return v4;
    }
    
    int main() {
        string inpName;
        unsigned int ans;
        inpName = "hans acmer";
        ans = solve (inpName);
        dbg (inpName, ans);
        inpName = "Hans774882968";
        ans = solve (inpName);
        dbg (inpName, ans);
        inpName = "scuctf";
        ans = solve (inpName);
        dbg (inpName, ans);
        inpName = "hans00";
        ans = solve (inpName);
        dbg (inpName, ans);
        inpName = "HANS774882968";
        ans = solve (inpName);
        dbg (inpName, ans);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    参考资料

    1. https://www.bilibili.com/video/BV1FF41137c7
    2. x64dbg手动脱upx壳:https://zhuanlan.zhihu.com/p/34263050
    3. MFC中的GetDlgItemInt方法:https://blog.csdn.net/lihui126/article/details/42489267
    4. GetSystemTime function:https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime
  • 相关阅读:
    十大排序:快速排序
    springboot使用配置ElasticSearch完整保姆全教程
    MapStruct简单入门
    FinClip PC 终端支持更新,现已兼容抖音与支付宝小程序
    9.21号作业
    分库分表
    Golang【Web 入门】 08 集成 Gorilla Mux
    四、卷积、转置卷积(上卷积)大小计算公式
    SpringMVC
    Jhipster介绍和使用
  • 原文地址:https://blog.csdn.net/hans774882968/article/details/126684991