Quantcast
Channel: Embedded Systems – devttys0
Viewing all articles
Browse latest Browse all 35

Reverse Engineering a DTV Converter

$
0
0

I have an old DTV converter sitting around gathering dust, so I thought it would be interesting to take a look inside:

Inside the DTV Converter

As you can see, there’s not much there: a Thomson TV tuner, an IR receiver, 32MB of RAM and a 2MB flash chip (on the underside of the board). What really makes this interesting though is the LGDT1111 SoC; this is a DTV chip manufactured by LG, so it’s a little different than the Broadcom/Atheros/Ralink/etc SoCs found in a lot of other consumer devices. It is very popular with many DTV converters though, so determining its CPU architecture and reversing the underlying firmware could be interesting.

Digging around on the Internet turned up a nice block diagram of the LGDT1111 (courtesy of MVPtek):

LGDT1111 Block Diagram

The MVPtek web site states that the SoC uses an “AMR926EJ-STM” controller…could they mean an ARM926EJ-STM? Hmmm…

According to the block diagram the LGDT1111 does have a UART connection, and indeed there is a four pin connector on the board above the SoC with the pins nicely labeled:

DTV Serial Port

The serial port settings are 115200 baud, 8 bits, no stop bits, 1 parity bit. Here’s the boot messages that are dumped over the serial connection:

V1.05.40 May 20 2008, 16:24:11 sungwee@sungwee /cygdrive/d/boot_t/src
Config Baud Rate  : 115200 bps
System Clock Rate : 175 MHz
U-Boot Mem offset : Text/Data [00e00000, 00e19e9b], BSS [00e19e9c, 00e3e7c3]

RAM Configuration:
Bank #0: 00000000 32 MB
manufature ID : 0xc2, Device ID: 0x49
Flash:  2 MB
In:    serial
Out:   serial
Err:   serial
Set Flash Memory Structure...
Set Region   for Bootrom from 00:00 0x2c000000 (131072bytes)
Set Region01 for Appl    from 00:05 0x2c020000 (917504bytes)
cacheID : 1d0d20d2
write-back, register 7, format C
cache separated
D-cache  4kbytes 4-way 8 words (32bytes)
I-cache  4kbytes 4-way 8 words (32bytes)
Masks:: Index: c0000000, Int: 000000c0, Seg: 000003e0
Compare 00e13d8c 2c013d8c
Boot from address 2c000000
Boot from flash
MMU CR : 000510f8 (00004000)-> 000550fb
Processing BIZ file-from Flash: 0x2c020000
Aux data is symbol table(399362 bytes), pSymTab=0x1dfa8c
[Application Code]
Loading[4] Image from 2c020800 to 00010000(+1512037)
  -- TRY 0 ==>  2298510 bytes loaded in 0.778 sec, rc=0
  -- Checking CRC32[Bin ] ==>  Good, in 0.158 sec
