本帖最后由 lizhirui 于 2019-4-26 11:01 编辑
本题的原理是虚拟机
题目下载链接如下:
strange_int_740dfa57eab289ada9d30380ef4393d5.zip
(1.13 KB, 下载次数: 102)
本题下载之后发现一个Image.bin文件,使用Winhex打开后,可以很容易发现在偏移0x1FE处有55 AA,表示这是一个分区,联合一些经验,可以初步判断这是软盘镜像的MBR,因此,使用IDA以16位方式加载这个Binary,可以得到这样的代码:[Asm] 纯文本查看 复制代码 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位代码的形式重加载,可以得到以下的代码:
[Asm] 纯文本查看 复制代码
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[ebx*4]
seg000:00000240 mov edx, [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[ecx*8]
seg000:00000257 mov [esi], eax
seg000:00000259 mov [esi+4], 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开始存储,这里列出全部的中断向量: 中断编号 | 入口地址 | 0x21 | 0x00000B7C | 0x22 | 0x00000B8A | 0x23 | 0x00000BA1 | 0x24 | 0x00000BC1 | 0x25 | 0x00000BE1 | 0x26 | 0x00000BFC | 0x27 | 0x00000C17 | 0x28 | 0x00000C32 | 0x29 | 0x00000C4F | 0x2A | 0x00000C6C | 0x2B | 0x00000C84 | 0x2C | 0x00000C96 | 0x2D | 0x00000CB5 | 0x2E | 0x00000CF7 | 0x2F | 0x00000CE0 | 0x30 | 0x00000CD4 |
以上也可以在bochs虚拟机的调试模式通过info idt命令查看。
seg000:0000025F处的call JumpToNextHandler代码如下:
[Asm] 纯文本查看 复制代码
seg000:00000268 JumpToNextHandler proc near ; CODE XREF: seg000:loc_25F p
seg000:00000268 mov edi, large ds:0B78h
seg000:0000026E lea edi, ds:0D48h[edi*4]
seg000:00000275 mov eax, [edi]
seg000:00000277 mov large ds:65h, al
seg000:0000027C mov ecx, [edi+4]
seg000:0000027F mov eax, [edi+8]
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寄存器)。
则十六个中断的作用分别如下:
中断编号 | 功能描述 | 0x21 | buf[a] = b | 0x22 | buf[a] = buf | 0x23 | buf[a] = code[buf] | 0x24 | code[buf[a]] = buf | 0x25 | buf[a] += buf | 0x26 | buf[a] -= buf | 0x27 | buf[a] ^= buf | 0x28 | buf[a] <<= (buf& 0xFF) | 0x29 | buf[a] >>= (buf& 0xFF) | 0x2A | buf[a] &= buf | 0x2B | index = a | 0x2C | if(buf == 0){index = a} | 0x2D | if(buf != 0){index = a} | 0x2E | 终止CPU运行,即hlt指令 | 0x2F | 输出flag正确提示 | 0x30 | 输出flag错误提示 |
在每次指令执行后,index会自增3(除了0x2B指令的情形以及0x2C、0x2D指令的跳转条件成立的情形以外)。然后我利用C#做了个虚拟机,并在模拟执行的时候同时输出类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[5]{0,0,0,0,0};
var index = 0;
var idata = new int[data.Length / 4];
for(var i = 0;i < idata.Length;i++)
{
idata[i] = data[i * 4] | (data[i * 4 + 1] << 8) | (data[i * 4 + 2] << 16) | (data[i * 4 + 3] << 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[lastindex + 1]));
Console.WriteLine("Arg2 = " + string.Format("{0:X2}",idata[lastindex + 2]));
for(var i = 0;i < buf.Length;i++)
{
Console.Write(string.Format("{0:X2} {1:X2} {2:X2} {3:X2} ",buf[i] & 0xFF,(buf[i] >> 8) & 0xFF,(buf[i] >> 16) & 0xFF,(buf[i] >> 24) & 0xFF));
}
Console.WriteLine("");
Console.WriteLine("第" + cnt + "次");
Console.WriteLine();
if(lastcmd == 0x29 || lastcmd == 0x2A || lastcmd == 0x2B || lastcmd == 0x2C)
{
Console.ReadKey();
}
cnt++;
lastcmd = idata[index];
lastindex = index;*/
stb.Append(index + ":\r\n");
switch(idata[index])
{
case 0x21:
buf[idata[index + 1]] = idata[index + 2];
stb.Append("buf[" + idata[index + 1] + "] = " + idata[index + 2] + ";\r\n");
break;
case 0x22:
buf[idata[index + 1]] = buf[idata[index + 2]];
stb.Append("buf[" + idata[index + 1] + "] = buf[" + idata[index + 2] + "];\r\n");
break;
case 0x23:
buf[idata[index + 1]] = idata[buf[idata[index + 2]]];
stb.Append("buf[" + idata[index + 1] + "] = code[buf[" + idata[index + 2] + "]];");
if(buf[idata[index + 2]] >= (0x204 + 0x6F) / 4)
{
stb.Append("//读Patch");
}
else if(buf[idata[index + 2]] >= 0x204 / 4)
{
stb.Append("//读敏感区域,偏移:" + buf[idata[index + 2]]);
}
else
{
stb.Append("//" + buf[idata[index + 2]] + "");
}
stb.Append("\r\n");
break;
case 0x24:
idata[buf[idata[index + 1]]] = buf[idata[index + 2]];
stb.Append("code[buf[" + idata[index + 1] + "]] = buf[" + idata[index + 2] + "];");
if(buf[idata[index + 1]] >= (0x204 + 0x6F) / 4)
{
stb.Append("//写Patch");
}
else if(buf[idata[index + 1]] >= 0x204 / 4)
{
stb.Append("//写敏感区域,偏移:" + buf[idata[index + 1]]);
}
else
{
stb.Append("//" + buf[idata[index + 1]] + "");
}
stb.Append("\r\n");
break;
case 0x25:
buf[idata[index + 1]] += buf[idata[index + 2]];
stb.Append("buf[" + idata[index + 1] + "] += buf[" + idata[index + 2] + "];\r\n");
break;
case 0x26:
buf[idata[index + 1]] -= buf[idata[index + 2]];
stb.Append("buf[" + idata[index + 1] + "] -= buf[" + idata[index + 2] + "];\r\n");
break;
case 0x27:
buf[idata[index + 1]] ^= buf[idata[index + 2]];
stb.Append("buf[" + idata[index + 1] + "] ^= buf[" + idata[index + 2] + "];\r\n");
break;
case 0x28:
buf[idata[index + 1]] <<= buf[idata[index + 2]] & 0xFF;
stb.Append("buf[" + idata[index + 1] + "] <<= buf[" + idata[index + 2] + "];\r\n");
break;
case 0x29:
buf[idata[index + 1]] >>= buf[idata[index + 2]] & 0xFF;
stb.Append("buf[" + idata[index + 1] + "] >>= buf[" + idata[index + 2] + "];\r\n");
break;
case 0x2A:
buf[idata[index + 1]] &= buf[idata[index + 2]];
stb.Append("buf[" + idata[index + 1] + "] &= buf[" + idata[index + 2] + "];\r\n");
break;
case 0x2B:
index = buf[idata[index + 1]];
stb.Append("jmp " + buf[idata[index + 1]] + ";\r\n");
goto start;
break;
case 0x2C:
stb.Append("if(buf[" + idata[index + 2] + "] == 0)\r\n{\r\n\tjmp " + buf[idata[index + 1]] + ";\r\n}\r\n");
if(buf[idata[index + 2]] == 0)
{
stb.Append("//上述跳转已成立\r\n\r\n");
index = buf[idata[index + 1]];
goto start;
}
break;
case 0x2D:
stb.Append("if(buf[" + idata[index + 2] + "] != 0)\r\n{\r\n\tjmp " + buf[idata[index + 1]] + ";\r\n}\r\n");
if(buf[idata[index + 2]] != 0)
{
//if(buf[idata[index + 1]] != 126)
{
stb.Append("//上述跳转已成立\r\n\r\n");
index = buf[idata[index + 1]];
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程序如下(已经过人工简化和分析):
[C] 纯文本查看 复制代码
0:
i = 129;
6:
code[0] = 0;//0 t = 0
9:
buf[2] = code[i];//读敏感区域,偏移:129-137
12:
buf[3] = buf[2];//buf[3] = code[i]
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];//buf[3] = code[i] << 8
21:
buf[2] ^= buf[3];//buf[2] = code[i] ^ (code[i] << 8)
24:
buf[3] <<= buf[4];//buf[3] = code[i] << 16;
27:
buf[2] ^= buf[3];//buf[2] = (code[i] ^ (code[i] << 8)) ^ (code[i] << 16)
30:
buf[3] <<= buf[4];//buf[3] = code[i] << 24;
33:
buf[2] ^= buf[3];//buf[2] = (code[i] ^ (code[i] << 8)) ^ (code[i] << 16) ^ (code[i] << 24)
36:
buf[3] ^= buf[3];//buf[3] = 0
39:
buf[4] = code[buf[3]];//0 buf[4] = t
42:
code[buf[3]] = buf[2];//0 t = (code[i] ^ (code[i] << 8)) ^ (code[i] << 16) ^ (code[i] << 24)
45:
buf[2] ^= buf[4];//buf[2] = ((code[i] ^ (code[i] << 8)) ^ (code[i] << 16) ^ (code[i] << 24)) ^ ((code[i - 1] ^ (code[i - 1] << 8)) ^ (code[i - 1] << 16) ^ (code[i - 1] << 24))
48:
code[i] = buf[2];//写敏感区域,偏移:129-137 code[i] = ((code[i] ^ (code[i] << 8)) ^ (code[i] << 16) ^ (code[i] << 24)) ^ ((code[i - 1] ^ (code[i - 1] << 8)) ^ (code[i - 1] << 16) ^ (code[i - 1] << 24))
51:
buf[1] = 1;
54:
i += buf[1];
57:
buf[1] = i;
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
78:
buf[0] = 129;
81:
buf[1] = buf[0];
84:
buf[2] = 9;
87:
buf[1] += buf[2];
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:129
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:138
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
//上述跳转已成立
126:
原理已经大致写在了注释中,126处的跳转如果成立,则输出错误信息,如果不成立,就会转入下面的条件判断:
[C] 纯文本查看 复制代码
0:
buf[0] = 129;
3:
buf[1] ^= buf[1];
6:
code[buf[1]] = buf[1];//0
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:129
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:129
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:130
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:130
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:131
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:131
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:132
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:132
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:133
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:133
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:134
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:134
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:135
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:135
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:136
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:136
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
//上述跳转已成立
9:
buf[2] = code[buf[0]];//读敏感区域,偏移:137
12:
buf[3] = buf[2];
15:
buf[4] = 8;
18:
buf[3] <<= buf[4];
21:
buf[2] ^= buf[3];
24:
buf[3] <<= buf[4];
27:
buf[2] ^= buf[3];
30:
buf[3] <<= buf[4];
33:
buf[2] ^= buf[3];
36:
buf[3] ^= buf[3];
39:
buf[4] = code[buf[3]];//0
42:
code[buf[3]] = buf[2];//0
45:
buf[2] ^= buf[4];
48:
code[buf[0]] = buf[2];//写敏感区域,偏移:137
51:
buf[1] = 1;
54:
buf[0] += buf[1];
57:
buf[1] = buf[0];
60:
buf[2] = 129;
63:
buf[1] -= buf[2];
66:
buf[2] = 9;
69:
buf[1] -= buf[2];
72:
buf[2] = 9;
75:
if(buf[1] != 0)
{
jmp 9;
}
78:
buf[0] = 129;
81:
buf[1] = buf[0];
84:
buf[2] = 9;
87:
buf[1] += buf[2];
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:129
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:138
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:130
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:139
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:131
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:140
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:132
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:141
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:133
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:142
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:134
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:143
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:135
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:144
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:136
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:145
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
//上述跳转已成立
90:
buf[3] = code[buf[0]];//读敏感区域,偏移:137
93:
buf[4] = code[buf[1]];//读敏感区域,偏移:146
96:
buf[3] -= buf[4];
99:
buf[4] = 126;
102:
if(buf[3] != 0)
{
jmp 126;
}
105:
buf[3] = 1;
108:
buf[0] += buf[3];
111:
buf[1] += buf[3];
114:
buf[2] -= buf[3];
117:
buf[4] = 90;
120:
if(buf[2] != 0)
{
jmp 90;
}
123:
很容易看出,上面的算法是首先将前9个四字节整数进行一种很特殊的异或之后,和前一项的异或结果再行异或得到了每个新的整数值,又后面的判断的值,对于一切偏移地址为x的整数(共9个数),其异或后的值如果与偏移地址为(x + 9)处的整数相等,则会触发成功提示分支。
根据上面的条件,我编写了如下的程序来求出真正的flag:
[C#] 纯文本查看 复制代码
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[data.Length / 4];
for(var i = 0;i < idata.Length;i++)
{
idata[i] = data[i * 4] | (data[i * 4 + 1] << 8) | (data[i * 4 + 2] << 16) | (data[i * 4 + 3] << 24);
}
var t = 0;
var newdata = new int[idata.Length];
for(var i = 0;i < 9;i++)
{
newdata[i] = (idata[i] ^ (idata[i] << 8) ^ (idata[i] << 16) ^ (idata[i] << 24)) ^ t;
t = (idata[i] ^ (idata[i] << 8) ^ (idata[i] << 16) ^ (idata[i] << 24));
}
newdata[9] = idata[9];
//var sub = (idata[0] ^ (idata[0] << 8) ^ (idata[0] << 16) ^ (idata[0] << 24)) - ((idata[idata.Length - 2] ^ (idata[idata.Length - 2] << 8) ^ (idata[idata.Length - 2] << 16) ^ (idata[idata.Length - 2] << 24)) ^ (idata[idata.Length - 1] ^ (idata[idata.Length - 1] << 8) ^ (idata[idata.Length - 1] << 16) ^ (idata[idata.Length - 1] << 24)));
/*var sub = newdata[0] - newdata[newdata.Length - 1];
Console.WriteLine("差为" + sub);*/
var answer = new int[idata.Length];
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[i - 1] ^ (answer[i - 1] << 8) ^ (answer[i - 1] << 16) ^ (answer[i - 1] << 24);
}
if(tc == idata[i + 9])
{
answer[i] = j;
Console.WriteLine("0x{0:X8}",j);
break;
}
}
}
while(true);
}
}
}
输出结果如上:根据其特点,只需要从第一个字符开始推导即可,由于式子难以逆向,所以本次采用暴力求解的方式,
首先枚举第一个四字节整数的所有可能性,然后在确定下第一个整数的情况下,暴力求解第二个整数,以此类推,求出全部的9个整数。我的虚拟机的提示如下:
bochs运行原始镜像的提示如下:
因此其是完全正确的
由提示可知刚才patch的全部内容即为flag,即flag为flag{e064d5aa-5a72-11e9-9200-88e9fe80feaf}
|