[160CrackMe]aLoNg3x.1

点击About - Help之后可以发现这个游戏的规则就是输入正确的Username和Codice,让按钮控件OK(无法click)和Cancella消失(可以click),然后显示出被其遮挡的logo
在这里插入图片描述

分析工具

  • IDR
  • DelphiDecompiler(DeDe)
  • IDA
  • OllyDbg

本篇博客采用的测试数据如下:

Name:1234567890
Codice:098765432

首先DelphiDecompiler查看一下这个程序的事件、控件等基本信息

下面是各个事件函数及对应的地址映射,可以根据这个在OD中定位分析
在这里插入图片描述
DeDe中每一个事件(函数)点击进入反汇编窗口之后都可以看到DeDe给我们列出来的一些有用的注释,方便我们反汇编分析。

例如CodiceChange函数,就标出了与之相关的控件和所调用的自定义函数。
在这里插入图片描述
但这个还是不够详细,这方面IDR其实更详细一些,可以两个结合着看,
下面是一些控件信息。
在这里插入图片描述

object Principale: TPrincipale
  Left = 279
  Top = 228
  BorderStyle = bsDialog
  Caption = 'CrackMe by - aLoNg3x - v. 1.00'
  ClientHeight = 132
  ClientWidth = 292
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  PixelsPerInch = 96
  TextHeight = 13
  object _Nome: TLabel
    Left = 8
    Top = 8
    Width = 31
    Height = 13
    Caption = 'Nome:'
  end
  object _Codice: TLabel
    Left = 8
    Top = 32
    Width = 36
    Height = 13
    Caption = 'Codice:'
  end
  object Pannello: TPanel
    Left = 0
    Top = 64
    Width = 292
    Height = 68
    Align = alBottom
    BevelOuter = bvLowered
    TabOrder = 0
    object Image1: TImage
      Left = 1
      Top = 1
      Width = 290
      Height = 66
      Align = alClient
      Picture.Data = {
        ...}
      Stretch = True
    end
    object Ok: TButton
      Left = 24
      Top = 16
      Width = 113
      Height = 41
      Caption = 'Ok'
      Enabled = False
      TabOrder = 0
      OnClick = OkClick
    end
    object Cancella: TButton
      Left = 152
      Top = 16
      Width = 113
      Height = 41
      Caption = 'Cancella'
      TabOrder = 1
      OnClick = CancellaClick
    end
  end
  object Nome: TEdit
    Left = 56
    Top = 8
    Width = 89
    Height = 21
    MaxLength = 10
    TabOrder = 1
    OnChange = NomeChange
  end
  object Codice: TEdit
    Left = 56
    Top = 32
    Width = 89
    Height = 21
    TabOrder = 2
    Text = '0'
    OnChange = CodiceChange
  end
  object About: TButton
    Left = 168
    Top = 8
    Width = 105
    Height = 49
    Caption = 'About - Help'
    TabOrder = 3
    OnClick = AboutClick
  end
end

CodiceChange

既然OK没办法点,那么首先分析CodiceChange函数。

DeDe可以看出这个函数起始地址在0x442C78,那么在OD中Ctrl+G定位到这个函数,并下断点,经过测试发现这个函数在我们输入Codice时会断下来,即被调用。

为了看清楚这个函数对Codice和Name的处理,先Alt+B把这个断点去掉,输入测试用的Name和Codice之后再恢复那个函数的断点,鼠标移至被调试程序的窗口(这样做是为了给程序传入一个“鼠标移动”的消息,让程序不是处于等待消息的状态),这样程序就断在了这个函数处,接着就可以动态调试。
在这里插入图片描述
一直单步运行下去发现给函数442A3C传入了Name,同时可以注意到在DeDe中这个函数还是一个自定义函数
在这里插入图片描述
在这里插入图片描述
再看这个442A3C函数执行之后还有一个test语句和jnz语句,我们试着爆破把这个je跳转语句变成jnz,可以发现Ok按钮已经可以按了,说明这个函数的返回值至关重要。

下面看看这个函数的算法,为了更便于理清算法思路,可以结合IDA静态分析,IDA中先函数窗口Ctrl+F定位到CodiceChange函数,然后定位到这个sub_442A3C函数,伪代码如下:

