Disassembling the firmware of the Siemens (Infeneon) C167CS-LM microcontroller

Hello everyone!

Recently, I needed to disassemble the firmware on a very old Infineon C167CS-LM microcontroller (don’t ask why).
I remembered I had an IDA Pro. I launched IDA Pro and opened the firmware binary file. I selected the C166 family, and in the models from this family, I selected the C167CS. IDA disassembled only a very small portion of the code, namely:
basic hardware traps:
RESET_HANDLER,
BTRAP_HANDLER, etc.

But there weren’t enough interrupts disassembled, there should have been 56, as stated in the documentation for this microcontroller, but only 10 interrupt declarations were disassembled. But since this file only stored ROM data, and not the full 16 MB dump, I deleted all segments except the ROM. I extended the ROM itself to the end of the file (to address 0x7FFFFF, the file is 512 KB in size), and nothing changed. Then I tried manually marking the bytes that should be interrupt declarations as code using the C key. This definitely helped, but the jmps instructions that appeared in place of the previously unknown bytes were invalid, since they referenced the address of the 9th segment, which physically doesn’t exist (I remind you that the file is 512 KB in size).
I also noticed one peculiarity: the declarations, initially correctly interpreted by the ida, only jumped to segment 0, while the invalid ones only jumped to segment 9.

Example valid declaration:
ROM:00000028 BTRAP:
jmps 0, BTRAP_HANDLER
Example invalid declaration:
ROM:00000040: CC0INT:
jmps 9, 250h; 90250h

I didn’t install any ida plugins. If anyone knows how to fix this problem, please write here.

I am new to this business, so please don’t judge me too harshly in this matter.

firmware file

I would be grateful for any information that will help me with this difficult task.

Thanks in advance!

In general, IDA only proactively disassembles code using known entrypoints (e.g. interrupt vectors) and following code flow which uses standard conventions.

There are several reasons not all code is disassembled.

Missing code

It seems most interrupt handlers are in code segment 9 which is not present in the loaded portion, so only the handlers in the segment 0 were disassembled.

Non-standard calling convention

It seems some of the functions used by the startup code (sub_698, sub_71E) use r3 as the return register instead of the usual call/ret pattern. So you need to convert the values loaded into r3 to offset and continue disassembly at that location manually. It may be easier to perform this task in text listing mode (press space to switch between it and graph mode). The end result looks like this:

ROM:00000590                 mov     r3, #loc_598
ROM:00000594                 jmpa    cc_UC, sub_698
ROM:00000598 ; ---------------------------------------------------------------------------
ROM:00000598
ROM:00000598 loc_598:                                ; DATA XREF: ROM:00000590↑o
ROM:00000598                 mov     r0, #0E000h
ROM:0000059C                 mov     r1, #0E790h
ROM:000005A0                 mov     r2, #4
ROM:000005A2                 mov     r3, #loc_5AA
ROM:000005A6                 jmpa    cc_UC, sub_698
ROM:000005AA ; ---------------------------------------------------------------------------
ROM:000005AA
ROM:000005AA loc_5AA:                                ; DATA XREF: ROM:000005A2↑o
ROM:000005AA                 mov     r0, #0C000h
ROM:000005AE                 mov     r1, #0D7FEh
ROM:000005B2                 mov     r2, #4
ROM:000005B4                 mov     r3, #loc_5BA
ROM:000005B8                 jmpr    cc_UC, sub_698
ROM:000005BA ; ---------------------------------------------------------------------------
ROM:000005BA
ROM:000005BA loc_5BA:                                ; DATA XREF: ROM:000005B4↑o
ROM:000005BA                 mov     ADDRSEL2, #883h
ROM:000005BE                 mov     r0, #0
ROM:000005C2                 mov     r1, #3FFEh
ROM:000005C6                 mov     r2, #8
ROM:000005C8                 mov     r3, #loc_5CE
ROM:000005CC                 jmpr    cc_UC, sub_698
ROM:000005CE ; ---------------------------------------------------------------------------
ROM:000005CE
ROM:000005CE loc_5CE:                                ; DATA XREF: ROM:000005C8↑o
ROM:000005CE                 mov     ADDRSEL2, #2000h
ROM:000005D2                 mov     WDTCON, #9201h
ROM:000005D6                 srvwdt
ROM:000005DA                 mov     r0, #0EF00h
ROM:000005DE                 mov     r1, #0EFFFh
ROM:000005E2                 mov     r2, #10h
ROM:000005E6                 mov     r3, #loc_5EE
ROM:000005EA                 jmpa    cc_UC, sub_71E
ROM:000005EE ; ---------------------------------------------------------------------------
ROM:000005EE
ROM:000005EE loc_5EE:                                ; DATA XREF: ROM:000005E6↑o