Moving 7270 symbols(312102 bytes) from 0x001dfa8c to 0x0026d2c4
Load Image to 00010000...
Start from    00010000...
MMU CR : 000540fb (00004000)-> 000540fa
cacheID : 1d0d20d2
write-back, register 7, format C
cache separated
D-cache  4kbytes 4-way 8 words (32bytes)
I-cache  4kbytes 4-way 8 words (32bytes)
Masks:: Index: c0000000, Int: 000000c0, Seg: 000003e0
MMU CR : 000550fa (00004000)-> 000550ff
[37m[44mnTxtSyms     = 7270[0m
[37m[44mpSymTabBase  = [0x26d2d8..0x2827a0)[0m
[37m[44mpSymHashBase = [0x2827a4..0x274470)[0m
[37m[44mpSymStrBase  = [0x2b0317..0x2ceac6)[0m
[37m[44mnDwarfLst    = 184[0m
[37m[44mpDwarfLst    = [0x28607c..0x28663c)[0m
[37m[44mpDwarfData   = [0x28663c..0x029cdb)[0m
000.025:root    ] >> InitPool  ]]  [0x002cf000-0x01000000]
000.026:root    ] SM_MAT_POOL[ 4] = 0x002cf000(0x004000)
000.027:root    ] SM_MAT_POOL[ 8] = 0x002d3000(0x00c000)
000.028:root    ] SM_MAT_POOL[16] = 0x002df000(0x018000)
000.029:root    ] SM_MAT_POOL[32] = 0x002f7000(0x030000)
000.030:root    ] SM_MAT_POOL[64] = 0x00327000(0x0a0000)
000.031:root    ] >> InitBuddy ]] Addr=0x3c7000, Size=0xc39000
000.033:root    ] >> Total Free Heap Size = 0xd05000
Stack... 336860180 0023fa48
New Application booted, uart = 0xfe000000, baud = 115200

[30m[43mSystem Clock : 175MHz[0m
Starting  V1.6.05 , Jun 25 2008, 21:52:48, from @:/cygdrive/d/Work/Cubic2/Source/TCU/app/D2A1D

Start app init task..
AppInitTask: tid 0xd, priority 0x32
App_Initialize Task..
Dmc_Init()
Starting MiniShell, priority 8 ...
Minishell TID 0x37 (55) running
-------------------------------------------
val pad sel : 0x30001d10(0x46fc)
-------------------------------------------
-------------------------------------------
val pad sel : 0x30001d10(0x46fc)
-------------------------------------------
  gpio 6 [GPIO_KP_POWER] reset..
Mini Shell Task Spawned..
Shell> 000.070:tInit   ] [30m[43mConfiguring D2A_MODE with CVBS and S-video for LGDT1111T[0m
[30m[43mConfiguring D2A_MODE
[0m000.070:tInit   ] creating Queue tuner0Q  ... qid is 0x00240934
000.071:tInit   ] creating Queue     SqIR ... qid is 0x00240968
000.072:tInit   ]   2 Message queues are created
000.073:tInit   ] creating Sema4  VDPVSyn ...
000.073:tInit   ] creating Sema4 MuteSema ...
000.073:tInit   ] creating Sema4  OSDSema ...
000.073:tInit   ] creating Sema4 SectFilr ...
000.074:tInit   ] creating Sema4   AInfo  ...
000.074:tInit   ] creating Sema4   VInfo  ...
000.074:tInit   ] creating Sema4  NvmSem  ...
000.074:tInit   ] creating Sema4      IR  ...
000.075:tInit   ] creating Sema4    TIME  ...
000.075:tInit   ]   9 Semaphores are created
000.075:tInit   ]   0 Partitions are created
 install gpio 1 (group 0, offset 1) isr 105d10
 enable gpio group 0 interrupt
 install gpio 6 (group 0, offset 6) isr 106724
 enable gpio group 0 interrupt
reboot line successfully set to 'boot flash'
Thomson tuner 0xc2 detected
DHL_DEV_Init:
manufature ID : 0xc2, Device ID: 0x49
vendor: MX, device: 29LV160B
DHL_DMX_Init:
000.092:tInit   ] >> InitPool  ]]  [0x01000000-0x01084000]
000.093:tInit   ] >> InitBuddy ]] Addr=0x1000000, Size=0x084000
000.094:tInit   ] >> Total Free Heap Size = 0xd78bc4
 SM_SDEC_POOL inited
 sdec_io inited
 PWM inited
PSI_InitSF:
 SDEC isr 0x10, handler changed
 SDEC GPB IRQ inited

///////////////////////////////////////////////////////////////////

	   ------- Project CB2 -------  

			 - Build date: Jun 25 2008 21:51:18
			 - BOARD : D2A1T (2)

	 # Board  Version		: 2

	 # Kernel : uC/OS-II LGDT1111 1.6.05
	   - Core Driver version	: 0.0
	   - DSTHAL version		: 609181
	   - FE u-code version		: 000
	   - HDMI driver version	: 0.0

	 # Channel MW		: 609181
	   - EPG MW		: 707121
	   - Caption MW		: 0

	 # Application Version	: M5.97A_T 080625A

