前言
摸鱼选手表示最近鱼儿很多。。。好吧我摊牌了,最近忙着考驾照,真折磨人啊,赶紧补一补入门的逆向,不然白给了要。本次准备写两个Re的题解,都是来自攻防世界的进阶
正言
re1-100
自从上次入门后,现在做题的顺序基本摸清了,并且现在的题还没有出现啥花指令这些恶心内容,先把文件放到exeinfope
中查看文件属性:
64位的ELF
文件,直接拖到IDApro
来看吧:
// TAGS: ['file']
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
__pid_t v3; // eax
size_t v4; // rax
ssize_t v5; // rbx
bool v6; // al
char **argva; // [rsp+0h] [rbp-1D0h]
bool bCheckPtrace; // [rsp+13h] [rbp-1BDh]
ssize_t numRead; // [rsp+18h] [rbp-1B8h]
ssize_t numReada; // [rsp+18h] [rbp-1B8h]
char bufWrite[200]; // [rsp+20h] [rbp-1B0h]
char bufParentRead[200]; // [rsp+F0h] [rbp-E0h]
unsigned __int64 v13; // [rsp+1B8h] [rbp-18h]
argva = (char **)argv;
v13 = __readfsqword(0x28u);
bCheckPtrace = detectDebugging();
if ( pipe(pParentWrite) == -1 )
exit(1);
if ( pipe(pParentRead) == -1 )
exit(1);
v3 = fork();
if ( v3 != -1 )
{
if ( v3 )
{
close(pParentWrite[0]);
close(pParentRead[1]);
while ( 1 )
{
printf("Input key : ", argva);
memset(bufWrite, 0, 0xC8uLL);
gets(bufWrite, 0LL);
v4 = strlen(bufWrite);
v5 = write(pParentWrite[1], bufWrite, v4);
if ( v5 != strlen(bufWrite) )
printf("parent - partial/failed write", bufWrite);
do
{
memset(bufParentRead, 0, 0xC8uLL);
numReada = read(pParentRead[0], bufParentRead, 0xC8uLL);
v6 = bCheckPtrace || checkDebuggerProcessRunning();
if ( v6 )
{
puts("Wrong !!!\n");
}
else if ( !checkStringIsNumber(bufParentRead) )
{
puts("Wrong !!!\n");
}
else
{
if ( atoi(bufParentRead) )
{
puts("True");
if ( close(pParentWrite[1]) == -1 )
exit(1);
exit(0);
}
puts("Wrong !!!\n");
}
}
while ( numReada == -1 );
}
}
close(pParentWrite[1]);
close(pParentRead[0]);
while ( 1 )
{
memset(bufParentRead, 0, 0xC8uLL);
numRead = read(pParentWrite[0], bufParentRead, 0xC8uLL);
if ( numRead == -1 )
break;
if ( numRead )
{
if ( childCheckDebugResult() )
{
responseFalse();
}
else if ( bufParentRead[0] == '{' )
{
if ( strlen(bufParentRead) == 42 )
{
if ( !strncmp(&bufParentRead[1], "53fc275d81", 0xAuLL) )
{
if ( bufParentRead[strlen(bufParentRead) - 1] == '}' )
{
if ( !strncmp(&bufParentRead[31], "4938ae4efd", 0xAuLL) )
{
if ( !confuseKey(bufParentRead, 42) )
{
responseFalse();
}
else if ( !strncmp(bufParentRead, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 0x2AuLL) )
{
responseTrue();
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
}
exit(1);
}
exit(1);
}
把main
全放上来了,其中__readfsqword(0x28u)
这一段代码我见过不下三回了,这段代码是通常用于alarm函数,防止调试,后续我们还可以看到这里存在很多反调试的机制,因为这个题一调试就没了。。。
v6 = bCheckPtrace || checkDebuggerProcessRunning();
这里还存在两个反调试的,这里就没跟进了,因为题目既然不想让我们反调试,那么说明考点不在这。
来看主要的逻辑:
为bufParentRead
申请了一块内存,接着讲在pParentWrite[0]
读取到的数据存储在bufParentRead
中,其实也就是我们输入的,通过if
判断我们可以知道,如果想responseTrue()
执行,则这一段输入的长度是42,而且以{}为开头结尾,很大概率这就是flag
。
既然要!strncmp
,也就是说两者要相等,可以发现输入的前十位必须是53fc275d81
,第31位开始是4938ae4efd
,也就是说输入必须是
{53fc275d81********************4938ae4efd}
中间我们是不知道的,接下来就是一个confuseKey
,跟进发现这是一个十分低级的混淆:
bool __cdecl confuseKey(char *szKey, int iKeyLength)
{
char szPart1[15]; // [rsp+10h] [rbp-50h]
char szPart2[15]; // [rsp+20h] [rbp-40h]
char szPart3[15]; // [rsp+30h] [rbp-30h]
char szPart4[15]; // [rsp+40h] [rbp-20h]
unsigned __int64 v7; // [rsp+58h] [rbp-8h]
v7 = __readfsqword(0x28u);
*(_QWORD *)szPart1 = 0LL;
*(_DWORD *)&szPart1[8] = 0;
*(_WORD *)&szPart1[12] = 0;
szPart1[14] = 0;
*(_QWORD *)szPart2 = 0LL;
*(_DWORD *)&szPart2[8] = 0;
*(_WORD *)&szPart2[12] = 0;
szPart2[14] = 0;
*(_QWORD *)szPart3 = 0LL;
*(_DWORD *)&szPart3[8] = 0;
*(_WORD *)&szPart3[12] = 0;
szPart3[14] = 0;
*(_QWORD *)szPart4 = 0LL;
*(_DWORD *)&szPart4[8] = 0;
*(_WORD *)&szPart4[12] = 0;
szPart4[14] = 0;
if ( iKeyLength != 42 )
return 0;
if ( !szKey )
return 0;
if ( strlen(szKey) != 42 )
return 0;
if ( *szKey != '{' )
return 0;
strncpy(szPart1, szKey + 1, 10uLL);
strncpy(szPart2, szKey + 11, 0xAuLL);
strncpy(szPart3, szKey + 21, 0xAuLL);
strncpy(szPart4, szKey + 31, 0xAuLL);
memset(szKey, 0, iKeyLength);
*szKey = '{';
strcat(szKey, szPart3);
strcat(szKey, szPart4);
strcat(szKey, szPart1);
strcat(szKey, szPart2);
szKey[41] = '}';
return 1;
}
逻辑应该十分清晰了,直接就是分成四块,然后按照3412的顺序重新拼接得到的是{daf29f59034938ae4efd53fc275d81053ed5be8c}
,因此直接还原就行,duck不必写脚本
{53fc275d81053ed5be8cdaf29f59034938ae4efd}
srm-50
拿到文件同样是先看看是什么属性的文件:
是一个win32gui
,打开后发现是一个要输入邮箱和number的程序:
还是老样子,放到IDApro
里一探究竟:
既然主体就是一个窗口,那么我们首先肯定是找WinMain
这个函数的详解也说明一下:
- 函数原型:
int DialogBoxParam(HINSTANCE hInstance,LPCTSTR IpTemplateName,HWND hWndParent, DLGPROC IPDialogFunc,LPARAM dwlnitParam);
参数:
hlnstance:标识一个模块的实例,该模块的可执行文件含有对话框模板。
IpTemplateName:标识对话框模板。此参数可以指向一个以NULL结尾的字符串的指针,该字符串指定对话框模扳名,或是指定对话框模板的资源标识符的一个整型值。如果此参数指定了一个资源标识符,则它的高位字一定为零,且低位字一定含有标识符。一定用MAKEINTRESOURCE宏指令创建此值。
hWndParent:指定拥有对话框的窗口。
IpDialogFunc:指向对话框过程的指针。有关更详细的关于对话框过程的信息,请参见DialogProc。
dwInitParam:指定传递到对话框过程中的 WM_INITDIALOG 消息 IParam 参数的值。
返回值:如果函数调用成功则返回值为在对函数EndDialog的调用中的nResult参数,该EndDialog函数用于中止对话框。如果函数调用失败,则返回值为-1。若想获得错误信息,请调用GetLastError函数。
因此重点看DialogFunc
的内容,也就是程序的逻辑
BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
HMODULE v5; // eax
HICON v6; // eax
HMODULE v7; // eax
HCURSOR v8; // ST20_4
HWND v9; // eax
CHAR String; // [esp+8h] [ebp-340h]
CHAR v11[4]; // [esp+108h] [ebp-240h]
char v12; // [esp+10Ch] [ebp-23Ch]
char v13; // [esp+10Dh] [ebp-23Bh]
char v14; // [esp+10Eh] [ebp-23Ah]
char v15; // [esp+10Fh] [ebp-239h]
char v16; // [esp+110h] [ebp-238h]
char v17; // [esp+111h] [ebp-237h]
char v18; // [esp+112h] [ebp-236h]
char v19; // [esp+113h] [ebp-235h]
char v20; // [esp+114h] [ebp-234h]
char v21; // [esp+115h] [ebp-233h]
char v22; // [esp+116h] [ebp-232h]
char v23; // [esp+117h] [ebp-231h]
CHAR Text; // [esp+208h] [ebp-140h]
char Src[16]; // [esp+308h] [ebp-40h]
__int128 v26; // [esp+318h] [ebp-30h]
int v27; // [esp+328h] [ebp-20h]
__int128 v28; // [esp+32Ch] [ebp-1Ch]
int v29; // [esp+33Ch] [ebp-Ch]
__int16 v30; // [esp+340h] [ebp-8h]
if ( a2 == 16 )
{
EndDialog(hDlg, 0);
return 0;
}
if ( a2 == 272 )
{
v5 = GetModuleHandleW(0);
v6 = LoadIconW(v5, (LPCWSTR)0x67);
SetClassLongA(hDlg, -14, (LONG)v6);
v7 = GetModuleHandleW(0);
v8 = LoadCursorW(v7, (LPCWSTR)0x66);
v9 = GetDlgItem(hDlg, 1);
SetClassLongA(v9, -12, (LONG)v8);
return 1;
}
if ( a2 != 273 || (unsigned __int16)a3 != 1 )
return 0;
memset(&String, (unsigned __int16)a3 - 1, 0x100u);
memset(v11, 0, 0x100u);
memset(&Text, 0, 0x100u);
GetDlgItemTextA(hDlg, 1001, &String, 256);
GetDlgItemTextA(hDlg, 1002, v11, 256);
if ( strstr(&String, "@") && strstr(&String, ".") && strstr(&String, ".")[1] && strstr(&String, "@")[1] != '.' )
{
v28 = xmmword_410AA0;
v29 = 1701999980;
*(_OWORD *)Src = xmmword_410A90;
v30 = 46;
v26 = xmmword_410A80;
v27 = 3830633;
if ( strlen(v11) != 16
|| v11[0] != 'C'
|| v23 != 'X'
|| v11[1] != 'Z'
|| v11[1] + v22 != 155 // v22=65
|| v11[2] != '9'
|| v11[2] + v21 != 155 // v21=98
|| v11[3] != 'd'
|| v20 != '7'
|| v12 != 'm'
|| v19 != 'G'
|| v13 != 'q'
|| v13 + v18 != 170 // v18=57
|| v14 != '4'
|| v17 != 'g'
|| v15 != 'c'
|| v16 != '8' )
{
strcpy_s(&Text, 0x100u, (const char *)&v28);
}
else
{
strcpy_s(&Text, 0x100u, Src);
strcat_s(&Text, 0x100u, v11);
}
}
else
{
strcpy_s(&Text, 0x100u, "Your E-mail address in not valid.");
}
MessageBoxA(hDlg, &Text, "Registeration", 0x40u);
return 1;
}
这个逻辑也是十分清晰的,我们要的就是v11
,但是看这个题的时候很纳闷,就是只给了v11的前四位,而且后面几位也并没有什么函数涉及,但是双击v11
发现这些变量都是连在一起的:
依次类推,正好16位(v11本身有3位),因此只需要将:
解出来排列好即可,&String
接受的就是邮箱,这里有strstr
其实也就是匹配邮箱是否合格,只要是合格的邮箱都能成功,最后输入解出来的v11
即可:
考完驾照还得多补补Web和密码
,web
是越来越难了,可能也是我越来越菜了,哦不对一直很菜qwq,下一阶段的目标就是先把vue
搞定,然后爬虫再写几个项目,buu的web刷起来!!!!