Renesas RH850 Attacks (Part 2 of 7)

April 8th, 2025 by Brian

Work conducted by Ibrahima Keita.

Target Introduction: The Renesas RH850

Continuing our 7-part series on attacking the Renesas RH850.

Renesas RH850

The Renesas RH850 is a 32-bit RISC CPU by Renesas Electronics. It is a refresh of the NEC V850, and has been developed by Renesas as of 2018. It is commonly utilized in the automotive industry, and has a CAN interface, I2C, and UART support. It also has a main oscillator with variable clock speed (16/20/24 MHz), and is easily programmable and readable with the Renesas E1 Flash Programmer and E2 Studio.

The RH850 also contains an embedded flash region that stores both code and data. The exact size of this flash depends on the version of the RH850 you have, as they can come in 6MB, 8MB, and other variants.

Furthermore, it also has a series of protections that the developer can enable, preventing unauthorized access to the processor. As shown below, the protections are one-time password, ID authentication, and a disabled programming mode.

Communicating with an RH850

The Renesas E1 programmer is the intended tool for provisioning and programming an RH850. The pinout for its connector can be found below:

All communication to the RH850 from the E1 programmer uses the Universal Asynchronous Receiver-Transmitter (UART) protocol, which is a two-wire protocol allowing for information exchange between devices. This means that any device that can talk UART at the same baud rate that the RH850 expects should be able to act as a programmer.

This also means that using a logic analyzer (such as the Saleae Logic) allows us to easily sniff the traffic between the programmer and an RH850, allowing us to gain a deeper understanding of what exactly is being sent. As such, I was able to emulate an E1 programmer performing a memory read by performing logic captures and writing a Python library to send out the expected packets.

Documentation on how the connection is established, and how a flash read is performed, can be found below.

Notation & Structures

Before proceeding onwards, I will define data structures which will be heavily used in the workflows shown below.

Packets

Every packet has a basic structure, expanded upon by the specific process that utilizes it. The serialized structure is as follows:

struct RH_PACKET {
    uint8_t Begin;
    uint16_t Length;
    uint8_t Index_Result;
    uint8_t Parameters[Length - 1];
    uint8_t Checksum;
    uint8_t End;
}

Please note: the packet itself uses big endian, meaning the most significant byte will appear first in memory.

RH_PACKET.Begin
  • Each (serialized) RH_PACKET starts with either 01 or 81.
    • When the serialized packet starts with 01, this is what is known as a RH_COMMAND packet.
    • When the serialized packet starts with 81, this is what is known as a RH_DATA packet. This is the result of 0x1 | RH_ISDATA, where RH_ISDATA is 0x80.
RH_PACKET.Length
  • The value of this field represents how many bytes are left to expect from the communication channel. This value should be, at minimum, 1.
RH_PACKET.Index_Result
  • The value of this field depends on the purpose of the packet.
    • In a RH_COMMAND packet (RH_PACKET.Begin == 0x1), this specifies the command index. Here are some of the commands.
Command codeCommand nameDescription
0x00Inquiry commandReturn ACK (to determine the current phase)
0x12Erase commandErase data on target area
0x13Write commandWrite data on target area
0x15Read commandRead data on target area
0x30ID authentication commandAuthenticate ID for connection with the device
0x34Baud rate setting commandSet baud rate for UART
0x3ASignature request commandGet signature information
0x3BArea information request commandGet area information
  • In a RH_DATA packet (Begin == 0x81), this specifies the result number. RH_DATA is utilized for acknowledgements, errors, and flash contents. Below is a list of the possible result codes.
Status codeDescription
0x00 | Command codeOK (ongoing normally): used in Response code [RES]
0x80 | Command codeERR (occurrence of an error): used in Response code [RES]
0x00OK (successful completion)
0xC0Unsupported command error
0xC1Packet error (Illegal length, Missing ETX, and so forth)
0xC2Checksum error
0xC3Flow error
0xD0Address error
0xD4Baud rate margin error
0xDAProtection error
0xDBID mismatch error
0xDCSerial programming disable error
0xE1Erase error
0xE2Write error
0xE7Sequencer error
RH_PACKET.Parameters
  • In a RH_COMMAND packet (Begin == 0x1), this represents the parameters for each command. Each command has different parameter sizes (as some commands have no parameters, some have many parameters).
  • See the Renesas Documentation to identify most of the parameters.
RH_PACKET.Checksum
  • The value of the checksum is a trivial calculation.
    • Compute the 8-bit sum for for the length, result/index, and parameters bytes, allowing overflow. Call this value k.
    • Calculate the two’s complement of k, by inverting all of its bits and adding 1; call this k′.
    • The checksum is now k′. Use this for a value.
RH_PACKET.End
  • Always 0x3, irrespective of the packet type.

Connecting to the RH850 & Performing a Flash Read

This will describe how to communicate with an RH850 via UART, and how to perform a flash read with it.

Handshake

The following sequence must happen before the flash programmer starts responding to commands. This forces the processor to change its mode to some command processing mode. This will be the expected flow, given the chip’s FLMD0 line is pulled high to VCC.

sequenceDiagram
    Programmer->>+RH850: [00 00 00 00 00 00 00 00 00 00]
    RH850->>+Programmer: [00]
    Programmer->>+RH850: [55]
    RH850->>+Programmer: [C1]

Establishing a Connection & Getting Into Command Acceptance Mode

Before the RH850 can communicate properly with the E1 programmer, we need to set connection parameters. The following workflow is how it is done.

Register Request

