lizhirui 发表于 2019-4-21 21:12

(原创)第十二届全国大学生信息安全竞赛strange_int题解

本帖最后由 lizhirui 于 2019-4-26 11:01 编辑

本题的原理是虚拟机
题目下载链接如下:

本题下载之后发现一个Image.bin文件,使用Winhex打开后,可以很容易发现在偏移0x1FE处有55 AA,表示这是一个分区,联合一些经验,可以初步判断这是软盘镜像的MBR,因此,使用IDA以16位方式加载这个Binary,可以得到这样的代码:seg000:0000               jmp   far ptr 7C0h:5
seg000:0005 ; ---------------------------------------------------------------------------
seg000:0005               mov   ax, cs
seg000:0007               mov   ds, ax
seg000:0009               mov   ss, ax
seg000:000B               mov   sp, 400h
seg000:000E               cld
seg000:000F               mov   ax, 3
seg000:0012               int   10h             ; - VIDEO - SET VIDEO MODE
seg000:0012                                       ; AL = mode
seg000:0014               mov   dx, 0
seg000:0017               mov   cx, 2
seg000:001A               mov   ax, 1000h
seg000:001D               mov   es, ax
seg000:001F               assume es:nothing
seg000:001F               xor   bx, bx
seg000:0021               mov   ax, 228h
seg000:0024               int   13h             ; DISK - READ SECTORS INTO MEMORY
seg000:0024                                       ; AL = number of sectors to read, CH = track, CL = sector
seg000:0024                                       ; DH = head, DL = drive, ES:BX -> buffer to fill
seg000:0024                                       ; Return: CF set on error, AH = status, AL = number of sectors read
seg000:0026               jnb   short loc_2A
seg000:0028
seg000:0028 loc_28:                                 ; CODE XREF: seg000:loc_28j
seg000:0028               jmp   short loc_28
seg000:002A ; ---------------------------------------------------------------------------
seg000:002A
seg000:002A loc_2A:                                 ; CODE XREF: seg000:0026j
seg000:002A               cli
seg000:002B               mov   ax, 1000h
seg000:002E               mov   ds, ax
seg000:0030               assume ds:nothing
seg000:0030               xor   ax, ax
seg000:0032               mov   es, ax
seg000:0034               assume es:nothing
seg000:0034               mov   cx, 2000h
seg000:0037               sub   si, si
seg000:0039               sub   di, di
seg000:003B               rep movsb
seg000:003D               mov   ax, 7C0h
seg000:0040
seg000:0040 loc_40:                                 ; DATA XREF: seg000:0012r
seg000:0040               mov   ds, ax
seg000:0042               assume ds:nothing
seg000:0042               lidt    fword ptr ds:6Fh ; 初始化GDT和IDT
seg000:0047               lgdt    fword ptr ds:75h
seg000:004C
seg000:004C loc_4C:                                 ; DATA XREF: seg000:0024r
seg000:004C               mov   ax, 1
seg000:004F               lmsw    ax            ; 开启保护模式
seg000:0052               jmp   far ptr 8:0

其中第一行的jmp far ptr 7c0h:5,由于MBR的加载地址是0x7C00处,联合Intel处理器实模式的寻址规则,可以得出,这个跳转的目标地址是0x7C05,也就是紧接着的下一条指令,下面的代码很明显在初始化段寄存器和栈指针。
紧接着int 10h调用BIOS中断设置了一下显示模式,接下来的int13h加载软盘的0磁道0柱面2扇区开始的28个扇区到内存的1000:0000h处,然后跳到loc_2A处,这部分代码再将刚才读入的扇区数据移动到内存的0x00000000处,紧接着初始化GDT和IDT,并开启保护模式,此时GDT表中包含一个Code段和一个Data段,基址都是0x00000000。

seg000:0052处的跳转利用段选择子跳入了32位代码段,因此接下来的代码用IDA以32位代码的形式重加载,可以得到以下的代码:

