bad mouse

题目给了一个usb接口的小电路板,插上之后发现是一个模拟的鼠标,这个鼠标挨个会画出flag的字符,然而画字符的速度是递减的,显然在比赛结束前是画不完的…

首先把题目给的固件转化为二进制形式,工具见hex2bin,之后用ida打开,处理器选择Atmel AVR,下面的型号选ATmega32或者其它的型号,可以将这个固件反汇编出来.

反汇编的代码中,开头处有明显一部分数据,这一段数据都介于0x3f到0x7f之间,很有可能是存放鼠标画出的图案的bitmap,经过一系列的变换后,对照鼠标画出的图案,可以正确构造出这个图像.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from PIL import Image

import binascii
import base64

a = '403F42614463554148694A6B4C4B4E4D5051614F5C53565558576059656B676D696F7141646379454C774E79507B4D5D706F72717473767578777A797C7B7E7D484F51724C43464548474E69555B575D595F58715453565558575A595C5B5E5D605F667D6C456E477049716D6C6B7D6B78517A537C557E7778774175447D463F4841464544434D415049524B544D5251504F594D5C555E57605961555C5B6D5B606362696473756368676A496C4B7D4B4070427274737A774059426B44654543403F42435245476748614A4F4C4B4E4959515B535955595158576179656B676D696F674164636C6571777379757B7D4D706F7E77774B76557B4F463F7C7B444949514A634D554C5148475067545D565F586159735453655360676269646B657D605F62636465756368696A6B6C6B746D793F7B417D43455578777A75457D473F4541457D444346415149534B514D5149504F59715C635E6560675E795C5B655568736A736C756D4568676A65756D776F7571756D74737C534049424B444D455F403F513F4C534E55505751694C4B524F58715A435C7D5D5B58576151646F666F6871694164636C717179724B757D7479706F784F7C457E474049415B7C7B445B48514A534C554D6748474A49545B5D7E584F5251545359755A6F5C5F6B59605D605F6267647B7545683F6A6F6C6B754D783F7A417C437A5578777A597C5B4D794061426344434C4551575359555B5D6D504F586F5C655E676069617B5C5B6D5B686F6A716C736D4568677047747D763F7841795374734676487849577C5B7E5D403F'
b = binascii.a2b_hex(a)
arr = [0]*64
for i in b:
    arr[i - 0x3f] += 1
b = list(b)
for i in range(len(b)//2):
    b[i*2], b[i*2+1] = b[i*2+1], b[i*2]
d = [(b[i] - ((i % 0x40)+0x3f)) % 0x40 for i in range(len(b))]
c = [(d[2*i+1] << 6)+d[2*i] for i in range(len(d)//2)]
c = c[:288]
size = 4
blk = Image.new('RGB', (size, size), (0, 0, 0))
img = Image.new('RGB', (6*48*size, 12*size), (255, 255, 255))
for i in range(len(c)):
    for j in range(12):
        if (c[i] & 1) == 1:
            img.paste(blk, (i*size, j*size, (i+1)*size, (j+1)*size))
        c[i] >>= 1
img.save('ms.png')
# SECCON{379eaX85bTa99c695b36855i4Ycfa5b5}

四.3

这个题目类型和线上赛的时候一样,给出了程序运行过程中所有分支语句的选择情况,要求给出一个能产生相同结果的输入,只不过程序变得更加复杂了.

这个程序是一个x86模拟器,可以执行一部分的x86指令.找到主程序中的while循环的位置后,就可以把题目中给的trace结果分成很多段,每段对应一条模拟的x86指令.

对x86的指令编码稍有了解的话就可以知道第一个字节是Opcode,第二个是ModR/M,x86指令有不带参数的,有带2个寄存器为参数的,有后面跟着一个立即数作参数的,若是不带参数的,则不会触发ModR/M字节的解析,而带2个寄存器为参数的指令,其ModR/M字节的解析相对简单,只需提取出寄存器的序号即可.找到ModR/M字节的解析函数,根据该函数中的分支语句的触发情况就可以推出模拟的x86指令的类型.

最终的结果如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   0:   b8 04 00 00 00          mov    eax,0x4
   5:   b9 00 00 00 00          mov    ecx,0x0
   a:   39 c1                   cmp    ecx,eax
   c:   7f 00                   jg     0xe
   e:   01 d8                   add    eax,ebx
  10:   90                      nop
  11:   90                      nop
  12:   39 c1                   cmp    ecx,eax
  14:   7f 00                   jg     0x16
  16:   01 d8                   add    eax,ebx
  18:   90                      nop
  19:   90                      nop
  1a:   39 c1                   cmp    ecx,eax
  1c:   7f 00                   jg     0x1e
  1e:   01 d8                   add    eax,ebx
  20:   90                      nop
  21:   90                      nop
  22:   39 c1                   cmp    ecx,eax
  24:   7f 00                   jg     0x26
  26:   01 c1                   add    ecx,eax
  28:   90                      nop
  29:   90                      nop
  2a:   39 c1                   cmp    ecx,eax
  2c:   7f 00                   jg     0x2e
  2e:   01 c1                   add    ecx,eax
  30:   90                      nop
  31:   90                      nop
  32:   39 c1                   cmp    ecx,eax
  34:   7f 00                   jg     0x36
  36:   f4                      hlt

另外程序运行过程中对flags寄存器也有模拟,而其中每个位的置位和清除过程的分支选择不同,因此需要分析出每次运算指令之后的flags寄存器中CF、OF、SF的情况,最终拼凑出一个正确的取值.