Reversing ARM Cortex-M Bit Band addresses

While reverse-engineering the firmware on the Digoo DG-HOSA device which I have a couple of posts on already. I ran across some memory addresses that did not directly map to peripherals. I found the address ranges are called the Bit-band range and had special functionality allowing direct access to individual bits on peripherals. This post will give a quick summary of what these addresses are and how to unmap them to the normal peripheral addresses.

What is Bit Banding?

The Bit Banding is a feature of the ARM Cortex-M3/M4 allowing you to directly access specific bits within the Peripheral and SRAM regions. Normally you need to write a whole register at a time even if you want to change a single bit. There are memory regions allocated for this purpose they are named “Bit band alias” in this snip of the Processor memory map table.

From Cortex-M3 Technical Reference

To calculate the Bit band address for a specific bit on a port you use the following formula. This formula is also found in the Cortex-M3 Technical reference.

bit_word_addr = bit_band_base + ((byte_offset x 32) + (bit_number × 4))

bit_word_offset – position of the target bit in the bit-band memory region.
bit_word_addr – address of the word in the alias memory region that maps to the targeted bit.
bit_band_base – starting address of the alias region.
byte_offset – number of the byte in the bit-band region that contains the targeted bit.
bit_number – bit position (0-7) of the targeted bit.

This was a quick introduction to bit banding if you are looking for more information check out his post which goes into great detail on this feature.

Now to reverse a bit mapped address

Now that we how to forward map an address I wrote up some python code that takes in the address, unmaps it, and lists the original port and register information.

#!/usr/bin/python

def find_base_addr(bitmapped):
    if (0x22000000 < bitmapped and bitmapped < 0x23FFFFFF):
        bit_band_base = 0x22000000
        base_address = 0x20000000
    elif (0x42000000 < bitmapped and bitmapped < 0x43FFFFFF):
        bit_band_base = 0x42000000
        base_address = 0x40000000
    else:
        bit_band_base = 0
        base_address = 0
    return bit_band_base, base_address

def bitunmapper(bitmapped):
    bit_band_base, base_address = find_base_addr(bitmapped)
    unmap_port = int(((bitmapped - bit_band_base) & 0xfffff00) / 32)
    unmap_bits = int((bitmapped & 0x000000FF) / 4)
    if unmap_bits > 32:
        unmap_port += 0x4
        unmap_bits %= 32
    unmapped_address = base_address + int(unmap_port)
    
    print("Base Address: " + hex(unmapped_address))
    print("Port: " + hex(unmapped_address & 0xFFFFFF00))
    print("Register: " + hex(unmapped_address & 0x000000FF))
    print("Bit Map: " + str(int(unmap_bits)))

bitunmapper(0x4221811c)
bitunmapper(0x42218188) # GPIOB 2
bitunmapper(0x42218198) # GPIOB 6

I have also created a Ghidra plugin using this code to quickly resolve these addresses when working through a binary. You can download it from this link: https://github.com/suidroot/ghidra_scripts/blob/main/arm-bit-unmapper.py

Finally, here is a couple of screenshots of the plugin in action.

Enter Address
Results in the console

Author: Ben Mason

Technical Architect - Computer Networking - Security - Electronics Hobbyist - Sometimes Photographer - Spaceflight - Cat Enthusiast - HAM KC1GDJ

Leave a Reply

Your email address will not be published.