CrackMe-006

发布于 2023-04-13  60 次阅读


0x01 信息收集

image20210301170932919.png

Ok这个按钮默认是灰色的无法点击,然后Cancella是将Codice清0

根据About的提示:

image20210301171145773.png
image20210301171115098.png

说明是需要先输入正确的Nome和Codice

EXEINFO查壳,Delphi的程序:

image20210301171213251.png

拖进DarckDe4反编译

查看过程:

image20210301171434205.png
  • CodiceChange:Codice的值被修改触发的事件
  • OkClick:点击Ok触发的事件
  • NomeChange:Nome的值被修改触发的事件
  • CancellaClick:点击CancellaClick触发的事件
  • AboutClick:点击About触发的事件

拖进IDA,在IDA中,对应事件的回调函数被命名为:模块名_事件名:

所以这里的五个事件对应的回调函数名为:

  • TPrincipale_CodiceChange
  • TPrincipale_OkClick
  • TPrincipale_NomeChange
  • TPrincipale_CancellaClick
  • TPrincipale_AboutClick

在IDA中将所有的Delphi相关的签名加上,然后导出MAP,通过OD的LoadMapEx插件加载MAP文件,方便寻找函数

TPrincipale_CodiceChange

int __usercall TPrincipale_CodiceChange@<eax>(int a1@<eax>, int a2@<ebx>, int a3@<esi>)
{
 int v3; // ebx
 int v4; // eax
 int v5; // edx
 void *v6; // ST00_4
 int v7; // edx
 unsigned int v9; // [esp-14h] [ebp-24h]
 void *v10; // [esp-10h] [ebp-20h]
 int *v11; // [esp-Ch] [ebp-1Ch]
 int v12; // [esp-8h] [ebp-18h]
 int v13; // [esp-4h] [ebp-14h]
 unsigned int v14; // [esp+0h] [ebp-10h]
 int System::AnsiString; // [esp+4h] [ebp-Ch]
 void *v16; // [esp+8h] [ebp-8h]
 int v17; // [esp+Ch] [ebp-4h]
 int savedregs; // [esp+10h] [ebp+0h]

 System::AnsiString = 0;
 v14 = 0;
 v13 = a2;
 v12 = a3;
 v3 = a1;
 v11 = &savedregs;
 v10 = &loc_442D56;
 v9 = __readfsdword(0);
 __writefsdword(0, (unsigned int)&v9);
 TControl::GetText(*(TControl **)(a1 + 736));
 v4 = __linkproc__ ValLong(v16, &v17);
 if ( v17 )
{
   Sysutils::IntToStr(v4);
   Controls::TControl::SetText(*(Controls::TControl **)(v3 + 736), System::AnsiString);
}
 if ( *(_BYTE *)(*(_DWORD *)(v3 + 720) + 71) )
{
   TControl::GetText(*(TControl **)(v3 + 736));
   v6 = v16;
   TControl::GetText(*(TControl **)(v3 + 732));
   if ( (unsigned __int8)sub_442A3C(v14, v6) )
  {
     LOBYTE(v7) = 1;
    (*(void (__fastcall **)(_DWORD, int, _DWORD, int, int *, void *, unsigned int))(**(_DWORD **)(v3 + 716) + 96))(
       *(_DWORD *)(v3 + 716),
       v7,
       **(_DWORD **)(v3 + 716),
       v12,
       v11,
       v10,
       v9);
  }
   else
  {
    (*(void (__fastcall **)(_DWORD, _DWORD, _DWORD, int, int *, void *, unsigned int))(**(_DWORD **)(v3 + 716) + 96))(
       *(_DWORD *)(v3 + 716),
       0,
       **(_DWORD **)(v3 + 716),
       v12,
       v11,
       v10,
       v9);
  }
}
 else
{
   LOBYTE(v5) = 1;
  (*(void (__fastcall **)(_DWORD, int, _DWORD, int, int *, void *, unsigned int))(**(_DWORD **)(v3 + 716) + 96))(
     *(_DWORD *)(v3 + 716),
     v5,
     **(_DWORD **)(v3 + 716),
     v12,
     v11,
     v10,
     v9);
}
 __writefsdword(0, v14);
 v16 = &loc_442D5D;
 System::__linkproc__ LStrClr(&v14);
 System::__linkproc__ LStrClr(&System::AnsiString);
 return System::__linkproc__ LStrClr(&v16);
}

这里输入

image20210301180349338.png

此时修改的是Nome的值,所以触发的是NomeChange的事件,可以看到两次GetText分别都是Get Nome和Codice

image20210301180244268.png

往下调用了442A3C函数,这里猜测应该就是用来判断Nome和Codice是否合法的函数:

image20210301180622826.png

首先判断Nome的长度是否大于5,汇编指令表示为jle

image20210301180908296.png

这里三个Strlen都是对Nome的长度进行判断,过了之后进入一个循环:

image20210301181209536.png

这里循环体的算法可以大致理解为:

Nome = '123456'
sum = 0
Nome_len = len(Nome)
sum += Nome_len
for i in range(0, Nome_len-1):
  sum += (i + 1) * ord(Nome[i]) * ord(Nome[i+1])

最终得到0x9F8E,十进制40846

往下压入了Codice的值,并且调用了StrToInt,这里测试输入的值为123得到0x7b

image20210301182515230.png
Codice = '123'

hex(int(Codice,10)) => 0x7b

然后将上面得到的0x9F8E减去0x7b再与0x29A对比

image20210301182931851.png

那么这里应该就是关键比较了,将jnz用nop填充之后,往下执行,直接运行程序,最终可以看到,此时程序中的Ok这个按钮已经变成了可点击的状态。

image20210301183048350.png

然后看到CodiceChange,可以看到该函数也调用了442A3C对两个值进行判断,那么可以得出NomeChange和CodiceChange的真正逻辑判断是同样的,根据此就可以写出注册机了。

0x02 编写注册机

main.py:

import sys
import random
import string
from PyQt5.QtWidgets import QApplication, QMainWindow
from crackMe06 import Ui_MainWindow


class MyMainWindow(QMainWindow, Ui_MainWindow):
   name = ''
   serial = ''

   def __init__(self, parent=None):
       super(MyMainWindow, self).__init__(parent)
       self.setupUi(self)
       self.pushButton.clicked.connect(self.generate_name_serial)

   def generate_name(self):
       return ''.join(random.sample(string.ascii_letters + string.digits, random.randint(6,10)))

   def generate_serial(self, name):
       sum = 0
       Nome_len = len(self.name)
       sum += Nome_len
       for i in range(0, Nome_len-1):
           sum += (i + 1) * ord(self.name[i]) * ord(self.name[i+1])
       return str(sum - 0x29A)

   def generate_name_serial(self):
       self.name = self.generate_name()
       self.serial = self.generate_serial(self.name)
       self.textEdit.setText(self.name)
       self.textEdit_2.setText(self.serial)


if __name__ == '__main__':
   app = QApplication(sys.argv)
   myWin = MyMainWindow()
   myWin.show()
   sys.exit(app.exec_())