///////////////////////////////////////////////////////////////////

DhlPrintf Sema4 created
[APP tInit 13] 0001 debug trace ON
DHL_DEV_Init:
manufature ID : 0xc2, Device ID: 0x49
vendor: MX, device: 29LV160B
[NvRam tInit 13] 0001 FlashNvmInfo: blksz 0x10000-0x20, addr[0] 0x1e0000, addr[1] 0x1f0000
[DMC 0d tIni] 0001 Config:EEPROM size 0
[APP tInit 13] 0001
[APP tInit 13] 0001 .....Loading EEPROM block from FLASH
  Dump 'EEPROM' 256(0x100) bytes:
  00249af0: __ 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249b00: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249b10: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249b20: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249b30: 00 09 20 03 23 10 00 d1  00 00 00 00 00 00 00 00  .. .#..Ñ........
  00249b40: 00 00 00 00 00 00 00 01  00 00 00 00 00 00 00 00  ................
  00249b50: 00 00 00 00 09 00 00 00  00 00 00 00 00 00 00 00  ................
  00249b60: 00 00 00 09 00 01 00 00  00 00 00 00 00 00 00 00  ................
  00249b70: 00 00 01 00 00 00 00 00  00 04 00 00 00 00 00 00  ................
  00249b80: 00 00 00 00 00 00 00 3c  00 00 00 00 00 00 00 00  .......<........
  00249b90: 00 00 01 01 01 00 02 09  00 01 00 00 00 00 37 06  ..............7.
  00249ba0: 7f 01 00 00 00 00 00 00  00 30 30 30 30 00 00 00  .........0000...
  00249bb0: 00 03 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249bc0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249bd0: 00 36 00 00 00 00 00 00  00 00 00 00 00 00 00 00  .6..............
  00249be0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
  00249bf0: 00 __ __ __ __ __ __ __  __ __ __ __ __ __ __ __  ................
[APP tInit 13] 0001 Wait to Write SYSDB (After 15min)
[APP tInit 13] 0001 Wait to Write SYSDB (After 15min)
[APP tInit 13] 0001
[APP tInit 13] 0001
[APP tInit 13] 0001
[APP tInit 13] 0001 ConfigMask is 1663
[APP tInit 13] 0001
[APP tInit 13] 0001
[APP tInit 13] 0001 set rating passwd
[APP tInit 13] 0001 Rating 766 condition same
[APP tInit 13] 0001 ------------------------------------------------------
[APP tInit 13] 0001   CH 0,0 RF 2 #0, uid 0, ChannelType 0, CableStd 1
[APP tInit 13] 0001   ProgramNum 0, SourceId 0, PID pva: 0x0 0x0 0x0
[APP tInit 13] 0001   Audio vol 9, mute 0x0, lang 0
[APP tInit 13] 0001   TimeZone 1, DST 0
[APP tInit 13] 0001   CC: Active 0, Style 1, Size 1,  Src 2, Mode 9
[APP tInit 13] 0001     TxtCol 1, BgCol 0, TxtOp 0, BgOp 0, Edge type 0 col 1
[APP tInit 13] 0001     PenType 0, Author 55, Pos 0, mask 0x67f
[APP tInit 13] 0001 ------------------------------------------------------
[APP tInit 13] 0001 Load Air UCM
..initializing crc table..

Current: 'Air' 0-0, RF 2 #0, uid 0
  ---- UCM (total 0) [pid:p/v/a hex]----

[DMC 0d tIni] 0001 Notification callback 4f1f4 registerred.
[DMC 0d tIni] 0001 cmdChangeDisplay: cid 0
[DHL 0d tIni] 0001 DHL_AV_ChangeVideoFormat(4, 1, 2, 4, 0)
VDP_SetOutputAspectRatio: format -1, ar 2
**** vdp arc set: arc 3, zoom 1

 **** security not enabled ****

It looks like it’s using a U-Boot boot loader and running uC/OS-II, an open source RTOS. After this there is a lot of debug output as it scans channels for TV stations.

