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

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


调用方在堆栈上为可能的多个返回值创建空间,即从4*return_count中减去ESP。为局部变量创建一个堆栈框架
callee可以工作
callee将ESP恢复为与步骤3之前的相同
callee将返回值存储在其参数下方的堆栈中br />
现在,调用者将返回值和参数保留在本地堆栈框架中,后者位于前者之上,并进行清理。这是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将关闭。 br再现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)
}


在IDA中,测试功能应自动重命名为main_test

评论

由于您似乎拥有最新的IDA,并且崩溃了,因此您可以与支持人员联系以解决此问题。

一个好主意,谢谢。我有点害怕错误地使用语法,但是我会问他们有关问题。

如果这样声明,它会起作用吗? int __cdecl test(int a1,int a2,int * return1,int * return2)

亲爱的@AntonKukoba,感谢您的提议,可惜它不起作用。在这种情况下,它只是认为参数是int指针而不是int,没有其​​他改变。

你能给我一个样品,我想尝试

#1 楼

我一直在与IDA保持联系,但不幸的是,目前看来,这并不是一个好方法。在许多情况下,IDA假定函数的返回值必须存储在寄存器中。这也是当试图用分散的参数推翻这种固有假设时崩溃的原因。