Vector35 / Vector35/binaryninja-api

Stack clash protection using stack probing breaks good tracking of stack pointer

Open
#6,659 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Version and Platform (required):

  • Binary Ninja Version: 4.2.6455 (2c8da1e)

Bug Description:
When programs are compiled with gcc's -fstack-check, stack probes are inserted.
When moving the stack pointer by more than a page, every 0x1000 bytes the stack is probed to prevent jumping over the stack guard page.
This probing is done in a loop.
Probably due to changes to rsp in a loop, binja looses track of the stack pointer which leads to a not that great decompilation output.

Test program

#include <stdio.h>

struct bar
{
    int a;
    int b;
    long long c;
};

int main()
{
    char foao[0x5000];
    int foo = 22;
    struct bar bar = {1, 23, 4};
    int z3 = foo + bar.b;
    puts("Hello");
    printf("z3: %d", z3);
}

Stack probing loop:

0000000000001149 <main>:
    1149:	55                   	push   rbp
    114a:	48 89 e5             	mov    rbp,rsp
    114d:	4c 8d 9c 24 00 a0 ff 	lea    r11,[rsp-0x6000]
    1154:	ff 
    1155:	48 81 ec 00 10 00 00 	sub    rsp,0x1000 <-- change stack pointer
    115c:	48 83 0c 24 00       	or     QWORD PTR [rsp],0x0 <-- stack probe
    1161:	4c 39 dc             	cmp    rsp,r11
    1164:	75 ef                	jne    1155 <main+0xc> <--loop

Steps To Reproduce:
Please provide all steps required to reproduce the behavior:

  1. Open the original binary in dogbolt.org: https://dogbolt.org/?id=fbbce786-f166-4ef3-9308-a25a3721af92#Ghidra=137&BinaryNinja=93 (notice the *(i + ...) = )
  2. Patch out the stack probes and the loop.
  3. See how the *(i + ...) = disappears https://dogbolt.org/?id=48484467-f5ff-48ab-a735-c07d9140fe24#Ghidra=118&BinaryNinja=81
  4. For a more annoying example, open loader (from DEF CON Quals 25's callmerust challenge)
  5. https://dogbolt.org/?id=2968ec50-148d-4fc8-b51e-5888de471e7d#BinaryNinja=618&Hex-Rays=485&Ghidra=676 *(i + ...) =
  6. https://dogbolt.org/?id=9a0d20ea-f192-4d52-bbb6-60125e6286e1#Ghidra=665&BinaryNinja=601&Hex-Rays=480

Expected Behavior:
Not all those *(i + ...) = but individual variables.

Binary:

binaries_stack_probing.tar.gz

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

Reproduce the issue in Binary Ninja with the supplied binaries and the provided stack-probing example in main, then compare the original and probe-patched decompilation outputs. Investigate how the stack-probing loop affects stack-pointer tracking; done means the affected stack locations are recovered as individual variables rather than *(i + ...) expressions.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.