int __fastcall sub_442A3C(int a1, int a2)
{
  int v2; // ebx
  int v3; // edx
  int v4; // eax
  int v5; // ebx
  unsigned int v7[2]; // [esp-Ch] [ebp-1Ch] BYREF
  int *v8; // [esp-4h] [ebp-14h]
  int v9; // [esp+8h] [ebp-8h] BYREF
  int v10; // [esp+Ch] [ebp-4h]
  int savedregs; // [esp+10h] [ebp+0h] BYREF

  v9 = a2;
  v10 = a1;
  System::__linkproc__ LStrAddRef(a1);
  System::__linkproc__ LStrAddRef(v9);
  v8 = &savedregs;
  v7[1] = &loc_442AE5;
  v7[0] = NtCurrentTeb()->NtTib.ExceptionList;
  __writefsdword(0, v7);
  if ( unknown_libname_32(v10) <= 5 )
  {
    v5 = 0;
  }
  else
  {
    v2 = unknown_libname_32(v10);
    v3 = unknown_libname_32(v10) - 1;
    if ( v3 > 0 )
    {
      v4 = 1;
      do
      {
        v2 += v4 * *(v10 + v4) * *(v10 + v4 - 1);
        ++v4;
        --v3;
      }
      while ( v3 );
    }
    v5 = v2 - sub_407670(v9);
    if ( v5 == 666 )
      LOBYTE(v5) = 1;
    else
      v5 = 0;
  }
  __writefsdword(0, v7[0]);
  v8 = &loc_442AEC;
  System::__linkproc__ LStrArrayClr(&v9, 2);
  return v5;
}

直接看返回值v5在伪代码中的逻辑
在这里插入图片描述
根据这个666可以对应OD中的具体汇编代码部分,可以发现这个跳转明显是不能实现的
在这里插入图片描述
往上翻,看算法处理逻辑,差不多是一个循环中Name两两相乘,再乘ax中的值。每一回合ax都自增,循环结束之后最后还要减去Codeice,最终才跟666比较。

算法主体的反汇编即注释如下:

00442A8E  |.  B8 01000000   mov     eax, 1
00442A93  |>  8B4D FC       /mov     ecx, dword ptr [ebp-4]          ;  [加密算法的循环]
00442A96  |.  0FB64C01 FF   |movzx   ecx, byte ptr [ecx+eax-1]
00442A9B  |.  8B75 FC       |mov     esi, dword ptr [ebp-4]
00442A9E  |.  0FB63406      |movzx   esi, byte ptr [esi+eax]
00442AA2  |.  0FAFCE        |imul    ecx, esi
00442AA5  |.  0FAFC8        |imul    ecx, eax                        ;  每相邻2个相乘再乘ax,初始值ax=1(但后面会发现每次做完ax++)
00442AA8  |.  03D9          |add     ebx, ecx                        ;  结果跟上次循环的结果一直累加存储在bx,bx初始值是用户名的长度
00442AAA  |.  40            |inc     eax                             ;  每次循环之后ax++
00442AAB  |.  4A            |dec     edx                             ;  记录循环次数的
00442AAC  |.^ 75 E5         \jnz     short 00442A93                  ;  循环过后记录一下,测试数据最终处理结果是bx=0x1FD56
00442AAE  |>  8B45 F8       mov     eax, dword ptr [ebp-8]           ;  取出序列号传参传入下面的函数
00442AB1  |.  E8 BA4BFCFF   call    00407670                         ;  相当于atoi函数【记录一下返回值0x05E30A78是98765432的16进制形式】
00442AB6  |.  2BD8          sub     ebx, eax                         ;  运算结果-返回值,与0x29A即666的16进制作比较
00442AB8  |.  81FB 9A020000 cmp     ebx, 29A                         ;  上面的结果作比较,等于0x29A就OK可用
00442ABE  |.  75 04         jnz     short 00442AC4

还原一下这部分代码如下:

char Nome[] = "1234567890" // eax
char Codice[] = "098765432"
int length = strlen(Nome);   // esi
int nRet = length;           // ebx
for( int i=1;i<length;i++)
{
    nRet += Nome[i-1]*Nome[i]*i;
}
nRet -= atoi(Codice);
if( nRet == 0x29A )
{
    // 返回0x01,OK按钮可以使用
}else{
    // 返回0,OK按钮禁止使用
}

根据这个用python写了个局部的注册机

