32位窗口程序,用IDA打开,定位到一个主要函数DialogFunc,对函数的变量名重命名了一下,然后为了方便观察生成了一些数组,最终伪代码整理如下
INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int s[11]; // [esp+8h] [ebp-20030h] BYREF
CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
char temp[65536]; // [esp+10034h] [ebp-10004h] BYREF
if ( a2 == 272 )
return 1;
if ( a2 != 273 )
return 0;
if ( a3 == 1001 )
{
memset(String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, String, 0xFFFF);
if ( strlen(String) == 8 )
{
s[0] = 'Z';
s[1] = 'J';
s[2] = 'S';
s[3] = 'E';
s[4] = 'C';
s[5] = 'a';
s[6] = 'N';
s[7] = 'H';
s[8] = '3';
s[9] = 'n';
s[10] = 'g';
sub_4010F0(s, 0, 10);
memset(temp, 0, 0xFFFFu);
temp[0] = String[5];
temp[2] = String[7];
temp[1] = String[6];
v4 = sub_401000(temp, strlen(temp));
memset(temp, 0, 0xFFFFu);
temp[1] = String[3];
temp[0] = String[2];
temp[2] = String[4];
v5 = sub_401000(temp, strlen(temp));
if ( String[0] == s[0] + 34
&& String[1] == s[4]
&& 4 * String[2] - 141 == 3 * s[2]
&& String[3] / 4 == 2 * (s[7] / 9)
&& !strcmp(v4, "ak1w")
&& !strcmp(v5, "V1Ax") )
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
}
return 0;
}
if ( a3 != 1 && a3 != 2 )
return 0;
EndDialog(hDlg, a3);
return 1;
}
里面有个sub_4010F0函数,算法逻辑比较复杂,是对s字串进行处理(网上说是排序算法)要想得到处理后的结果可以有2种方法
- 一种方法可以直接OD调试运行一下可以从内存单元中得到最终结果,没必要复现内部算法,
- 也可以把伪代码修改成可运行的C代码,放到编译器运行一下
这2种方法我都进行了尝试。
先试试OD。一直单步运行到DialogFunc函数里后,执行那个函数之前有几处跳转语句,一旦触发跳转且会跳转到别处,我们就直接汇编成对应的反条件跳转指令,确保程序执行函数4010F0。
最后发现这个函数直接在栈内对字符串进行修改的,OD没有将其视作字符来处理(因为是对这些字符串的处理是以双字为单位进行操作,而非字节),所以直接数据窗口跟随可以看到最终结果。
另一种方法是把IDA反编译出来的代码扒拉下来(如下)放到IDE里运行也可以得出结果
#include<stdio.h>
using namespace std;
int sub_4010F0(char S[], int a, int b)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx
result = b;
for (i = a; i <= b; a = i)
{
v5 = i;
v6 = S[i];
if (a < result && i < result)
{
do
{
if (v6 > S[result])
{
if (i >= result) break;
++i;
S[v5] = S[result];
if (i >= result) break;
while (S[i] <= v6)
{
if (++i >= result)
goto LABEL_13;
}
if (i >= result) break;
v5 = i;
S[result] = S[i];
}
--result;
} while (i < result);
}
LABEL_13:
S[result] = v6;
sub_4010F0(S, a, i - 1);
result = b;
++i;
}
return result;
}
int main() {
char s[11] = { 'Z','J','S','E','C','a','N','H','3','n','g' };
sub_4010F0(s, 0, 10);
for (int i = 0; i < 11; i++)
{
printf("%c", s[i]);
}
}
运行结果与OD的一致
最终得到3CEHJNSZagn
,确实是对ASCII码进行了排序操作。👏👏👏
然后关键的函数是sub_401000
进去看看发现是base64编码
接下来分析完毕,整理一下exp
import base64
string=['' for i in range(8)]
key1='ak1w'
key2='V1Ax'
a=base64.b64decode(key1).decode('utf-8')
string[5]=a[0]
string[7]=a[2]
string[6]=a[1]
a=base64.b64decode(key2).decode('utf-8')
string[3]=a[1]
string[2]=a[0]
string[4]=a[2]
s='3CEHJNSZagn'
string[0]=chr(ord(s[0])+34)
string[1]=s[4]
string[2]=chr((3*ord(s[2])+141)//4)
flag=''
print('flag{'+flag.join(string)+'}')
flag{UJWP1jMp}