看雪-亦步亦趋 两种解法(动调OD/静调IDA)题解
程序基本逻辑
直接执行exe文件后按照题目输入用户名123456
,然后随便输入一个能够辨别的数字我输入的是1111111111
然后运行。
提示如下(因为图传有速率限制经可能不用图片或者用小图)
提示词可以作为关键字,基本逻辑应该是
- 获取ID
- 某些操作
- 获取注册码
- 某些操作
- 验证
解法一动调:本题最快的解法
通过关键字找到对应行,并向上找到关键函数,然后发现调用关键函数前push
了两个string
猜测可能是相关的。然后在条件调转处下了一个断点(或者上面的那个关键call
此处按照程序基本逻辑应该就是获取注册码后进行的比对操作了),然后F9运行,输入
- 123456
- 111111111111(随意输入为了好区分)
再按F9运行到断点处发现string1
和string2
被赋值,并且在关键call
前push
保存了各自的值分别是
- 894904543(应该就是真正的注册码)
- 111111111111(我输入的)
因为题目说flag就是用户名为123456的注册码那么这就是答案了即flag{894904543}
解法二静调:注册机
事先说明,这题使用这种解法会比较复杂
IDA打开,对应错误提示按shift
+F12
搜索关键字找到错误提示的内容点进去,找到对应的主函数,代码如下
INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
HICON v4; // eax
int v6; // edx
unsigned int v7; // esi
int v8; // eax
char v9; // cl
char v10; // bl
int v11; // edi
unsigned int v12; // ecx
char *v13; // esi
char v14; // al
char v15; // bl
int v16; // esi
unsigned int v17; // edi
char v18; // al
char v19; // cl
int v20; // edi
unsigned int v21; // ecx
char *v22; // esi
char v23; // al
char v24; // bl
unsigned int v25; // eax
_BYTE *v26; // esi
char v27; // bl
unsigned int v28; // eax
int v29; // ebx
unsigned __int64 v30; // rtt
int v31; // eax
int v32; // ebx
WPARAM v33; // [esp-4h] [ebp-4h]
int v34; // [esp-4h] [ebp-4h]
WPARAM v35; // [esp-4h] [ebp-4h]
int savedregs; // [esp+0h] [ebp+0h]
switch ( a2 )
{
case 0x110u:
v4 = LoadIconA(hModule, (LPCSTR)0x1F6);
SendMessageA(hWnd, 0x80u, 0, (LPARAM)v4);
SendDlgItemMessageA(hWnd, 100, 0xC5u, 0x55u, 0);
sub_401441(hMem);
break;
case 0x111u:
if ( !HIWORD(a3) )
{
if ( (_WORD)a3 == 1004 )
{
v33 = a3;
if ( GetDlgItemTextA(hWnd, 1002, String, 512) <= 3 )
return MessageBoxA(hWnd, Text, Caption, 0x10u);
lstrlenA(String);
v7 = 0;
v8 = 0;
do
{
v9 = *(_BYTE *)(v7 + v6);
v10 = byte_406328[v8++] ^ *(_BYTE *)(v7 + v6);
*(_BYTE *)(v6 + v7) = v10;
byte_406327[v8] = v9;
if ( v8 == 5 )
v8 = 0;
++v7;
}
while ( v7 < (unsigned int)&savedregs );
v11 = 0;
v12 = 0;
if ( &savedregs )
{
do
{
v13 = (char *)&savedregs - v12 - 1;
v14 = v13[v6];
v15 = v14 ^ byte_40632D[v11++];
v13[v6] = v15;
byte_40632C[v11] = v14;
if ( v11 == 5 )
v11 = 0;
++v12;
}
while ( v12 < (unsigned int)&savedregs );
}
v16 = 0;
v17 = 0;
if ( &savedregs )
{
do
{
v18 = *(_BYTE *)(v6 + v17);
v19 = v18 ^ byte_406332[v16++];
*(_BYTE *)(v6 + v17) = v19;
byte_406331[v16] = v18;
if ( v16 == 5 )
v16 = 0;
++v17;
}
while ( v17 < (unsigned int)&savedregs );
}
v20 = 0;
v21 = 0;
if ( &savedregs )
{
do
{
v22 = (char *)&savedregs - v21 - 1;
v23 = v22[v6];
v24 = v23 ^ byte_406337[v20++];
v22[v6] = v24;
byte_406336[v20] = v23;
if ( v20 == 5 )
v20 = 0;
++v21;
}
while ( v21 < (unsigned int)&savedregs );
}
v25 = 0;
dword_406345 = 0;
if ( &savedregs )
{
do
{
v26 = (char *)&dword_406345 + (v25 & 3);
v27 = *(_BYTE *)(v6 + v25++) + *v26;
*v26 = v27;
}
while ( v25 < (unsigned int)&savedregs );
}
v28 = dword_406345;
v29 = 0;
do
{
v30 = v28;
v28 /= 0xAu;
String1[v29++] = v30 % 0xA + 48;
}
while ( v28 );
v31 = lstrlenA(String1);
v32 = 0;
do
String2[v32++] = byte_406548[v31--];
while ( v31 );
lstrcpyA(String1, String2);
GetDlgItemTextA(hWnd, 100, byte_406949, 512);
if ( lstrcmpA(byte_406949, String1) )
MessageBoxA(hWnd, aNopeThatsNotIt, Caption, 0x10u);
else
MessageBoxA(hWnd, aYepThatsTheRig, aGoodBoy, 0x40u);
RtlZeroMemory(String1, 512);
RtlZeroMemory(String, 512);
RtlZeroMemory(String2, 512);
qmemcpy(byte_406327, &unk_406311, 0x16u);
}
else
{
if ( (_WORD)a3 == 1006 )
{
v34 = sub_401441(0);
ExitProcess(0);
}
if ( (_WORD)a3 == 1005 )
{
v35 = a3;
MessageBoxA(hWnd, aCodedByLafarge, aAbout, 0x40u);
}
}
}
break;
case 0x10u:
sub_401441(0);
EndDialog(hWnd, 0);
break;
}
return 0;
}
关注两次获取输入框的函数调用,可以发现主要是获取用户名后对用户名进行加密生成了注册码,然后第二次获取的是你输入的注册码(byte_406949
)和正确的(即String1
)比较来验证。
因此关键的就是这个String1
是怎么加密用户名得到的了
结合汇编代码
DB定义字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加1
DW定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
DD定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
.data:00406328 ; char byte_406328[4]
.data:00406328 byte_406328 db 0AAh ; DATA XREF: DialogFunc+CB↑r
.data:00406329 db 89h
.data:0040632A db 0C4h
.data:0040632B db 0FEh
.data:0040632C ; char byte_40632C[]
.data:0040632C byte_40632C db 46h ; DATA XREF: DialogFunc+103↑w
.data:0040632D ; char byte_40632D[4]
.data:0040632D byte_40632D db 78h ; DATA XREF: DialogFunc:loc_4011A3↑r
.data:0040632E db 0F0h
.data:0040632F db 0D0h
.data:00406330 db 3
.data:00406331 ; char byte_406331[]
.data:00406331 byte_406331 db 0E7h ; DATA XREF: DialogFunc+12C↑w
.data:00406332 ; char byte_406332[4]
.data:00406332 byte_406332 db 0F7h ; DATA XREF: DialogFunc+120↑r
.data:00406333 db 0FDh
.data:00406334 db 0F4h
.data:00406335 db 0E7h
.data:00406336 ; char byte_406336[]
.data:00406336 byte_406336 db 0B9h ; DATA XREF: DialogFunc+15A↑w
.data:00406337 ; char byte_406337[6]
.data:00406337 byte_406337 db 0B5h ; DATA XREF: DialogFunc:loc_4011FA↑r
.data:00406338 db 1Bh
.data:00406339 db 0C9h
.data:0040633A db 50h ; P
.data:0040633B db 73h ; s
.data:0040633C db 0
可以得到一些数组被赋予的初始值
char byte_406328[] = {0xaa,0x89,0xc4,0xfe};
char byte_40632C[] = {0x46};
char byte_40632D[] = {0x78,0xf0,0xd0,0x03};
char byte_406331[] = {0xe7};
char byte_406332[] = {0xf7,0xfd,0xf4,0xe7};
char byte_406336[] = {0xb9};
char byte_406337[] = {0xb5,0x1b,0xc9,0x50,0x73,0x00};
关键加密部分解读如下
if ( GetDlgItemTextA(hWnd, 1002, String, 512) <= 3 )
return MessageBoxA(hWnd, Text, Caption, 0x10u);
lstrlenA(String);//String为输入的注册码
v7 = 0;
v8 = 0;
do
{
v9 = *(_BYTE *)(v7 + v6);
v10 = byte_406328[v8++] ^ *(_BYTE *)(v7 + v6);
*(_BYTE *)(v6 + v7) = v10;
byte_406327[v8] = v9;
if ( v8 == 5 )
v8 = 0;
++v7;
}
while ( v7 < (unsigned int)&savedregs );
v11 = 0;
v12 = 0;
if ( &savedregs )
{
do
{
v13 = (char *)&savedregs - v12 - 1;
v14 = v13[v6];
v15 = v14 ^ byte_40632D[v11++];
v13[v6] = v15;
byte_40632C[v11] = v14;
if ( v11 == 5 )
v11 = 0;
++v12;
}
while ( v12 < (unsigned int)&savedregs );
}
v16 = 0;
v17 = 0;
if ( &savedregs )
{
do
{
v18 = *(_BYTE *)(v6 + v17);
v19 = v18 ^ byte_406332[v16++];
*(_BYTE *)(v6 + v17) = v19;
byte_406331[v16] = v18;
if ( v16 == 5 )
v16 = 0;
++v17;
}
while ( v17 < (unsigned int)&savedregs );
}
v20 = 0;
v21 = 0;
if ( &savedregs )
{
do
{
v22 = (char *)&savedregs - v21 - 1;
v23 = v22[v6];
v24 = v23 ^ byte_406337[v20++];
v22[v6] = v24;
byte_406336[v20] = v23;
if ( v20 == 5 )
v20 = 0;
++v21;
}
while ( v21 < (unsigned int)&savedregs );
}
v25 = 0;
dword_406345 = 0;
if ( &savedregs )
{
do
{
v26 = (char *)&dword_406345 + (v25 & 3);
v27 = *(_BYTE *)(v6 + v25++) + *v26;
*v26 = v27;
}
while ( v25 < (unsigned int)&savedregs );
}
v28 = dword_406345;
v29 = 0;
do
{
v30 = v28;
v28 /= 0xAu;
String1[v29++] = v30 % 0xA + 48;
}
while ( v28 );
v31 = lstrlenA(String1);
v32 = 0;
do
String2[v32++] = byte_406548[v31--];
//String2就是答案
while ( v31 );
lstrcpyA(String1, String2);
//String1就是答案