今天做了一道reverse,根本不会
看完wp(re学习笔记(9)BUUCTF-re-刮开有奖_re 刮开有奖_Forgo7ten的博客-CSDN博客),感觉茅塞顿开,遂写之
可以说我是抄他的,事实上是看了一遍他的然后自己做了一遍再写了一下wp巩固!
0x1题目描述
给了一个32位exe文件,打开后并不能得到有效的信息。
如下图:(跟没没啥按钮,我咋刮,这不扯犊子)
0x2 ida分析
丢入ida,映入眼帘的是一坨啥也不知道的代码。但是我知道WinMain可是一个exe文件的开始函数啊,随看向WinMain
笑了,这是什么啊!
Microsoft的文档:
从对话框模板资源创建模式对话框。 在显示对话框之前,函数会将应用程序定义的值作为WM_INITDIALOG消息的 lParam 参数传递给对话框过程。 应用程序可以使用此值初始化对话框控件。
INT_PTR DialogBoxParamA( [in, optional] HINSTANCE hInstance, [in] LPCSTR lpTemplateName, [in, optional] HWND hWndParent, [in, optional] DLGPROC lpDialogFunc, [in] LPARAM dwInitParam );
hInstance:
包含对话框模板的模块的句柄。 如果此参数为 NULL,则使用当前可执行文件。
lpTemplateName:
对话框模板。
hWndParent:
拥有对话框的窗口的句柄。
lpDialogFunc:
指向对话框过程的指针。
dwInitParam:
要传递给WM_INITDIALOG消息的 lParam 参数中的对话框的值。
不管咋样吧,前两个参数是模板,第三个是窗口句柄,第四个是指针,第五个是值。
不可能在模板中嵌入flag吧,那就只能去指针里头转转。
里头是这个鬼样子:
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 // 90+34=124
&& 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;
}
长度是8
进入红框中的函数看看
1. sub_4010F0:
从a2到a3的循环,清一色去(4*i+a1),a1应当是数组的首地址,a2,a3是数组的边界索引。
将其变为可执行的C代码(怎么变的??就复制到vs里头,然后哪里爆红改哪里)
注意:所有的4*i全改为i,因为他之前是汇编dword,访问下一个元素,地址需要+4,在c语言orc++之中,直接索引+1就可以解决了
int 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 = 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 = 4 * 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;
}
将v7中的元素全都弄进去。得到:
变成char
2.第二个函数
// a1 为 v9的地址
// a2 为 v9的长度
_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[4]; // [esp+18h] [ebp-4h]
v2 = a2 / 3;
v3 = 0;
if ( a2 % 3 > 0 )
++v2;
v4 = 4 * v2 + 1; // v4为长度
//
// 当进行编码的数据长度是3的倍数时,len=strlen(str_in)/3*4;
// 当进行编码的数据长度不是3的倍数时,len=(strlen(str_in)/3+1)*4;
// 为Base64加密
v5 = malloc(v4);
v6 = v5; // v6指向分配的动态内存空间
v15 = v5; // v15指向分配的动态内存空间
if ( !v5 )
exit(0); // 动态分配内存失败,退出
memset(v5, 0, v4); // 给分配的v5清零
v7 = a2;
v8 = v6; // v8指向分配的动态内存空间
v16 = v6; // v16指向分配的动态内存空间
if ( a2 > 0 )
{
while ( 1 )
{
v9 = 0;
v10 = 0;
v18[0] = 0;
do
{
if ( v3 >= v7 )
break;
++v10;
v9 = *(a1 + v3++) | (v9 << 8); // 将v9左移8位,然后与v9各个位二进制求或
}
while ( v10 < 3 );
v11 = v9 << 8 * (3 - v10); // v9向左移8的倍数位
v12 = 0;
v17 = v3;
v13 = 18;
do
{
if ( v10 >= v12 )
{
*(v18 + v12) = (v11 >> v13) & 63;
v8 = v16;
}
else
{
*(v18 + v12) = 64;
}
*v8++ = byte_407830[*(v18 + v12)]; // byte_407830 存放 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
// 此处更证明了为Base64加密
v13 -= 6;
++v12;
v16 = v8;
}
while ( v13 > -6 );
v3 = v17;
if ( v17 >= a2 )
break;
v7 = a2;
}
v6 = v15;
}
result = v6;
*v8 = 0;
return result;
}
4.在原函数中,有写成功条件:
string[0]==v7[0]+34=='3'+34= 'U'
string[1]=v10='J'
string[2]=(3*'E'+141)/4='W'
string[3]='Z'/9*2*4='P'
v4="ak1w"
v5="V1Ax"
在sub_401000函数中,可以看出是Base64编码,所以我们可以将v4带入base64解码里头,可以得到
string[5]='j'
string[6]='M'
string[7]='p'
同理可以得到:string[4]='1'
string="UJWP1jMP"
所以flag就是flag{UJWP1jMP}
0x3 总结
.base64加密的特征:
存有“A~Za~z0~9+/=”
数组长度是3的倍数-》直接长度÷3*4,若不是,则÷③后+1再×4