7

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

3asm_
  • 506
  • 6
  • 15

2 Answers2

11

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 () {
    loc_0x10000b2b4:

       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
       {
        loc_0x10000b2dc:

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

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

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

}

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

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

                     loc_10000b2e3:
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;
}
ikaerom
  • 261
  • 2
  • 8
2

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

pancake
  • 482
  • 2
  • 1
  • 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