There is an interactive shell available, and typing ‘?’ (no quotes) provides you with a list of supported commands.

The 'i' command, displays a list of running processes:


Shell> i
MiniShell: [i]
call function of '0x000dd3c4'
Name                       TID(Pr) UsPr Stack_Usage State
-------------------------- ------- ---- ----------- --------------------
root                       0x01  1   62  1286/ 8192 Evt 0, Msg 0
IRTask                     0x17 23   40   372/ 8144 Evt 240b08, Msg 0
SysTime                    0x20 32   31   327/ 8144 Evt 240ba4, Msg 0
PsipRx                     0x23 35   28   293/ 8144 Evt 240d10, Msg 0
AV                         0x24 36   27   301/ 8144 Evt 240c40, Msg 0
MainKey                    0x26 38   25  1696/32720 Evt 2413f8, Msg 0
t_AuxCC                    0x2a 42   21   356/16336 Evt 241120, Msg 0
t_DccDe                    0x2b 43   20   317/16336 Evt 2410ec, Msg 0
t_DccDm                    0x2c 44   19   353/16336 Evt 241188, Msg 0
t_VbiDe                    0x2d 45   18   325/16336 Evt 241328, Msg 0
CC_tTim                    0x2e 46   17   328/ 8144 Evt 0, Msg 0
EATask                     0x2f 47   16  1370/39952 Evt 0, Msg 0
tDmc                       0x30 48   15  1942/32720 Evt 240de0, Msg 0
tEpgSca                    0x31 49   14   445/ 8144 Evt 241050, Msg 0
tEpgEvt                    0x32 50   13   333/ 8144 Evt 2410b8, Msg 0
tTimer                     0x33 51   12  2570/ 8144 Evt 240fb4, Msg 0
App_Tim                    0x34 52   11   137/16336 Evt 0, Msg 0
SigMon                     0x35 53   10  1752/16336 Evt 2413c4, Msg 0
miniShe                    0x37 55    8  9000/16336 Evt 0, Msg 0
idle                       0x3a 58    5   336/ 4048 Evt 240830, Msg 0
NwtTask                    0x3b 59    4  1056/ 8144 Evt 0, Msg 0
uC/OS-II Stat              0x3e 62    1   105/  512 Evt 0, Msg 0
uC/OS-II Idle              0x3f 63    0    80/  512 Evt 0, Msg 0
-------------------------- ------- ---- ----------- --------------------
call 0xdd3c4 result = 0x49 (73)

And the 'key' command displays IR codes and their associated actions:

Shell> key
MiniShell: [key]
call function of '0x00052014'
[APP miniShe 55] 4398 -- key string help --
[APP miniShe 55] 4398   00800000   POWER_OFF
[APP miniShe 55] 4398   00800001   MUTE
[APP miniShe 55] 4398   00800002   CC
[APP miniShe 55] 4398   00800003   ALANG
[APP miniShe 55] 4398   00800004   SCREEN
[APP miniShe 55] 4398   00800005   FAV
[APP miniShe 55] 4398   00800006   STILL
[APP miniShe 55] 4398   00800007   INFO
[APP miniShe 55] 4398   00800008   EPG
[APP miniShe 55] 4398   00800009   SRS_MODE
[APP miniShe 55] 4398   00800010   UP
[APP miniShe 55] 4398   00800011   DOWN
[APP miniShe 55] 4398   00800012   LEFT
[APP miniShe 55] 4398   00800013   RIGHT
[APP miniShe 55] 4398   00800018   VOL_UP
[APP miniShe 55] 4398   00800019   VOL_DOWN
[APP miniShe 55] 4398   0080001a   CH_UP
[APP miniShe 55] 4398   0080001b   CH_DOWN
[APP miniShe 55] 4398   00800020   SELECT
[APP miniShe 55] 4398   00800021   PREV_CH
[APP miniShe 55] 4398   00800022   MENU
[APP miniShe 55] 4398   00800023   HELP
[APP miniShe 55] 4398   00800024   DRF
[APP miniShe 55] 4398   00800025   EXT_INPUT
[APP miniShe 55] 4398   00800026   CH_ADD
[APP miniShe 55] 4398   00800027   CH_DEL
[APP miniShe 55] 4398   00800028   EXIT
[APP miniShe 55] 4398   0080002e   ADT
[APP miniShe 55] 4398   00800030   0
[APP miniShe 55] 4398   00800031   1
[APP miniShe 55] 4398   00800032   2
[APP miniShe 55] 4398   00800033   3
[APP miniShe 55] 4398   00800034   4
[APP miniShe 55] 4398   00800035   5
[APP miniShe 55] 4398   00800036   6
[APP miniShe 55] 4398   00800037   7
[APP miniShe 55] 4398   00800038   8
[APP miniShe 55] 4398   00800039   9
[APP miniShe 55] 4398   00800043   SLEEP
[APP miniShe 55] 4398   00800044   SMART_PICTURE
[APP miniShe 55] 4398   00800045   SMART_SOUND
[APP miniShe 55] 4398   00800046   CH_BLOCK_EDIT
[APP miniShe 55] 4398   00800081   DOT
[APP miniShe 55] 4398   00800120   BACK
[APP miniShe 55] 4398 
call 0x52014 result = 0x1 (1)

