首先看看怎么个事
点开也就这样了,没有输入的点,感觉和之前的 "刮开有奖" 有一点点相像
winmain长这个样子
看到消息循环了,下一步肯定就是找回调函数了
乍一看还没有,函数一个个点进去看发现sub_401023(hInstance),一直点进去看,一直到遇到这个
这就是注册窗口的过程,可以看到回调函数是 sub_401014
但是点进去发现JUMPOUT,那肯定要尝试修复一波
非常简单,甚至没有花指令,直接创建函数即可
修复好后长这样
cpp
LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int v5; // eax
size_t v6; // eax
DWORD v7; // eax
int v8; // eax
int v9; // eax
int v10; // [esp+4Ch] [ebp-400h]
UINT v11; // [esp+50h] [ebp-3FCh]
CHAR v12[256]; // [esp+54h] [ebp-3F8h] BYREF
char v13[7]; // [esp+154h] [ebp-2F8h] BYREF
__int16 v14; // [esp+15Bh] [ebp-2F1h]
char v15; // [esp+15Dh] [ebp-2EFh]
char Str[253]; // [esp+160h] [ebp-2ECh] BYREF
__int16 v17; // [esp+25Dh] [ebp-1EFh]
char v18; // [esp+25Fh] [ebp-1EDh]
CHAR v19[256]; // [esp+260h] [ebp-1ECh] BYREF
CHAR String[4]; // [esp+360h] [ebp-ECh] BYREF
int v21; // [esp+364h] [ebp-E8h]
__int16 v22; // [esp+368h] [ebp-E4h]
CHAR Text[32]; // [esp+36Ch] [ebp-E0h] BYREF
struct tagRECT Rect; // [esp+38Ch] [ebp-C0h] BYREF
CHAR Buffer[100]; // [esp+39Ch] [ebp-B0h] BYREF
HDC hdc; // [esp+400h] [ebp-4Ch]
struct tagPAINTSTRUCT Paint; // [esp+404h] [ebp-48h] BYREF
int v28; // [esp+444h] [ebp-8h]
int v29; // [esp+448h] [ebp-4h]
LoadStringA(hInstance, 0x6Au, Buffer, 100);
v11 = Msg;
if ( Msg > 0x111 )
{
if ( v11 == 517 )
{
if ( strlen((const char *)String1) > 6 )
ExitProcess(0);
if ( strlen((const char *)String1) )
{
memset(v19, 0, sizeof(v19));
v6 = strlen((const char *)String1);
memcpy(v19, String1, v6);
v7 = strlen((const char *)String1);
sub_40101E(String1, v7, (LPSTR)String1);
strcpy(Str, "0kk`d1a`55k222k2a776jbfgd`06cjjb");
memset(&Str[33], 0, 0xDCu);
v17 = 0;
v18 = 0;
strcpy(v13, "SS");
*(_DWORD *)&v13[3] = 0;
v14 = 0;
v15 = 0;
v8 = strlen(Str);
sub_401005(v13, (int)Str, v8);
if ( _strcmpi((const char *)String1, Str) )
{
SetWindowTextA(hWndParent, "flag{}");
MessageBoxA(hWndParent, "Are you kidding me?", "^_^", 0);
ExitProcess(0);
}
memcpy(v12, &unk_423030, 0x32u);
v9 = strlen(v12);
sub_401005(v19, (int)v12, v9);
MessageBoxA(hWndParent, v12, 0, 0x32u);
}
++dword_428D54;
}
else
{
if ( v11 != 520 )
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
if ( dword_428D54 == 16 )
{
strcpy(String, "ctf");
v21 = 0;
v22 = 0;
SetWindowTextA(hWndParent, String);
strcpy(Text, "Are you kidding me?");
MessageBoxA(hWndParent, Text, Buffer, 0);
}
++dword_428D54;
}
}
else
{
switch ( v11 )
{
case 0x111u:
v29 = (unsigned __int16)wParam;
v28 = HIWORD(wParam);
v10 = (unsigned __int16)wParam;
if ( (unsigned __int16)wParam == 104 )
{
DialogBoxParamA(hInstance, (LPCSTR)0x67, hWndParent, (DLGPROC)DialogFunc, 0);
}
else
{
if ( v10 != 105 )
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
DestroyWindow(hWndParent);
}
break;
case 2u:
PostQuitMessage(0);
break;
case 0xFu:
hdc = BeginPaint(hWndParent, &Paint);
GetClientRect(hWndParent, &Rect);
v5 = strlen(Buffer);
DrawTextA(hdc, Buffer, v5, &Rect, 1u);
EndPaint(hWndParent, &Paint);
break;
default:
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
}
}
return 0;
}
分析一波,大致逻辑是,当消息编号大于0x111时
右键会校验String(实际上是没有地方给我们输入String的,需要我们手动的去改String的值)。如果String值校验不对, ++dword_428D54;
其他大于0x111的消息也会使得 ++dword_428D54;到十六次后会触发messagebox(没啥用)
然后就是一些基本控件的指令了。所见即所得就不说了
我们主要观察的代码是这段
if ( Msg > 0x111 )
cpp
if ( Msg > 0x111 )
{
if ( v11 == 517 )
{
if ( strlen((const char *)String1) > 6 )
ExitProcess(0);
if ( strlen((const char *)String1) )
{
memset(v19, 0, sizeof(v19));
v6 = strlen((const char *)String1);
memcpy(v19, String1, v6);
v7 = strlen((const char *)String1);
sub_40101E(String1, v7, (LPSTR)String1);
strcpy(Str, "0kk`d1a`55k222k2a776jbfgd`06cjjb");
memset(&Str[33], 0, 0xDCu);
v17 = 0;
v18 = 0;
strcpy(v13, "SS");
*(_DWORD *)&v13[3] = 0;
v14 = 0;
v15 = 0;
v8 = strlen(Str);
sub_401005(v13, (int)Str, v8);
if ( _strcmpi((const char *)String1, Str) )
{
SetWindowTextA(hWndParent, "flag{}");
MessageBoxA(hWndParent, "Are you kidding me?", "^_^", 0);
ExitProcess(0);
}
memcpy(v12, &unk_423030, 0x32u);
v9 = strlen(v12);
sub_401005(v19, (int)v12, v9);
MessageBoxA(hWndParent, v12, 0, 0x32u);
}
++dword_428D54;
}
String1会进入一个哈希加密
cpp
int __cdecl sub_4013A0(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
DWORD i; // [esp+4Ch] [ebp-24h]
CHAR String2[4]; // [esp+50h] [ebp-20h] BYREF
BYTE v6[16]; // [esp+54h] [ebp-1Ch] BYREF
DWORD pdwDataLen; // [esp+64h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+68h] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h] BYREF
if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
return 0;
}
}
加密完后去和Str比较,若相等,则输出flag
CryptCreateHash
函数的参数包括:
-
hProv
:加密服务提供者的句柄。 -
Algid
:指定要使用的哈希算法的 ALG_ID 值。 -
hKey
:如果哈希算法是带密钥的,则传入密钥的句柄;否则为0。 -
dwFlags
:标志位,通常为0。 -
phHash
:指向新创建的哈希对象句柄的指针。
CryptHashData
函数的参数包括:
-
hHash
:哈希对象的句柄。 -
pbData
:指向要进行哈希处理的数据的指针。 -
dwDataLen
:要进行哈希处理的数据的长度。 -
dwFlags
:标志位,通常为0。
CryptGetHashParam
函数的参数包括:
-
hHash
:哈希对象的句柄。 -
dwParam
:指定要获取的哈希参数。 -
pbData
:指向存储参数数据的缓冲区的指针。 -
pdwDataLen
:指向存储参数数据长度的变量的指针。 -
dwFlags
:标志位,通常为0。
[in] dwParam
查询类型。 此参数可以设置为以下查询之一。
展开表
值 | 含义 |
---|---|
HP_ALGID哈希算法 | 一个ALG_ID,指示创建哈希对象时指定的算法。 有关哈希算法的列表,请参阅 CryptCreateHash。 |
HP_HASHSIZE哈希值大小 | 指示哈希值中的字节数的 DWORD 值。 此值将因哈希算法而异。 应用程序必须在HP_HASHVAL值之前检索此值,以便分配正确的内存量。 |
HP_HASHVAL哈希值 | hHash 指定的哈希对象的哈希值或消息哈希。 此值基于前面通过 CryptHashData 和 CryptHashSessionKey 函数提供给哈希对象的数据生成。CryptGetHashParam 函数完成哈希。 调用 CryptGetHashParam 后,无法向哈希添加更多数据。 对 CryptHashData 或 CryptHashSessionKey 的其他调用失败。 使用哈希完成应用程序后,应调用 CryptDestroyHash 来销毁哈希对象。 |
HP_ALGID 的值是 0x0001,HP_HASHSIZE 的值是 0x0004,HP_HASHVAL 的值是 0x0002
把Str拖出来,找个在线MD5网站解码 其实哈希是不能解码的,但是可以用穷举,先正向加密,和密文对比
再次启动动态调试,将String1改为 123321 即可
解决
flag{n0_Zu0_b0_die}