mov   eax, 10h
seg000:00000205               mov   ds, eax
seg000:00000207               assume ds:nothing
seg000:00000207               lss   esp, large ds:0B5Ch
seg000:0000020E               call    IDT_Init
seg000:00000213
seg000:00000213 loc_213:
seg000:00000213               call    GDT_Init
seg000:00000218               mov   eax, 10h
seg000:0000021D               mov   ds, eax
seg000:0000021F               mov   es, eax
seg000:00000221               assume es:nothing
seg000:00000221               mov   fs, eax         ; DATA XREF: GDT_Init r
seg000:00000223               assume fs:nothing
seg000:00000223               mov   gs, eax
seg000:00000225               assume gs:nothing
seg000:00000225               lss   esp, large ds:0B5Ch
seg000:0000022C               xor   ebx, ebx
seg000:0000022E
seg000:0000022E loc_22E:                              ; CODE XREF: seg000:0000025D j
seg000:0000022E               nop
seg000:0000022F               cmp   ebx, 10h
seg000:00000232               jge   short loc_25F
seg000:00000234               mov   eax, 80000h
seg000:00000239               lea   edx, ds:0D08h
seg000:00000240               mov   edx,
seg000:00000242               mov   ax, dx
seg000:00000245               mov   dx, 8E00h
seg000:00000249               mov   ecx, 21h ; '!'
seg000:0000024E               add   ecx, ebx
seg000:00000250               lea   esi, ds:128h
seg000:00000257               mov   , eax
seg000:00000259               mov   , edx
seg000:0000025C               inc   ebx
seg000:0000025D               jmp   short loc_22E
seg000:0000025F ; ---------------------------------------------------------------------------
seg000:0000025F
seg000:0000025F loc_25F:                              ; CODE XREF: seg000:00000232 j
seg000:0000025F                                       ; seg000:00000266 j
seg000:0000025F               call    JumpToNextHandler
seg000:00000264               int   21h             ; DOS -
seg000:00000266               jmp   short loc_25F


其中,seg000:0000022E到seg000:0000025D处是一个执行16次的循环,目的是填充0x21~0x30的中断向量描述符,其镜像中的中断向量映射到内存的原始存储位置保存在0x00000D08处,中断向量从0x00000128开始存储,这里列出全部的中断向量:
中断编号入口地址
0x210x00000B7C
0x220x00000B8A
0x230x00000BA1
0x240x00000BC1
0x250x00000BE1
0x260x00000BFC
0x270x00000C17
0x280x00000C32
0x290x00000C4F
0x2A0x00000C6C
0x2B0x00000C84
0x2C0x00000C96
0x2D0x00000CB5
0x2E0x00000CF7
0x2F0x00000CE0
0x300x00000CD4

以上也可以在bochs虚拟机的调试模式通过info idt命令查看。
seg000:0000025F处的call    JumpToNextHandler代码如下:

seg000:00000268 JumpToNextHandler proc near             ; CODE XREF: seg000:loc_25F p
seg000:00000268               mov   edi, large ds:0B78h
seg000:0000026E               lea   edi, ds:0D48h
seg000:00000275               mov   eax,
seg000:00000277               mov   large ds:65h, al
seg000:0000027C               mov   ecx,
seg000:0000027F               mov   eax,
seg000:00000282               retn
seg000:00000282 JumpToNextHandler endp



该部分代码中的ds:0D48h对应的物理地址是0x00000D48,经过分析,该部分存储的其实是虚拟机的一系列指令(关于虚拟机的具体内容下面再说),此处就相当于是用虚拟机的指令集编写的程序,每条指令的长度为12个字节,其中前四个字节表示指令OpCode,后面两个字节分别表示两个操作数,或者说是指令的两个参数。
然后我们在bochs下调试的时候,发现int21h这个地方的21h经常会变为各种各样的数值。
然后我们就分析刚才的那16个中断的处理代码。 得到了如下的结论:
0x00000B64是一个长达20个字节的缓冲处理区,相当于一个拥有五个成员的int(4字节长度)数组。
0x00000D48包含了虚拟机的一系列指令以及一些附加数据。
我们假设0x0000B64的数组叫做buf,0x00000D48为code,两个数组均为int类型。
指令的两个参数分别叫做a和b。
假设指令指针为index(即指向下一指令的指针,例如X86架构中的EIP寄存器)。
则十六个中断的作用分别如下:

中断编号功能描述
0x21buf= b
0x22buf= buf
0x23buf= code]
0x24code]= buf
0x25buf+= buf
0x26buf-= buf
0x27buf^= buf
0x28buf<<= (buf& 0xFF)
0x29buf>>= (buf& 0xFF)
0x2Abuf&= buf
0x2Bindex= a
0x2Cif(buf== 0){index = a}
0x2Dif(buf!= 0){index = a}
0x2E终止CPU运行,即hlt指令
0x2F输出flag正确提示
0x30输出flag错误提示

