BUUCTF--Youngter-drive
测试文件:https://www.lanzous.com/ianojbc
如果运行程序提示缺少msvcr100d.dll文件,将此dll文件放程序的同一目录。(https://www.lanzous.com/iap3v6f)
准备
获取信息
- 32位文件
- 存在upx壳
代码分析
upx脱壳之后,打开主函数代码
int __thiscall main_0(void *this) { HANDLE v2; // [esp+D0h] [ebp-14h] HANDLE hObject; // [esp+DCh] [ebp-8h] sub_4110FF(this); ::hObject = CreateMutexW(0, 0, 0); j_strcpy(Dest, &Source); hObject = CreateThread(0, 0, StartAddress, 0, 0, 0); v2 = CreateThread(0, 0, sub_41119F, 0, 0, 0); CloseHandle(hObject); CloseHandle(v2); while ( dword_418008 != -1 ) ; sub_411190(); CloseHandle(::hObject); return 0; }
打开sub_4110FF
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); }
CreateThread函数起到创建新线程,调用函数执行用,值得注意的是这里使用了两次CreateThread,创建线程A,B。程序会执行完A之后,才会执行B,B执行后再次执行A,这样交替执行。
第一处CreateThread
打开StartAddress函数
void __stdcall StartAddress_0(int a1) { while ( 1 ) { WaitForSingleObject(hObject, 0xFFFFFFFF); if ( dword_418008 > -1 ) { sub_41112C(&Source, dword_418008); --dword_418008; Sleep(0x64u); } ReleaseMutex(hObject); } }
打开sub_41112C函数
出现堆栈指针不平衡
转到汇编代码,在option中开启stack pointer
在指针不平衡的上方,按下Alt+K,修改栈指针为0
正常打开函数了
char *__cdecl sub_411940(int a1, int a2) { char *result; // eax char v3; // [esp+D3h] [ebp-5h] 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; }
这就是一个判断大小写字符,对字符进行表替换。off_418000[0]的值为QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbn
第二处CreateThread
void __stdcall sub_411B10(int a1) { while ( 1 ) { WaitForSingleObject(hObject, 0xFFFFFFFF); if ( dword_418008 > -1 ) { Sleep(0x64u); --dword_418008; } ReleaseMutex(hObject); } }
这里少了对我们输入字符的操作函数,就仅仅对dword_418008变量进行减1操作。
sub_411190函数
回到主函数最后有个sub_411190函数,打开
int sub_411880() { int i; // [esp+D0h] [ebp-8h] for ( i = 0; i < 29; ++i ) { if ( Source[i] != off_418004[i] ) exit(0); } return printf("\nflag{%s}\n\n", Dest); }
这里我们了解到输入字符串变换后的值为off_418004,即TOiZiZtOrYaToUwPnToBsOaOapsyS
总结
dword_418008的起始值为0x1D,因此输入字符长度应该为30,在上面三个函数的分析,我们可以知道,程序通过对输入字符串前29个字符进行间隔的字符表替换,得到TOiZiZtOrYaToUwPnToBsOaOapsyS。
脚本
因为不知道哪个线程先执行,所以在写脚本时都试了下,应该是后面那个线程先执行。
# -*- coding:utf-8 -*- dec = "TOiZiZtOrYaToUwPnToBsOaOapsyS" flag = '' result = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm" for i in range(len(dec)): if i % 2 == 0: flag += dec[i] continue if(dec[i].isupper()): flag += chr(result.find(dec[i]) + 96) else: flag += chr(result.find(dec[i]) + 38) print ('flag{'+flag+'}')
get flag!
得到flag{ThisisthreadofwindowshahaIsES},不过我们输入的是30个字符,这里flag里面只有29个字符,最后一个字符应该都可以,不过正确答案是E,大不了一个个试就行。
flag{ThisisthreadofwindowshahaIsESE}