RuCTF quals 2014: Microcontroller (hardware)

The task was:

We have a special device for converting numbers into images. Can you tell us which keys did we press?

with a firmware hex file (firmware.hex), a display dump (display.dat) and a picture of the circuit:

breadboard (c) by ructf
breadboard (c) by ructf

Finding main

The first part was finding the main loop of the firmware. Browsing through the code we found an very suspicious loop with only two function calls and some initializing stuff before.

1
2
3
4
5
6
7
8
9
10
main: # 0x20e
  sts     RAM_A9, r1
  sts     RAM_A8, r1
  rcall   setup_pins
  rcall   init_display
  rcall   sub_1F5
loop:
  rcall   get_key
  rcall   handle_key
  rjmp    loop

Analyze the first function

After we found the main-loop we started analyzing the first function at 0x11f. Reading the assembly we saw multiple read and write operations on the I/O-pins of the keypad.

This was the get_key function, which saves the pressed digit into r24. If the key was the one with the asterisk or the hash or even no key was pressed it set r24 to 0xff.

Analyze the second function

The second function (at 0x17c)started working with the register r24, so we were quite sure it was the key handler.

Trying to understand what this function does, we analyzed the functions called within this function.

So we revealed:

  • mul_8 at 0x218: performs multiplication on 8bit registers
    r24 = r24 * r22
  • mul_16 at 0x221: performs multiplication on 16bit registers
    r24:r25 = r24:r25 * r22:r23
  • div_mod at 0x223: calculates modulo and quotient of two 16bit registers
    r24:r25 = r24:r25 % r22:r23
    r22:r23 = r24:r25 / r22:r23
  • div_mod2 at 0x247: calculates modulo and quotient of two 16bit registers, with some magic if the msb is set
    r24:r25 = r24:r25 % r22:r23
    r22:r23 = r24:r25 / r22:r23
  • send_data at 0x0a9: calculates n = (x1 - x0 + 1) * (y1 - y0 + 1) and sends some PCF8833 like spi magic to write n bytes on the display
    x0 = r24
    y0 = r22
    x1 = r20
    y1 = r18
    data = r16

Knowing these functions, it is “very easy” to understand the handle_key function:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
handle_key: # 0x17c
  push    r16

  mov     r30, r24
  lds     r24, last_key
  cp      r30, r24
  breq    do_ret          # return if same as last key

  cpi     r30, 0xFF
  breq    do_ret          # return if no/wrong key

  lds     r24, index
  subi    r24, 0xFF       # index++
  sts     index, r24
  sts     last_key, r30   # save last_key
  ldi     r25, 0
  ldi     r22, 0xE8
  ldi     r23, 0
  rcall   mul_16          # write_pos = index * 0x00e8
  ldi     r22, 0xBF
  ldi     r23, 0x5A
  rcall   div_mod2        # wraped = write_pos % 0x5abf
                          # tmp = write_pos / 0x5abf
  movw    r18, r24
  subi    r18, 0xFF       # wraped++
  sbci    r19, 0xFF
  movw    r24, r18
  ldi     r22, 0x84
  ldi     r23, 0
  rcall   div_mod         # x = wraped % 0x0084
                          # y = wraped / 0x0084
  mov     r18, r22
  mov     r20, r24

  mov     r24, r30
  ldi     r22, 0x19
  rcall   mul_8           # val = key * 0x19

  mov     r16, r24
  mov     r24, r20
  mov     r22, r18
  rcall   send_data       # send_data(x,y,x,y, val)


  lds     r24, index
  ldi     r22, 0x19
  rcall   mul_8           # val = index * 0x25
  mov     r16, r24
  ldi     r24, 0
  ldi     r22, 0
  ldi     r20, 0
  ldi     r18, 0
  rcall   send_data       # send_data(0,0,0,0,val)

do_ret:
  pop     r16
  ret

The program writes the pressed key (digit* 0x19) at the n * 0x38 + 1 position and saves n * 0x19 at position 0.

Using the following python script are we able to reveal the pressed keys.

1
2
3
4
5
6
7
#!/usr/bin/env python
data = file("display.dat").read()
keys = ord(data[0])/0x19
flag = ""
for i in range(keys):
  flag += str(ord(data[(i*0xe8)+1])/0x19)
print flag

The flag was: 424986074