Vector35 / Vector35/binaryninja-api

Arm64: Incorrect return address handling when the link register (x30)'s value is known

Open
#3,896 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Component: Core Effort: Low Impact: Low
Dominant language
C++
Stars
1.3k
Forks
298
Avg merge
5d 5h
Merged PRs (30d)
19

Description

A user reported a bug that BN does not handle the return address properly when the x30 value is known. The sample is obfuscated, and it is using the ret instruction to do a jump. At the end of the function, it first calculates the jump target in x30, and then returns. BN is unaware of the trick and during LLIL->MLIL translation, the information is lost and the code is treated as a regular return.

The user does not wish to share the binary in public, so I recreated the issue on a different binary. helloworld_bug.zip For comparison, the original binary is attached as well: helloworld.zip

100003f54  mov     x30, #0x1234
100003f58  add     sp, sp, #0x40
100003f5c  ret     

Given the above function epilog, it loads 0x1234 into x30. After the ret instruction, the pc will become 0x1234. The code effectively does a b 0x1234. The LLIL is fine:

  25 @ 100003f54  x30 = 0x1234
  26 @ 100003f58  sp = sp + 0x40
  27 @ 100003f5c  <return> jump(x30)

However, things are screwed up in MLIL:

  18 @ 100003f5c  return 0xa

To deal with this, we need to do the following things:

  1. When the x30 value is a known constant, translate the ret instruction into a jump
  2. When the x30 value is undetermined, check its value is actually the value of x30 at the function start. For example, if we look at the original binary, we can see that the x30 value is the __saved_x30.

image

  1. (what should we do if it is not the entry value of x30?)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Arm64 ret handling during LLIL-to-MLIL translation and compare the provided LLIL and MLIL examples. Reproduce the behavior with helloworld_bug.zip, then verify that a known x30 constant becomes a jump and determine how an undetermined x30 value should be handled, including the unresolved non-entry-value case.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers, reverse-engineering
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.