国赛 strange_int
参考文章地址https://www.52pojie.cn/thread-936377-1-1.html
https://qrzbing.cn/2019/04/27/CISCN2019-strange-int/
下载拿到文件使用linux file命令查看一下文件类型
为DOS/MBR主引导扇区。
放入winhex中查看,发现偏移0x1FE处有55 AA 表示这是一个分区,且主引导扇区的大小位512字节,可以判断前面为主引导记录的代码。
将程序放入IDA中,使用ALT+S修改0-0x200为16位分析的断,并右击code生成他的汇编
MBR:0000 jmp far ptr 7C0h:5
MBR:0005 ; ---------------------------------------------------------------------------
MBR:0005 mov ax, cs
MBR:0007 mov ds, ax
MBR:0009 mov ss, ax
MBR:000B mov sp, 400h
MBR:000E cld
MBR:000F mov ax, 3
MBR:0012 int 10h ; - VIDEO - SET VIDEO MODE
MBR:0012 ; AL = mode
MBR:0014 mov dx, 0
MBR:0017 mov cx, 2
MBR:001A mov ax, 1000h
MBR:001D mov es, ax
MBR:001F assume es:nothing
MBR:001F xor bx, bx
MBR:0021 mov ax, 228h
MBR:0024 int 13h ; DISK - READ SECTORS INTO MEMORY
MBR:0024 ; AL = number of sectors to read, CH = track, CL = sector
MBR:0024 ; DH = head, DL = drive, ES:BX -> buffer to fill
MBR:0024 ; Return: CF set on error, AH = status, AL = number of sectors read
MBR:0026 jnb short loc_2A
MBR:0028
MBR:0028 loc_28: ; CODE XREF: MBR:loc_28↓j
MBR:0028 jmp short loc_28
MBR:002A ; ---------------------------------------------------------------------------
MBR:002A
MBR:002A loc_2A: ; CODE XREF: MBR:0026↑j
MBR:002A cli
MBR:002B mov ax, 1000h
MBR:002E mov ds, ax
MBR:0030 assume ds:nothing
MBR:0030 xor ax, ax
MBR:0032 mov es, ax
MBR:0034 assume es:nothing
MBR:0034 mov cx, 2000h
MBR:0037 sub si, si
MBR:0039 sub di, di
MBR:003B rep movsb
MBR:003D mov ax, 7C0h
MBR:0040
MBR:0040 loc_40: ; DATA XREF: MBR:0012↑r
MBR:0040 mov ds, ax
MBR:0042 assume ds:nothing
MBR:0042 lidt fword ptr ds:6Fh
MBR:0047 lgdt fword ptr ds:75h
MBR:004C
MBR:004C loc_4C: ; DATA XREF: MBR:0024↑r
MBR:004C mov ax, 1
MBR:004F lmsw ax
MBR:0052 jmp far ptr 8:0
首先主引导扇区的地址被加载到0x7c00,跳转的目标地址是0x7c05,之后初始化段寄存器和栈指针,之后int 10h 调用bios中断设置显示模式。
之后int13h,使得将软盘(DL=0H) 上的0磁道(DH=0H)0柱面(CH=0H)2扇区(CL=2H)开始的28个扇区(AL=28H)读取(AH=02H)到内存的1000:0000h处(ES:BX=1000:0)。之后实现跳转
将DS:SI(1000:0)这段地址移动到ES:DI(0:0)处,移动了2000字节(cx=2000h)
接着初始化IDT和GDT(lidt,lgdt)之后开启保护模式(Imsw)并且跳转至32位代码段 从0x200开始
使用ALT+S进行重建
main:00000200 mov eax, 10h
main:00000205 mov ds, eax
main:00000207 assume ds:nothing
main:00000207 lss esp, large ds:0B5Ch
main:0000020E call sub_28B
main:00000213 call sub_283
main:00000218 mov eax, 10h ; DATA XREF: sub_28B+27↓r
main:0000021D mov ds, eax
main:0000021F mov es, eax
main:00000221 assume es:nothing
main:00000221 mov fs, eax ; DATA XREF: sub_283↓r
main:00000223 assume fs:nothing
main:00000223 mov gs, eax
main:00000225 assume gs:nothing
main:00000225
main:00000225 loc_225: ; DATA XREF: sub_28B+11↓o
main:00000225 lss esp, large ds:0B5Ch
main:0000022C xor ebx, ebx
main:0000022E
main:0000022E loc_22E: ; CODE XREF: sub_200+5D↓j
main:0000022E nop
main:0000022F cmp ebx, 10h
main:00000232 jge short loc_25F
main:00000234 mov eax, 80000h
main:00000239 lea edx, ds:0D08h[ebx*4]
main:00000240 mov edx, [edx]
main:00000242 mov ax, dx
main:00000245 mov dx, 8E00h
main:00000249 mov ecx, 21h ; '!'
main:0000024E add ecx, ebx
main:00000250 lea esi, ds:128h[ecx*8]
main:00000257 mov [esi], eax
main:00000259 mov [esi+4], edx
main:0000025C inc ebx
main:0000025D jmp short loc_22E
main:0000025F ; ---------------------------------------------------------------------------
main:0000025F
main:0000025F loc_25F: ; CODE XREF: sub_200+32↑j
main:0000025F ; sub_200+66↓j
main:0000025F call sub_268
main:00000264 int 21h ; DOS -
main:00000266 jmp short loc_25F
main:00000266 sub_200 endp
首先进行初始化 sub_28B和sub_283分别对LIDT和LGDT进行初始化。
首先看sub_28b,初始化中断描述符表
main:0000028B mov edx, 0FCh
main:00000290 mov eax, 80000h
main:00000295 mov ax, dx
main:00000298 mov dx, 8E00h
main:0000029C lea edi, loc_225+3 - unk_100//地址为0x128
main:000002A2 mov ecx, 100h
main:000002A7
main:000002A7 loc_2A7: ; CODE XREF: sub_28B+25↓j
main:000002A7 mov [edi], eax
main:000002A9 mov [edi+4], edx
main:000002AC add edi, 8
main:000002AF dec ecx
main:000002B0 jnz short loc_2A7
main:000002B2 lidt large fword ptr ds:11Ch
main:000002B9 retn
main:000002B9 sub_28B endp
循环了256次,使得ds:[128]处地址填充为800fch之后填充8e00h。
然后加载中断描述符寄存器地址为ds:[11c]。
查看寄存器FF 07 28 01 00 00 1F 00
参考文章操作系统之GDT和IDT
代码段选择器(IDT表基地址)为0x0128
之后再看GDT
main:00000283 lgdt large fword ptr ds:122h
main:0000028A retn
main:0000028A sub_283 endp
只有简短的三行 寄存器地址为 ds:[0x122]
1F 00 28 09 00 00 00
基址为0x0928长度为0x1F
两个表初始化完毕之后继续向下看。
分别给ds,es,fs,gs赋值
后面是一个16次循环,分别将从内存中ds:0xD08开始的数据填充到ds:0x128,而ds:0x128是IDT表基地址,执行后中断21h到30h的的入口地址全部改变
中断编号 | 入口地址 |
---|---|
0x21 | 0xb7c |
0x22 | 0x0B8A |
0x23 | 0x0BA1 |
0X24 | 0X0BC1 |
0X25 | 0X0BE1 |
0X26 | 0X0BFC |
0X27 | 0X0C17 |
0X28 | 0X0C32 |
0X29 | 0X0C4F |
0X2A | 0X0C6C |
0X2B | 0X0C84 |
0X2C | 0X0C96 |
0X2D | 0X0CB5 |
0X2E | 0X0CF7 |
0X2F | 0X0CE0 |
0X30 | 0X0CD4 |
继续向下分析,执行sub_268函数
sub_268 proc near ; CODE XREF: sub_200:loc_25F↑p
main:00000268 mov edi, large ds:0B78h
main:0000026E lea edi, ds:0D48h[edi*4]
main:00000275 mov eax, [edi]
main:00000277 mov large ds:65h, al
main:0000027C mov ecx, [edi+4]
main:0000027F mov eax, [edi+8]
main:00000282 retn
main:00000282 sub_268 endp
函数从ds:0x0B78那获取值作为ds:0D48h的偏移,将ds:0D48h处的值分别赋值给 给ds:65h,ecx,eax
然而地址ds:65h地址为汇编
main:00000264 int 21h
所以此处中断是随着 ds:0d48h[edi * 4]的值改变。此处便是虚拟机。而ecx,和eax就为其参数,之后将0B78处的值加3,从而执行0d48[edi*4]处的下一个中断
main:00000EF8 lea ecx, dword_C78 - unk_100
main:00000EFE mov eax, [ecx]
main:00000F00 add eax, 3
main:00000F03 mov [ecx], eax
main:00000F05 iret
于是我们对各个中断逐一分析,由于涉及0x0b64处地址较多将其简化为buf为DWORD型数组,ecx为c,eax为a
将0x0d48简化为code为DWORD型数组
中断编号 | 操作内容 |
---|---|
0x21 | buf[c]=a |
0x22 | buf[c]=buf[a] |
0x23 | buf[c]=code[buf[a]] |
0x24 | code[buf[c]]=buf[a] |
0x25 | buf[c]=buf[c]+buf[a] |
0x26 | buf[c]=buf[c]-buf[a] |
0x27 | buf[c]=buf[c]^buf[a] |
0x28 | buf[c]=buf[c]<<(buf[a]&0xFF) |
0x29 | buf[c]=buf[c]>>(buf[a]&0xFF) |
0x2A | buf[c]=buf[c]&buf[a] |
0x2B | ds:0x0b78=buf[c] |
0X2C | if {buf[a]==0} ds:0x0b78=buf[c] |
0X2D | if{buf[a]!=0} ds:0x0b78=buf[c] |
0X2E | 暂停 |
0X2F | 打印正确 |
0x30 | 打印错误 |
分析完毕之后,用Lazy IDA将数据dump下来写脚本将其流程打印出来 进行虚拟指令的处理
data = [
0x00000021, 0x00000000, 0x00000081, 0x00000027, 0x00000001, 0x00000001, 0x00000024, 0x00000001, 0x00000001,
0x00000023, 0x00000002, 0x00000000, 0x00000022, 0x00000003, 0x00000002, 0x00000021, 0x00000004, 0x00000008,
0x00000028, 0x00000003, 0x00000004, 0x00000027, 0x00000002, 0x00000003, 0x00000028, 0x00000003, 0x00000004,
0x00000027, 0x00000002, 0x00000003, 0x00000028, 0x00000003, 0x00000004, 0x00000027, 0x00000002, 0x00000003,
0x00000027, 0x00000003, 0x00000003, 0x00000023, 0x00000004, 0x00000003, 0x00000024, 0x00000003, 0x00000002,
0x00000027, 0x00000002, 0x00000004, 0x00000024, 0x00000000, 0x00000002, 0x00000021, 0x00000001, 0x00000001,
0x00000025, 0x00000000, 0x00000001, 0x00000022, 0x00000001, 0x00000000, 0x00000021, 0x00000002, 0x00000081,
0x00000026, 0x00000001, 0x00000002, 0x00000021, 0x00000002, 0x00000009, 0x00000026, 0x00000001, 0x00000002,
0x00000021, 0x00000002, 0x00000009, 0x0000002D, 0x00000002, 0x00000001, 0x00000021, 0x00000000, 0x00000081,
0x00000022, 0x00000001, 0x00000000, 0x00000021, 0x00000002, 0x00000009, 0x00000025, 0x00000001, 0x00000002,
0x00000023, 0x00000003, 0x00000000, 0x00000023, 0x00000004, 0x00000001, 0x00000026, 0x00000003, 0x00000004,
0x00000021, 0x00000004, 0x0000007E, 0x0000002D, 0x00000004, 0x00000003, 0x00000021, 0x00000003, 0x00000001,
0x00000025, 0x00000000, 0x00000003, 0x00000025, 0x00000001, 0x00000003, 0x00000026, 0x00000002, 0x00000003,
0x00000021, 0x00000004, 0x0000005A, 0x0000002D, 0x00000004, 0x00000002, 0x0000002F, 0x00000000, 0x00000000,
0x00000030, 0x00000000, 0x00000000
]
i = 0
while (i<=126):
if data[i] == 0x21:
print ("buf[%d]=%d" % (data[i+1], data[i+2]))
if data[i] == 0x22:
print ("buf[%d]=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x23:
print ("buf[%d]=code[buf[%d]]" % (data[i+1], data[i+2]))
if data[i] == 0x24:
print ("code[buf[%d]]=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x25:
print ("buf[%d]+=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x26:
print ("buf[%d]-=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x27:
print ("buf[%d]^=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x28:
print ("buf[%d]<<=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x29:
print ("buf[%d]>>=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x2A:
print ("buf[%d]&=buf[%d]" % (data[i+1], data[i+2]))
if data[i] == 0x2B:
print ("i =buf[%d]" % (data[i+1]))
if data[i] == 0x2c:
print ("if buf[%d]==0 i=buf[%d]" % (data[i+2], data[i+1]))
if data[i] == 0x2d:
print ("if buf[%d]!=0 i=buf[%d]" % (data[i+2], data[i+1]))
if data[i] == 0x2e:
print "pause"
if data[i] == 0x2f:
print "correct"
if data[i] == 0x30:
print "wrong"
i+=3
打印出结果如下
buf[0]=129
buf[1]^=buf[1]
code[buf[1]]=buf[1]
buf[2]=code[buf[0]]
buf[3]=buf[2]
buf[4]=8
buf[3]<<=buf[4]
buf[2]^=buf[3]
buf[3]<<=buf[4]
buf[2]^=buf[3]
buf[3]<<=buf[4]
buf[2]^=buf[3]
buf[3]^=buf[3]
buf[4]=code[buf[3]]
code[buf[3]]=buf[2]
buf[2]^=buf[4]
code[buf[0]]=buf[2]
buf[1]=1
buf[0]+=buf[1]
buf[1]=buf[0]
buf[2]=129
buf[1]-=buf[2]
buf[2]=9
buf[1]-=buf[2]
buf[2]=9
if buf[1]!=0 i=buf[2]
buf[0]=129
buf[1]=buf[0]
buf[2]=9
buf[1]+=buf[2]
buf[3]=code[buf[0]]
buf[4]=code[buf[1]]
buf[3]-=buf[4]
buf[4]=126
if buf[3]!=0 i=buf[4]
buf[3]=1
buf[0]+=buf[3]
buf[1]+=buf[3]
buf[2]-=buf[3]
buf[4]=90
if buf[2]!=0 i=buf[4]
correct
wrong
进行整理与分析,首先这串代码一共循环9次执行指令
code[i]=code[i] ^(code[i]<<8) ^ (code[i]<<16) ^(code[i]<<24) ^ code[i-1]
之后再执行9次比较
buf[i]与buf[i+9]进行9次比较。
解密脚本:
a=[0x57635565, 0x06530401, 0x1F494949, 0x5157071F, 0x575F4357, 0x57435E57, 0x4357020A, 0x575E035E,0x0f590000,0x0]
for x in range (0,9):
m4=a[x]&0xff
m3=(a[x]&0xff00)>>8
m2=(a[x]&0xff0000)>>16
m1=(a[x]&0xff000000)>>24
p1=m4
p2=p1^m3
p3=p1^m2^p2
p4=p1^p2^p3^m1
flag=p1+(p2<<8)+(p3<<16)+(p4<<24)
print hex(flag)
a[x+1]=a[x]^a[x+1]
得到结果
0x34363065
0x61613564
0x3761352d
0x31312d32
0x392d3965
0x2d303032
0x39653838
0x30386566
0x66616566
将原文patch之后,放入boches之后运行即可提示成功