强制 Ghidra 的反编译器显示未使用的堆栈变量

逆向工程 吉德拉 嵌入式
2021-07-09 01:51:11

我正在使用 Ghidra 反转 ARM64 引导加载程序。引导加载程序不使用 MMU,因此专门处理物理地址,在这个特定的芯片上,这些地址都适合 32 位(内存映射只有 4GB)。因此,即使内核是 ARM64,一些外设的 DMA 地址寄存器也只有 32 位宽。

我发现反编译器省略了处理这些寄存器的特定函数的重要指令。该函数在其堆栈上设置一些数据结构,然后将指向它们的(32 位)指针加载到 DMA 地址寄存器中。但是,由于指针被截断以适应寄存器,Ghidra 似乎没有意识到堆栈变量曾经被引用过,因此反编译器输出完全忽略了它们!

这是一个 C 语言的演示,具有类似的代码流(加上额外的 hack 以使其成功运行不适合 32 位的指针)。使用 GCC 编译它在 ARM64(有或没有优化)和 x86(没有优化)上都为我展示了这个问题:

#include <stdio.h>
#include <stdint.h>

void print_stack_data(uint32_t addr_low) {
    uint32_t *ptr;

    // Infer the top half of the address from our own stack's location. This
    // is purely to make this demo work; in my real binary, the addresses in
    // question are physical addresses that are known to always have a top
    // half of zero.
    ptr = (uint32_t *)(((uint64_t)&ptr & 0xffffffff00000000) | addr_low);

    printf("%08x, %08x, %08x, %08x\n", ptr[0], ptr[1], ptr[2], ptr[3]);
}

int main() {
    // Some arbitrary data.
    uint32_t numbers[4] = {0x1234, 0x5678, 0x99999999, 0};

    // More operations, yay!
    numbers[3] = (numbers[0] << 16) + numbers[1];

    // Put the parameter into a new variable so that the compiler doesn't
    // just put the entire pointer into the argument register, which Ghidra
    // does understand.
    uint32_t numbers_low = (uint64_t)numbers;

    // Ghidra doesn't realize we're giving out a stack address here, so the
    // decompiler doesn't show all the previous operations on numbers.
    print_stack_data(numbers_low);
}

下面是 Ghidra 对优化的 ARM64 二进制文件的反编译。请注意,numbers根本不存在:

Ghidra 反编译器截图

有没有办法解决这个问题而不涉及修补二进制文件(这是我目前正在做的)?我是否可以强制反编译器将一系列堆栈地址视为已使用,即使它看不到它们的使用位置?

0个回答
没有发现任何回复~