PoC or GTFO, Volume 2

Home > Other > PoC or GTFO, Volume 2 > Page 19
PoC or GTFO, Volume 2 Page 19

by Manul Laphroaig


  Adding support for the non-standard commands in the same order as the official software, I got a copy of the complete 256K codeplug from SPI Flash instead of the beginning of Internal Flash. Hooray!

  To upload a codeplug back into the radio, I modified the download() function of the host-side script to enable programming mode and properly wait for the state to return to dfuDNLOAD_IDLE before sending each block.

  This was enough to write my own codeplug from one radio into a second, but it had a nasty little bug! I forgot to erase the codeplug memory, so the radio got a bitwise AND of two valid codeplugs.63

  A second trip with the USB sniffer shows that these four blocks were erased, and that the upload address must be set to zero after the erasure.

  0x00000000 0x00010000 0x00020000 0x00030000

  Erasing those blocks properly gave me a tool that correctly reads and writes the radio codeplug!

  Codeplug Format

  Now that I could read and write the codeplug memory of my MD380, I wanted to be able to edit it. Parts of the codeplug are nice and easy to reverse, with strings as UTF16L and numbers being either integers or BCD. Checksums don’t seem to matter, and I’ve not yet been able to brick my radios by uploading damaged firmware images.

  The Radio Name is stored as a string at 0x20b0, while the Radio ID Number is an integer at 0x2080. The intro screen’s text is stored as two strings at 0x2040 and 0x2054.

  CHIRP, a ham radio application for editing radio codeplugs, has a bitwise library that expects memory formats to be defined as C structs with base addresses. By loading a bunch of contacts into my radio and looking at the resulting structure, it was easy to rewrite it for CHIRP.

  Repeatedly changing the codeplug with the manufacturer’s application, then comparing the hexdumps gave me most of the radio’s important features. Patience and a few more rounds will give me the rest of them, and then my CHIRP plugin can be cleaned up for inclusion.

  Unfortunately, not everything of importance exists within the codeplug. It would be nice to export the call log or the text messages, but such commands don’t exist and the messages themselves are nowhere to be found inside of the codeplug. For that, we’ll need to break into the firmware.

  Dumping the Bootloader

  Now that I had a working codeplug tool, I’d like a cleartext dump of firmware. Recall from page 314 that forgetting to send the custom command 0x91 0x01 leaves the radio in a state where the beginning of code memory is returned for every read. This is an interrupt table!

  From this table and the STM32F405 datasheet, we know the code flash begins at 0x08000000 and RAM begins at 0x2000-0000. Because the firmware updater only writes to regions at and after 0x0800C000, we can guess that the first 48k are a recovery bootloader, with the region after that holding the application firmware. As all of the interrupts are odd, and because the radio uses a Cortex M4 core, we know that the firmware is composed exclusively of Thumb (and Thumb2) code, with no old fashioned ARM instructions.

  Figure 10.18: MD380 Recovery Bootloader IVT

  Sure enough, I was able to dump the whole bootloader by reading a single page of 0xC000 bytes from the application mode. This bootloader is the one used for firmware updates, which can be started by holding PTT and the unlabeled button above it when turning on the power switch.64

  This trick doesn’t expose enough memory to dump the application, but it was valuable to me for two very important reasons. First, this bootloader gave me some proper code to begin reverse engineering, instead of just external behavioral observations. Second, the recovery bootloader contains the keys and code needed to decrypt an application image, but to get at that decrypted image, I first had to do some soldering.

  Radio Disassembly (BOOT0 Pin)

  As I stress elsewhere, the MD380 has three applications in it: (1) Tytera’s Radio Application, (2) Tytera’s Recovery Bootloader, and (3) STMicro’s Bootloader ROM. The default boot process is for the Recovery Bootloader to immediately start the Radio Application unless Push-To-Talk (PTT) and the button above it are held during boot, in which case it waits to accept a firmware update. There is no key sequence to start the STMicro Bootloader ROM, so a bit of disassembly and soldering is required.

  This ROM contains commands to read and write all of memory, as well as to begin execution at any arbitrary address. These commands are initially locked down, but on page 327, I’ll show how to get around the restrictions.

  To open your radio, first remove the battery and the four Torx screws that are visible from the back of the device. Then unscrew the antenna and carefully pry off the two knob covers. Beneath each knob and the antenna, there are rings that screw in place to secure them against the radio case; these should be moved by turning them counter-clockwise using a pair of sturdy, dull tweezers.

  Once the rings have been removed, the radio’s main board can be levered up at the bottom of the radio, then pulled out. Be careful when removing it, as it is attached with a Zero Insertion Force (ZIF) connector to the LCD/Keypad board, as well as by a short connector to the speaker.

  The STMicro Bootloader is started by pulling the BOOT0 pin of the STM32F405 high while restarting the radio. I did this by soldering a thin wire to the test pad near that pin, wrapping the wire around a screw for strain relief, then carefully feeding it out through the microphone/speaker port.

  (An alternate method involves removing BOOTO’s pull-down resistor, then fly-wiring it to the pull-up on the PTT button. Thanks to tricky power management, this causes the radio to boot normally, but to reboot into the Mask ROM.)

  Figure 10.19: Removing the Antenna Rings

  Figure 10.20: Inside the MD380

  Bootloader RE

  Once I finally had a dump of Tytera’s bootloader, it was time to reverse engineer it.65

  The image is 48K in size and should be loaded to 0x08000000. Additionally, I placed 192K of RAM at 0x20000000. It’s also handy to create regions for the I/O banks of the chip, in order to help track those accesses. (IDA and Radare2 will think that peripherals are global variables near 0x40000000.)

  After wasting a few days exploring the command set, I had a decent, if imperfect, understanding of the Tytera Bootloader but did not yet have a cleartext copy of the application image. Getting a bit impatient, I decided to patch the bootloader to keep the device unprotected while loading the application image using the official tools.

  I had to first explore the STM32 Standard Peripheral Library to find the registers responsible for locking the chip, then hunt for matching code.

  Figure 10.21: Tapping the BOOT0 Pin

  The way flash protection works is that byte 1 of FLASH->OPTCR (at 0x40023C15) is set to the protection level. 0xAA is the unprotected state, while 0xCC is the permanent lock. Anything else, such as 0x55, is a sort of temporary lock that allows the application to be wiped away by the Mask ROM bootloader, but does not allow the application to be read out.

  Tytera is using this semi-protected mode, so you can pull the BOOT0 pin of the STM32F4xx chip high to enter the Mask ROM bootloader.66 This process is described on page 324.

  Sure enough, at 0x08001FB0, I found a function that’s very much like the example FLASH_OB_RDPConfig function from stm-32f4xx_flash.c. I call the local variant rdp_lock().

  This function is called from main() with a parameter of 0x55 in the instruction at 0x080044A8.

  Patching that instruction to instead send 0xAA as a parameter prevents the bootloader from locking the device. (We’re just swapping aa 20 in where 55 20 used to be.)

  Dumping the Application

  Once I had a jailbroken version of the recovery bootloader, I flashed it to a development board and installed an encrypted MD380 firmware update using the official Windows tool. Sure enough, the application installed successfully!

  After the update was installed, I rebooted the board into its ROM by holding the BOOT0 pin high. Since the recovery bootloader has been patched to leave the chip unlocked, I was free to dump all of Flash to a file for revers
