disarm - Quick & (very) dirty command line instruction lookup for ARM64

What is this?

A simple command line utility that takes as an argument a 32-bit hexadecimal number, and parses it as an ARM-64 instruction, providing the disassembly. I found myself using otool -j -tV | grep ... on random binaries too many times while looking for opcodes (especially in ARM64 injected code, like CydiaSubstrate's, see below), so I refactored my disassembler with a simple main() - and the result is presented herein. I'm making this available for OS X, iOS, Linux, and Android.


Edit: The download link is right here - Tar containing OS X/iOS (ARM64) Linux (ELF32/64) and Android

Though this is a part of jtool's functionality (my OSX/iOS tool, which only works on Mach-O), disarm as a standalone tool can come in handy for anyone reversing ARM64 - on Android as well, and theoretically any ARM64-based OS. At some point I hope to integrate this into DexTRA so the latter can also disassemble ART's poorly generated native code.

Examples

For example, consider manually crafted code, e.g CydiaSubstrate:

Zephyr:JTool morpheus$ jtool -d 29c4 CydiaSubstrate
	29c4    MOVZ   W8, #43455, LSL #16; ; ->R8 = 0xa9bf0000 
        29c8    MOVK   X8, #8166; ; R8 += 1fe6 =.. 0xa9bf1fe6 
        29cc    STR    W8, [X19, #0]; ; *((X19) + 0x0) = X8 0xa9bf1fe6
        29d0    MOVZ   W8, #43455, LSL #16; ; ->R8 = 0xa9bf0000 
        29d4    MOVK   X8, #6116; ; R8 += 17e4 =.. 0xa9bf17e4 
        29d8    STR    W8, [X19, #4]; ; *((X19) + 0x4) = X8 0xa9bf17e4
        29dc    MOVZ   W8, #43455, LSL #16; ; ->R8 = 0xa9bf0000 
        29e0    MOVK   X8, #4066; ; R8 += fe2 =.. 0xa9bf0fe2 
        29e4    STR    W8, [X19, #8]; ; *((X19) + 0x8) = X8 0xa9bf0fe2
        29e8    MOVZ   W8, #43455, LSL #16; ; ->R8 = 0xa9bf0000 
        29ec    MOVK   X8, #2016; ; R8 += 7e0 =.. 0xa9bf07e0 
        29f0    STR    W8, [X19, #12]; ; *((X19) + 0xc) = X8 0xa9bf07e0

As you can see, jtool can figure out the values, but can't actually "understand" that's injected code, so it won't disassemble those further.
With disarm, you could decipher the instructions like so:

Zephyr:JTool morpheus$ ./disarm 0xa9bf1fe6
0xa9bf1fe6	STP X6, X7, [SP,#-16]!
Zephyr:JTool morpheus$ ./disarm 0xa9bf17e4
0xa9bf17e4	STP X4, X5, [SP,#-16]!
Zephyr:JTool morpheus$ ./disarm 0xa9bf0fe2
0xa9bf0fe2	STP X2, X3, [SP,#-16]!
Zephyr:JTool morpheus$ ./disarm 0xa9bf07e0
0xa9bf07e0	STP X0, X1, [SP,#-16]!
# you get the idea...

v0.2: File disassembly

Added an option to dump arbitrary files and attempt to disassemble. This is super useful for dumped bootloaders (e.g. iBoot, or Samsung S6, as shown here):

morpheus@Zephyr(~)$~/Documents/Work/JTool/disarm sboot.bin  | more
...
# Try to dump file - when the instructions make sense,
# you know it has to be code
0x00000000      0x00000010      DCD 0x10  # Offset of code
0x00000004      0xe99c208a      DCD 0xe99c208a 
0x00000008      0x00000000      DCD 0x0 
0x0000000c      0x00000000      DCD 0x0 
0x00000010      0x14000002      B 0x18   # branch to main
        -------------------------------
0x00000014      0x14000000      B 0x14   halt 
        -------------------------------
0x00000018      0x58000a80      LDR X0, #336            ; 0x168 
0x0000001c      0xb9400000      LDR W0, [X0, #0] 
0x00000020      0xd2b00001      MOVZ X1, 0x8000, LSL #16 
0x00000024      0x6a01001f      ANDS W31, W0, W1 
0x00000028      0x54000740      B.EQ 0x110 
0x0000002c      0x58000a20      LDR X0, #324            ; 0x170 
0x00000030      0xb9400000      LDR W0, [X0, #0] 
0x00000034      0x7200001f      TST W0, #1 
0x00000038      0x540006c0      B.EQ 0x110
0x0000003c      0x580009e0      LDR X0, #79
0x00000040      0xb9400000      LDR W0, [X0, #0]
0x00000044      0x12000400      AND W0, W0, #0x3 
0x00000048      0x71000c1f      CMP W0, #3
0x0000004c      0x540000c0      B.EQ 0x64
..
0x00000060      0x1400002c      B 0x110
       ---------------------------------
0x00000064      0x58000920      LDR X0, #73
0x00000068      0xb9400000      LDR W0, [X0, #0]
0x0000006c      0x12000400      AND W0, W0, #0x3 

Also added an Android Binary. ARMv7 binary, only because it was quicker to compile - will also work on ARM64 devices, of course.

Note, that there's a greater chance of unrecognized instructions in bootloaders (ISB, DSB, etc). If you find any (DCDs...) just let me know, and I'll add them in

v0.3: Register following

Also added most ARM64 ELx registers. Whew

v0.4: Auto-String

disarming a file will automatically figure out printable string arguments on calls (i.e. BL instructions), and print them out. This is really useful for finding panic, printf and the like in pretty much any binary of any OS - boot loader, kernel, or arbitrary user mode - provided it is ARM64 (for now). All you need to do is grep quotes ("). A few examples:

Zephyr:JTool morpheus$ disarm ~/Documents/Android/Devices/6E/sboot.bin | grep \" 
..
0x0003fa54	0x9400147f	BL 0x44c50 	; = 0x44c50("Kernel Image..")
0x0003fa6c	0x94001479	BL 0x44c50 	; = 0x44c50("- %s..")
0x0003facc	0x94001461	BL 0x44c50 	; = 0x44c50(" This is a non-secure chip. Skip...")
0x0003fb04	0x94001453	BL 0x44c50 	; = 0x44c50(" BL1 from SD CARD..")
0x0003fb38	0x94001446	BL 0x44c50 	; = 0x44c50("%s: failed.(%d)..")
0x0003fbd4	0x9400141f	BL 0x44c50 	; = 0x44c50("%s: passed.(%d)..","ace_hash_sha_digest"..))
0x0003fc18	0x9400140e	BL 0x44c50 	; = 0x44c50("%s: failed.(%d)..","ace_hash_sha_digest"..))
0x0003fc6c	0x940013f9	BL 0x44c50 	; = 0x44c50(".. SHA_INIT: 0x%08x 0x%08x..")
0x0003fc88	0x940013f2	BL 0x44c50 	; = 0x44c50(" sizeof  HASH_INFO:%d, ace_hash_ctx_t: %d, image_info: %d, shared_sigdata_t:%d....")
0x0003fcd8	0x940013de	BL 0x44c50 	; = 0x44c50("%s: failed.(%d)..","ace_hash_sha_init"..))
0x0003fd1c	0x940013cd	BL 0x44c50 	; = 0x44c50(" SHA_UPDATE-S: 0x%08x@0x%08x..")
0x0003fd68	0x940013ba	BL 0x44c50 	; = 0x44c50("%s: failed.(%d)..","ace_hash_sha_update"..))
0x0003fdac	0x940013a9	BL 0x44c50 	; = 0x44c50(" SHA_FINAL: 0x%08x 0x%08x %d..")
0x0003fe10	0x94001390	BL 0x44c50 	; = 0x44c50("%s: failed.(%d)..","ace_hash_sha_final"..))
0x00040260	0x9400127c	BL 0x44c50 	; = 0x44c50("reboot reason: ")
0x00040308	0x94001252	BL 0x44c50 	; = 0x44c50("Core stat at previous(IRAM)..")
0x000403a4	0x9400122b	BL 0x44c50 	; = 0x44c50("Core stat at previous(KERNEL)..")
..
Zephyr:JTool morpheus$ disarm ~/Documents/iOS/JB/TaiG8.3/kernel.8.4.iPhone6.dump | grep \" 
0x000025a0      0x9403736b      BL 0xdf34c      ; = 0xdf34c(?,"/var/vm/swapfile"..)) # strncpy
0x00002c38      0x94008ca9      BL 0x25edc      ; = 0x25edc(?,"default_pager"..))    # lck_grp_init
0x00002de4      0x940075a6      BL 0x2047c      ; = 0x2047c(""%s[KERNEL]: %s"")      # panic!
0x00002e44      0x9400758e      BL 0x2047c      ; = 0x2047c(""can't start backing store monitor thread"")

# And on iBoot (8.x) Wicked useful, qwupz :-)
bash-3.2# disarm /Users/morpheus/Documents/iOS/JB/iBoot/iBootDump-iPod7,1-8.4  | grep \" 
0x000004e4	0x9400346e	BL 0xd69c 	; = 0xd69c("asp_fw")
0x00000500	0x94003467	BL 0xd69c 	; = 0xd69c("nand_syscfg")
0x00000510	0x94005a85	BL 0x16f24 	; = 0x16f24("nand_syscfg")
0x00000604	0x94008ecd	BL 0x24138 	; = 0x24138("iBoot not saving panic log...")
0x0000064c	0x94008ebb	BL 0x24138 	; = 0x24138("Panic saved, full reset...")
...
# ... Oh, and - AAPL - those hashes instead of messages in 9.x will only get you this far. 
bash-3.2# 

Limitations

Plenty. I don't support all the ARM64 instruction set. I admit - I'm weak. I couldn't read through the entire 5,000 pages of the ARMv8 Reference document. To see what I do support, run the tool with "opcodes".

This is not meant to be a complete tool, and you may end up just getting your input echoed back at you as a DCD 0x..... If you think those instructions are useful, and want to help me improve this tool, just drop me a note - preferably through The book forum.

Q&A

Changelog