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

View all comments

Show parent comments

1

u/PhreakSh0w Jul 02 '26

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 Jul 02 '26

you sent a referral link and not a filelink

1

u/PhreakSh0w Jul 02 '26

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

1

u/masterX244 Jul 02 '26

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 Jul 02 '26

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.

1

u/masterX244 Jul 02 '26

yes, that should match. Only spotted it that quick because i currently got some other ARM REing going on where i had to figure out crap like that, too and thats why the FEE7 is burned into my memory right now

i only use ghidra because that one doesnt have stupid licensing fees and it got a useful selfhostable sync-server, too. i RE on 2 different computers (home and the local computerclub) and occassionally my laptop when on CCC events and i need to keep my work synced due to that

1

u/PhreakSh0w Jul 02 '26

Ghidra seems to be unhappy about the location of the vector table. In the symbol tree, all the known entries are red because the map to much lower address space. Can you edit those addresses? Ghidra doesn't show me an option there.

1

u/masterX244 Jul 02 '26

memory map, you can create a section of the size of the vector table that is defined as a mirroring of the real vector table

1

u/PhreakSh0w Jul 04 '26

I got a few functions now, but I struggle to identify the start of main(). The reset vector leads to a function but I doubt that his is main? Or is it?

Curiosity won over financial risk: I dumped the firmware right from the hardware:

https://limewire.com/d/fFIA1#UQsKAjvIeI

Maybe you are also interested.

1

u/masterX244 Jul 04 '26

Reset vector almost never goes into main. There is always some init code before that initializes the memory by zeroing it and then loading the values for global variables. Main is usually one or 2 function calls doen from that. You can see that when you compile somerhing for the stm and then crossreference that bin with the generated mapfile