Is is possible to retrieve the dissassembly of Objective-C methods declared in Mach-O files using Radare 2 ?


Essentially, it's the following workflow:

r2 -A /path/to/binary   // load the binary and perform initial analysis
afl                     // print function symbols
pdf @sym.of.interest    // disassemble
pdc @sym.of.interest    // "decompile" or generate pseudo code

Here is an example of how to disassemble a function of MachoViewer:

@0x4B6169:/$ r2 -A /Applications/MachOView.app/Contents/MacOS/MachOView 2>/dev/null
 -- Run .dmm* to load the flags of the symbols of all modules loaded in the debugger
[0x100001da0]> afl | grep sym.__MachOLayout_get
0x10000b252    4 49           sym.__MachOLayout_getSectionByIndex:_
0x10000b283    4 49           sym.__MachOLayout_getSection64ByIndex:_
0x10000b2b4    4 49           sym.__MachOLayout_getSymbolByIndex:_
0x10000b2e5    4 49           sym.__MachOLayout_getSymbol64ByIndex:_
0x10000b316    4 49           sym.__MachOLayout_getDylibByIndex:_
[0x100001da0]> pdf @sym.__MachOLayout_getSymbolByIndex:_
            ;-- func.10000b2b4:
            ;-- method.MachOLayout.getSymbolByIndex::
╒ (fcn) sym.__MachOLayout_getSymbolByIndex:_ 49
│           0x10000b2b4      55             push rbp
│           0x10000b2b5      4889e5         mov rbp, rsp
│           0x10000b2b8      89d0           mov eax, edx
│           0x10000b2ba      488b151f760f.  mov rdx, qword [rip + 0xf761f] ; [0x1001028e0:8]=176 LEA sym._OBJC_IVAR___MachOLayout.symbols ; sym._OBJC_IVAR___MachOLayout.symbols
│           0x10000b2c1      488b0c17       mov rcx, qword [rdi + rdx]
│           0x10000b2c5      488b541708     mov rdx, qword [rdi + rdx + 8] ; [0x8:8]=0x280000003
│           0x10000b2ca      4829ca         sub rdx, rcx
│           0x10000b2cd      48c1fa03       sar rdx, 3
│           0x10000b2d1      4839d0         cmp rax, rdx
│       ┌─< 0x10000b2d4      7306           jae 0x10000b2dc
│       │   0x10000b2d6      488b04c1       mov rax, qword [rcx + rax*8]
│      ┌──< 0x10000b2da      eb07           jmp 0x10000b2e3
│      ││   ; JMP XREF from 0x10000b2d4 (sym.__MachOLayout_getSymbolByIndex:_)
│      │└─> 0x10000b2dc      488d05e5fd0a.  lea rax, [rip + 0xafde5]   ; 0x1000bb0c8 ; sym.__MachOLayoutgetSymbolByIndex:_::notfound ; sym.__MachOLayoutgetSymbolByIndex:_::notfound
│      │    ; JMP XREF from 0x10000b2da (sym.__MachOLayout_getSymbolByIndex:_)
│      └──> 0x10000b2e3      5d             pop rbp
╘           0x10000b2e4      c3             ret
[0x100001da0]> quit

Generating some form of pseudo code ("decompiling") works similarly through the ingeniously logic command input:

[0x100001da0]> pdc @sym.__MachOLayout_getSymbolByIndex:_
function sub.__MachOLayoutgetSymbolByIndex:_.notfound_2b4 () {

       push rbp
       rbp = rsp
       eax = edx
       rdx = qword sym._OBJC_IVAR___MachOLayout.symbols //[0x1001028e0:8]=176 ; "__text"
       rcx = qword [rdi + rdx]
       rdx = qword [rdi + rdx + 8] //[0x8:8]=0x280000003
       rdx -= rcx
       rdx >>= 3
       var = rax - rdx
       jae 0x10000b2dc          //unlikely

         //JMP XREF from 0x10000b2d4 (sub.__MachOLayoutgetSymbolByIndex:_.notfound_2b4)
           rax = [sym.__MachOLayoutgetSymbolByIndex:_::notfound] //method.__MachOLayoutgetSymbolByIndex:_.notfound ; 0x1000bb0c8 ; method.__MachOLayoutgetSymbolByIndex:_.notfound

             //JMP XREF from 0x10000b2da (sub.__MachOLayoutgetSymbolByIndex:_.notfound_2b4)
               pop rbp

           } while (?);
       } while (?);


Inside hopper v4 you can do the same as follows:

hopperv4 -e /Applications/MachOView.app/Contents/MacOS/MachOView

This opens hopper and you can click on the "Proc." tab, and search for the function. Once you click on it, you will see the following disassembly (radare2 1.5.0 0 @ darwin-x86-64 git.1.5.0, commit: HEAD build: 2017-05-31__14:31:32):

000000010000b2b4         push       rbp                                         ; Objective C Implementation defined at 0x1000f5de8 (instance method), DATA XREF=0x1000f5de8
000000010000b2b5         mov        rbp, rsp
000000010000b2b8         mov        eax, edx
000000010000b2ba         mov        rdx, qword [_OBJC_IVAR_$_MachOLayout.symbols]
000000010000b2c1         mov        rcx, qword [rdi+rdx]
000000010000b2c5         mov        rdx, qword [rdi+rdx+8]
000000010000b2ca         sub        rdx, rcx
000000010000b2cd         sar        rdx, 0x3
000000010000b2d1         cmp        rax, rdx
000000010000b2d4         jae        loc_10000b2dc

000000010000b2d6         mov        rax, qword [rcx+rax*8]
000000010000b2da         jmp        loc_10000b2e3

000000010000b2dc         lea        rax, qword [__ZZ32-[MachOLayout getSymbolByIndex:]E8notfound] ; CODE XREF=-[MachOLayout getSymbolByIndex:]+32

000000010000b2e3         pop        rbp                                         ; CODE XREF=-[MachOLayout getSymbolByIndex:]+38
000000010000b2e4         ret

Clearly, the pseudo-code inside hopper is at this moment still quite better than what I could get inside radare2. The above disassembly in pseudo code inside hopper (v4.2.1) reads:

struct nlist * -[MachOLayout getSymbolByIndex:](void * self, void * _cmd, unsigned int arg2) {
    rax = arg2;
    rcx = self->symbols;
    if (rax < SAR(*(self + *_OBJC_IVAR_$_MachOLayout.symbols + 0x8) - rcx, 0x3)) {
            rax = *(rcx + rax * 0x8);
    else {
            rax = -[MachOLayout getSymbolByIndex:]::notfound;
    return rax;

Just use rabin2 -c .... now i have to fill silly text to fill 30 chars

  • 1
    rabin2 -c will only list classes. My question is on the disassembly of one of these methods. – 3asm_ Dec 31 '15 at 18:16

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.