BUUCTF_刮开有奖
打开程序,一个大大的刮开有奖,没有其他有用的信息…,如图:
用IDA载入,找到WinMain函数,按F5载入,如图:
看到DialogBoxParamA函数,该函数从对话框模板资源创建模式对话框,应用程序可以使用此值初始化对话框控件(MSDN文档截取的)。既然有初始化对话框,那么必然有读取对话框的函数,我们点进DialogBoxParamA找一下,如图:
噢!看到了GetDlgItemTextA函数,我们发现他在DialogFunc中,我们用F5载入它看看,如下:
BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int v7; // [esp+8h] [ebp-20030h]
int v8; // [esp+Ch] [ebp-2002Ch]
int v9; // [esp+10h] [ebp-20028h]
int v10; // [esp+14h] [ebp-20024h]
int v11; // [esp+18h] [ebp-20020h]
int v12; // [esp+1Ch] [ebp-2001Ch]
int v13; // [esp+20h] [ebp-20018h]
int v14; // [esp+24h] [ebp-20014h]
int v15; // [esp+28h] [ebp-20010h]
int v16; // [esp+2Ch] [ebp-2000Ch]
int v17; // [esp+30h] [ebp-20008h]
CHAR String; // [esp+34h] [ebp-20004h]
char v19; // [esp+35h] [ebp-20003h]
char v20; // [esp+36h] [ebp-20002h]
char v21; // [esp+37h] [ebp-20001h]
char v22; // [esp+38h] [ebp-20000h]
char v23; // [esp+39h] [ebp-1FFFFh]
char v24; // [esp+3Ah] [ebp-1FFFEh]
char v25; // [esp+3Bh] [ebp-1FFFDh]
char v26; // [esp+10034h] [ebp-10004h]
char v27; // [esp+10035h] [ebp-10003h]
char v28; // [esp+10036h] [ebp-10002h]
if ( a2 == 272 )
return 1;
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 == 1001 )
{
memset(&String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, &String, 0xFFFF);
if ( strlen(&String) == 8 ) // 函数读取到的字符串的长度是8
{
v7 = 'Z';
v8 = 'J';
v9 = 'S';
v10 = 'E';
v11 = 'C';
v12 = 'a';
v13 = 'N';
v14 = 'H';
v15 = '3';
v16 = 'n';
v17 = 'g';
sub_4010F0((int)&v7, 0, 10);
memset(&v26, 0, 0xFFFFu);
v26 = v23; // String[5]
v28 = v25; // String[7]
v27 = v24; // String[6]
v4 = sub_401000((int)&v26, strlen(&v26));
memset(&v26, 0, 0xFFFFu);
v27 = v21; // String[3]
v26 = v20; // String[2]
v28 = v22; // String[4]
v5 = sub_401000((int)&v26, strlen(&v26));
if ( String == v7 + 0x22
&& v19 == v11
&& 4 * v20 - 141 == 3 * v9
&& v21 / 4 == 2 * (v14 / 9)
&& !strcmp(v4, "ak1w")
&& !strcmp(v5, "V1Ax") )
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
}
return 0;
}
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
return 0;
EndDialog(hDlg, (unsigned __int16)a3);
return 1;
}
大概看一下,得到一些信息:
1.
我们得知flag应该是8个字符.
2.
等下要跟进sub_4010F0去看看有没改变字符串.
3.
同样也要跟进sub_401000看看.
okk,接下来先进sub_4010F0看看,如下:
int __cdecl sub_4010F0(int a1, int a2, int a3)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx
result = a3; // 10
for ( i = a2; i <= a3; a2 = i )
{
v5 = 4 * i;
v6 = *(_DWORD *)(4 * i + a1);
if ( a2 < result && i < result )
{
do
{
if ( v6 > *(_DWORD *)(a1 + 4 * result) )
{
if ( i >= result )
break;
++i;
*(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
if ( i >= result )
break;
while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = 4 * i;
*(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
}
--result;
}
while ( i < result );
}
LABEL_13:
*(_DWORD *)(a1 + 4 * result) = v6;
sub_4010F0(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
好复杂!!!😵,把它转化为可执行的C代码,把*(_DWORD*) 删掉,因为这是汇编的表示,然后将各种基址+偏移的表示也换成数组的寻址,如下:
#include <stdio.h>
#include <string.h>
int sub_4010F0(char* a1, int a2, int a3)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx
result = a3;
for (i = a2; i <= a3; a2 = i)
{
v5 = i;
v6 = a1[i];
if (a2 < result && i < result)
{
do
{
if (v6 > a1[result])
{
if (i >= result)
break;
++i;
a1[v5] = a1[result];
if (i >= result)
break;
while (a1[i] <= v6)
{
if (++i >= result)
goto LABEL_13;
}
if (i >= result)
break;
v5 = i;
a1[result] = a1[i];
}
--result;
} while (i < result);
}
LABEL_13:
a1[result] = v6;
sub_4010F0(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
int main(void)
{
char str[] = "ZJSECaNH3ng";
sub_4010F0(str,0,10);
printf("%s", str);
return 0;
}
执行得到结果,如图:
回到DialogFunc函数,接着往下看,点进v23,看到下图,这个String刚好是8位,而V23是String[5]的位置,按照顺序把v18~v25排好序.
接下来就可以进入sub_401000看看了,如下:
_BYTE *__cdecl sub_401000(int a1, int a2)
{
int v2; // eax
int v3; // esi
size_t v4; // ebx
_BYTE *v5; // eax
_BYTE *v6; // edi
int v7; // eax
_BYTE *v8; // ebx
int v9; // edi
signed int v10; // edx
int v11; // edi
signed int v12; // eax
signed int v13; // esi
_BYTE *result; // eax
_BYTE *v15; // [esp+Ch] [ebp-10h]
_BYTE *v16; // [esp+10h] [ebp-Ch]
int v17; // [esp+14h] [ebp-8h]
int v18; // [esp+18h] [ebp-4h]
v2 = a2 / 3;
v3 = 0;
if ( a2 % 3 > 0 )
++v2;
v4 = 4 * v2 + 1;
v5 = malloc(v4);
v6 = v5;
v15 = v5;
if ( !v5 )
exit(0);
memset(v5, 0, v4);
v7 = a2;
v8 = v6;
v16 = v6;
if ( a2 > 0 )
{
while ( 1 )
{
v9 = 0;
v10 = 0;
v18 = 0;
do
{
if ( v3 >= v7 )
break;
++v10;
v9 = *(unsigned __int8 *)(v3++ + a1) | (v9 << 8);
}
while ( v10 < 3 );
v11 = v9 << 8 * (3 - v10);
v12 = 0;
v17 = v3;
v13 = 18;
do
{
if ( v10 >= v12 )
{
*((_BYTE *)&v18 + v12) = (v11 >> v13) & 0x3F;
v8 = v16;
}
else
{
*((_BYTE *)&v18 + v12) = 64;
}
*v8++ = byte_407830[*((char *)&v18 + v12)];
v13 -= 6;
++v12;
v16 = v8;
}
while ( v13 > -6 );
v3 = v17;
if ( v17 >= a2 )
break;
v7 = a2;
}
v6 = v15;
}
result = v6;
*v8 = 0;
return result;
}
又是好复杂!但是往下拉看见一个byte_407830,emmmm点进去看看,耶嘿!发现一个base64!如图:
差不多了,回到DialogFunc看到下面有个 if 判断,判断完了就弹出一个MessageBoxA,这个 if 判断应该很关键,先是 v7 + 0x22,算一下,得到 U ( 0x33+0x22),String[0]便是U,然后 v19 == v11,V19排在String[1],所以String[1]是J,下面的两个strcmp应该是上面的base64加密后得到的结果与 ak1w 和 U1Ax 对比.
写个python脚本把它解密了!如下:
import base64
str1 = 'ak1w'
str2 = 'V1Ax'
flag1 = base64.b64decode(str1)
flag2 = base64.b64decode(str2)
print(flag1)
print(flag2)
运行出结果,如图:
那这两个谁前谁后呢,然后看见第三和第四条语句 4 * v20 - 141 == 3 * v9 和 v21 / 4 == 2 * (v14 / 9),应该是来验证的我们算一下第一条,v9是’E’为69,j是106,W是87,一通计算猛如虎,v20是W,okk,那么就是WP1先.
至此,String已经全部破解完成!!!,为UJWP1jMp .