Sorry for the slow-down in posts – I stored up a load of posts, then posted them too quickly.
Since the last post, I have identified a lot of functionality in the code, including:
- TX/RX subs for all the UARTS
- Locations of TX/RX buffers for all UARTS
- Multiply, divide, modulus and exponent subs
- Conversion subs (ASCII->hex etc.)
- EEPROM read/write subs
- 7 segment display subs
- Buzzer subs
- Reading button state
- Memory copy, search etc.
- Hardware initilisation
- Two intetesting subs that are called frequently to move working memory onto stack and back again
The board supports several GPRS modems – the Wavecom GR64 on the boards I have, but also Cinterion and Telit. The AT command set between the modems is completely different, so there are often several sections of code for common functionality like “Activate PDP context”. I’ve only looked at the Wavecom parts.
Having all this has given me enough to tie observed behaviour (just from using the Dualcom board, and the logic traces) back to specific areas of the code. Seeing the strings on the UART and searching for the address where that string is stored in flash/EEPROM is very useful.
I’m fairly sure the code is compiled C from IAR Embedded Workbench – the multiply, divide and startup are identical. If I compile code from Renesas Cubesuite, it looks very different.
There is still a lot of code that looks like assembly though – some of the memory operations, calling convention, and string searching look very odd to be compiled C.
One thing that caught my eye during the serial trace was that the board repeatedly checks for SMS messages. This suggests it is waiting for something to arrive, possibly commands
Digging around a bit, in the manual for the Dualcom GSM (not the GPRS), it mentions SMS remote control – sending commands to the CSL Dualcom:
This is interesting. A 6 digit PIN and some limited commands. It wouldn’t be the first time that PINs are defaulted to a certain value or derived from open information like the ICCID or number. It also wouldn’t be the first time that there are undocumented or hidden commands for a device, or even a backdoor.
We’re going to need to have a dig about in the code to see how these commands are dealt with. A good starting point would be to find where the string AT+CMGR (read text messages) is used, and follow on from there.
AT+CMGR is stored in the flash at 0x1BE6 (0x1000-0x2000 is almost exclusively strings and lookup tables). If we search for this address, we find the following chunk:
// State 0x86
// Request text messages
0c657 afc8f5 MOVW AX,!0F5C8H // This is used as a time out
0c65a 7c80 XOR A,#80H
0c65c 440180 CMPW AX,#8001H
0c65f dc07 BC $0C668H
0c661 d46a CMP0 0FFE6AH
0c663 61f8 SKNZ
0c665 ee3902 BR $!0C8A1H
0c668 e1 ONEB A
0c669 fc64d700 CALL sub_Serial_ResetBuffers
0c66d 32b61e MOVW BC,#1EB6H // AT+CMGR=
0c670 e1 ONEB A
0c671 fcd1e100 CALL sub_Serial_WriteString_e1d1
0c675 8f49f7 MOV A,!0F749H
0c678 72 MOV C,A
0c679 f3 CLRB B
0c67a e1 ONEB A
0c67b fc04e200 CALL sub_Serial_WriteHexAsDec_e204
0c67f f46b CLRB 0FFE6BH
0c681 f46a CLRB 0FFE6AH
0c683 cf41f704 MOV !0F741H,#4H
0c687 530d MOV B,#0DH //CR
0c689 e1 ONEB A
0c68a fcb2e100 CALL sub_Serial_WriteChar_e1b3
0c68e 30a302 MOVW AX,#2A3H
0c691 bfc8f5 MOVW !0F5C8H,AX // set timeout to 675
0c694 cf4af704 MOV !0F74AH,#4H // State 4 next
0c698 ee0602 BR $!0C8A1H
This is a massive state machine. The “state” is stored in 0xF74A, checked at the beginning of the sub and then a branch performed to the current state.. State 0x86 sends “AT+CMGR=1” to UART1. It then sets the next state to 0x4.
Each one of the states sets a counter in 0xFF5C8 which is decremented in the timer interrupt. If this is hit, the state machine seems to be reset in most cases.
// State 0x4
// State after requesting text messages
0c69b afc8f5 MOVW AX,!0F5C8H // timeout
0c69e 7c80 XOR A,#80H
0c6a0 440180 CMPW AX,#8001H
0c6a3 dc42 BC $0C6E7H
0c6a5 8f19f7 MOV A,!0F719H
0c6a8 4c02 CMP A,#2H
0c6aa 61d8 SKNC
0c6ac eef201 BR $!0C8A1H
0c6af 32c01e MOVW BC,#1EC0H // "REC "
0c6b2 e1 ONEB A
0c6b3 fcd3de00 CALL sub_Serial_FindInRX_ded3
0c6b7 d1 CMP0 A
0c6b8 dd0d BZ $0C6C7H
0c6ba 30a302 MOVW AX,#2A3H
0c6bd bfc8f5 MOVW !0F5C8H,AX
0c6c0 cf4af718 MOV !0F74AH,#18H // State 0x18 next
0c6c4 eeda01 BR $!0C8A1H
0c6c7 32c61e MOVW BC,#1EC6H // "STO "
0c6ca e1 ONEB A
0c6cb fcd3de00 CALL sub_Serial_FindInRX_ded3
0c6cf d1 CMP0 A
0c6d0 dd07 BZ $0C6D9H
0c6d2 cf4af717 MOV !0F74AH,#17H // State 0x17 next
0c6d6 eec801 BR $!0C8A1H
0c6d9 3152410a BT 0FFE41H.5H,$0C6E7H
0c6dd afdef5 MOVW AX,!0F5DEH
0c6e0 7c80 XOR A,#80H
0c6e2 440180 CMPW AX,#8001H
0c6e5 dc0b BC $0C6F2H
0c6e7 cf49f70f MOV !0F749H,#0FH
0c6eb cf4af796 MOV !0F74AH,#96H // State 0x96 next
0c6ef eeaf01 BR $!0C8A1H
State 0x4 searches the receive buffer on UART1 for the characters “REC” or “STO”. “REC” is what we would see if there was a text message to read, and if this is found we move to stat 0x18. This calls 0xC301 and the flow continues from there. It might be better to describe this as a process rather than show ASM.
The format of the text message received would be as follows:
+CMGR: “REC UNREAD”,“+447747008670”,“Matt L”,“02/11/19,09:57:28+00”,145,36,0,0,“ +447785016005”,145,8
The code, give or take, works as follows.
1. Search RX buffer for string READ – this finds REC UNREAD and REC READ, both found in text messages. If not found, abort.
2. Keep on going until a + is found – the start of the phone number
3. Loop until a non-numeric character is found, storing the number in 0xFE17F. The number is used later to send a text back.
4. Loop until a carriage return is found. This is the end of the text detail and the start of the actual text.
5. Copy the message to address 0xFE20E.
6. Read in a 6-digit PIN from EEPROM at 0x724. I can’t see where this is set in the Windows utility.
7. Check that the 6-digit PIN is at the beginning of the message.
8. Call a function to parse the rest of the message and act on it.
This is where it gets mildly interesting – not only are there the documented commands but there are ones that include “CALL” and “4 2 xxxxxxxxxx”….