BUUCTF-RE-Youngter-drive

一、查壳

BUUCTF-RE-Youngter-drive

 

 

 UPX壳

二、脱壳

可以直接构造命令脱壳

(你没安装【UPX】是不能直接upx -d 的哈)

也可以使用工具

BUUCTF-RE-Youngter-drive

三、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()  函数

BUUCTF-RE-Youngter-drive

进入  ②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、函数的堆栈是不平衡

BUUCTF-RE-Youngter-drive

 解决方法:

 在菜单栏的Options→General,然后勾选stack pointer

然后找到出问题的部分 

在View里查看他的汇编代码

解决方法(1)这个是野路子,我也不大明白原理,但可以试试

从跳转的角度

BUUCTF-RE-Youngter-drive

 

 

 跳到下一个函数时,是用的条件跳转,将jz改为jmp(无条件跳转)也可以解决问题

解决方法(2)参考

IDA中Options->General选中Stack pointer可以查看堆栈指针

如果是由于堆栈不平衡导致的,可以看到retn前面是-04,修改pop

BUUCTF-RE-Youngter-drive

 

 

选中它,快捷键 ait+k,修改为以下值

BUUCTF-RE-Youngter-drive

 

 

 再次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脱壳

多线程运行

上一篇:AIX 输入lsdev -Cc disk 报错 lsdev: 0514-521 Cannot find information in the predefined device


下一篇:Google Drive:在线创建谷歌团队无限网盘(转载)