一、刮开有奖
所需工具:
- IDA(反编译工具)
- exeinfope(查壳工具)
- CaptfEncoder(编码转换)
题解:
拖入exeinfope 检查是否套壳[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
发现并没有。
查看文件是32位还是64位。方法如下:使用记事本直接打开你的exe文件。只要看第二行即可,第二行开头不远处有PE两个字母,再后面两个空格后第三个字符就是标记了,如果是字母L的话,就是32位应用程序,如果是d?就表示是64位应用程序。
将.exe文件拖入IDA 32中。F5进行反编译。
在WinMain函数中找到关键函数
DialogFunc
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; }
首先根据
if ( strlen(String) == 8 )
可知String
长度为8。观察v7-v16在寄存器中的位置是连续的:
在后续函数
sub_4010F0
运行中可直接写成一个数组,为了方便后续观察将其定义为char
类型。转入函数
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; 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() { char v[] = "ZJSECaNH3ng"; sub_4010F0(v,0,10); printf("%s", v); return 0; }
获得
v
的值为3CEHJNSZagn
对应回原来的变量如下:3 C E H J … v7[0] v7[1] v8 v9 v10 … 返回
DialogFunc
函数,发现成功条件为: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")
很容易就知道
String[0] = '3' + 34
也就是U
,String[1] = 'J'
而
v4 == "ak1w" && v5 == "V1Ax"
返回去看到函数
sub_401000
发现是Base64编码。转换后可得第一次时v18为
jMp
通过赋值语句:可知:
String[5]-[7] = "jMp"
同理第二次的解码可以得到
Sting[2]-[4] = "WP1"
最终将
String
拼接起来,获得flag:flag{UJWP1jMp}
版权声明:本文为firfis原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。