BUUCTF[刮开有奖]
下载附件后,拖进die分析
32位无壳,直接拖进ida分析
shift+f12查看字符串
发现可疑字符串,双击进去发现包含它的函数
双击函数名后按Tab/F5反编译查看函数
INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int v7[2]; // [esp+8h] [ebp-20030h] BYREF
int v8; // [esp+10h] [ebp-20028h]
int v9; // [esp+14h] [ebp-20024h]
int v10; // [esp+18h] [ebp-20020h]
int v11; // [esp+1Ch] [ebp-2001Ch]
int v12; // [esp+20h] [ebp-20018h]
int v13; // [esp+24h] [ebp-20014h]
int v14; // [esp+28h] [ebp-20010h]
int v15; // [esp+2Ch] [ebp-2000Ch]
int v16; // [esp+30h] [ebp-20008h]
CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
char v18[65536]; // [esp+10034h] [ebp-10004h] BYREF
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 )
{
v7[0] = 90;
v7[1] = 74;
v8 = 83;
v9 = 69;
v10 = 67;
v11 = 97;
v12 = 78;
v13 = 72;
v14 = 51;
v15 = 110;
v16 = 103;
sub_4010F0(v7, 0, 10);
memset(v18, 0, 0xFFFFu);
v18[0] = String[5];
v18[2] = String[7];
v18[1] = String[6];
v4 = (const char *)sub_401000(v18, strlen(v18));
memset(v18, 0, 0xFFFFu);
v18[1] = String[3];
v18[0] = String[2];
v18[2] = String[4];
v5 = (const char *)sub_401000(v18, strlen(v18));
if ( String[0] == v7[0] + 34
&& String[1] == v10
&& 4 * String[2] - 141 == 3 * v8
&& String[3] / 4 == 2 * (v13 / 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;
}
DialogFunc()函数
DialogFunc()
函数的作用通常是处理对话框的创建和消息传递。这个函数通常与图形用户界面(GUI)编程有关,特别是在使用 Windows API 或类似库时。它处理由对话框窗口接收的所有消息,例如按钮点击、文本输入等。
memset函数
memset()
函数是 C 标准库中的一个函数,用于将一块内存区域的内容设置为指定的值。它通常用于初始化或重置内存中的数据。
GetDlgItemTextA()函数
GetDlgItemTextA()
函数是 Windows API 中的一个函数,用于从指定的对话框控件(例如编辑框、静态文本控件等)中检索文本。这个函数是 GetDlgItemText()
函数的 ANSI 版本(带有 A
后缀),用于处理 ANSI 字符集。
加密逻辑分析
首先看这一部分
10h-08h=08h,一个int四个字节,正好对应v7两个值的内存
14h-10h=04h,也是一个int类型的4个字节
即v7-v16为一个数组的值
继续往下看,发现加密函数sub_4010F0()
继续往下看
发现v4,v5是分别在string对v18赋值后再经过sub_401000()函数
并将最后的结果v4与ak1w比较,v5与V1Ax比较
所以加密的逻辑为,v7数组先经过sub_4010F0()函数得到新的数组值,再将新的数组值赋值给v4,v5。v4,v5经过函数sub_401000()再与给出的两个字符串做比较。所以我们的重心便是分析两个加密函数
sub_401000()函数
首先看sub_401000()
观察函数发现是典型的base64加密,再看byte_407830发现是基础的base表
那么将已得到的字符串进行base64解密
分别得到string的第2,3,4个字符(v5比较的字符串base64解密)为:WP1
string的第5,6,7个字符(v4比较的字符串base64解密)为:jMp
sub_4010F0()函数
进入分析,就是一个加密的算法,直接将函数整个复制下来
其中的 _DWORD 对应的就是无符号字符类型
再根据这四个判断还原出string的前四个字符
#include <bits/stdc++.h> //竞赛编程中常用头文件,包含所有c++标准库头文件
#define _DWORD unsigned char
using namespace std;
char v7[15] = {90, 74, 83, 69, 67, 97, 78, 72, 51, 110, 103};
char string[10];
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 = *(_DWORD *)(i + a1);
if (a2 < result && i < result)
{
do
{
if (v6 > *(_DWORD *)(a1 + result))
{
if (i >= result)
break;
++i;
*(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + result);
if (i >= result)
break;
while (*(_DWORD *)(a1 + i) <= v6)
{
if (++i >= result)
goto LABEL_13;
}
if (i >= result)
break;
v5 = i;
*(_DWORD *)(a1 + result) = *(_DWORD *)(i + a1);
}
--result;
} while (i < result);
}
LABEL_13:
*(_DWORD *)(a1 + result) = v6;
sub(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
int main()
{
sub_4010F0(v7, 0, 10);
cout << v7 << endl;
string[0] = v7[0] + 34;
string[1] = v7[4];
string[2] = (3 * v7[2] + 141) / 4;
string[3] = 8 * (v7[7] / 9);
cout << string << endl;
}
运行代码得到
再去前文得到的string2,3,4,5,6,7组合得到flag
flag{UJWp1jMp}