There are some other interesting commands sprinkled in there, such as the video_freeze command that allows you to freeze the on screen video without interrupting the audio (an option I couldn't find anywhere in the menu-driven user interface), or the rating_enable command which allows you to enable/disable the parental content filters without a password (bye-bye V-chip!).

This is all interesting, but if we really want to get down and dirty with this thing we're going to need some firmware. There are of course no firmware updates for these converter boxes, so instead we'll remove the flash chip and dump its contents using flashbin and a Gumbi board (raw flash image can be downloaded here):

$ flashbin --chip=mx29lv160 --read=flash.bin
Reading all bytes starting at address 0x0…

[################################################] 100.00%

All of the strings in the dumped firmware image appear to be related to the bootloader, but a binwalk scan reveals a gzip compressed section of data at offset 0x20800:

DECIMAL         HEX             DESCRIPTION
-------------------------------------------------------------------------------------------------------
30216           0x7608          uImage header, header size: 64 bytes, header CRC: 0xE15744, created: Sat Jun 20 18:12:44 1970, image size: 14767988 bytes, Data Address: 0xE15790, Entry Point: 0xE157A4, data CRC: 0xE3520001, image name: \341\240@\002\341\240p\003\343\240`
101737          0x18D69         LZMA compressed data, properties: 0x80, dictionary size: 21364736 bytes, uncompressed size: 33554432 bytes
133120          0x20800         gzip compressed data, from Unix, DD-WRT date: Wed Dec 31 19:00:00 1969

Running a code scan with binwalk (option -A) against the extracted gzip contents confirms that it contains big endian ARM instructions:

DECIMAL         HEX             DESCRIPTION
---------------------------------------------------------------
896             0x380           ARMEB function prologue
1132            0x46C           ARMEB function epilogue
1360            0x550           ARMEB function epilogue
1436            0x59C           ARMEB function prologue
1464            0x5B8           ARMEB function epilogue
1472            0x5C0           ARMEB function prologue
1544            0x608           ARMEB function epilogue
1652            0x674           ARMEB function prologue
1844            0x734           ARMEB function epilogue
2224            0x8B0           ARMEB function prologue
2292            0x8F4           ARMEB function epilogue
2360            0x938           ARMEB function epilogue
2456            0x998           ARMEB function prologue
2596            0xA24           ARMEB function epilogue
2700            0xA8C           ARMEB function prologue
2752            0xAC0           ARMEB function epilogue
2848            0xB20           ARMEB function epilogue
2856            0xB28           ARMEB function prologue
...

Based on this and a quick look at the strings, this is definitely looking like the OS code, so let's get it loaded into IDA. Based on the boot messages obtained from the serial port, the gzipped data we found at offset 0x20800 gets loaded into memory at 0x10000, which is where execution starts (also note the pSymTab address...this will be useful later!):

...
Boot from address 2c000000
Boot from flash
MMU CR : 000510f8 (00004000)-> 000550fb
Processing BIZ file-from Flash: 0x2c020000
Aux data is symbol table(399362 bytes), pSymTab=0x1dfa8c
[Application Code]
Loading[4] Image from 2c020800 to 00010000(+1512037)
  -- TRY 0 ==>  2298510 bytes loaded in 0.778 sec, rc=0
  -- Checking CRC32[Bin ] ==>  Good, in 0.158 sec
Moving 7270 symbols(312102 bytes) from 0x001dfa8c to 0x0026d2c4
Load Image to 00010000...
Start from    00010000...

Loading the extracted code into IDA at the offset 0x10000 results in a decent initial analysis:

IDA initial code analysis

String references also check out, and we have several promising candidates for common library functions, such as snprintf:

Proper string references

sub_3EA1C is probably snprintf

This is encouraging, but with almost 4,000 functions, manually identifying them will take too long. Looking at the strings in IDA, there appears to be a list of strings that correspond to function and symbol names, starting at address 0x222ADF:

Symbol strings

Now we need to find out how to correlate these strings to their appropriate functions. The boot messages mentioned a symbol table at 0x1DFA8C, so we'll start looking there. At address 0x1DFAAC, we find what appears to be a list of symbol structures:

Symbol tables

The symbol structure appears to be:

struct symbol
{
     uint32_t func_address;         /* Function pointer to this symbol's function */
     uint32_t next_func_address;    /* Function pointer to the next symbol's function */
     uint32_t symbol_name_offset;   /* Offset of the symbol name in the symbol strings listing */
};

Applying this structure to the first entry in the above IDA screenshot, the func_address is 0x10000 which is the entry point for our code. Adding its symbol_name_offset value (0x1BAB2) to the address where we found all the symbol strings (0x222ADF), we get: 0x222ADF + 0x1BAB2 = 0x23E591. Here we find the string "start_code":

Symbol name for the entry point

This looks good! A quick IDAPython script takes care of renaming all of the functions to their appropriate symbol names:

start_names = 0x222ADF
start_addrs = 0x1DFAAC

addr = start_addrs
symbol_addr = BADADDR

while symbol_addr != 0:
        symbol_addr = Dword(addr)
        next_symbol = Dword(addr + 4)
        string_addr = start_names + Dword( addr + 8 )
        symbol_name = GetString(string_addr)

        try:
                if GetSegmentAttr(symbol_addr, SEGATTR_TYPE) == 2:
                        MakeFunction(symbol_addr)
        except:
                pass

        MakeName(symbol_addr, symbol_name)
        print '0x%X   %s' % (symbol_addr, symbol_name)

        addr += (4*3)

After running this script, 96% of our functions have been named, including the function we previously identified as snprintf:

Named functions

snprintf properly identified and named

Coupling this with the available source code for the uC/OS-II RTOS, we could really go to town on figuring out how exactly this thing works if we were so inclined. But scrolling through the list of functions, one of the function names caught my attention:

Now that's an interesting function name...

App_CheckRemoteBackdoorKey? :)

It turns out that this function is called by the MainRemoteKeyTask function, which passes App_CheckRemoteBackdoorKey the infrared key code received from the IR remote control. If one of several special IR codes is detected by App_CheckRemoteBackdoorKey, MainRemoteKeyTask will preform several actions such as providing a service menu, running a factory test, and even performing and over the air firmware upgrade:

MainRemoteKeyTask

These are actually pretty neat little boxes, and there is a surprising amount of potential for customization and modification. I can envision a wireless microcontroller that provides an enhanced user interface via the serial port and can automate commands, such as switching the channel when your favorite show comes on. Unfortunately, DTV reception is terrible here which really takes all the fun out of any cool mods like this. Oh well, I'm off to watch some Netflix.


Viewing all articles
Browse latest Browse all 35

Trending Articles