(原创)第十二届全国大学生信息安全竞赛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}
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位代码段 52user 发表于 2019-4-25 11:53
ALt+S 设置新区段可以达到类似效果
不知道你的图为啥我看不到。。选择16位的话是乱码😂,我就单独开的16位的段了,大概是这么个效果:
大佬。tqltql!思路很清晰,学到了! 请问,eg000:0052处的跳转利用段选择子跳入了32位代码段
这里为什么是32位 这也太猛了吧 sanjsan 发表于 2019-4-22 21:55
这也太猛了吧
23333干了8个小时,第一次干虚拟机的程序逆向 lizhirui 发表于 2019-4-22 21:59
23333干了8个小时,第一次干虚拟机的程序逆向
我第一道虚拟机就做了5小时,太菜了{:1_932:} sanjsan 发表于 2019-4-22 22:12
我第一道虚拟机就做了5小时,太菜了
666比我做得快这么多啊 学习学习,谢谢 这个确实比较厉害哈!