在每次指令执行后,index会自增3(除了0x2B指令的情形以及0x2C、0x2D指令的跳转条件成立的情形以外)。然后我利用C#做了个虚拟机,并在模拟执行的时候同时输出类C的代码供之后分析。代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 虚拟机
{
    class Program
    {
      static int[] data = new int[]
      {
                0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
                0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
                0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
                0x27, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
                0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
                0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
                0x09, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
                0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00,
                0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
                0x04, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
                0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
                0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
                0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
                0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00,
                0x2D, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x38, 0x62, 0x64, 0x61, 0x65, 0x34, 0x35, 0x36, 0x2D, 0x35, 0x61, 0x63,
                0x38, 0x2D, 0x31, 0x31, 0x65, 0x39, 0x2D, 0x61, 0x31, 0x63, 0x31, 0x2D, 0x38, 0x38, 0x65, 0x39,
                0x66, 0x65, 0x38, 0x30, 0x66, 0x65, 0x61, 0x66, 0x65, 0x55, 0x63, 0x57, 0x01, 0x04, 0x53, 0x06,
                0x49, 0x49, 0x49, 0x1F, 0x1F, 0x07, 0x57, 0x51, 0x57, 0x43, 0x5F, 0x57, 0x57, 0x5E, 0x43, 0x57,
                0x0A, 0x02, 0x57, 0x43, 0x5E, 0x03, 0x5E, 0x57, 0x00, 0x00, 0x59, 0x0F, 0x77, 0x72, 0x6F, 0x6E,
                0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x63, 0x6F, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20,
                0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x20, 0x69, 0x73, 0x20, 0x66, 0x6C,
                0x61, 0x67, 0x7B, 0x59, 0x6F, 0x75, 0x72, 0x50, 0x61, 0x74, 0x63, 0x68, 0x7D, 0x20, 0x20, 0x20,
                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
      };

      static void Main(string[] args)
      {
            var buf = new int{0,0,0,0,0};
            var index = 0;

            var idata = new int;

            for(var i = 0;i < idata.Length;i++)
            {
                idata = data | (data << 8) | (data << 16) | (data << 24);
            }

            var lastcmd = 0x21;
            var lastindex = 0;
            var cnt = 0;

            var stb = new StringBuilder();

            while(true)
            {
                start:
                /*Console.WriteLine("Cmd = " + string.Format("{0:X2}",lastcmd));
                Console.WriteLine("Addr = " + string.Format("{0:X2}",lastindex));
                Console.WriteLine("NewAddr = " + string.Format("{0:X2}",index));
                Console.WriteLine("Arg1 = " + string.Format("{0:X2}",idata));
                Console.WriteLine("Arg2 = " + string.Format("{0:X2}",idata));
               
                for(var i = 0;i < buf.Length;i++)
                {
                  Console.Write(string.Format("{0:X2} {1:X2} {2:X2} {3:X2} ",buf & 0xFF,(buf >> 8) & 0xFF,(buf >> 16) & 0xFF,(buf >> 24) & 0xFF));
                }

                Console.WriteLine("");
                Console.WriteLine("第" + cnt + "次");
                Console.WriteLine();

                if(lastcmd == 0x29 || lastcmd == 0x2A || lastcmd == 0x2B || lastcmd == 0x2C)
                {
                  Console.ReadKey();
                }

                cnt++;
                lastcmd = idata;
                lastindex = index;*/
                stb.Append(index + ":\r\n");

                switch(idata)
                {
                  case 0x21:
                        buf] = idata;
                        stb.Append("buf[" + idata + "] = " + idata + ";\r\n");
                        break;

                  case 0x22:
                        buf] = buf];
                        stb.Append("buf[" + idata + "] = buf[" + idata + "];\r\n");
                        break;

                  case 0x23:
                        buf] = idata]];
                        stb.Append("buf[" + idata + "] = code + "]];");

                        if(buf] >= (0x204 + 0x6F) / 4)
                        {
                            stb.Append("//读Patch");
                        }
                        else if(buf] >= 0x204 / 4)
                        {
                            stb.Append("//读敏感区域,偏移:" + buf]);
                        }
                        else
                        {
                            stb.Append("//" + buf] + "");
                        }

                        stb.Append("\r\n");
                        break;

                  case 0x24:
                        idata]] = buf];
                        stb.Append("code + "]] = buf[" + idata + "];");
                        
                        if(buf] >= (0x204 + 0x6F) / 4)
                        {
                            stb.Append("//写Patch");
                        }
                        else if(buf] >= 0x204 / 4)
                        {
                            stb.Append("//写敏感区域,偏移:" + buf]);
                        }
                        else
                        {
                            stb.Append("//" + buf] + "");
                        }

                        stb.Append("\r\n");

                        break;

                  case 0x25:
                        buf] += buf];
                        stb.Append("buf[" + idata + "] += buf[" + idata + "];\r\n");
                        break;

                  case 0x26:
                        buf] -= buf];
                        stb.Append("buf[" + idata + "] -= buf[" + idata + "];\r\n");
                        break;

                  case 0x27:
                        buf] ^= buf];
                        stb.Append("buf[" + idata + "] ^= buf[" + idata + "];\r\n");
                        break;

                  case 0x28:
                        buf] <<= buf] & 0xFF;
                        stb.Append("buf[" + idata + "] <<= buf[" + idata + "];\r\n");
                        break;

                  case 0x29:
                        buf] >>= buf] & 0xFF;
                        stb.Append("buf[" + idata + "] >>= buf[" + idata + "];\r\n");
                        break;

                  case 0x2A:
                        buf] &= buf];
                        stb.Append("buf[" + idata + "] &= buf[" + idata + "];\r\n");
                        break;

                  case 0x2B:
                        index = buf];
                        stb.Append("jmp " + buf] + ";\r\n");
                        goto start;
                        break;

                  case 0x2C:
                        stb.Append("if(buf[" + idata + "] == 0)\r\n{\r\n\tjmp " + buf] + ";\r\n}\r\n");

                        if(buf] == 0)
                        {
                            stb.Append("//上述跳转已成立\r\n\r\n");
                            index = buf];
                            goto start;
                        }                     

                        break;

                  case 0x2D:
                        stb.Append("if(buf[" + idata + "] != 0)\r\n{\r\n\tjmp " + buf] + ";\r\n}\r\n");

                        if(buf] != 0)
                        {
                            //if(buf] != 126)
                            {
                              stb.Append("//上述跳转已成立\r\n\r\n");
                              index = buf];
                              goto start;
                            }
                        }

                        break;

                  case 0x30:
                        Console.WriteLine("wrong");
                        goto exit;
                        break;

                  case 0x2F:
                        Console.WriteLine("correct");
                        Console.WriteLine("flag is flag{YourPatch}");
                        goto exit;
                        break;

                  case 0x2E:
                        Console.WriteLine("CPU已终止运行");
                        goto exit;
                        break;
                }

                index += 3;
            }

            exit:
                Console.WriteLine("代码:");
                Console.WriteLine(stb.ToString());
                File.WriteAllText(@"D:\exercise\CTF\20190421\strange_int_740dfa57eab289ada9d30380ef4393d5\code.txt",stb.ToString());
                while(true);
      }
    }
}


