In addition to finding the most frequently called functions, we should go through the memory map and identify importants parts of it.
One part of this that is very important to how the device operates is the vector table, right at the bottom of the flash.
The vector table contains addresses that are called when certain interrupts are triggered. For these microcontrollers, this is structured like this:
So we take a look right at the beginning of the disassembly:
00000 00 NOP 00001 01 ADDW AX,AX 00002 82 INC C 00003 2084 SUBW SP,#84H 00005 2086 SUBW SP,#86H 00007 2088 SUBW SP,#88H 00009 208a SUBW SP,#8AH 0000b 208c SUBW SP,#8CH 0000d 208e SUBW SP,#8EH 0000f 2090 SUBW SP,#90H 00011 2092 SUBW SP,#92H 00013 2001 SUBW SP,#1H 00015 247d23 SUBW AX,#237DH 00018 292494 MOV A,9424H[C] 0001b 2096 SUBW SP,#96H 0001d 203a SUBW SP,#3AH 0001f 21 ? 00020 be20 MOVW PM0,AX 00022 3c21 SUBC A,#21H 00024 92 DEC C 00025 225921 SUBW AX,!2159H 00028 ba22 MOVW [DE+22H],AX 0002a 9820 MOV [SP+20H],A 0002c 00 NOP 0002d 209a SUBW SP,#9AH 0002f 209c SUBW SP,#9CH 00031 209e SUBW SP,#9EH 00033 20a0 SUBW SP,#0A0H 00035 20a2 SUBW SP,#0A2H 00037 20a4 SUBW SP,#0A4H 00039 20a6 SUBW SP,#0A6H 0003b 205e SUBW SP,#5EH 0003d 23 SUBW AX,BC 0003e d7 RET 0003f 226023 SUBW AX,!2360H 00042 a820 MOVW AX,[SP+20H] 00044 aa20 MOVW AX,[DE+20H] 00046 ac20 MOVW AX,[HL+20H] 00048 ae20 MOVW AX,PM0 0004a b020b2 DEC !0B220H 0004d 20b4 SUBW SP,#0B4H 0004f 20b6 SUBW SP,#0B6H 00051 20b8 SUBW SP,#0B8H 00053 20ba SUBW SP,#0BAH 00055 20ff SUBW SP,#0FFH
The disassembler has tried to disassemble when it shouldn’t – a common issue. Though, to be honest, it should know that this area is a vector table.
So if we re-organise the hex file into something a bit more readable, we get this:
0000 -> 0100 * RESET 0004 -> 2082 0006 -> 2086 0008 -> 2088 000A -> 208A 000C -> 208C 000E -> 208E 0010 -> 2090 0012 -> 2092 0014 -> 2401 * INTST3 0016 -> 237D * INTSR3 0018 -> 2429 * INTSRE3 001A -> 2094 001C -> 2096 001E -> 213A * INST0 0020 -> 20BE * INTSR0 0022 -> 213C * INTSRE0 0024 -> 2292 * INTST1 0026 -> 2159 * INTSR1 0028 -> 22BA * INTSRE1 002A -> 2098 002C -> 2000 * INTTM00 002E -> 209A 0030 -> 209C 0032 -> 209E 0034 -> 20A0 0036 -> 20A2 0038 -> 20A4 003A -> 20A6 003C -> 235E * INTST2 003E -> 22D7 * INTSR2 0040 -> 2360 * INTSRE2 0042 -> 20A8 0044 -> 20AA 0046 -> 20AC 0048 -> 20AE 004A -> 20B0 004C -> 20B2 004E -> 20B4 0050 -> 20B6 0052 -> 20B8 0054 -> 20BA
Notice how a lot of the addresses are just incrementing- 20AA, 20AC, 20AE. This is just a massive block of RETI instructions – i.e. the interrupt handler just returns immediately – it is not implemented.
02092 61fc RETI 02094 61fc RETI 02096 61fc RETI 02098 61fc RETI 0209a 61fc RETI 0209c 61fc RETI 0209e 61fc RETI 020a0 61fc RETI 020a2 61fc RETI 020a4 61fc RETI 020a6 61fc RETI 020a8 61fc RETI 020aa 61fc RETI 020ac 61fc RETI 020ae 61fc RETI 020b0 61fc RETI 020b2 61fc RETI
All of the vectors that are marked with an asterisk and with a name are implemented or used by the board. There are some important handlers here – mainly the serial IO.
Reset jumps to 0x100. I’ll save looking at that for another time – mostly the reset vector will be setting up buffers, memory, pointers, some checks.
You can also see we have groups of interrupt handlers for INTST* (transmit finished), INTSR* (receive finished), INTSRE* (receive error). These are for the the UARTs 0-3 respectively. Their implementation is very similar – let’s look at UART1 which is used for the GPRS modem.
// INTST1 02292 c1 PUSH AX 02293 c3 PUSH BC 02294 c7 PUSH HL 02295 fbb6e0 MOVW HL,!0E0B6H 02298 afb4e0 MOVW AX,!0E0B4H 0229b 47 CMPW AX,HL 0229c dd17 BZ $22B5H 0229e dbb4e0 MOVW BC,!0E0B4H 022a1 49b8e4 MOV A,0E4B8H[BC] // Get data from E4B8 using offset from E0B4 022a4 9e44 MOV SIO10,A // Move to serial data TX register 022a6 a2b4e0 INCW !0E0B4H // Increment the offset 022a9 afb4e0 MOVW AX,!0E0B4H 022ac 440a04 CMPW AX,#40AH // Is the offset greater than 1034? If so reset to 0 022af dc04 BC $22B5H 022b1 f6 CLRW AX 022b2 bfb4e0 MOVW !0E0B4H,AX 022b5 c6 POP HL 022b6 c2 POP BC 022b7 c0 POP AX 022b8 61fc RETI
Again – I’m not really currently interested in precise detail, just an idea of what is happening. This handler takes a byte from a buffer at 0xE4B8 and writes it into the transmit register. That buffer will appear elsewhere in the code and hint to us when something is being sent out of UART1.
We can then go through all of the other UART/serial functions and identify potential transmit/receive buffers.
Interestingly, INTST0 and INTST2 are just RETI instructions. Why do these not require a transmit empty interrupt handler? Is it handled in software elsewhere?
The next handler that stands out from the others is INTTM00. This is the timer interrupt for timer 0 which will fire when the timer hits a certain value.
// INTTM00 02000 c1 PUSH AX 02001 c3 PUSH BC 02002 c7 PUSH HL 02003 aefc MOVW AX,0FFFFCH 02005 c1 PUSH AX 02006 a0b3f6 INC !0F6B3H 02009 8fb3f6 MOV A,!0F6B3H 0200c 5c03 AND A,#3H 0200e 4c03 CMP A,#3H 02010 df38 BNZ $204AH 02012 a0b4f6 INC !0F6B4H 02015 fcfc2801 CALL !!128FCH 02019 fc932601 CALL !!12693H 0201d fcf22701 CALL !!127F2H 02021 f45c CLRB 0FFE5CH 02023 fc132a01 CALL !!12A13H // 7SEG display 02027 fcaa3201 CALL !!132AAH // Buttons 0202b 8fb4f6 MOV A,!0F6B4H 0202e 5c03 AND A,#3H 02030 dd08 BZ $203AH 02032 91 DEC A 02033 dd0b BZ $2040H 02035 91 DEC A 02036 dd0e BZ $2046H 02038 ef10 BR $204AH 0203a fccbff00 CALL !!0FFCBH // Analog 0203e ef0a BR $204AH 02040 fcd13101 CALL !!131D1H 02044 ef04 BR $204AH 02046 fc063301 CALL !!13306H 0204a fc742e01 CALL !!12E74H 0204e fc84ff00 CALL !!0FF84H 02052 72 MOV C,A 02053 81 INC A 02054 dd24 BZ $207AH 02056 62 MOV A,C 02057 70 MOV X,A 02058 f1 CLRB A 02059 01 ADDW AX,AX 0205a 04b8f5 ADDW AX,#0F5B8H 0205d 16 MOVW HL,AX 0205e f6 CLRW AX 0205f b1 DECW AX 02060 bb MOVW [HL],AX 02061 62 MOV A,C 02062 d1 CMP0 A 02063 dd11 BZ $2076H 02065 2c11 SUB A,#11H 02067 dd05 BZ $206EH 02069 91 DEC A 0206a dd06 BZ $2072H 0206c ef0c BR $207AH 0206e e46a ONEB 0FFE6AH 02070 ef08 BR $207AH 02072 e46b ONEB 0FFE6BH 02074 ef04 BR $207AH 02076 fcf7fc00 CALL !!0FCF7H 0207a c0 POP AX 0207b befc MOVW 0FFFFCH,AX 0207d c6 POP HL 0207e c2 POP BC 0207f c0 POP AX
This looks like it is fired periodically. A number of counters are used so that portions of the subroutine are only run now and then.
There are a lot of calls, and if we look to them we can clearly identify function:
// Suspect from IO this is output to 7 seg 12a13 d45d CMP0 0FFE5DH 12a15 f1 CLRB A 12a16 61f8 SKNZ 12a18 e1 ONEB A 12a19 9d5d MOV 0FFE5DH,A 12a1b d45d CMP0 0FFE5DH 12a1d dd24 BZ $12A43H 12a1f 8f46f6 MOV A,!0F646H 12a22 d448 CMP0 0FFE48H 12a24 dd0a BZ $12A30H 12a26 36b4f6 MOVW HL,#0F6B4H 12a29 31d50e BF [HL].5H,$12A3AH 12a2c 51ff MOV A,#0FFH 12a2e ef0a BR $12A3AH 12a30 d446 CMP0 0FFE46H 12a32 dd06 BZ $12A3AH 12a34 36b4f6 MOVW HL,#0F6B4H 12a37 31f3f2 BT [HL].7H,$12A2CH 12a3a 712305 CLR1 P5.2H // These are the common cathodes 12a3d 713205 SET1 P5.3H 12a40 9d06 MOV P6,A // P6 is the 7SEG 12a42 d7 RET 12a43 8f47f6 MOV A,!0F647H 12a46 d449 CMP0 0FFE49H 12a48 dd0a BZ $12A54H 12a4a 36b4f6 MOVW HL,#0F6B4H 12a4d 31d50e BF [HL].5H,$12A5EH 12a50 51ff MOV A,#0FFH 12a52 ef0a BR $12A5EH 12a54 d447 CMP0 0FFE47H 12a56 dd06 BZ $12A5EH 12a58 36b4f6 MOVW HL,#0F6B4H 12a5b 31f3f2 BT [HL].7H,$12A50H 12a5e 712205 SET1 P5.2H // common cathodes flip 12a61 713305 CLR1 P5.3H 12a64 9d06 MOV P6,A 12a66 d7 RET
From the IO, we can see this is likely to be updating the 7 segment LED displays.
The method used – of setting one common cathode, then the segments for that half, then the other common cathode, then the segments for that half – means that this needs to be called relatively frequently otherwise flicker will be detected by the eye.
// Button detection and debounce? 132aa 31220217 BT P2.2H,$132C5H // Button A 132ae 4029e0ff CMP !0E029H,#0FFH 132b2 dd24 BZ $132D8H 132b4 a029e0 INC !0E029H 132b7 4029e007 CMP !0E029H,#7H 132bb df1b BNZ $132D8H 132bd cf29e0ff MOV !0E029H,#0FFH 132c1 e445 ONEB 0FFE45H 132c3 ef13 BR $132D8H 132c5 d529e0 CMP0 !0E029H 132c8 dd0e BZ $132D8H 132ca b029e0 DEC !0E029H 132cd 4029e0f8 CMP !0E029H,#0F8H 132d1 df05 BNZ $132D8H 132d3 f529e0 CLRB !0E029H 132d6 f445 CLRB 0FFE45H 132d8 31320216 BT P2.3H,$132F2H // Button B 132dc 402ae0ff CMP !0E02AH,#0FFH 132e0 dd23 BZ $13305H 132e2 a02ae0 INC !0E02AH 132e5 402ae007 CMP !0E02AH,#7H 132e9 df1a BNZ $13305H 132eb cf2ae0ff MOV !0E02AH,#0FFH 132ef e444 ONEB 0FFE44H 132f1 d7 RET 132f2 d52ae0 CMP0 !0E02AH 132f5 dd0e BZ $13305H 132f7 b02ae0 DEC !0E02AH 132fa 402ae0f8 CMP !0E02AH,#0F8H 132fe df05 BNZ $13305H 13300 f52ae0 CLRB !0E02AH 13303 f444 CLRB 0FFE44H 13305 d7 RET
Again, from the IO, we can see that the buttons are being polled. There’s also some counters changing – probably some debounce.
// Analog something or other 0ffcb f1 CLRB A 0ffcc 71042a MOV1 CY,0FFE2AH.0H 0ffcf 7189 MOV1 A.0H,CY 0ffd1 70 MOV X,A 0ffd2 f1 CLRB A 0ffd3 710ce3 MOV1 CY,ADIF 0ffd6 61dc ROLC A,1 0ffd8 6158 AND A,X 0ffda dd23 BZ $0FFFFH 0ffdc 710be3 CLR1 ADIF 0ffdf 4031ff07 CMP !ADS,#7H 0ffe3 8d1f MOV A,ADCRH 0ffe5 df0b BNZ $0FFF2H 0ffe7 9f0af6 MOV !0F60AH,A 0ffea 717b30 CLR1 ADCS 0ffed ce3106 MOV ADS,#6H 0fff0 ef09 BR $0FFFBH 0fff2 9f0bf6 MOV !0F60BH,A 0fff5 717b30 CLR1 ADCS 0fff8 ce3107 MOV ADS,#7H 0fffb 00 NOP 0fffc 717a30 SET1 ADCS 0ffff d7 RET
This does something with one of the ADC inputs. I’ve not seen anything of interest that uses analog yet, so I’ll not look into this more currently. It could be the input voltage (the boare can alarm on this) or PSTN line voltage.
There aren’t many other clearly idenfiable subroutines, but these few clearly identifiable ones give me confidence that this interrupt handler is most handling periodic IO.
This program structure of calling time-sensitive IO using a timer interrupt is fairly common in embedded systems. It means that IO is serviced regularly, allowing more time consuming (or non deterministic time) processing to happen outside of the interrupt in the main code. It means there are a lot of buffers and global variables to pass data back and forth that we can look at and play with.
From a security perspective, it can also produce problems. If we can stall something in the timer interrupt – by buffer overflow, bad input or so on – it can be possile to lock up a device. I’d hope that the board used a watchdog timer to recover from this though.