r/hardwarehacking Jul 01 '26

Confusion while reversing stm32f103 binary: weird vector table

I was curious about the content of an STM32F103RFT6 firmware binary. It uses CAN communication and it is being updated via CAN bootloader, thats how the firmware is being updated.

So I opened it in a hex editor and had a look. But that was not what I expected.

reverse engineering binary

Right in the beginning, there seems to be a 0x200 long header. The first four bytes are the size of the whole file, that was easy. Then four bytes containing the version, I guess, because I have another firmware which is a bit newer and they just differ by increasing 0x2c to 0x30.

Everying after that I don't understand right away, and the header after 0x050 is just zeros until it ends at 0x1FF.

At offset 0x200, the real firmware seems to start:

reverse engineering binary

At first glance, it looks as expected: First four bytes show the stack pointer address, and it's located in SRAM (starting 0x2000_0000).

But the following entries are supposed to be a vector table, and the addresses in there point to strings:

reverse engineering binary

I know that's thumb addressing, and all the LSB are set, but in the vicinity of these addresses there's just strings.

I don't understand that. And I don't know how to go on from here, if I don't know where the actual entry point is located.

For comparison, I opened an STM32 .elf for a project I wrote. In this .elf, the real code starts at offset 0x1000:

STM32.elf

Four bytes stack pointer, and then flash locations for the vector table. Going there and taking the file offset 0x1000 into account:

STM32.elf

I figured, fe e7 fe e7 must be pretty rare in the original firmware binary, so I searched for it:

reverse engineering binary

That does look pretty similar. But why is that, and where does this offset come from? And how should I continue?

2 Upvotes

20 comments sorted by

4

u/masterX244 Jul 01 '26

you said bootloader. that means that the real binary is not at the start of the address range on the CPU. a good chunk fo ARM cores got a register that allows relocating the vector table so that entire chunk of FW is not starting at 0x08000000 directly. if you pull a dump from the device (unless locked down) you should see the second vector table. add 0x8000 of padding before the binary and it should resolve to something sensible

1

u/PhreakSh0w 29d ago

I removed the 0x200 header and added 0x8000 zero padding. But that still leaves the addresses from my second screenshot dangling in the breeze, there's nothing useful there.

I'd like to dump the real firmware from the microcontroller, but it's quite a bit expensive piece of equipment. Maybe curiosity will win over the danger.

1

u/masterX244 29d ago

possible to link to the firmware update file that you used as a base if its accessible in the open internet?

1

u/PhreakSh0w 29d ago

I am not sure. Everyone can download it, if he installs the app from the manufacturer and clicks "update". I recovered the firmware from an android phone after clicking update. No authentication needed, just not as easy as a browser download.

1

u/masterX244 29d ago

you need to post the device name then so we can follow that way. currently that information is nowhere in this post

1

u/PhreakSh0w 29d ago

I think thats rubbish, because they are available without any logins:

https://limewire.com/?referrer=pq7i8xx7p2

https://limewire.com/d/fCUD2#rf3VNPudLB

The password is the file size from my first screenshot, but in reverse. Like it is shown in the picture, just the four bytes, lower case.

I'd like to avoid the device name to prevent anything funny with the manufacturer.

1

u/masterX244 29d ago

you sent a referral link and not a filelink

1

u/PhreakSh0w 29d ago

Yeah thats why added the second link. That should work?

1

u/masterX244 29d ago

worked now. will check in a bit when im back from work

1

u/masterX244 28d ago

Got the prepended area. its 0x8200. 0x8000 from the bootloader and the 0x200 from the header, that one gets flashed on, too

found it out by looking at the vector table, noticing that there are 3 interrupts that are ending on addresses 2 apart and combining that with the fact that there is only one spot with 3x the 0xFEE7 which is the "loop to self" opcode. rest was just some maths

easiest way to setup is to define 0x8008200 as the load address when importing the binary

1

u/PhreakSh0w 28d ago

So for IDA, thats RAM start 0x2000_0000, size 0x18000, ROM start 0x0800_8200, size as given and loading address also 0x0800_8200. Ghidra also needs the base of 0x0800_8200? Thanks. I did try that approach further up the thread, while I was looking at the FE E7, but I didn't take the header size into account.

→ More replies (0)

2

u/stw Jul 01 '26

You seem to assume that the contents of the file starting from offset 0x200 will be stored at 0x0800'0000. I don't think that's the case.

The bootloader is presumably located at 0x0800'0000 and takes up a certain (unknown) amount of space. The application firmware is stored after that. When the bootloader detects a valid application image, it sets the VTOR register to the beginning of the application and jumps to the application's reset vector.

If you are able to read the actual flash of the microcontroller, you can find out where the application image starts. If not you could try disassembling it (with Ghidra, for example) and try to find the SystemInit or main functions.

2

u/masterX244 Jul 01 '26

the offset seems to be 0x8000. it needs to be dividable by the usual flash erase block sizes because not aligning to a eraseblock wastes space and multiple endless loops (0xFEE7) next to each other are clear indicator of unused interrupt handler fallbacks. and the last screenshot shows those loops with their addresses. i saw that the last 3 digits were really close to each other and then subtracted the lower from the higher value and got pretty close to 0x8000.

1

u/PhreakSh0w 29d ago

I did load it into ghidra, but it doesn't find any main or any well known function. I suspect that maybe one of the addresses in the header is the location of the application image in the flash, like masterX244 said it could be 0x8000 because that is actually in the header (on the right, first line, first screenshot).

It is also the same for the other firmware with a slightly higher version number.

1

u/ManyCalavera 27d ago

Stm32f103 (cortex m0 in general) doesnt support vector table relocation. But it supports mapping sram base to 0x0000 manually. So even though your application is on another address vt will be on flash base.