First, request registers from the chip.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<REG_REQ>()
    RH850->>+Programmer: RH_DATA<REG_REQ>(SUCCESS)
    Programmer->>+RH850: RH_DATA<REG_REQ>(ACK)
    RH850->>+Programmer: RH_DATA<REG_REQ>(ACK)
Frequency Set

Next, set the frequencies of the chip.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<SET_FREQ>(OC = 0xF42400, CC = 0x07270E00)
    RH850->>+Programmer: RH_DATA<SET_FREQ>(SUCCESS)
    Programmer->>+RH850: RH_DATA<SET_FREQ>(ACK)
    RH850->>+Programmer: RH_DATA<SET_FREQ>(ACK)
Baud Rate Set

Then, set the baud rate. (0xF4240 = 1000000 bps).

sequenceDiagram
    # Change Frequency
    Programmer->>+RH850: RH_COMMAND<SET_BAUD_RATE>(NewBaud = 0xF4240)
    RH850->>+Programmer: RH_DATA<SET_BAUD_RATE>(SUCCESS)
Inquire

Confirm we are in the new mode.

Note: The results of this operation depend on whether or not serial programming is enabled. If serial programming is enabled, then sending an inquiry packet will result in a success message, as shown below.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<INQUIRE>()
    RH850->>+Programmer: RH_DATA<INQUIRE>(SUCCESS)

However, if serial programming is disabled, then this message will appear, telling us that the RH850 will refuse to let us communicate with it, as shown below.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<INQUIRE>()
    RH850->>+Programmer: RH_DATA<INQUIRE>(SERIAL_PROGRAMMING_DISABLED)
Command Acceptance Mode

Tell the chip to accept commands.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<COMMAND_ACCEPTANCE>()
    RH850->>+Programmer: RH_DATA<COMMAND_ACCEPTANCE>(SUCCESS)
    Programmer->>+RH850: RH_DATA<COMMAND_ACCEPTANCE>(ACK)
    RH850->>+Programmer: RH_DATA<COMMAND_ACCEPTANCE>(ACK)
ID Authentication

This following command is optional, and is only needed if the RH850 has a stored ID. If it does not have an ID, the chip will be ready to accept commands. Otherwise, this command needs to be sent to allow for communication.

From here, we need to send the ID command, with our ID as a parameter.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<ID_AUTHENTICATION>(ID)
    RH850->>+Programmer: RH_DATA<ID_AUTHENTICATION>(SUCCESS)
    Programmer->>+RH850: RH_DATA<ID_AUTHENTICATION>(ACK)
    RH850->>+Programmer: RH_DATA<ID_AUTHENTICATION>(ACK) (if ID matches)
    RH850->>+Programmer: RH_DATA<ID_AUTHENTICATION>(NACK) (if ID does not match)

From here on out, the chip will maintain the state, and go into command acceptance mode.

Flash Read

Now that we have established a connection, let us attempt to perform a flash read. Request the signature from the processor.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<SIGREQ>()
    RH850->>+Programmer: RH_DATA<SIGREQ>(SUCCESS)
    Programmer->>+RH850: RH_DATA<SIGREQ>(ACK)
    RH850->>+Programmer: RH_DATA<SIGREQ>(ACK)

Don’t know what this command does either, but my educated guess is that it will allow for reading of the flash data.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<UNKNOWN_71>(Param = 0xFF)
    RH850->>+Programmer: RH_DATA<UNKNOWN_71>(SUCCESS)

After this, we can start requesting data from regions of memory. You can request blocks of memory between an address range. My understanding is that we can only get 0x400 bytes at a given time. So, you can just keep doing the process below until you get all the data you requested.

sequenceDiagram
    Programmer->>+RH850: RH_COMMAND<READ>(LowAddress = <LowerBound>, HighAddress = <HigherBound>)
    RH850->>+Programmer: RH_DATA<READ>(SUCCESS)

    Programmer->>+RH850: RH_DATA<SIGREQ>(ACK)
    RH850->>+Programmer: RH_DATA<RAW>()
    Programmer->+RH850: ...
    Programmer->>+RH850: RH_DATA<SIGREQ>(ACK)
    RH850->>+Programmer: RH_DATA<RAW>()

If all goes well, the packet you receive from here you should have the data sent to you, and you can parse it accordingly.

Revisiting the Security Features

As seen in the workflow for connecting and performing a flash read, there are two major immediate obstacles.

  • The device will refuse to let us communicate if serial programming is disabled (see the Inquire command flow).
  • If an ID was provisioned, it needs to be sent in order to unlock the device for reading and writing.

Even before we get to the ID, let us address the serial programming ability. Now, I find it very weird that the device will tell us over serial that we cannot communicate with it over serial, especially after we already sent data to it. Imagine asking someone if they can hear you, and they say no; that’s exactly what’s happening here. So, clearly it is contradictory.

But what if we could somehow get the device to believe that it can in fact communicate with us? For it to know that it cannot communicate with us, there has to be some value stored somewhere that lets it make that decision. Of course, it will not let us communicate with it, so we cannot just send a command to change that value. However, what if we could somehow mess with the logic it uses to determine that, which would allow us to further communicate with it?

Furthermore, if we could potentially mess with the logic for the serial programming check, could we extend that to bypass the ID authentication restriction (if it exists)?

These two questions were what we sought to answer during the course of this project. We want to bypass the serial programming protection and ID authentication features, but for that, we’re going to need a little bit of help.

We know the software might be secure, but what about the hardware itself? What if we attempt to utilize the laws of physics against the hardware, and force it to behave differently, perhaps in our favor? With these ideas in mind, we started looking at potential hardware attacks, which we start to detail in the next few blog posts.

On to Part 3, side-channel attacks and fault injection.