类型推断不一致

逆向工程 艾达 反编译 吉德拉 杰布
2021-07-02 20:43:12

我正在针对以下C代码测试几个反编译器


static int bar(int i) {
    return ++i;
}

static int apply(int (*fun)(int), int i) {
    return i % fun(i);
}

static int foo(int (*app)(int (*fun)(int), int), int i)  {
    return i / app(bar, i);
}

int main() {
    return foo(apply, 7);
}

这是由 just 编译的clang test.c

; main
0x0         push rbp
0x1         mov rbp, rsp
0x4         sub rsp, 0x10
0x8         mov dword ptr [rbp-0x4], 0x0
0xf         mov rdi, @apply
0x19        mov esi, 0x7
0x1e        call foo
0x23        add rsp, 0x10
0x27        pop rbp
0x28        ret

; foo
0x30        push rbp
0x31        mov rbp, rsp
0x34        sub rsp, 0x20
0x38        mov [rbp-0x8], rdi
0x3c        mov [rbp-0xc], esi
0x3f        mov eax, [rbp-0xc]
0x42        mov rcx, [rbp-0x8]
0x46        mov esi, [rbp-0xc]
0x49        mov rdi, @bar
0x53        mov [rbp-0x10], eax
0x56        call rcx
0x58        mov edx, [rbp-0x10]
0x5b        mov [rbp-0x14], eax
0x5e        mov eax, edx
0x60        cdq
0x61        mov esi, [rbp-0x14]
0x64        idiv esi
0x66        add rsp, 0x20
0x6a        pop rbp
0x6b        ret

我知道参数/参数检测的一些限制(来自对另一个问题的回答)。但是每个反编译器似乎都以某种方式在其反编译语言的类型系统中存在不一致(我认为它们都试图反编译为C或 伪C)。

IDA v.7.4.191122 (评估版)给出:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  return foo(apply, 7LL, envp);
}

__int64 __fastcall foo(int (__fastcall *a1)(__int64 (__fastcall *)(), _QWORD), unsigned int a2)
{
  return (unsigned int)((int)a2 / a1(bar, a2));
}

我不显示的结果barapply因为当时已经在这里存在不一致:IDA检测到foo被称为与3个参数main,但后来得出结论,foo实际上已经2个参数。

接下来,Ghidra v9.1.build.2019-oct-23

void main(void)
{
  foo(apply,7);
  return;
}

ulong foo(code *param_1,uint param_2,undefined8 param_3)
{
  int iVar1;

  iVar1 = (*param_1)(bar,(ulong)param_2,param_3,param_1);
  return (long)(int)param_2 / (long)iVar1 & 0xffffffff;
}

其具有相反的意见:foo被称为在main以2个参数,但在其定义中foo有3个参数。

JEB v.3.8.0.201912242244 (评估版本):

unsigned long main() {
  return foo(&apply, 7L);
}

unsigned long foo(unsigned long param0) {
  unsigned int v0 = v1;
  param0();
  return (unsigned long)(v0 / ((unsigned int)v2));
}

这给出了 的完美结果main,但随后声称它foo是只有 1 个参数的函数(虽然它显示param0(),但它保持param0unsigned long)。

实际上,反编译的结果并不正确(这在某种程度上是可以理解的),但它们甚至不一致。我错过了一些配置吗?

1个回答

IDA结果看起来不错。我相信 的签名main来自任何类型FLIRT或其他功能识别 - 它检测到该功能是main,因此给它默认的主签名。它看起来像foo拆解得很好。您可以通过按y函数调用来配置自己的签名您必须了解整个过程是非常启发式的,并且此信息不会出现在二进制文件中的任何位置。

我在 中多次看到它IDA,它为函数创建了一个很好的签名,但有时会使用额外/缺失的参数调用它。我相信这是一个错误,而不是可配置的东西。