28C3 - Version 2.3.5

28th Chaos Communication Congress
Behind Enemy Lines

Speakers
Guillaume Delugré
Schedule
Day Day 2 - 2011-12-28
Room Saal 3
Start time 20:30
Duration 01:00
Info
ID 4735
Event type Lecture
Track Hacking
Language used for presentation English
Feedback

Reverse-engineering a Qualcomm baseband

Despite their wide presence in our lives, baseband chips are still nowadays poorly known and understood from a system point of view. Some presentations have hilighted vulnerabilities in GSM stacks across various models of basebands (cf. 27c3: All your baseband are belong to us by R-P. Weinmann). However none of them actually focused on the details of how a baseband operating system really works. This is the focus of our presentation. From the study of a simple 3G USB stick equipped with a Qualcomm baseband, we will discuss how to dump the volatile memory, reverse-engineer the proprietary RTOS, and ultimately execute and debug code while trying to preserve the real-time system constraints.

Introduction

The following work has resulted from a straightforward observation: security in the baseband world is something hard to reach. Anyone trying to get into it is confronted with two obstacles. At the network level, one has to apprehend the extremely massive 3GPP specifications. At the system level, basebands are just undocumented and closed-source pieces of code running in embedded chips. Consequently, a baseband is mostly seen as a blackbox running code for a terrifyingly complex network stack.

Given the complexity of the involved network protocols, and the fact that telephony stacks are historically old pieces of code, it is fairly acceptable to think that vulnerabilities can be found inside basebands. Ralf-Philipp Weinmann has already demonstrated this claim during the 27C3 event in 2010. Finding and triggering vulnerabilities in basebands sound very appealing, but we have to remember that these are only preliminary steps before the final exploitation. And for any exploitation to succeed, one has to know the environment into which the code is currently running. What is the architecture? What is the operating system? What does the memory look like? How is structured the heap? Can I safely return to some point and resume the execution?

For those reasons and out of curiosity, I started exploring the core of a Qualcomm baseband. The targeted device is the Icon 225 3G USB stick. It embeds a MSM6280 Qualcomm baseband based on the ARMv5TEJ architecture, plus two proprietary DSPs. No application processor is present on those USB sticks. Qualcomm basebands are also notably present on HTC phones.

Dumping the device memory

The first step for understanding the baseband code is to manage to get a look at it. Plugging the USB stick fires up three serial ports over the USB link. The first one is used to handle Hayes commands to control the modem. The two other ones are unknown at first glance. However I remarked that a little tool for SIM-unlocking a device made use of one of those serial ports. After dumping the USB packets, it appeared this serial link actually handles diagnostic commands for Qualcomm. The protocol used is very simple and allows at least writing and executing code into a small region of the memory.

Injecting a custom payload allowed me to quickly dump the entire contents of the memory (32MB). On the ARM architecture, the first piece of code to be executed is a ROM located at 0xffff0000. Reverse-engineering this primary bootloader (PBL) gives us the entry point to the secondary bootloader (SBL). Then disassembling the RAM dump from this address clearly indicates we have one-to-one physical to virtual memory mapping.

Reverse engineering the RTOS

The embedded code inside the baseband is a proprietary operating system from Qualcomm. The real-time microkernel seems to be called REX, while the operating system itself is named AMSS.

I have reverse-engineered most part of the microkernel primitives including:

  • the scheduler
  • the inter-tasks communication mechanism
  • the asynchronous/deferred procedure calls mechanism
  • the timers
  • the heap memory structure and allocation routines

The kernel implements lightweight processes called tasks. All tasks share the same virtual address space. MMU is set up at boot time with a virtual to physical mapping and the first 12MB of memory are marked read-only. NX is not enabled (thus everything is executable).

Three tasks are created automatically at boot time:

  • the idle task
  • the DPC task, responsible for dispatching deferred procedure calls
  • the main task, responsible for running all the other tasks

When fully started, AMSS is made up of approximatively 70 running tasks. They are dedicated to hardware management (DSP, USB, USIM, Vocoder, ...), network stacks management for each layer (GSM L1/L2/L3, SMS, RRC, LLC, and so on), and miscellaneous features (in particular the diagnostic task). Although the USB stick is only intended to be used for data over 3G, the operating system is a full-blown baseband supporting all kinds of telephony stacks and features.

The tasks communicate with each other by the mean of signals and buffer queues. A command buffer is pushed on a FIFO queue and a signal is sent to the task for processing.

Regarding the memory allocation management, the operating system mainly uses two kinds of heaps. The first heap has a classical free blocks-tracking structure where tasks can allocate arbitrary memory blocks using the malloc/free functions. Another kind of heap is also used on top of the former to represent the memory as a contiguous stream of data that tasks can produce and consume (suited for network data flow).

Code execution and debugging

Static analysis of the whole operating system is possible, but the code is pretty massive and a lot of interactions between different tasks are involved at run-time. Since code execution is possible on the device, I investigated how to dynamically debug system code. I present here the architecture of the debugger I am currently writing (this is still a work in progress).

The main point is to be able to debug the operating system with the fewest possible side-effects. In a nutshell, the debugger has to be real-time compliant as much as possible. For the communication with the debugger, I decided to reuse the diagnostic task channel over USB by implementing custom command handlers. The debugger then relies on the GDB server protocol implemented over the diagnostic channel protocol, itself being over USB.

We have access to the interrupt vectors, and we can put BKPT instructions anywhere as well (everything is running in ARM supervisor mode and we can disable the MMU if necessary). If the exception address is a watchpoint, we dump the state of registers and stack, and set up a DPC to acknowledge the debugger of the event. Then execution is immediately resumed. If the exception address is a breakpoint, then we set up a DPC for the debugger and put the task into a wait state allowing other tasks to be immediately scheduled. The execution for the waiting task can be resumed by the debugger by sending it a special signal.

The debugger is making use of its own separated heap and queue at a high address, not to interfere with other operating system tasks while processing debug events.

Of course some tasks will need to process code at timely events, especially those at the lowest layers, so specific care has to be taken not to put breakpoints that would possibly break the RF processing.

ARMv5 has no native support for single-stepping the code. Single-step is implemented by predicting the next PC address and putting a breakpoint at it.

Notes and further thoughts

Information about the code execution environment on basebands is clearly lacking in the literature. On the contrary of previous presentations on the same topic, this presentation focuses on the details of a proprietary baseband operating system, in this case Qualcomm's. I intend to do a demonstration of the debugger for the presentation, and to release the source code later on.

Future areas of work may include a study of the proprietary DSPs and the possibility to locally fuzz the baseband without using a base station.