ghidra icon indicating copy to clipboard operation
ghidra copied to clipboard

Indirect switch statement not always decompiled.

Open russdill opened this issue 4 years ago • 2 comments

I have an ARM binary with a someone unique switch statement/jump table. The jump table consists of a length byte and then offset bytes. The PC advances by the offset byte * 2. The code performs a branch with link (bl) to the jump table function with uses the link register to locate the jump table and branch to the appropriate target. The jump table function looks like this:

   0x0001baad:	push	{r4, r5}
   0x0001baaf:	mov	r4, lr
   0x0001bab1:	subs	r4, r4, #1
   0x0001bab3:	ldrb	r5, [r4, #0]
   0x0001bab5:	adds	r4, r4, #1
   0x0001bab7:	cmp	r3, r5
   0x0001bab9:	bcs.n	0x1babc
   0x0001babb:	mov	r5, r3
   0x0001babd:	ldrb	r3, [r4, r5]
   0x0001babf:	lsls	r3, r3, #1
   0x0001bac1:	adds	r3, r4, r3
   0x0001bac3:	pop	{r4, r5}
   0x0001bac5:	bx	r3

Without any intervention, none of these are handled. It either just falls through to the first case, or stops decompilation upon encountering the illegal instruction bytes that make up the jump table. However, if I modify the instruction flow of the bl instruction to branch, in many cases the switch statement miraculously appears.

In some cases however, the decompiled code shows the jump table code inlined into the function, but fails to handle the switch with:

                    /* WARNING: Could not execute MULTIEQUAL */
                    /* WARNING: Treating indirect jump as call */
(*(code *)(BYTE_ARRAY_0001d3d5 + (uint)BYTE_ARRAY_0001d3d5[i] * 2))();

This is currently tested with Ghidra 9.1.

russdill avatar Nov 26 '19 09:11 russdill

I had a similar problem and spent quite some time trying to figure out how I could coerce the decompiler into recognizing the switch statement via annotations and cross references. Turns out that none of that is necessary; there's a predefined function fixup called switch8_r3 for this type of switch; the "ARM Pre-Pattern Analyzer" will actually try to identify these jump table functions and apply the fixup - it just doesn't recognize the specific code you (and I) ran across.

The fix is simple: Edit the jump table function and there's a small "Call Fixup" dropdown at the bottom left. Change that to switch8_r3 and all your switch statements will magically appear in the decompiler :)

dojoe avatar Oct 03 '20 12:10 dojoe

Since I recently dealt with a similar problem, I know what to look for: In recent versions of Ghidra, since 10.2, the switch helper functions are recognized through a file called ARM_switch_patterns.xml, which also contains the pattern mentioned above. The semantics of the call fixup are defined in ARM.cspec, in the <callfixup name="switch8_r3"> element.

@russdill if you update to a more recent Ghidra version and re-run auto-analysis from scratch, does it decompile the switches in your ARM binary?

neuschaefer avatar Dec 22 '23 11:12 neuschaefer