Translation

This post is also available in Simplified Chinese.

bad mouse

The challenge provided a small USB circuit board. Once connected, it behaved like an emulated mouse and drew the flag one character at a time. Its drawing speed continually decreased, so it clearly could not finish before the competition ended.

First convert the supplied firmware to binary with a tool such as hex2bin. Open it in IDA, select Atmel AVR as the processor and ATmega32 (or another suitable model) as the device, and the firmware can be disassembled.

Near the beginning of the disassembly is an obvious block of data whose values all fall between 0x3f and 0x7f. It likely contains the bitmap for the patterns drawn by the mouse. After applying a sequence of transformations and comparing the result with what the mouse drew, the image can be reconstructed correctly.

 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}

Flag drawn by bad mouse

四.3

Like its online qualifier counterpart, this challenge provides the outcome of every branch taken while a program ran and asks for an input that produces the same trace. This time, however, the program is considerably more complex.

The program is an x86 emulator supporting a subset of x86 instructions. After locating the main program’s while loop, the supplied trace can be divided into many segments, each corresponding to one emulated x86 instruction.

Anyone somewhat familiar with x86 encoding knows that the first byte is the opcode and the second is ModR/M. Some x86 instructions have no operands, some take two registers, and others are followed by an immediate operand. Instructions without operands do not trigger ModR/M parsing. For instructions with two register operands, parsing ModR/M is relatively simple: it only needs to extract the register numbers. By finding the ModR/M parser and observing which branches it takes, we can infer the type of each emulated x86 instruction.

The final result is:

 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

The program also emulates the flags register. Setting and clearing each bit produces different branch choices, so the CF, OF, and SF values after every arithmetic instruction must also be derived and assembled into a valid input.