上述代码输出的第一个类C程序如下(已经过人工简化和分析):

0:
i = 129;
6:
code = 0;//0 t = 0
9:
buf = code;//读敏感区域,偏移:129-137
12:
buf = buf;//buf = code
15:
buf = 8;
18:
buf <<= buf;//buf = code << 8
21:
buf ^= buf;//buf = code ^ (code << 8)
24:
buf <<= buf;//buf = code << 16;
27:
buf ^= buf;//buf = (code ^ (code << 8)) ^ (code << 16)
30:
buf <<= buf;//buf = code << 24;
33:
buf ^= buf;//buf = (code ^ (code << 8)) ^ (code << 16) ^ (code << 24)
36:
buf ^= buf;//buf = 0
39:
buf = code];//0 buf = t
42:
code] = buf;//0 t = (code ^ (code << 8)) ^ (code << 16) ^ (code << 24)
45:
buf ^= buf;//buf = ((code ^ (code << 8)) ^ (code << 16) ^ (code << 24)) ^ ((code ^ (code << 8)) ^ (code << 16) ^ (code << 24))
48:
code = buf;//写敏感区域,偏移:129-137 code = ((code ^ (code << 8)) ^ (code << 16) ^ (code << 24)) ^ ((code ^ (code << 8)) ^ (code << 16) ^ (code << 24))
51:
buf = 1;
54:
i += buf;
57:
buf = i;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

