• 网鼎杯-re-signal


    下载附件打开后就是一个32位的pe文件,用IDA(32)打开,F5定位至主函数main处,如图:

     函数_main()就是一个初始化的函数,qmemcpy则是将unk_403040地址处的值赋给v4,再进入vm_operad()函数,可知,vm_operad()函数既是关键函数

    进入后,如下:

     1 int __cdecl vm_operad(int *a1, int a2)
     2 {
     3   int result; // eax
     4   char Str[100]; // [esp+13h] [ebp-E5h]
     5   char v4[100]; // [esp+77h] [ebp-81h]
     6   char v5; // [esp+DBh] [ebp-1Dh]
     7   int v6; // [esp+DCh] [ebp-1Ch]
     8   int v7; // [esp+E0h] [ebp-18h]
     9   int v8; // [esp+E4h] [ebp-14h]
    10   int v9; // [esp+E8h] [ebp-10h]
    11   int v10; // [esp+ECh] [ebp-Ch]
    12 
    13   v10 = 0;
    14   v9 = 0;
    15   v8 = 0;
    16   v7 = 0;
    17   v6 = 0;
    18   while ( 1 )
    19   {
    20     result = v10;
    21     if ( v10 >= a2 )//是否>=114
    22       return result;
    23     switch ( a1[v10] )//若a1[v10]存在
    24     {
    25       case 1:
    26         v4[v7] = v5;
    27         ++v10;
    28         ++v7;
    29         ++v9;
    30         break;
    31       case 2:
    32         v5 = a1[v10 + 1] + Str[v9];
    33         v10 += 2;
    34         break;
    35       case 3:
    36         v5 = Str[v9] - LOBYTE(a1[v10 + 1]);#这里取了a1[v10+1]的低位,数据中有FFFFFF开头的,这里为负数,如FFFFFFF1,取它的低位,也就是F1
    37         v10 += 2;
    38         break;
    39       case 4:
    40         v5 = a1[v10 + 1] ^ Str[v9];
    41         v10 += 2;
    42         break;
    43       case 5:
    44         v5 = a1[v10 + 1] * Str[v9];
    45         v10 += 2;
    46         break;
    47       case 6:
    48         ++v10;
    49         break;
    50       case 7:
    51         if ( v4[v8] != a1[v10 + 1] )//若有一个不等的情况存在,则直接退出程序
    52         {
    53           printf("what a shame...");
    54           exit(0);
    55         }
    56         ++v8;
    57         v10 += 2;
    58         break;
    59       case 8:
    60         Str[v6] = v5;
    61         ++v10;
    62         ++v6;
    63         break;
    64       case 10:
    65         read(Str);
    66         ++v10;
    67         break;
    68       case 11:
    69         v5 = Str[v9] - 1;
    70         ++v10;
    71         break;
    72       case 12:
    73         v5 = Str[v9] + 1;
    74         ++v10;
    75         break;
    76       default:
    77         continue;
    78     }
    79   }
    80 }

    该函数的逻辑就是根据传入的a1的值,遍历进入相应的case,并执行相应的操作。但有一个我们注意的点,那就是我们要找到进行输入的函数或者操作在哪。

    观察该函数,发现case 10中有一个read(Str)操作,进去后发现

     因此我们就知道了,首先要进入case 10,我们才能进行输入,而且,字符串的大小为15位!,否则错误并退出程序。

    回到vm_operad()函数中,我们要去查看a1(也就是传入的v4)地址处的值,才能知道具体进行了什么操作。把数据提取后如图:

     数据第一个恰是10,也就是会执行case 10里的操作,输入我们的数据,继续遍历进行操作,直至进入case 7中,v4[v8]的值等于a1[v10+1],这里我们要注意,是在a1[v10]==7的时候,我们才进入case 7 ,因此a1[v10+1]=a1中,7数字之后的数,也就是说,输入的数据进行一系列的操作后,要与每个7之后的数据相比较,将这些数据提取出来,即为 [34, 63, 52, 50,   114, 51, 24, -89,  49,-15, 40,-124,  -63,  30,  122]。

    到这里该函数的逻辑就清楚了,我们只需要根据遍历传入的值,将进行的操作逆一下就可以了。这里有几种做法:

    ①动态调试,写出相应的操作后,直接写出逆回去的式子

    ②使用c/c++或者python,根据算法,更改数据顺序,写出逆算法(这里可以参考https://bbs.pediy.com/thread-259595.htm

    ③使用angr写脚本直接得出

    下面介绍第一种和第三种。

    方法①:

    使用IDA动态调试(选择remote windows server, 在本地运行win32_remote.exe),运行后如下:

     设置好相应断点

     单步运行,写下相应的操作,如下:

    1. v4[0]=(Str[0]^0x10)-5
    2. v4[1]=(Str[1]^0x20)*3
    3. v4[2]=(Str[2]-2)-1
    4. v4[3]=(Str[3]+1)^4
    5. v4[4]=(Str[4]*3)&0xff-0x21
    6. v4[5]=(Str[5]-1)-1 7. v4[6]=(Str[6]^0x9)-0x20 8. v4[7]=(Str[7]+0x51)^0x24 9. v4[8]=(Str[8]+1)-1 10.v4[9]=(Str[9]*2)+0x25 11.v4[10]=(Str[10]+0x36)^0x41 12.v4[11]=(Str[11]+0x20)*1 13.v4[12]=(Str[12]*3)+0x25 14.v4[13]=(Str[13]^9)-0x20 15.v4[14]=(Str[14]+0x41)+1

    逆过去后,脚本如下:

    b=[34, 63, 52, 50,   114, 51, 24, 256-89,  49,256-15, 40,256-124,  256-63,  30,  122]#(负数取了最低位,因此,直接取后面那部分)
    flag=[0]*15
    flag[0]=(b[0]+5)^0x10
    flag[1]=(b[1]//3)^0x20
    flag[2]=(b[2]+3)
    flag[3]=(b[3]^4)-1
    flag[4]=(b[4]+0x21)//3
    flag[5]=b[5]+2
    flag[6]=(b[6]+0x20)^9
    flag[7]=(b[7]^0x24)-0x51
    flag[8]=b[8]
    flag[9]=(b[9]-0x25)//2
    flag[10]=(b[10]^0x41)-0x36
    flag[11]=(b[11]-0x20)
    flag[12]=(b[12]-0x25)//3
    flag[13]=(b[13]+0x20)^9
    flag[14]=(b[14]-1)-0x41
    print(flag)
    
    for i in flag:
        print(chr(i),end='')

    运算结果如下:

     方法③:

    脚本如下:

    import angr
    import sys
    
    def Go():
        path_to_binary="signal.exe"
        project=angr.Project(path_to_binary,auto_load_libs=False)
        initial_state=project.factory.entry_state()
        simulation=project.factory.simgr(initial_state)
    
        simulation.explore(find=0x0040179E,avoid=0x004016E6)
    
        if simulation.found:
            solution_state=simulation.found[0]
            solution=solution_state.posix.dumps(0)
            print("[+] Success! Solution is: ",solution)
        
        else:
            print('Could not find the solution')
    
    if __name__ == "__main__":
        Go()

    结果如下:

     有关angr可以参考以下三篇文章:

    https://www.anquanke.com/post/id/212816

    https://www.anquanke.com/post/id/213423

    https://www.anquanke.com/post/id/214288

  • 相关阅读:
    Linux 部署 .net
    转载:什么才是真正的 RESTful 架构
    Web Api资料
    nginx 配置
    Nginx 资料
    session、cookie资料
    WCF 资料
    投资
    解决在控制层springmvc框架发出的400状态的错误
    解决springmvc在单纯返回一个字符串对象时所出现的乱码情况(极速版)
  • 原文地址:https://www.cnblogs.com/jane315/p/13631939.html
Copyright © 2020-2023  润新知