是个EXE文件首先打开看一下,只有一个带有刮开有奖的消息框,没有什么线索然后老套路拉进Exeinfo PE查看是32位的文件拉进IDApro x86 看到 WinMain 当然毫不犹豫的进去看一下,然后F5查看一下伪代码,
看到了GetDlgItemTextA函数,发现它在DialogFunc中,我们用F5载入它看看,
BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi@5
const char *v5; // edi@5
BOOL result; // eax@14
int v7; // [sp+8h] [bp-20030h]@5
int v8; // [sp+Ch] [bp-2002Ch]@5
int v9; // [sp+10h] [bp-20028h]@5
int v10; // [sp+14h] [bp-20024h]@5
int v11; // [sp+18h] [bp-20020h]@5
int v12; // [sp+1Ch] [bp-2001Ch]@5
int v13; // [sp+20h] [bp-20018h]@5
int v14; // [sp+24h] [bp-20014h]@5
int v15; // [sp+28h] [bp-20010h]@5
int v16; // [sp+2Ch] [bp-2000Ch]@5
int v17; // [sp+30h] [bp-20008h]@5
CHAR String; // [sp+34h] [bp-20004h]@4
char v19; // [sp+35h] [bp-20003h]@6
char v20; // [sp+36h] [bp-20002h]@5
char v21; // [sp+37h] [bp-20001h]@5
char v22; // [sp+38h] [bp-20000h]@5
char v23; // [sp+39h] [bp-1FFFFh]@5
char v24; // [sp+3Ah] [bp-1FFFEh]@5
char v25; // [sp+3Bh] [bp-1FFFDh]@5
char &Str; // [sp+10034h] [bp-10004h]@5
char v27; // [sp+10035h] [bp-10003h]@5
char v28; // [sp+10036h] [bp-10002h]@5
if ( a2 == 272 )
{
result = 1;
}
else
{
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 == 1001 )
{
memset(&String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, &String, 0xFFFF);
if ( strlen(&String) == 8 )
{
v7 = 90;
v8 = 74;
v9 = 83;
v10 = 69;
v11 = 67;
v12 = 97;
v13 = 78;
v14 = 72;
v15 = 51;
v16 = 110;
v17 = 103;
sub_4010F0((int)&v7, 0, 10);
memset(&&Str, 0, 0xFFFFu);
&Str = v23;
v28 = v25;
v27 = v24;
v4 = sub_401000((int)&&Str, strlen(&&Str));
memset(&&Str, 0, 0xFFFFu);
v27 = v21;
&Str = v20;
v28 = v22;
v5 = sub_401000((int)&&Str, strlen(&&Str));
if ( String == v7 + 34
&& 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);
result = 1;
}
return result;
}
粗略的看一下我们可以知道我们要找的flag应该是string,长度为8;`
memset(&String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, &String, 0xFFFF);
if ( strlen(&String) == 8
然后看第51行函数sub_4010F0在对v7~v17进行某种操作,进入sub_4010F0函数
int __cdecl sub_4010F0(int a1, int a2, int a3)
{
int result; // eax@1
int i; // esi@1
int v5; // ecx@2
int v6; // edx@2
result = a3;
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;
}
执行后的结果为3CEHJNSZagn
这就是v7~v17执行后的所对应的字符串
回到DialogFunc函数,通过观察变量v7到v25定义时的地址(或者双击变量名),我们知道它们在地址中是相邻的`
通过观察代码
sub_4010F0((int)&v7, 0, 10);
memset(&&Str, 0, 0xFFFFu);
&Str = v23;
v28 = v25;
v27 = v24;
v4 = sub_401000((int)&&Str, strlen(&&Str));
memset(&&Str, 0, 0xFFFFu);
v27 = v21;
&Str = v20;
v28 = v22;
v5 = sub_401000((int)&&Str, strlen(&&Str));
那么可以理解,DialogFunc函数中,是把经过sub_4010F0处理后的字符串的倒数第3位到倒数第1位的值赋给v4,sub_401000是将倒数第6~倒数第4位赋给v5。
接下来就进入sub_401000看看了
代码如下
_BYTE *__cdecl sub_401000(int a1, signed int a2)
{
int v2; // eax@1
signed int v3; // esi@1
size_t v4; // ebx@3
_BYTE *v5; // eax@3
_BYTE *v6; // edi@3
signed int v7; // eax@5
_BYTE *v8; // ebx@5
int v9; // edi@8
signed int v10; // edx@8
signed int v11; // edi@11
signed int v12; // eax@11
signed int v13; // esi@11
_BYTE *result; // eax@18
_BYTE *v15; // [sp+Ch] [bp-10h]@3
_BYTE *v16; // [sp+10h] [bp-Ch]@5
signed int v17; // [sp+14h] [bp-8h]@11
int v18; // [sp+18h] [bp-4h]@8
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 = *(_BYTE *)(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[*((_BYTE *)&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,点进去看看,
显而易见这是base64加密,
再次回到DialogFunc通过观察代码if判断语句判断完了就弹出一个MessageBoxA,这个 if 判断应该很关键,先是 v7 + 34,算一下,得到 U ( 51+34),String[0]便是U,然后 v19 == v11,V19排在String[1],所以String[1]是J,下面的两个strcmp应该是上面的base64加密后得到的结果与 ak1w 和 U1Ax 对比.
if ( String == v7 + 34
&& v19 == v11
&& 4 * v20 - 141 == 3 * v9
&& v21 / 4 == 2 * (v14 / 9)
&& !strcmp(v4, "ak1w")
&& !strcmp(v5, "V1Ax") )
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
然后通过python脚本解密ak1w和V1Ax
import base64
str1 = 'ak1w'
str2 = 'V1Ax'
flag1 = base64.b64decode(str1)
flag2 = base64.b64decode(str2)
print(flag1)
print(flag2)
得到 jMp和WP1
然后就是谁前谁后的问题了,看if判断语句中的
&& 4 * v20 - 141 == 3 * v9
&& v21 / 4 == 2 * (v14 / 9)
应该是来验证的,我们算一下第一条,v9是’E’为69,j是106,W是87,一通计算下来,v20是W,那么就是WP1先.
再加上flag格式 即flag{UJWP1jMp}