查壳upx壳,直接拖入kali upx -d
脱壳,尝试运行无果,拖入ida F5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int sub_411BD0 () { printf ( "1111111111111111111111111111111111111111111111111111111111111111111111111111111\n" "*******************************************************************************\n" "************** ****************************************************\n" "************** ******** ********************* *************\n" "************** ********* ********************* ***************************\n" "************** ********* ********************* ***************************\n" "************** ********* ********************* ***************************\n" "************** ******* ********************** ***************************\n" "************** **** ************************* ***************************\n" "************** * *************************** **************\n" "************** *** ************************* ***************************\n" "************** ****** *********************** ***************************\n" "************** ******** ********************* ***************************\n" "************** ********** ******************* ***************************\n" "************** *********** ***************** *************\n" "*******************************************************************************\n" "1111111111111111111111111111111111111111111111111111111111111111111111111111111\n" ); printf ("input flag:\n" ); return scanf ("%36s" , &Source); }
sub_411BD0
函数是把输入的 flag 存到全局缓冲区 Source
里面,而且最多只存 36 个字符
由API可知这道题涉及到多线程,CreateThread
API 会创建新线程,CreateMutex
创建一个互斥变量,用于防止多线程中出现资源争用,此变量存于全局句柄::hObject
中。创建了两线程StartAddress
、sub_41119F
,跟进查看,两者代码相似,区别在与StartAddress
中的一句sub_41112C(&Source, dword_418008);
。两者交替执行,直至dword_418008 > -1
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void __stdcall StartAddress_0 (int a1) { while ( 1 ) { WaitForSingleObject(hObject, 0xFFFFFFFF ); if ( dword_418008 > -1 ) { sub_41112C(&Source, dword_418008); --dword_418008; Sleep(0x64 u); } ReleaseMutex(hObject); } } void __stdcall sub_411B10 (int a1) { while ( 1 ) { WaitForSingleObject(hObject, 0xFFFFFFFF ); if ( dword_418008 > -1 ) { Sleep(0x64 u); --dword_418008; } ReleaseMutex(hObject); } }
跟入sub_41112C
却警告positive sp value has been found
,百度得知是栈平衡缺失所导致的问题(函数调用前的sp值与调用结束的sp值不相同),可手动调整sp值解决。
在 选项 -> 常规中勾选堆栈指针,即可在地址旁显示sp的值
调到警告处,可发现sp值确实发生了偏差,在sp值为0时,又运行了pop edp
这条语句导致栈不平衡
在00411A03
使用快捷键Alt+k
,将新旧sp差值改为0x0,修改完成后即可F5得到伪代码
此函数就是加密函数,参数a1
为缓冲区Source地址,a2
为常量dword_418008
,值为29,线程每执行一次减一,两线程公用。sub_411940
判断了字符是不是字母再变换,大写字母变换成 off_418000[0][*(_BYTE *)(a2 + a1) - 38]
,小写字母变换成 off_418000[0][*(_BYTE *)(a2 + a1) - 96]
, off_418000[0]
是一个字符串,内容是 QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm
。注意是两线程交替执行,所以只会有一半的字符改变 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 char *__cdecl sub_411940 (int a1, int a2) { char *result; char v3; v3 = *(_BYTE *)(a2 + a1); if ( (v3 < 97 || v3 > 122 ) && (v3 < 65 || v3 > 90 ) ) exit (0 ); if ( v3 < 97 || v3 > 122 ) { result = off_418000[0 ]; *(_BYTE *)(a2 + a1) = off_418000[0 ][*(char *)(a2 + a1) - 38 ]; } else { result = off_418000[0 ]; *(_BYTE *)(a2 + a1) = off_418000[0 ][*(char *)(a2 + a1) - 96 ]; } return result; }
看到sub_411190()
,此函数用经变换过后的Source
与off_418004[]
逐位对比,off_418004[]
内容为TOiZiZtOrYaToUwPnToBsOaOapsyS
1 2 3 4 5 6 7 8 9 10 11 int sub_411880 () { int i; for ( i = 0 ; i < 29 ; ++i ) { if ( Source[i] != off_418004[i] ) exit (0 ); } return printf ("\nflag{%s}\n\n" , Dest); }
结合以上分析给出以下脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 off_418000 = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm" off_418004 = "TOiZiZtOrYaToUwPnToBsOaOapsyS" flag='' for i in range(len(off_418004)): if i %2 == 0 : flag += off_418004[i] continue for j,k in enumerate(off_418000): if off_418004[i] == k: if chr(j+38 ).isupper(): flag += chr(j+38 ) else : flag += chr(j+96 ) print (flag)
但得出的flag提交发现不正确,百度知道还需添加一个字符E
才能通过。之后发现变换后的字符串末尾应该有结束符,所以输入的 flag 应该比变换后的字符串多一个字符,因为多出来的那个字符经过变换会变成结束符。
请我吃生蚝
感谢鼓励