Sometimes jump tables are borked, especially when a binary has to be loaded
rwx
, specifically the issue is with writable memory. So fixing up the jump
table from int32_t jt[0xd]
to int32_t const jt[0xd]
makes Binary Ninja happy
again.
This is a relevant GitHub issue
Example
In a recent firmware I encountered that issue. The whole blob was mapped as
rwx
because some of the memory ranges would change, but I didn’t know which
ones yet, and I didn’t want to hand-pick the ranges either.
In one of the functions I was analyzing, the MLIL got really confused and I ended not seeing all the code paths, because they were never reached. Binary Ninja will also tag broken jumps. 1
![Broken jump: 00000e2e jump(&jt + 2 * zx.d([&jt + (var_s << 1)].w))](/binaryninja/jump_broken_graph.png#center)
Broken Jump in MLIL. Notice no outgoing paths.
We can see that it tries to calculate the offset with the + 2 * (var_s << 1)
,
but it can’t be confident that this doesn’t change, because the memory is marked
as writeable. So we have to manually inspire that confidence!
Looking at jt
we see that it’s recognized as a uint16_t
, but it doesn’t
resolve the whole table.

To help with the size of the array, Binary Ninja luckily analyzed the values for
var_s
as 0x0 - 0xd
(which we know is correct based on the analysis).

Based on this information we can set the jump table to uint16_t const jt[0xd]
.
![Variable jt as uint16_t const jt[0xd]](/binaryninja/jt_after.png#center)
With this change, Binary Ninja immediately fixes the jump and it resolves all the paths in the MLIL.

Fixed jump in graph view with all paths going out.
That change also populates up to the HLIL that now shows a pretty switch statement and the code paths depending on that input variable (based on the type field of a struct).
-
Looking for “Unresolved Indirect Control Flow” in the Tag Browser lists all those. It might be worth going through some/most of them, depending on the project. ↩︎