e engineering and patching.

  Reversing the Application

  Reverse engineering the application isn’t terribly difficult, provided a few tricks are employed. In this section, I’ll share a few. Note that all pointers in this section are specific to Version 2.032, but similar functionality exists in newer firmware revisions.

  At the beginning, the image appears almost entirely without symbols. Not one function or system call comes with a name, but it’s easy to identify a few strings and I/O ports. Starting from those, related functions—those in the same .C source file—are often located next to one another in memory, providing hints as to their meaning.

  The operating system for the application is an ARM port of MicroC/OS-II, an embedded real-time operating system that’s quite well documented in the book of the same name by Jean J. Labrosse. A large function at 0x0804429C that calls the operating system’s OSTaskCreateExt function to make a baker’s dozen of threads. Each of these conveniently has a name, conveniently describing the system interrupt, the real-time clock timer, the RF PLL, and other useful functions.

  As I had already reverse engineered most of the SPI Flash codeplug, it was handy to work backward from codeplug addresses to identify function behavior. I did this by identifying spiflash_-read at 0x0802fd82 and spiflash_write at 0x0802fbea, then tracing all calls to these functions. Once these have been identified, finding codeplug functions is easy. Knowing that the top line of startup text is 32 bytes stored at 0x2040 in the codeplug, finding the code that prints the text is as simple as looking for calls to spiflash_read(&foo, 0x2040, 20).

  Thanks to the firmware author’s stubborn insistence on 1-indexing, many of the structures in the codeplug are indexed by an address just before the real one. For example, the list of radio channel settings is an array that begins at 0x1ee00, but the functions that access this array have code along the lines of spiflash_read(&foo, 64*index+0x1edc0, 64).

  One mystery that struck me when reverse engineering the codeplug was that I didn’t find a missed call list or any sent or received text messages. Sure enough, the firmware shows that text messages are stored after the end of the 256K SPI Flash codeplug that the radio exposes to the world.

  Code that accesses the C5000 baseband chip can be reverse engineered in a similar fashion to the codeplug. The chip’s datasheet is very well handled by Google Translate, and plenty of functions can be identified by writes to C5000 registers of similar functions.67

  Be careful to note that the C5000 has multiple memories on its primary SPI bus; if you’re not careful, you’ll confuse the registers, internal RAM, and the Vocoder buffers. Also note that a lot of registers are missing from the datasheet; please get in touch with me if you happen to know what they do.

  Finally, it is crucially important to be able to sort through the DMR packet parsing and construction routines quickly. For this, I’ve found it handy to keep paper printouts of the DMR standard, which are freely available from ETSI.68 Link-Local addresses (LLIDs) are 24 bits wide in DMR, and you can often locate them by searching for code that masks against 0x00FFFFFF.69

  Patching for Promiscuity

  While it’s fun to reverse engineer code, it’s all a bit pointless until we write a nifty patch. Complex patches can be introduced by hooking function calls, but let’s start with some useful patches that only require changing a couple of bits. Let’s enable promiscuous receive mode, so the MD380 can receive from all talk groups on a known repeater and timeslot.

  In DMR, audio is sent to either a Public Talkgroup or a Private Contact. These each have a 24-bit LLID, and they are distinguished by a bit flag elsewhere in the packet. For a concrete example, 3172 is used for the Northeast Regional amateur talkgroup, while 444 is used for the Bronx TRBO talkgroup. If an unmodified MD380 is programmed for just 3172, it won’t decode audio addressed to 444.

  There is a function at 0x0803ec86 that takes a DMR audio header as its first parameter and decides whether to play the audio or mute it as addressed to another group or user. I found it by looking for access to the user’s local address, which is held in RAM at 0x2001c65c, and the list of LLIDs for incoming listen addresses, stored at 0x2001c44c.

  To enable promiscuous reception to unknown talkgroups, the following talkgroup search routine can be patched to always match on the first element of listengroup[]. This is accomplished by changing the instruction at 0x0803ee36 from 0xd1ef (JNE) to 0x46c0 (NOP).

  A similar JNE instruction at 0x0803ef10 can be replaced with a NOP to enable promiscuous reception of private calls. Care in real-world patches should be taken to reduce side effects, such as by forcing a match only when there’s no correct match, or by skipping the missed-call logic when promiscuously receiving private calls.

  DMR Scanning

  After testing to ensure that my patches worked, I used Radio Reference to find a few local DMR stations and write them into a codeplug for my modified MD380. Soon enough, I was hearing the best gossip from a university’s radio dispatch.70

  Later, I managed to find a DMR network that used the private calling feature. Sure enough, my radio would ring as if I were the one being called, and my missed call list quickly grew beyond my two local friends with DMR radios.

  A New Bootloader

  Unfortunately, the MD380’s application consumes all but the first 48K of Flash, and that 48K is consumed by the recovery bootloader. Since we neighbors have jailbroken radios with a ROM bootloader accessible, we might as well wipe the Tytera bootloader and replace it with something completely new, while keeping the application intact.

  Luckily, the fine folks at Tytera have made this easy for us! The application has its own interrupt table at 0x0800C000, and the RESET handler—whose address is stored at 0x0800C004—automatically moved the interrupt table, cleans up the stack, and performs other necessary chores.

  Firmware Distribution

  Since this article was written, DD4CR has managed to free up 200K of the application by gutting the Chinese font. She also broke the (terrible) update encryption scheme, so patched or rewritten firmware can be packaged to work with the official updater tools from the manufacturer.

  Patrick Hickey W7PCH has been playing around with from-scratch firmware for this platform, built around the FreeRTOS scheduler. His code is already linking into the memory that DD4CR freed up, and it’s only a matter of time before fully-functional community firmware can be dual-booted on the MD380.

  In this article, you have learned how to jailbreak your MD380 radio, dump a copy of its application, and begin patching that application or writing your own, new application.

  Perhaps you will add support for P25, D-Star, or System Fusion. Perhaps you will write a proper scanner, to identify unknown stations at a whim. Perhaps you will make DMR adapter firmware, so that a desktop could send and receiver DMR frames in the raw over USB. If you do any of these things, please tell me about it!

  73 from Manhattan,

  the home of Pizza Rat and Bodega Cats!

  Travis KK4VCZ

  11 Welcoming Shores of the Great Unknown

  IN A FIT OF STUBBORN OPTIMISM,

  PASTOR MANUL LAPHROAIG

  AND HIS CLEVER CREW

  SET SAIL TOWARD

  WELCOMING SHORES OF

  THE GREAT UNKNOWN!

  11:1 All aboard!

  Neighbors, please join me in reading this twelfth release of the International Journal of Proof of Concept or Get the Fuck Out, a friendly little collection of articles for ladies and gentlemen of distinguished ability and taste in the field of software exploitation and the worship of weird machines. This is our twelfth release, given on paper to the fine neighbors of Heidelberg.

  Our own Pastor Laphroaig opens this issue on page 342 by confessing to be a fan of junk hacking! He tells us to ignore the publicity and drama around a hack, to ignore even its target and its CVE. Instead, we should learn the mechanism of the hack, the clever tricks that make it work. Programming these mechanisms in nifty ways, be they ever so old, is surel
