Article count:922 Read by:3074353

Account Entry

Tampering with MCU specific code data to fix the bug

Latest update time:2024-08-28
    Reads:

Source | Embedded Base

If the product you develop has a bug, how will you fix it?

Today I will share with you a method to fix microcontroller BUGs by tampering with specific code data.

Overview

In the development of embedded products, it is inevitable that the final products shipped will have various bugs due to various reasons. Usually, the product firmware is upgraded to solve the problem.

I remember when I was maintaining a BLE product in the company, due to insufficient pre-research of the platform and improper setting of OTA parameters, a few products could not be OTA-ed. After analysis, it was only necessary to change the value of a parameter in the code.

However, the product is in the hands of the user, and OTA is the only way to update the code, so the only way is to resend the product to the user. Later, I wondered if it would be possible to prepare an interface in advance to support the dynamic transmission of a small amount of code to the product for temporary operation, and to fix the product's tricky bugs by modifying the Flash code data at a specific location?

Leave an extra backdoor. Sometimes, it is just one or two lines of code or a few incorrect initialization parameters that cause difficult problems in the product. In this case, this method can also be used as an emergency measure, although the operation is more complicated.

Create a demo project

This article takes the STM 32F103C8T6 microcontroller as an example to create a demonstration project, which is divided into two projects: app and bootloader. That is, the MCU's Flash is divided into two areas, "app" and "bootloader". The bootloader is placed in the 24KB area starting at 0x8000000, and the app is placed in the subsequent area starting at 0x8006000. The bootloader completes the modification of the app's Flash data.

1. App project Note that the app project needs to modify the ROM start address on Keil.

Also set the vector offset at the beginning of the app code (calling one line of code):
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x6000);

The logic of the app project is: first execute the LED flashing process at three different speeds in sequence (20ms, 200ms, 500ms, switching on and off), and finally enter a loop state to switch the LED state flashing once per second. The code is as follows:

void init_led(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     
    GPIO_Init(GPIOB, &GPIO_InitStructure);    

    GPIO_ResetBits(GPIOB, GPIO_Pin_10);
    GPIO_SetBits(GPIOB, GPIO_Pin_10); 
}

void led_blings_1(void)
{
    uint32_t i;

    for (i = 0; i < 10; i++)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(20);  

        GPIO_ResetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(20);
    }
}

void led_blings_2(void)
{
    uint32_t i;

    for (i = 0; i < 10; i++)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(200);  

        GPIO_ResetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(200);
    }
}

void led_blings_3(void)
{
    uint32_t i;

    for (i = 0; i < 10; i++)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(500);  

        GPIO_ResetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(500);
    }
}

int main()
{
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x6000);

    SysTick_Init(72);

    init_led();

    led_blings_1();
    led_blings_2();
    led_blings_3();

    while (1)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(1000);  

        GPIO_ResetBits(GPIOB, GPIO_Pin_10); 
        delay_ms(1000);
    }
}

In order to analyze the assembly and view the bin file data, we need to add two commands in Keil to generate .dis disassembly and .bin code files respectively. (The specific directory situation is similar to the one in the previous example)

fromelf --text -a -c --output=all.dis Obj\Template.axf

fromelf --bin --output=test.bin Obj\Template.axf

First, burn the app code into the MCU. Note that in the burning settings, select "Erase Sectors" to only erase the areas that need to be burned.

2. Bootloader project

In the bootloader, it is divided into two parts, the unchanged code part and the variable code part (error_process function).

When compiling for the first time, error_process is written as an empty function. When we need to modify the App, we recompile the project to fill in the error_process function.

In order to not affect the link address of the previous function when recompiling the project, the error_process function is deliberately placed at the last address 0x8000800 in the code area. The reason is that the original project size is 1.51KB, and the erase page size is 2KB, so 2KB alignment is required. The address of the alignment is selected as 0x8000800. The code is as follows:


#define FLASH_PAGE_SIZE 2048
#define ERROR_PROCESS_CODE_ADDR 0x8000800

void error_process(void) __attribute__((section(".ARM.__at_0x8000800")));

void init_led(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; 
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     
    GPIO_Init(GPIOB, &GPIO_InitStructure);    

    GPIO_ResetBits(GPIOB, GPIO_Pin_10);
    GPIO_SetBits(GPIOB, GPIO_Pin_10); 
}

uint32_t pageBuf[FLASH_PAGE_SIZE / 4];

void error_process(void)
{

}

void eraseErrorProcessCode(void)
{
    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | 
                    FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    FLASH_ErasePage(ERROR_PROCESS_CODE_ADDR);
    FLASH_Lock();
}

