Is is possible to retrieve the dissassembly of Objective-C methods declared in Mach-O files using Radare 2 ?
2 Answers
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;
}
Just use rabin2 -c
.... now i have to fill silly text to fill 30 chars
-
1rabin2 -c will only list classes. My question is on the disassembly of one of these methods.– 3asm_Dec 31, 2015 at 18:16