y not—“junk” think of it instead as an educational journey to far and exotic shores, on which this issue’s great crew of authors stands ready to take you, neighbors!

  In a fit of nostalgia for the good old vector arcade games, Trammel Hudson extended MAME to support native vector displays of the 1983 Star Wars arcade game on both his Tektronix 1720 scope and a Vectrex home vector display. Find it on page 347.

  Eric Davisson contributes a 512-byte game for the PC BIOS on page 355. He discusses some nifty tricks for self-rewriting code in 16-bit Real Mode and shows that the fancier features of an operating system aren’t needed to have a little fun—and that programming a constrained environment can be great fun indeed!

  On page 374, Peter Ferrie describes his work toward a universal bypass for the E7 protection mode used on a number of Apple ][ disks. This is a follow up to his encyclopedic coverage of protection modes for this platform in PoC‖GTFO 10:7.

  Ryan Speers and Travis Goodspeed have begun a series of tourist guides, intended to quickly introduce reverse engineers to a new platform. Page 387 provides a lightning-fast introduction to ARM’s Cortex M series, which you’ll find in modern devices with a megabyte or less of Flash memory. Page 403 contains similar notes for the Texas Instruments MSP430, MSP430X, and MSP430X2 architectures, a 16-bit competitor to the PIC and AVR.

 

‹ Prev