78:
buf = 129;
81:
buf = buf;
84:
buf = 9;
87:
buf += buf;
90:
buf = code];//读敏感区域,偏移:129
93:
buf = code];//读敏感区域,偏移:138
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
//上述跳转已成立

126:



原理已经大致写在了注释中,126处的跳转如果成立,则输出错误信息,如果不成立,就会转入下面的条件判断:

0:
buf = 129;
3:
buf ^= buf;
6:
code] = buf;//0
9:
buf = code];//读敏感区域,偏移:129
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:129
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:130
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:130
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:131
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:131
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:132
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:132
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:133
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:133
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:134
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:134
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:135
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:135
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:136
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:136
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
//上述跳转已成立

9:
buf = code];//读敏感区域,偏移:137
12:
buf = buf;
15:
buf = 8;
18:
buf <<= buf;
21:
buf ^= buf;
24:
buf <<= buf;
27:
buf ^= buf;
30:
buf <<= buf;
33:
buf ^= buf;
36:
buf ^= buf;
39:
buf = code];//0
42:
code] = buf;//0
45:
buf ^= buf;
48:
code] = buf;//写敏感区域,偏移:137
51:
buf = 1;
54:
buf += buf;
57:
buf = buf;
60:
buf = 129;
63:
buf -= buf;
66:
buf = 9;
69:
buf -= buf;
72:
buf = 9;
75:
if(buf != 0)
{
      jmp 9;
}
78:
buf = 129;
81:
buf = buf;
84:
buf = 9;
87:
buf += buf;
90:
buf = code];//读敏感区域,偏移:129
93:
buf = code];//读敏感区域,偏移:138
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:130
93:
buf = code];//读敏感区域,偏移:139
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:131
93:
buf = code];//读敏感区域,偏移:140
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:132
93:
buf = code];//读敏感区域,偏移:141
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:133
93:
buf = code];//读敏感区域,偏移:142
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:134
93:
buf = code];//读敏感区域,偏移:143
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:135
93:
buf = code];//读敏感区域,偏移:144
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:136
93:
buf = code];//读敏感区域,偏移:145
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
//上述跳转已成立

90:
buf = code];//读敏感区域,偏移:137
93:
buf = code];//读敏感区域,偏移:146
96:
buf -= buf;
99:
buf = 126;
102:
if(buf != 0)
{
      jmp 126;
}
105:
buf = 1;
108:
buf += buf;
111:
buf += buf;
114:
buf -= buf;
117:
buf = 90;
120:
if(buf != 0)
{
      jmp 90;
}
123:



很容易看出,上面的算法是首先将前9个四字节整数进行一种很特殊的异或之后,和前一项的异或结果再行异或得到了每个新的整数值,又后面的判断的值,对于一切偏移地址为x的整数(共9个数),其异或后的值如果与偏移地址为(x + 9)处的整数相等,则会触发成功提示分支。

