tl; dr:我正在反转为 Windows 编译的 GoLang 1.10 可执行文件。我试图让 IDA 正确识别调用约定。
详细信息:我正在查看使用以下(非标准)调用约定的 x86 可执行文件。
- 调用者在堆栈上为可能的多个返回值创建空间,即
4*return_count
从ESP
. - 调用者将被调用者的参数压入堆栈,调用被调用者
- callee 为局部变量创建一个堆栈帧
- 被调用者确实有效
- 被调用者恢复
ESP
到第 3 步之前的一样 - 被调用者将返回值存储在其参数下方的堆栈中
- 呼叫返回
调用者现在有返回值和参数仍然在本地堆栈帧中,后者在前者之上,并进行清理。这是一个 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
.