void(*boot_jump2App)();

void boot_loadApp(uint32_t addr)
{
    uint8_t i;

    if (((*(vu32*)addr) & 0x2FFE0000) == 0x20000000)    
    {
        boot_jump2App = (void(*)())*(vu32*)(addr + 4);      

        __set_MSP(*(vu32*)addr);

        for (i = 0; i < 8; i++)
        {
            NVIC->ICER[i] = 0xFFFFFFFF
            NVIC->ICPR[i] = 0xFFFFFFFF
        }

        boot_jump2App();        

        while (1);
    }
}

int main()
{
    uint32_t flag;

    SysTick_Init(72);

    flag = *((uint32_t *)ERROR_PROCESS_CODE_ADDR);

    if ((flag != 0xFFFFFFFF) && (flag != 0))
    {
        init_led();
        GPIO_ResetBits(GPIOB, GPIO_Pin_10); 

        delay_ms(1000);
        delay_ms(1000);

        error_process();
        eraseErrorProcessCode();
    }

    boot_loadApp(0x8006000);

    while (1);
}


Once the main function is entered, the 32-bit data at address 0x8000800 is read. If it is not all F or all 0, then there is a function body that needs to be executed. Then the LED will light up for 2 seconds to indicate that the bootloader recognizes that there is a handler that needs to be executed (of course, some judgment mechanisms such as whether the error_process code data is complete are also required here, which are omitted for demonstration here). After the handler is executed, the handler is erased (the data becomes all F) to avoid repeated erasing of the Flash every time the power is turned on.
The data of the error_process function code is directly written to 0x8000800 through the data interface during normal use of the product (this part of the demo is omitted). After compiling, check the generated bin file to extract the code of the error_process part and transfer it to the Flash address 0x8000800.
When programming the bootloader code into the MCU, select "Erase Sectors" in the programming settings to erase only the areas that need to be programmed. Change the ROM address back to 0x08000000 in the keil settings.

Modify app specific parameters