根据上面的条件,我编写了如下的程序来求出真正的flag:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 逆向输入求解
{
    class Program
    {
      static void Main(string[] args)
      {
            var data = new int[]{0x38, 0x62, 0x64, 0x61, 0x65, 0x34, 0x35, 0x36, 0x2D, 0x35, 0x61, 0x63,
                0x38, 0x2D, 0x31, 0x31, 0x65, 0x39, 0x2D, 0x61, 0x31, 0x63, 0x31, 0x2D, 0x38, 0x38, 0x65, 0x39,
                0x66, 0x65, 0x38, 0x30, 0x66, 0x65, 0x61, 0x66, 0x65, 0x55, 0x63, 0x57, 0x01, 0x04, 0x53, 0x06,
                0x49, 0x49, 0x49, 0x1F, 0x1F, 0x07, 0x57, 0x51, 0x57, 0x43, 0x5F, 0x57, 0x57, 0x5E, 0x43, 0x57,
                0x0A, 0x02, 0x57, 0x43, 0x5E, 0x03, 0x5E, 0x57, 0x00, 0x00, 0x59, 0x0F, 0x77, 0x72, 0x6F, 0x6E,
                0x67, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x63, 0x6F, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20,
                0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x20, 0x69, 0x73, 0x20, 0x66, 0x6C,
                0x61, 0x67, 0x7B, 0x59, 0x6F, 0x75, 0x72, 0x50, 0x61, 0x74, 0x63, 0x68, 0x7D, 0x20, 0x20, 0x20,
                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};

            var idata = new int;

            for(var i = 0;i < idata.Length;i++)
            {
                idata = data | (data << 8) | (data << 16) | (data << 24);
            }

            var t = 0;
            var newdata = new int;

            for(var i = 0;i < 9;i++)
            {
                newdata = (idata ^ (idata << 8) ^ (idata << 16) ^ (idata << 24)) ^ t;
                t = (idata ^ (idata << 8) ^ (idata << 16) ^ (idata << 24));
            }

            newdata = idata;

            //var sub = (idata ^ (idata << 8) ^ (idata << 16) ^ (idata << 24)) - ((idata ^ (idata << 8) ^ (idata << 16) ^ (idata << 24)) ^ (idata ^ (idata << 8) ^ (idata << 16) ^ (idata << 24)));
            /*var sub = newdata - newdata;
            Console.WriteLine("差为" + sub);*/

            var answer = new int;

            for(var i = 0;i < 9;i++)
            {
                var tc = 0;

                for(var j = 0;j <= 0x7FFFFFFF;j++)
                {
                  tc = j ^ (j << 8) ^ (j << 16) ^ (j << 24);

                  if(i > 0)
                  {
                        tc ^= answer ^ (answer << 8) ^ (answer << 16) ^ (answer << 24);
                  }

                  if(tc == idata)
                  {
                        answer = j;
                        Console.WriteLine("0x{0:X8}",j);
                        break;
                  }
                }

               
            }


            while(true);
      }
    }
}



输出结果如上:根据其特点,只需要从第一个字符开始推导即可,由于式子难以逆向,所以本次采用暴力求解的方式,
首先枚举第一个四字节整数的所有可能性,然后在确定下第一个整数的情况下,暴力求解第二个整数,以此类推,求出全部的9个整数。我的虚拟机的提示如下:

bochs运行原始镜像的提示如下:

因此其是完全正确的


由提示可知刚才patch的全部内容即为flag,即flag为flag{e064d5aa-5a72-11e9-9200-88e9fe80feaf}

lizhirui 发表于 2019-4-22 21:58

qvq 发表于 2019-4-22 21:54
请问,eg000:0052处的跳转利用段选择子跳入了32位代码段
这里为什么是32位

由于这段代码是MBR,因此启动时CPU还是实模式,此时执行的大部分是16位指令,然后在保护模式切换前后,有几条32位指令,在那行jmp执行前,利用lgdt加载了GDT,利用lmsw切换到了保护模式,而在保护模式下CPU就是正式以32位模式执行了,虽然也可以执行16位程序,但是通过情理上推导以及你可以去查看一下GDT中的第1号项(8是段选择子,对应GDT中的第1号项),描述也显示该段是32位代码段

40m41h42t 发表于 2019-4-25 17:01

52user 发表于 2019-4-25 11:53
ALt+S 设置新区段可以达到类似效果

不知道你的图为啥我看不到。。选择16位的话是乱码&#128514;,我就单独开的16位的段了,大概是这么个效果:


depy 发表于 2019-4-22 21:45

大佬。tqltql!思路很清晰,学到了!

qvq 发表于 2019-4-22 21:54

请问,eg000:0052处的跳转利用段选择子跳入了32位代码段
这里为什么是32位

sanjsan 发表于 2019-4-22 21:55

这也太猛了吧

lizhirui 发表于 2019-4-22 21:59

sanjsan 发表于 2019-4-22 21:55
这也太猛了吧

23333干了8个小时,第一次干虚拟机的程序逆向

sanjsan 发表于 2019-4-22 22:12

lizhirui 发表于 2019-4-22 21:59
23333干了8个小时,第一次干虚拟机的程序逆向

我第一道虚拟机就做了5小时,太菜了{:1_932:}

lizhirui 发表于 2019-4-22 22:14

sanjsan 发表于 2019-4-22 22:12
我第一道虚拟机就做了5小时,太菜了

666比我做得快这么多啊

hxijin 发表于 2019-4-22 23:15

学习学习,谢谢

rbgaoshou 发表于 2019-4-23 07:36

这个确实比较厉害哈!
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: (原创)第十二届全国大学生信息安全竞赛strange_int题解