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.
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.