qilingframework / qilingframework/qiling
Cortex-M Vector Table Offset Register not used during mcu emulation
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 6.1k
- Forks
- 798
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 9
Description
Describe the bug
When emulating an mcu based on the cortex-m, Qiling never uses the value stored in the Vector Table Offset Register. This register is used to indicate an offset for the base address of the vector table. This produces a bug when emulating a firmware that is using this register to indicate an offset to use its vector table. In my case the vector table is used in the firmware to fetch interrupt handler, the bug makes the firmware trying to fetch interrupt handlers from wrong address.
Sample Code
ql = Qiling(["./toto.elf"],
archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f103, verbose=QL_VERBOSE.DISABLED)
ql.hw.create('scb')
ql.hw.create('gpioa')
ql.hw.create('usart2').watch()
ql.hw.create('rcc')
ql.hw.create('afio')
ql.hw.create('exti')
ql.hw.create('gpioc')
ql.hw.show_info()
ql.hw.usart2.send("totototo".encode())
ql.run(count=1000000)
Expected behavior
Just take into account the value present in the register.
Additional context
Tested on the dev branch.
This register is laying in the SCB part of the CPU memory, as shown in the code above once scb hardware added to the emulation, the memory is perfectly handled, read and write to it works fine (checked by using DISASM debug log).
Suggested correction
For me this was problematic when the firmware uses interruption so here is how I corrected the problem. It has been tested and worked perfectly but I'm not sure this is the right place to implement this correction plus I've hardcoded the address which is very ugly.
In qiling/arch/cortex_m.py in interrupt_handler function :
def interrupt_handler(self, ql: Qiling, intno: int):
basepri = self.regs.basepri & 0xf0
if basepri and basepri <= ql.hw.nvic.get_priority(intno):
return
if intno > IRQ.HARD_FAULT and (self.regs.primask & 0x1):
return
if intno != IRQ.NMI and (self.regs.faultmask & 0x1):
return
if ql.verbose >= QL_VERBOSE.DISASM:
ql.log.debug(f'Handle the intno: {intno}')
with QlInterruptContext(ql):
isr = intno + 16
offset = isr * 4
# ============= personnal modifications =============
#Here qiling doesn't care about the SCB_VTOR which is not normal for the cortex M
SCB_VTOR = int.from_bytes(ql.mem.read(0xe000ed08,4), byteorder='little')
entry = ql.mem.read_ptr(offset + SCB_VTOR)
# =======================================
exc_return = 0xFFFFFFFD if self.using_psp() else 0xFFFFFFF9
self.regs.write('ipsr', isr)
self.regs.write('pc', entry)
self.regs.write('lr', exc_return)
ql.log.debug(hex(self.effective_pc))
self.uc.emu_start(self.effective_pc, 0, 0, 0xffffff)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in qiling/arch/cortex_m.py, specifically interrupt_handler, and review how the vector-table entry is read. Reproduce the issue with the supplied Cortex-M STM32F103 setup and verify that interrupt handlers are fetched using the SCB Vector Table Offset Register. Done means firmware using a relocated vector table reaches the correct handler.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- embedded-iot, reverse-engineering
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100