0x01 信息收集
Ok这个按钮默认是灰色的无法点击,然后Cancella是将Codice清0
根据About的提示:
说明是需要先输入正确的Nome和Codice
EXEINFO查壳,Delphi的程序:
拖进DarckDe4反编译
查看过程:
- 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);
}
这里输入
此时修改的是Nome的值,所以触发的是NomeChange的事件,可以看到两次GetText分别都是Get Nome和Codice
往下调用了442A3C函数,这里猜测应该就是用来判断Nome和Codice是否合法的函数:
首先判断Nome的长度是否大于5,汇编指令表示为jle
这里三个Strlen都是对Nome的长度进行判断,过了之后进入一个循环:
这里循环体的算法可以大致理解为:
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
Codice = '123'
hex(int(Codice,10)) => 0x7b
然后将上面得到的0x9F8E
减去0x7b
再与0x29A
对比
那么这里应该就是关键比较了,将jnz用nop填充之后,往下执行,直接运行程序,最终可以看到,此时程序中的Ok这个按钮已经变成了可点击的状态。
然后看到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_())
Comments NOTHING