如何在 IDA Pro (GoLang) 中指定基于堆栈的返回值

逆向工程 艾达 x86 调用约定
2021-06-29 00:02:51

tl; dr:我正在反转为 Windows 编译的 GoLang 1.10 可执行文件。我试图让 IDA 正确识别调用约定。

详细信息:我正在查看使用以下(非标准)调用约定的 x86 可执行文件。

  1. 调用者在堆栈上为可能的多个返回值创建空间,即4*return_countESP.
  2. 调用者将被调用者的参数压入堆栈,调用被调用者
  3. callee 为局部变量创建一个堆栈帧
  4. 被调用者确实有效
  5. 被调用者恢复ESP到第 3 步之前的一样
  6. 被调用者将返回值存储在其参数下方的堆栈中
  7. 呼叫返回

调用者现在有返回值和参数仍然在本地堆栈帧中,后者在前者之上,并进行清理。这是一个 GoLang 1.10 可执行文件,但我正在描述我在这里看到的调用约定,我不知道任何参考。如果已知 Windows 编译的可执行文件中的 GoLang 调用约定;我也会对此非常感兴趣。

然而,更重要的是,我正在寻找一种方法来让 IDA 正确理解这个调用约定。例如,请考虑以下代码:

 test proc near                   ; test receives two arguments
   sub     esp, 18h               ; create stack frame
   mov     eax, [esp+1Ch]         ; argument 1 to test:
   mov     [esp], eax             ;  moved to top of stack frame
   mov     eax, [esp+20h]         ; argument 2 to test:
   mov     [esp+4], eax           ;  moved second to top of stack frame
   call    sub1                   ; call sub1(test_arg_1, test_arg_2)
   mov     eax, [esp+14h]         ; retrieve return_value_1
   mov     ecx, [esp+10h]         ; retrieve return_value_2
   mov     [esp], ecx           
   mov     [esp+4], eax
   call    sub2                   ; call sub2(return_value_1, return_value_2)
   movzx   eax, byte ptr [esp+8]  ; retrieve single return value
   xor     eax, 1                 ; negate result
   mov     [esp+24h], al          ; store result below arguments to test
   add     esp, 18h               ; close stack frame
   retn                           ; return

我试图给出sub1更多参数:

int __cdecl sub1(int, int, int, int, int, int)

但不幸的是,反编译代码中并未反映出该函数的最后两个“参数”实际上是传递给 的返回值sub2

int __cdecl test(int a1, int a2)
{
  int v2; // ST10_4
  int v3; // ST14_4
  unsigned __int8 v4; // ST08_1
  int esp_08; // [esp+8h] [ebp-10h]
  int esp_0C; // [esp+Ch] [ebp-Ch]
  int esp_10; // [esp+10h] [ebp-8h]
  int esp_14; // [esp+14h] [ebp-4h]
  void *retaddr; // [esp+18h] [ebp+0h]

  sub1(a1, a2, esp_08, esp_0C, esp_10, esp_14);
  sub2(v2, v3);
  return v4 ^ 1;
}

为了尝试使用这种高级调用约定语法,我还创建了一个包含两个 32 位整数字段的结构,并声明sub1如下:

struc_1 __usercall sub1@<0:^8.4,4:^12.4>(int, int)

我尝试了几种变体,不同的偏移量,也只返回本机类型,但 IDA 7.1 总是崩溃并显示以下消息:

哎呀!发生内部错误 1004。进一步的工作是不可能的,IDA 将关闭。

有什么好的方法或解决方法可以让 IDA 识别这个调用约定并在反编译代码中准确地反映它?

再现 MWE

要生成上述示例,您可以使用以下test.go源文件:

package main
import "os"

func sub2(x int, y int) bool { return x == y }
func sub1(x int, y int) (int, int) { return y,x }
func test(x int, y int) bool { return ! sub2(sub1(x,y)) }

func main() {
  test(1,2)
  os.Exit(0)
}

您可以按如下方式编译(禁用优化很重要):

set GOARCH=386
go build -gcflags=-N -gcflags=-l test.go

在 IDA 中,测试函数应该自动重命名为main_test.

1个回答

我一直在与 IDA 支持人员保持联系,但不幸的是,现在似乎没有什么好方法可以做到这一点。在许多情况下,IDA 假定函数的返回值必须存储在寄存器中。这也是试图用分散的论点推翻这个固有假设时崩溃的原因。