In the app project, take the "led_blings_1" function as an example, the disassembly is as follows:


    $t
    i.led_blings_1
    led_blings_1
        0x08006558:    b510        ..      PUSH     {r4,lr}
        0x0800655a:    2400        .$      MOVS     r4,#0
        0x0800655c:    e010        ..      B        0x8006580 ; led_blings_1 + 40
        0x0800655e:    f44f6180    O..a    MOV      r1,#0x400
        0x08006562:    4809        .H      LDR      r0,[pc,#36] ; [0x8006588] = 0x40010c00
        0x08006564:    f7fffea2    ....    BL       GPIO_SetBits ; 0x80062ac
        0x08006568:    2014        .       MOVS     r0,#0x14
        0x0800656a:    f7ffffaf    ....    BL       delay_ms ; 0x80064cc
        0x0800656e:    f44f6180    O..a    MOV      r1,#0x400
        0x08006572:    4805        .H      LDR      r0,[pc,#20] ; [0x8006588] = 0x40010c00
        0x08006574:    f7fffe98    ....    BL       GPIO_ResetBits ; 0x80062a8
        0x08006578:    2014        .       MOVS     r0,#0x14
        0x0800657a:    f7ffffa7    ....    BL       delay_ms ; 0x80064cc
        0x0800657e:    1c64        d.      ADDS     r4,r4,#1
        0x08006580:    2c0a        .,      CMP      r4,#0xa
        0x08006582:    d3ec        ..      BCC      0x800655e ; led_blings_1 + 6
        0x08006584:    bd10        ..      POP      {r4,pc}
    $d
        0x08006586:    0000        ..      DCW    0
        0x08006588:    40010c00    ...@    DCD    1073810432

Since the LED is alternately on and off every 20ms, if we think there is a problem with this parameter and want to change it to 100ms, from the assembly point of view, we need to change two lines of code:

    0x08006568:    2014        .       MOVS     r0,#0x14

    0x08006578:    2014        .       MOVS     r0,#0x14

    改为

    0x08006568:    2064        2       MOVS     r0,#0x64

    0x08006578:    2064        2       MOVS     r0,#0x64

The error_process function in the bootloader project is implemented as follows:

void error_process(void)
{
    #define MODIFY_FUNC_ADDR_START 0x08006558

    uint32_t alignPageAddr = MODIFY_FUNC_ADDR_START / FLASH_PAGE_SIZE * FLASH_PAGE_SIZE;
    uint32_t cnt, i;

    // 1. copy old code
    memcpy(pageBuf, (void *)alignPageAddr, FLASH_PAGE_SIZE);

    // 2. change code.
        //由于Flash操作2KB页的特性,0x08006558不满2kb,因此偏移为0x558,0x558/4=342
    pageBuf[90 + 256] = (pageBuf[90 + 256] & 0xFFFF0000) | 0x2064;
    pageBuf[94 + 256] = (pageBuf[94 + 256] & 0xFFFF0000) | 0x2064;

    // 3. erase old code, copy new code.
    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | 
                    FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    FLASH_ErasePage(alignPageAddr);

    cnt = FLASH_PAGE_SIZE / 4;
    for (i = 0; i < cnt; i++)
    {
        FLASH_ProgramWord(alignPageAddr + i * 4, pageBuf[i]);
    }

    FLASH_Lock();
}

Due to the 2KB page erase feature of Flash, the Flash page data of the code area to be modified is first copied to the buffer, then the data in the buffer is modified, then the relevant Flash page is erased, and finally the modified data in the buffer is written back to Flash. The disassembly of the error_process function is as follows:

    $t
    .ARM.__at_0x8000800
    error_process
        0x08000800:    b570        p.      PUSH     {r4-r6,lr}
        0x08000802:    4d1a        .M      LDR      r5,[pc,#104] ; [0x800086c] = 0x8006000
        0x08000804:    142a        *.      ASRS     r2,r5,#16
        0x08000806:    4629        )F      MOV      r1,r5
        0x08000808:    4819        .H      LDR      r0,[pc,#100] ; [0x8000870] = 0x20000008
        0x0800080a:    f7fffcbd    ....    BL       __aeabi_memcpy ; 0x8000188
        0x0800080e:    4818        .H      LDR      r0,[pc,#96] ; [0x8000870] = 0x20000008
        0x08000810:    f8d00568    ..h.    LDR      r0,[r0,#0x568]
        0x08000814:    f36f000f    o...    BFC      r0,#0,#16
        0x08000818:    f2420164    B.d.    MOV      r1,#0x2064
        0x0800081c:    4408        .D      ADD      r0,r0,r1
        0x0800081e:    4914        .I      LDR      r1,[pc,#80] ; [0x8000870] = 0x20000008
        0x08000820:    f8c10568    ..h.    STR      r0,[r1,#0x568]
        0x08000824:    4608        .F      MOV      r0,r1
        0x08000826:    f8d00578    ..x.    LDR      r0,[r0,#0x578]
        0x0800082a:    f36f000f    o...    BFC      r0,#0,#16
        0x0800082e:    f2420164    B.d.    MOV      r1,#0x2064
        0x08000832:    4408        .D      ADD      r0,r0,r1
        0x08000834:    490e        .I      LDR      r1,[pc,#56] ; [0x8000870] = 0x20000008
        0x08000836:    f8c10578    ..x.    STR      r0,[r1,#0x578]
        0x0800083a:    f7fffd53    ..S.    BL       FLASH_Unlock ; 0x80002e4
        0x0800083e:    2035        5       MOVS     r0,#0x35
        0x08000840:    f7fffcca    ....    BL       FLASH_ClearFlag ; 0x80001d8
        0x08000844:    4628        (F      MOV      r0,r5
        0x08000846:    f7fffccd    ....    BL       FLASH_ErasePage ; 0x80001e4
        0x0800084a:    14ae        ..      ASRS     r6,r5,#18
        0x0800084c:    2400        .$      MOVS     r4,#0
        0x0800084e:    e007        ..      B        0x8000860 ; error_process + 96
        0x08000850:    4a07        .J      LDR      r2,[pc,#28] ; [0x8000870] = 0x20000008
        0x08000852:    f8521024    R.$.    LDR      r1,[r2,r4,LSL #2]
        0x08000856:    eb050084    ....    ADD      r0,r5,r4,LSL #2
        0x0800085a:    f7fffd0d    ....    BL       FLASH_ProgramWord ; 0x8000278
        0x0800085e:    1c64        d.      ADDS     r4,r4,#1
        0x08000860:    42b4        .B      CMP      r4,r6
        0x08000862:    d3f5        ..      BCC      0x8000850 ; error_process + 80
        0x08000864:    f7fffcfe    ....    BL       FLASH_Lock ; 0x8000264
        0x08000868:    bd70        p.      POP      {r4-r6,pc}
    $d
        0x0800086a:    0000        ..      DCW    0
        0x0800086c:    08006000    .`..    DCD    134242304
        0x08000870:    20000008    ...     DCD    536870920

Then these 124 bytes are the function data that will eventually be transferred to 0x8000800. After the transfer is completed, the MCU is soft reset, and the bootloader tampers with the Flash data of the app to achieve the purpose of changing the program function.

Why do we need to tamper with the app data when the bootloader is running? In theory, the app can run immediately after receiving the updated data of the error_process function when it is running, but because it involves modifying the app's own code, some related functions involved in the Flash modification may be temporarily damaged, causing the code to crash.

Skip some functions of the app

If you want to skip the "led_blings_1" function, there are 2 ways:

1. Skip inside the function


即将以下汇编语句

    0x0800655a:    2400        .$      MOVS     r4,#0

    修改为

    0x0800655a:    e013        .$      B             0x08006584

At the entry of the "led_blings_1" function, the instruction is modified to jump directly to the exit of the function. As for the assembled machine code and usage, there is relevant information at the end of the article for reference.

Because the byte offset of the modification is 0x55a, which is the high 2 bytes of the element with index 342 of pageBuf, the following modification needs to be made in the error_process function:

pageBuf[342] = (pageBuf[342] & 0x0000FFFF) | 0xe0130000;        


2. Skip the function call

The main function is compiled as follows:


    $t
    i.main
    main
        0x080065f8:    f44f41c0    O..A    MOV      r1,#0x6000
        0x080065fc:    f04f6000    O..`    MOV      r0,#0x8000000
        0x08006600:    f7fffe5c    ..\.    BL       NVIC_SetVectorTable ; 0x80062bc
        0x08006604:    2048        H       MOVS     r0,#0x48
        0x08006606:    f7ffff01    ....    BL       SysTick_Init ; 0x800640c
        0x0800660a:    f7ffff85    ....    BL       init_led ; 0x8006518
        0x0800660e:    f7ffffa3    ....    BL       led_blings_1 ; 0x8006558
        0x08006612:    f7ffffbb    ....    BL       led_blings_2 ; 0x800658c
        0x08006616:    f7ffffd3    ....    BL       led_blings_3 ; 0x80065c0
        0x0800661a:    e011        ..      B        0x8006640 ; main + 72
        0x0800661c:    f44f6180    O..a    MOV      r1,#0x400
        0x08006620:    4808        .H      LDR      r0,[pc,#32] ; [0x8006644] = 0x40010c00
        0x08006622:    f7fffe43    ..C.    BL       GPIO_SetBits ; 0x80062ac
        0x08006626:    f44f707a    O.zp    MOV      r0,#0x3e8
        0x0800662a:    f7ffff4f    ..O.    BL       delay_ms ; 0x80064cc
        0x0800662e:    f44f6180    O..a    MOV      r1,#0x400
        0x08006632:    4804        .H      LDR      r0,[pc,#16] ; [0x8006644] = 0x40010c00
        0x08006634:    f7fffe38    ..8.    BL       GPIO_ResetBits ; 0x80062a8
        0x08006638:    f44f707a    O.zp    MOV      r0,#0x3e8
        0x0800663c:    f7ffff46    ..F.    BL       delay_ms ; 0x80064cc
        0x08006640:    e7ec        ..      B        0x800661c ; main + 36
    $d
        0x08006642:    0000        ..      DCW    0
        0x08006644:    40010c00    ...@    DCD    1073810432

The following is the calling statement

    0x0800660e:    f7ffffa3    ....    BL       led_blings_1 ; 0x8006558

Simply change this statement to a null statement nop (0xbf00) to skip the call. Since the command occupies 4 bytes and nop is a two-byte command, it is replaced with two nop commands.

    0x0800660e:    bf00bf00    ....    NOP        

Because the byte offset of the modification is 0x60e, which is the high 2 bytes of the element with subscript 387 and the low 2 bytes of the element with subscript 388 of pageBuf, the following modification needs to be made in the error_process function:

pageBuf[387] = (pageBuf[387] & 0x0000FFFF) | 0xbf000000
pageBuf[388] = (pageBuf[388] & 0xFFFF0000) | 0x0000bf00



Autumn The recruitment has already begun. If you are not well prepared, Autumn It's hard to find a good job.


Here is a big employment gift package for everyone. You can prepare for the spring recruitment and find a good job!



Latest articles about

 
EEWorld WeChat Subscription

 
EEWorld WeChat Service Number

 
AutoDevelopers

About Us Customer Service Contact Information Datasheet Sitemap LatestNews

Room 1530, Zhongguancun MOOC Times Building,Block B, 18 Zhongguancun Street, Haidian District,Beijing, China Tel:(010)82350740 Postcode:100190

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号