一、查壳
UPX壳
二、脱壳
可以直接构造命令脱壳
(你没安装【UPX】是不能直接upx -d 的哈)
也可以使用工具
三、IDA分析
1 int main_0() 2 { 3 HANDLE v1; // [esp+D0h] [ebp-14h] //HANDLE等价于void* 4 HANDLE hObject; // [esp+DCh] [ebp-8h] 5 6 sub_4110FF(); //输入字符串的函数,下展开① 7 ::hObject = CreateMutexW(0, 0, 0); 8 j_strcpy(Dest, Source); //把输入的字符串,复制给Dest 9 hObject = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)StartAddress, 0, 0, 0);//加密函数② 10 v1 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_41119F, 0, 0, 0);//CreateThread就是创建线程,所以推测这是多线程运行。sub_41119F③ 11 CloseHandle(hObject); //②和③的两个进程会交替执行 12 CloseHandle(v1); 13 while ( dword_418008 != -1 ) 14 ; 15 sub_411190();//④ 16 CloseHandle(::hObject); 17 return 0; 18 }
进入 ①sub_4110FF() 函数
进入 ②StartAddress 函数 先进入该函数加密,然后通过sleep()函数暂停线程,切到另一个线程执行函数③...循环到flag完整输出,也就是说对字符串的奇数位加密,偶数位不加密
1 void __stdcall StartAddress_0(int a1) 2 { 3 while ( 1 ) 4 { 5 WaitForSingleObject(hObject, 0xFFFFFFFF); 6 if ( dword_418008 > -1 ) 7 { 8 sub_41112C((int)Source, dword_418008); //这个函数进不去 9 --dword_418008; 10 Sleep(0x64u); 11 } 12 ReleaseMutex(hObject); 13 } 14 }
sub_41112C() 提示如下图 参考资料1、参考资料2
不能F5的原因 可能有两个
1、是在于跳转,一个函数里面的一个跳转在未知的情况下会出现不能F5
2、函数的堆栈是不平衡
解决方法:
在菜单栏的Options→General,然后勾选stack pointer
然后找到出问题的部分
在View里查看他的汇编代码
解决方法(1)这个是野路子,我也不大明白原理,但可以试试
从跳转的角度
跳到下一个函数时,是用的条件跳转,将jz改为jmp(无条件跳转)也可以解决问题
解决方法(2)参考
IDA中Options->General选中Stack pointer可以查看堆栈指针
如果是由于堆栈不平衡导致的,可以看到retn前面是-04,修改pop
选中它,快捷键 ait+k,修改为以下值
再次F5
进入加密函数sub_41112C()函数中
1 char *__cdecl sub_411940(int a1, int a2)//a1,a2分别是数据Source,dword_418008=1Dh 2 { 3 char *result; // eax 4 char v3; // [esp+D3h] [ebp-5h] 5 6 v3 = *(_BYTE *)(a2 + a1); 7 if ( (v3 < ‘a’ || v3 > 'z') && (v3 < 'A' || v3 > 'Z') ) //不是字母退出 8 exit(0); 9 if ( v3 < 'a' || v3 > 'z' ) //是大写字母与字符串进行替换并-38 10 { 11 result = off_418000[0];//off_418000=QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasd 12 *(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 38]; 13 } 14 else 15 { 16 result = off_418000[0];//是小写字母与字符串替换后-96 17 *(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 96]; 18 } 19 return result; 20 }
进入 ③sub_41119F()函数中
1 void __stdcall sub_411B10(int a1) 2 { 3 while ( 1 ) 4 { 5 WaitForSingleObject(hObject, 0xFFFFFFFF); 6 if ( dword_418008 > -1 ) 7 { 8 Sleep(0x64u); 9 --dword_418008; 10 } 11 ReleaseMutex(hObject); 12 } 13 }
进入④sub_411190()函数
1 int sub_411880() 2 { 3 int i; // [esp+D0h] [ebp-8h] 4 5 for ( i = 0; i < 29; ++i ) 6 { 7 if ( Source[i] != off_418004[i] ) //off_418004[i]='TOiZiZtOrYaToUwPnToBsOaOapsyS' 8 exit(0); //比较source[i]的值与off_418004[i] 9 } 10 return printf("\nflag{%s}\n\n", Dest); 11 }
解题脚本:
a='TOiZiZtOrYaToUwPnToBsOaOapsyS' b='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasd' flag='' for i in range(len(a)): if i%2==0: flag+=a[i] continue for j,k in enumerate(b):#enumerate()函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标j是数据项,k是下标项 if a[i]==k: if chr(j+38).isupper(): # isupper() 检查所传的字符是否是大写字母 flag+=chr(j+38) else: flag+=chr(j+96) print(flag)
由于后面得出的flag函数只加密29个字母,但实际上你加密的有30个,所以最后一个在安恒赛里是可以随便填写的。
30个这个点是在第二个线程里dword_418008初始值为1Dh,即10进制的29。它是从29开始-1,那么就是30位。
但是BUU比较不智能,只能加E。
四、flag
flag{ThisisthreadofwindowshahasESqE}
五、收获点
解决不能F5的问题
提示411A04:positive sp value has been found
UPX脱壳
多线程运行