TraceMe.exe是一个示例程序,用于逆向的学习,简单地模拟了注册机制。
我们把它拖进IDA中无脑F5一波:
双击DialogFunc进入这个函数:
BOOL __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
int v5; // ebx
HWND v6; // eax
HWND v7; // eax
HWND v8; // eax
HICON v9; // eax
CHAR String2[4]; // [esp+8h] [ebp-F4h]
int v11; // [esp+Ch] [ebp-F0h]
int v12; // [esp+10h] [ebp-ECh]
__int16 v13; // [esp+14h] [ebp-E8h]
char v14; // [esp+16h] [ebp-E6h]
CHAR v15; // [esp+18h] [ebp-E4h]
char v16; // [esp+2Eh] [ebp-CEh]
CHAR v17; // [esp+30h] [ebp-CCh]
__int16 v18; // [esp+44h] [ebp-B8h]
char v19; // [esp+46h] [ebp-B6h]
CHAR String; // [esp+48h] [ebp-B4h]
CHAR String1; // [esp+98h] [ebp-64h]
qmemcpy(&v15, byte_405060, 0x16u);
v11 = dword_405054;
v16 = byte_405060[22];
v14 = byte_40505E;
qmemcpy(&v17, &unk_405038, 0x14u);
*(_DWORD *)String2 = dword_405050;
v18 = *((_WORD *)&unk_405038 + 10);
v13 = word_40505C;
v12 = dword_405058;
v19 = *((_BYTE *)&unk_405038 + 22);
if ( a2 == 16 )
{
DestroyWindow(hWnd);
return 1;
}
if ( a2 == 272 )
{
v9 = LoadIconA(hInstance, (LPCSTR)0x70);
SendMessageA(hWnd, 0x80u, 1u, (LPARAM)v9);
SendDlgItemMessageA(hWnd, 110, 0xC5u, 0x50u, 0);
return 1;
}
if ( a2 != 273 )
return 0;
if ( (signed int)(unsigned __int16)a3 > 1013 )
{
if ( (unsigned __int16)a3 == 1014 || (unsigned __int16)a3 == 40002 )
DialogBoxParamA(hInstance, (LPCSTR)0x67, hWnd, sub_401020, 0);
return 0;
}
if ( (unsigned __int16)a3 != 1013 )
{
if ( (unsigned __int16)a3 == 2 || (unsigned __int16)a3 == 1002 )
{
SendMessageA(hWnd, 0x10u, 0, 0);
return 0;
}
return 0;
}
v5 = GetDlgItemTextA(hWnd, 110, &String, 81);
GetDlgItemTextA(hWnd, 1000, &String1, 101);
if ( String && v5 >= 5 )
{
if ( sub_401340(&String1, &String, v5) )
{
lstrcpyA(::String1, String2);
v6 = GetDlgItem(hWnd, 110);
EnableWindow(v6, 0);
v7 = GetDlgItem(hWnd, 1000);
EnableWindow(v7, 0);
v8 = GetDlgItem(hWnd, 1000);
}
else
{
lstrcpyA(::String1, &v17);
v8 = GetDlgItem(hWnd, 1000);
}
}
else
{
lstrcpyA(::String1, &v15);
v8 = GetDlgItem(hWnd, 110);
}
SetFocus(v8);
MessageBeep(0);
DialogBoxParamA(hInstance, (LPCSTR)0x79, hWnd, sub_401060, 0);
return 0;
}
看到第60行:
v5 = GetDlgItemTextA(hWnd, 110, &String, 81);
GetDlgItemTextA(hWnd, 1000, &String1, 101);
if ( String && v5 >= 5 )
{
if ( sub_401340(&String1, &String, v5) )
{
lstrcpyA(::String1, String2);
v6 = GetDlgItem(hWnd, 110);
EnableWindow(v6, 0);
v7 = GetDlgItem(hWnd, 1000);
EnableWindow(v7, 0);
v8 = GetDlgItem(hWnd, 1000);
}
else
{
lstrcpyA(::String1, &v17);
v8 = GetDlgItem(hWnd, 1000);
}
}
这里就是程序主要的逻辑:
使用函数GetDlgItemTextA读取输入的用户名和序列号,如果用户名超长则截取前100位。
然后送入函数sub_401340计算,计算结果是true则验证通过。
进入sub_401340这个函数看下:
BOOL __cdecl sub_401340(LPCSTR lpString1, LPSTR a2, int a3)
{
int v3; // ecx
int v4; // esi
signed int i; // eax
v3 = 3;
v4 = 0;
for ( i = 0; v3 < a3; ++i )
{
if ( i > 7 )
i = 0;
v4 += (unsigned __int8)byte_405030[i] * (unsigned __int8)a2[v3++];
}
wsprintfA(a2, aLd, v4);
return lstrcmpA(lpString1, a2) == 0;
}
这就是具体的算法。逻辑大概就是根据传入的变量lpstring、a2、a3计算序列号,并与用户输入的序列号比对。
针对该逻辑,给出python3写的根据用户名计算序列号的代码:
f=[0x0C, 0x0A, 0x13, 0x09, 0x0C, 0x0B, 0x0A, 0x08]
inputs="asdfg"
f2=list(inputs)
a2=[]
for c in f2:
a2.append(ord(c))
v3=3
v4=0
a3=len(inputs)
i=0
while v3<a3:
if(i>7): i=0
fi = f[i]
a2v3=a2[v3]
v4+=fi*a2v3
v3+=1
i+=1
print(hex(v4))
变量inputs就是输入的用户名,这里我们输入asdfg,运行这段python代码,看到输出注册码为2254:
把用户名和注册码输入进去,看到验证通过: