I am attempting to disassemble a ROM image for a BCM4366C [1] after reading a blog post where researchers reversed some other Broadcom chips [2] . I have analyzed the ROM image in Ghidra and I have tried to identify the malloc, memcpy, free and printf functions, but now I am struggling to understand what I believe to be the main malloc loop. I believe the entry point to malloc is at 00012124, based on the large number of xrefs Ghidra has found where r0 is set to a small immediate value followed by a branch to this address, eg:

        00018dcc 4f f4 a2 70     mov.w      r0,#0x144
        00018dd0 f9 f7 a8 f9     bl         malloc              ;; 00012124

At 00012124 there is a branch to 00011eb8 which I believe to be the guts of the malloc function:

                             LAB_00011eb8                                    XREF[2]:     00012126(j), 0001212c(j)  
        00011eb8 2d e9 f7 4f     push       { r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r1...
        00011ebc 03 30           add        r0,#0x3
        00011ebe 4f f0 01 0b     mov.w      r11,#0x1
        00011ec2 20 f0 03 04     bic        r4,r0,#0x3
        00011ec6 0b fa 01 f1     lsl.w      r1,r11,r1
        00011eca b1 f5 80 4f     cmp.w      r1,#0x4000
        00011ece 34 bf           itE        cc
        00011ed0 8b 46           mov.cc     r11,r1
        00011ed2 4f f4 80 4b     mov.cs.w   r11,#0x4000
        00011ed6 90 f0 a7 f8     bl         FUN_000a2028                                     undefined FUN_000a2028()
        00011eda 39 4b           ldr        r3,[DAT_00011fc0]                                = 002007B8h
        00011edc 00 22           mov        r2,#0x0
        00011ede bb f1 04 0f     cmp.w      r11,#0x4
        00011ee2 38 bf           it         cc
        00011ee4 4f f0 04 0b     mov.cc.w   r11,#0x4
        00011ee8 d3 f8 00 a0     ldr.w      r10,[r3,#0x0]=>DAT_002007b8
        00011eec 15 46           mov        r5,r2
        00011eee 91 46           mov        r9,r2
        00011ef0 13 46           mov        r3,r2
        00011ef2 cb f1 00 01     rsb        r1,r11,#0x0
        00011ef6 0b f1 ff 3b     add.w      r11,r11,#0xffffffff
        00011efa 01 91           str        r1,[sp,#Stack[-0x2c]]
        00011efc cd f8 00 b0     str.w      r11,[sp,#0x0]=>Stack[-0x30]
                             LAB_00011f00                                    XREF[1]:     00011f4a(j)  
        00011f00 41 68           ldr        r1,[r0,#0x4]
        00011f02 19 b3           cbz        r1,LAB_00011f4c
        00011f04 d1 f8 00 80     ldr.w      r8,[r1,#0x0]
        00011f08 a0 45           cmp        r8,r4
        00011f0a 1d d3           bcc        LAB_00011f48
        00011f0c dd f8 04 b0     ldr.w      r11,[sp,#Stack[-0x2c]]
        00011f10 01 f1 08 07     add.w      r7,r1,#0x8
        00011f14 07 eb 08 0c     add.w      r12,r7,r8
        00011f18 c4 eb 0c 06     rsb.w      r6,r4,r12
        00011f1c 0b ea 06 06     and.w      r6,r11,r6
        00011f20 be 42           cmp        r6,r7
        00011f22 11 d3           bcc        LAB_00011f48
        00011f24 dd f8 00 b0     ldr.w      r11,[sp,#0x0]=>Stack[-0x30]
        00011f28 1b ea 07 0f     tst.w      r11,r7
        00011f2c 02 d0           beq        LAB_00011f34
        00011f2e f7 1b           sub        r7,r6,r7
        00011f30 07 2f           cmp        r7,#0x7
        00011f32 09 d9           bls        LAB_00011f48
                             LAB_00011f34                                    XREF[1]:     00011f2c(j)  
        00011f34 c4 eb 08 07     rsb.w      r7,r4,r8
        00011f38 57 45           cmp        r7,r10
        00011f3a 05 d2           bcs        LAB_00011f48
        00011f3c 62 46           mov        r2,r12
        00011f3e 35 46           mov        r5,r6
        00011f40 67 b1           cbz        r7,LAB_00011f5c
        00011f42 ba 46           mov        r10,r7
        00011f44 81 46           mov        r9,r0
        00011f46 0b 46           mov        r3,r1
                             LAB_00011f48                                    XREF[4]:     00011f0a(j), 00011f22(j), 
                                                                                          00011f32(j), 00011f3a(j)  
        00011f48 08 46           mov        r0,r1
        00011f4a d9 e7           b          LAB_00011f00
                             LAB_00011f4c                                    XREF[1]:     00011f02(j)  
        00011f4c 48 46           mov        r0,r9

There is a loop entering at 00011f00 with the reverse jump at 00011f4a, and a single jump out of the loop at 00011f40.

The line that is confusing me is the first line of the loop (00011f00) -- here the instruction ldr r1,[r0,#0x4] would be using r0 as a base address and then dereferencing r0+0x4 into r1 (because of the [], indicating immediate offset addressing). However, I can't find any instructions prior to this where r0 is loaded into, it appears to only be loaded with a small immediate value at the malloc call sites (which I thought was the callers specifying the size of memory they want alloc'd). Apart from the 'wrapper' at 00012124, the only other block of code I am encountering in my in-my-head trace of the execution is the branch at 00011ed6, and the function there also does not touch r0.

The loop ends by moving r1 into r0 before jumping back to the start, at which point the ldr r1,[r0,#0x4] instruction is executed again. Based on this I had thought that this loop is walking over the linked list of free heap blocks: each heap block has a pointer to the next heap block at offset 0x4, which is retrieved at the start of the loop.

Is the instruction at 00011f00 actually dereferencing the size as a base address? Or is r0 being written to earlier in the flow and I cannot see where it is happening?

Additionally, I would love to know if I am on the right track with understanding the allocator, but if you can help me with this one ldr instruction I would be very grateful! 🙏

Thank you :)

[1] https://github.com/seemoo-lab/bcm_misc/blob/master/bcm4366c/rom.bin
[2] https://blog.quarkslab.com/reverse-engineering-broadcom-wireless-chipsets.html

EDIT: FUN_000a2028 is defined as:

                             *                          FUNCTION                          *
                             undefined FUN_000a2028()
             undefined         r0:1           <RETURN>
             undefined4        Stack[-0x4]:4  local_4                                 XREF[1]:     000a202e(W)  
                             FUN_000a2028                                    XREF[2]:     FUN_00011dc4:00011de0(c), 
        000a2028 10 b5           push       { r4, lr }
        000a202a 02 4c           ldr        r4,[DAT_000a2034]                                = 00200418h
        000a202c 24 68           ldr        r4,[r4,#0x0]=>DAT_00200418
        000a202e 01 94           str        r4,[sp,#local_4]
        000a2030 10 bd           pop        { r4, pc }

  • Are you overlooking the return value of FUN_000a2028? edit: also if you haven't seen this, I would recommend reading: googleprojectzero.blogspot.com/2017/04/… (part 1 of 2) as it covers a lot of broadcom internals and generic analysis – mumbel Jun 28 at 19:05
  • Thanks for your reply @mumbel :) From what I understand FUN_000a2028 is not returning anything via r0, although now I look at it I am more confused. I have added the disassembly of FUN_000a2028 to the question and at 000a202e it looks like it's trying to store r4 at the address pointed to by lr, as this has been pushed onto the stack at sp+#4. Am I reading this correctly? Also thank you for the additional link :) – wcerfgba Jun 28 at 19:48
  • You should probably trace down the DAT_ used in FUN_000a2028. I would guess that this is a trampoline to get functions through an API. For example, there is low (loaded from the chip) and high (loaded from firmware blob by kernel) memory, with a series of these trampolines at the tail end of low memory to connect the two for functions the chip does not implement. And you should have a long list of function pointers just a few hundred bytes into the firmware blob – mumbel Jun 28 at 20:07
  • if you're just working off that bin you linked and also not your device's specific firmware, you can use the 4366c from here: git.kernel.org/pub/scm/linux/kernel/git/firmware/… . In ghidra you can file->add to program at 0x00200000. sadly rom.bin is patched against fw already, alot will be broken as its calling into an unknown firmware bin file. you can try to fix this by applying the fw patches from that link to your bin. at 00204000 you'll see a list of {dstaddr,srcaddr} copy the 0x10 bytes from src to dst – mumbel Jun 29 at 0:44
  • Thanks for your additional comments @mumbel :) So far I have been trying to analyse the ROM in isolation: I wanted to see how far I could get before I purchased a Bus Pirate and a device and tried pulling down the RAM. I couldn't find any xrefs to the DAT_00200418 elsewhere in the ROM. I will try your suggestion with the additional firmware from Linux kernel :) How did you determine it needs loading at 0x00200000 ? – wcerfgba Jun 29 at 8:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.