Unhandled/non-recognized code patterns

The code at 0000389C seems to perform an indexed table jump (i.e. a switch statement)

ROM:00003892                 movbz   r0, byte_C11A
ROM:00003896                 shl     r0, #1
ROM:00003898                 extp    #0, #1
ROM:0000389C                 mov     r0, [r0+#3CAh]
ROM:000038A0                 jmpi    cc_UC, [r0]

and 3CA contains a table of offsets:

ROM:000003CA                 dw loc_38A2, unk_38EC, unk_3992, unk_39BA, unk_39EE, unk_3A1E
ROM:000003D6                 dw unk_3A60, unk_3AA6, unk_3ACC, unk_3AEC, unk_3B22, unk_3B64
ROM:000003E2                 dw unk_3B9A, unk_3BD4, unk_3C10, unk_3C3A

it can be marked up manually using the manual switch idiom.

Indirectly referenced code

sub_35F6 seems to use a table of functions invoked using indirect call instruction:

ROM:0000361E                 mov     r8, #34Ah
ROM:00003622                 mov     r9, #0
ROM:00003626                 jmpr    cc_UC, loc_367E
<skipped>
ROM:0000366E                 mov     r4, [r8+#6]
ROM:00003672                 nop
ROM:00003674                 calli   cc_UC, [r4]
ROM:00003678 ; ---------------------------------------------------------------------------
ROM:00003678
ROM:00003678 loc_3678:                               ; CODE XREF: sub_35F6+3A↑j
ROM:00003678                 add     r7, #1
ROM:0000367A                 add     r8, #8
ROM:0000367E
ROM:0000367E loc_367E:                               ; CODE XREF: sub_35F6+30↑j
ROM:0000367E                 cmp     r7, #10h
ROM:00003682                 jmpr    cc_C, loc_3628

After creating a struct and applying it to the location 34A, we can uncover more code:

ROM:0000034A functab:        ftab_entry <10h, 1Fh, 1Fh, 1Fh, 0, sub_2754>
ROM:0000034A                                         ; DATA XREF: sub_35F6+28↓o
ROM:00000352                 ftab_entry <11h, 0, 14h, 0, 0, unk_2D52> ; jumptable 000038A0 case 2
ROM:0000035A                 ftab_entry <1Ah, 1Fh, 1Fh, 0, 0, unk_2C16>
ROM:00000362                 ftab_entry <20h, 1Fh, 1Fh, 1Fh, 0, sub_2480>
ROM:0000036A                 ftab_entry <23h, 18h, 18h, 0, 0, unk_2B4E>
ROM:00000372                 ftab_entry <27h, 1Fh, 1Fh, 1, 0, sub_2504>
ROM:0000037A                 ftab_entry <31h, 1Fh, 1Fh, 0, 0, unk_2F10>
ROM:00000382                 ftab_entry <34h, 0, 14h, 0, 0, sub_28D2>
ROM:0000038A                 ftab_entry <36h, 0, 14h, 0, 0, sub_29B2>
ROM:00000392                 ftab_entry <37h, 0, 14h, 0, 0, unk_2A1E>
ROM:0000039A                 ftab_entry <3Bh, 0, 14h, 0, 0, unk_32C8>
ROM:000003A2                 ftab_entry <3Dh, 18h, 18h, 0, 0, unk_2A6C>
ROM:000003AA                 ftab_entry <3Eh, 1Fh, 1Fh, 1Fh, 0, loc_24EC>
ROM:000003B2                 ftab_entry <81h, 1Fh, 0, 0, 0, loc_2124>
ROM:000003BA                 ftab_entry <82h, 1Fh, 0, 0, 0, loc_215C>
ROM:000003C2                 ftab_entry <83h, 1Fh, 1Fh, 1Fh, 0, loc_218E>

Even then there is still unexplored code between the functions but that will be an exercise for the reader :slight_smile: .

So ida wasn’t mistaken about the presence of the 9th segment?

From the C166V2 manual:

Segments are contiguous blocks of 64 KBytes each. They are referenced via the Code
Segment Pointer (CSP) for code fetches and via an explicit segment number for data
accesses overriding the standard DPP scheme.
During code fetching, segments are not changed automatically, but rather must be
switched explicitly. The instructions JMPS, CALLS, and RETS will do this. Larger
sequential programs make sure that the highest used code location of a segment
contains an unconditional branch instruction to the respective following segment, to
prevent the prefetcher from trying to leave the current segment.

your file is only 512KB long (0x80000), so seems to contain only segments 0 to 7. Possibly the dump does not use the usual 64KB chunk per segment but another layout. It’s hard to say without knowing the exact hardware and dumping process.