A Simple Analysis of STM32 Firmware

A Simple Analysis of STM32 Firmware

This article is a highlight from the Kanxue Forum.

Author ID on Kanxue Forum: Shaobanjia

After reading the STM32 firmware reverse engineering thread on the forum (https://bbs.pediy.com/thread-272811.htm), I found a help request in the comments about the STM32 chip program that uses the XTEA encryption algorithm, but I couldn’t understand the data sorting issue (https://bbs.pediy.com/thread-272872.htm). So I thought I would analyze it and practice a bit.

A Simple Analysis of STM32 Firmware

Based on the help request, I summarized the useful information.

A firmware named stm32f103RCT6.bin suggests that the MCU model is stm32f103RCT6.
The MCU uses XTEA encryption when communicating with device A.
The key used during MCU registration is BA2F96A9.
The two encryption examples during communication between the MCU and device A are as follows:
Plaintext 110 BE 62 F8 E8 DC 34 46 Ciphertext 18C 79 F5 D1 5E A9 46 2D Plaintext 20E 77 50 C8 C6 27 E1 BF Ciphertext 236 0A 1A 6E 6E FE F0 84

Next, the goal is to find the encryption function and restore the encryption process based on the above information.

Start

First, open the stm32f103RCT6.bin file with IDA, select ARM little-endian, and enter.

A Simple Analysis of STM32 Firmware

Opening directly as a Binary file shows that IDA did not recognize any functions.

A Simple Analysis of STM32 Firmware

Habitually, I check the first few bytes for “D” three times, as shown in the figure, and can boldly guess that the loading base address is 0x8000000. In fact, it is indeed so, of course, you can check the datasheet according to the MCU model stm32f103RCT6. For more methods to determine the base address, please refer to this forum post on firmware security and loading address analysis (https://bbs.pediy.com/thread-267719.htm), which details various methods to determine the base address, so I won’t elaborate here.

A Simple Analysis of STM32 Firmware

Edit > Segments > Rebase program… Reset the base address to 0x8000000. After setting the base address, we have made a big step towards success.

A Simple Analysis of STM32 Firmware

Next, press Alt + L to start selecting from the first few addresses to the end, and right-click to select “Analyze selected area”.

A Simple Analysis of STM32 Firmware

A Simple Analysis of STM32 Firmware

A prompt box appears; select Analyze, and wait for the IDA analysis process to finish.

A Simple Analysis of STM32 Firmware

At this point, you can see that IDA has recognized many functions.

A Simple Analysis of STM32 Firmware

Next, use the Findcrypt plugin to search for encryption constants to see if there are any findings. Search > Find crypto constants, as shown in the figure. You can see a TEA delta encryption constant, which corresponds to the XTEA encryption used during MCU communication.

A Simple Analysis of STM32 Firmware

Track and view the sub_800E288 function that references this constant; the F5 result is shown in the figure below:

A Simple Analysis of STM32 Firmware

Comparing with the XTEA encryption C code online, we can see that the parameters of the sub_800E288 function from left to right are the encryption key, encryption input, and encryption output.

A Simple Analysis of STM32 Firmware

Referring to the XTEA encryption C code above, we can rename some variables as shown in the figure. You can see that the key input during encryption is not directly used as the XTEA encryption key but has undergone some operations for transformation.

A Simple Analysis of STM32 Firmware

Next, we analyze the encryption process in detail. First, in the front part, there are many left shifts of 24, 16, and 8. Familiar friends may quickly realize that this is for endianness conversion. Big-endian refers to “high order first, low order last,” while little-endian refers to “low order first, high order last.” For example, for the same 4-byte byte array “12 34 56 78,” in big-endian, it is treated as 0x12345678, while in little-endian, it is treated as 0x78563412. The operations “<<24 <<16 <<8” are used to convert the 4-byte array into a big-endian integer (the MCU is little-endian, so directly using *(int *) type cast would yield a little-endian int).

A Simple Analysis of STM32 Firmware

Ignoring the left shifts for endianness conversion, we can see that there is no special processing for the input “in” before the actual XTEA encryption, while each byte of the key undergoes XOR processing with some constants. Similarly, seeing the values “0x66, 0x6F…”, these values, if familiar, can easily be recognized as ASCII codes.

A Simple Analysis of STM32 Firmware

Press the TAB key to switch to view the disassembled code, as shown in the figure. You can see that these constants are based on the offset starting at address 0x8030A30.

A Simple Analysis of STM32 Firmware

Viewing address 0x8030A30, pressing “A” gives a string as shown in the figure, which is clearly a string with a story.

A Simple Analysis of STM32 Firmware

Returning to the decompiled code window, pressing F5 again shows that the decompiled code is clearer. The key is XORed with the corresponding byte of the above string before being used for XTEA encryption.

A Simple Analysis of STM32 Firmware

Now looking at the latter part, as shown in the figure, the latter part is actually standard XTEA encryption, but there is also endianness conversion for the output of the encryption result. “HIBYTE(x) BYTE2(x) BYTE1(x)” is similar to the operations “<<24 <<16 <<8” seen earlier, which are commonly used in data endianness conversion.

A Simple Analysis of STM32 Firmware

Is this the end? – The answer is no. The help request mentioned that the MCU registration key is “BA2F96A9”, which is only 4 bytes in size, but the encryption function’s key input has 16 bytes, so there is still some process to analyze from the MCU registration key “BA2F96A9” to the encryption key userKey.

Press “X” on sub_800E288 to view its cross-references, as shown in the figure. There is only one call.

A Simple Analysis of STM32 Firmware

View that call, as shown in the figure. The first parameter, the encryption key, is passed in at address 0x20000104, so the encryption key is stored at address 0x20000104. Next, we just need to check which functions write data to address 0x20000104 to find the key transformation function.

A Simple Analysis of STM32 Firmware

Next, use Text Search to search for 0x20000104 to see which functions use address 0x20000104.

A Simple Analysis of STM32 Firmware

As shown in the figure, the search results are not many. After checking them one by one, we can see that only the sub_800F3C8 function has code that writes data to address 0x20000104, and the parameter of sub_800F3C8 is of unsigned int type (exactly 4 bytes), so we can basically confirm that this function is the one that transforms the MCU registration key into the encryption key.

A Simple Analysis of STM32 Firmware

A Simple Analysis of STM32 Firmware

Next, we extract the decompiled pseudocode from the sub_800F3C8 and sub_800E288 functions and write a simple program in VS Code to verify it. The code is as follows:

#include <stdio.h>
#include "defs.h"
void keyExpand(unsigned int key, _BYTE *outKey);
void XTEA(_BYTE *userkey, _BYTE *in, _BYTE *out);

void keyExpand(unsigned int key, _BYTE *outKey) {
    int i; // r0
    for (i = 0; i < 16; i = (i + 1))
        *(i + outKey) = key >> (8 * (3 - i % 4));
}

void XTEA(_BYTE *userkey, _BYTE *in, _BYTE *out) {
    unsigned int v3; // r3
    unsigned int v4; // r4
    unsigned int v5; // r5
    unsigned int i; // r6
    int v7[4]; // [sp+0h] [bp-34h]
    unsigned int v8; // [sp+10h] [bp-24h]
    unsigned int v9; // [sp+14h] [bp-20h]

    char aStefanlovesmay[] = "StefanLovesMaya!";

    if (userkey != 0 && in != 0 && out != 0) {
        v8 = (*in << 24) + (in[1] << 16) + (in[2] << 8) + in[3];
        v9 = (in[4] << 24) + (in[5] << 16) + (in[6] << 8) + in[7];
        v7[0] = (userkey[3] ^ aStefanlovesmay[3])
                + ((*userkey ^ aStefanlovesmay[0]) << 24)
                + ((userkey[1] ^ aStefanlovesmay[1]) << 16)
                + ((userkey[2] ^ aStefanlovesmay[2]) << 8);
        v7[1] = (userkey[7] ^ aStefanlovesmay[7])
                + ((userkey[4] ^ aStefanlovesmay[4]) << 24)
                + ((userkey[5] ^ aStefanlovesmay[5]) << 16)
                + ((userkey[6] ^ aStefanlovesmay[6]) << 8);
        v7[2] = (userkey[11] ^ aStefanlovesmay[11])
                + ((userkey[8] ^ aStefanlovesmay[8]) << 24)
                + ((userkey[9] ^ aStefanlovesmay[9]) << 16)
                + ((userkey[10] ^ aStefanlovesmay[10]) << 8);
        v7[3] = (userkey[15] ^ aStefanlovesmay[15])
                + ((userkey[12] ^ aStefanlovesmay[12]) << 24)
                + ((userkey[13] ^ aStefanlovesmay[13]) << 16)
                + ((userkey[14] ^ aStefanlovesmay[14]) << 8);
        v3 = v8;
        v4 = v9;
        v5 = 0;
        for (i = 0; i < 0x20; ++i) {
            v3 += (((16 * v4) ^ (v4 >>> 5)) + v4) ^ (v7[v5 && 3] + v5);
            v5 -= -0x9E3779B9;
            v4 += (((16 * v3) ^ (v3 >>> 5)) + v3) ^ (v7[(v5 >>> 11) && 3] + v5);
        }
        *out = HIBYTE(v3);
        out[1] = BYTE2(v3);
        out[2] = BYTE1(v3);
        out[3] = v3;
        out[4] = HIBYTE(v4);
        out[5] = BYTE2(v4);
        out[6] = BYTE1(v4);
        out[7] = v4;
    }
}

int main() {
    unsigned int key = 0xBA2F96A9;
    _BYTE outKey[16];
    keyExpand(key, outKey);
    printf("outKey: ");
    for (int i = 0; i < 16; i++) {
        printf("%02x ", outKey[i]);
    }
    printf("\n");
    // _BYTE in[] = {0x10, 0xBE, 0x62, 0xF8, 0xE8, 0xDC, 0x34, 0x46};
    _BYTE in[] = {0x0E, 0x77, 0x50, 0xC8, 0xC6, 0x27, 0xE1, 0xBF};
    _BYTE out[8];
    printf("in: ");
    for (int i = 0; i < 8; i++) {
        printf("%02x ", in[i]);
    }
    printf("\n");
    XTEA(outKey, in, out);
    printf("out: ");
    for (int i = 0; i < 8; i++) {
        printf("%02x ", out[i]);
    }
    return 0;
}

The output result is shown in the figure below. The output result uses the second set of data from the above example (the first set has the same effect). You can see that the encryption result matches the expectation, thus successfully restoring the entire XTEA encryption process.

A Simple Analysis of STM32 Firmware

A Simple Analysis of STM32 Firmware

Kanxue ID: Shaobanjia

https://bbs.pediy.com/user-home-855195.htm

*This article is original by Shaobanjia from Kanxue Forum, please indicate the source when reprinting from the Kanxue community.

A Simple Analysis of STM32 Firmware

Summit Review: https://mp.weixin.qq.com/s/eEbc8k8H9Pc2K0d_AHG3BA

# Previous Recommendations

1. CVE-2022-21882 Privilege Escalation Vulnerability Study Notes

2. Wibu Certificate – Initial Exploration

3. Windows 10 1909 Reverse Engineering of APIC Interrupts and Experiments

4. Analysis and Simulation of EAF Mechanism Under EMET

5. SQL Injection Learning Sharing

6. Issues and POCs of V8 Array.prototype.concat Function

A Simple Analysis of STM32 Firmware
A Simple Analysis of STM32 Firmware

Share the Ball

A Simple Analysis of STM32 Firmware

Like the Ball

A Simple Analysis of STM32 Firmware

Watching the Ball

Leave a Comment