name=input("Nome:")
length=len(name)
temp=length
for i in range(1,length):
    temp+=ord(name[i-1])*ord(name[i])*i
codice=temp-666
codice=print("Codice:"+str(codice))

那么为了方便下一步分析,就先计算出Name为1234567890的正确对应的Codice为0x1FD56-0x29A,即130390-666=129724,如下图。
在这里插入图片描述

在这里插入图片描述
发现按下OK之后Codice输入框就立刻清零了,接下来开始分析OkClick函数。

OkClick

给OkClick事件函数下断点,F9跑飞之后输入测试数据,这里输入1234567890和129724,OK可以点击之后点击,程序断在OKClick事件函数处。

分析发现这个函数先检测了一下Cancel控件是否可见,下面的跳转直接改成je(避免它检测出来cancel控件设置的可见导致不运行加密函数)
在这里插入图片描述
接着就是和CodiceChange函数一样的设计,关键函数明显就是442BA0,返回值决定了Ok控件是否可见。
在这里插入图片描述
直接单步步入这个函数

在执行这个函数主要的加密算法之前,这个函数还进行了一些无关紧要的操作,虽然可以忽视,但为了以防万一,还是大概查了查这些API函数是干什么用的。
在这里插入图片描述
接下来从长度获取之后看这个函数的具体逻辑,先IDA静态分析看看伪C代码(已经根据IDR对一些未解析出来名字的函数的名称稍作修改,并进行必要删减)

int __usercall sub_442BA0@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ebx>, int a4@<esi>)
{
  int v5; // ebx
  int v6; // esi
  int v7; // eax
  char v8; // zf
  unsigned int v10[2]; // [esp-14h] [ebp-20h] BYREF
  int *v11; // [esp-Ch] [ebp-18h]
  int v12; // [esp-8h] [ebp-14h]
  int v13; // [esp-4h] [ebp-10h]
  int v14; // [esp+0h] [ebp-Ch] BYREF
  int v15; // [esp+4h] [ebp-8h]
  int v16; // [esp+8h] [ebp-4h]
  int savedregs; // [esp+Ch] [ebp+0h] BYREF

  v15 = 0;
  v14 = 0;
  v13 = a3;
  v12 = a4;
  v16 = a1;
  LStrAddRef(a1);
  v11 = &savedregs;
  v10[1] = &loc_442C67;
  v5 = 0;
  IntToStr(a2);
  LStrLAsg(&v14, v15);
  if ( lstrlen(v15) <= 5 )          //长度必须大于5
  {
    v5 = 0;
  }
  else
  {
    v6 = lstrlen(v15);            //获取长度
    if ( v6 >= 1 )
    {
      do
      {
        v7 = UniqueString(&v14);
        *(v7 + v6 - 1) = v6 * (*(v15 + v6 - 1) * *(v15 + v6 - 1)) % 25 + 65;
        --v6;
      }
      while ( v6 );
    }
    LStrCmp(v14, v16);
    if ( v8 )
    {
      LStrCmp(v16, v14);
      if ( v8 )
        LOBYTE(v5) = 1;
      else
        v5 = 0;
    }
  }
  v11 = &loc_442C6E;
  LStrArrayClr(&v14, 3);
  return v5;
}

分析好之后还原一下算法。主要含义就是从最后一位开始计算,一个个替换成大写字母,最后再跟Name比较。。

char Nome[] = "1234567890" // eax
char code[] = "129724"
int nLen = strlen(code);   // esi
int nRet = 0;              // eax
for( int i=0;i<nLen;i++)
{
    int nTmp = code[nLen-1-i]*code[nLen-1-i]*(nLen-i);
    nRet = nTmp%25 + 0x41;        // 转换为大写字母
    code[nLen-1-i] = nRet;
}
int nrt = strCmp(code,Nome);  //x2,两次比较用户名和生成后的字符串是否相同

if( nrt == 0 )
{
    // 返回0x01,OK按钮隐藏
}else{
    // 返回0,OK按钮显示
}

现在看来,从理论上来说,从这个程序的设计来看没办法写注册机,现在只能强制爆破。。。
在这里插入图片描述

CancellaClick

看内部算法把注册机搞出来的概率比较小了,而且这个函数的算法还是跟前面2个的类似,所以直接暴破了
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Em0s_Er1t

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值