我正在使用IDA Pro 6.5,但得到了函数及其参数的偏移量。
它看起来像这样:

.text:0000C0DE        int __cdecl func(char* a1, int a2, int a3, int a4, int a5, int a6, char* a7)


大量外部参照(超过200个!)

是否有任何方法可以将对列表的每次调用的a1a7参数转储? />
我知道我可以使用IDC遍历外部参照列表,但是一旦执行了call func指令,就不知道如何从堆栈中获取参数。

#1 楼

如果您有Hex-Rays Decompiler,我建议对整个二进制文件进行反编译,然后重新编译func(的反编译结果。发展。

#2 楼

手动方法:您可以在每次调用后写出相关的参数值(和其他相关信息)作为注释。这样,该信息就会显示在交叉引用列表中。到达调用指令后,您可以访问模拟的迷你堆栈和模拟的寄存器,以查看它们是否具有恒定(已知)值,并相应地生成上述注释。

#3 楼

问题在于这些参数可能并且在大多数情况下将是计算得出的,而不是常量,因此您实际上并没有一条可以为您提供参数的指令。考虑以下测试程序:

#include <stdio.h>

int a, b, c, d, e, f, g, h, i, j;

void func(int a1, int a2, int a3, int a4, int a5) {
        printf("%d %d %d %d %d\n", a1, a2, a3, a4, a5);
}

int main(void) {
        int x, y;

        x=g+c;
        y=d*e+i;

        func(1, a, b+c, 7, j);
        printf("blarfl\n");
        func(e, j*c, 3, x, y);
}


编译(带有-O0且不带符号)并将其加载到IDA之后,主要功能如下:
push    rbp
mov     rbp, rsp
sub     rsp, 10h
mov     edx, cs:g
mov     eax, cs:c
add     eax, edx
mov     [rbp+var_8], eax
mov     edx, cs:d
mov     eax, cs:e
imul    edx, eax
mov     eax, cs:i
add     eax, edx
mov     [rbp+var_4], eax
mov     ecx, cs:j
mov     edx, cs:b
mov     eax, cs:c
add     edx, eax
mov     eax, cs:a
mov     r8d, ecx
mov     ecx, 7
mov     esi, eax
mov     edi, 1
call    func
mov     edi, offset s   ; "blarfl"
call    _puts
mov     edx, cs:j
mov     eax, cs:c
mov     esi, edx
imul    esi, eax
mov     eax, cs:e
mov     ecx, [rbp+var_4]
mov     edx, [rbp+var_8]
mov     r8d, ecx
mov     ecx, edx
mov     edx, 3
mov     edi, eax
call    func
leave


定义函数后

; __int64 __cdecl func(int XYZ1, int XYZ2, int XYZ3, int XYZ4, int XYZ5)


main的拆卸变成

push    rbp
mov     rbp, rsp
sub     rsp, 10h
mov     edx, cs:g
mov     eax, cs:c
add     eax, edx
mov     [rbp+XYZ4], eax
mov     edx, cs:d
mov     eax, cs:e
imul    edx, eax
mov     eax, cs:i
add     eax, edx
mov     [rbp+XYZ5], eax
mov     ecx, cs:j
mov     edx, cs:b
mov     eax, cs:c
add     edx, eax        ; XYZ3
mov     eax, cs:a
mov     r8d, ecx        ; XYZ5
mov     ecx, 7          ; XYZ4
mov     esi, eax        ; XYZ2
mov     edi, 1          ; XYZ1
call    func
mov     edi, offset s   ; "blarfl"
call    _puts
mov     edx, cs:j
mov     eax, cs:c
mov     esi, edx
imul    esi, eax        ; XYZ2
mov     eax, cs:e
mov     ecx, [rbp+XYZ5]
mov     edx, [rbp+XYZ4]
mov     r8d, ecx        ; XYZ5
mov     ecx, edx        ; XYZ4
mov     edx, 3          ; XYZ3
mov     edi, eax        ; XYZ1
call    func
leave
retn


,因此您会看到ida在指定参数的位置自动生成注释。您可以使用PrevNotTail从每个外部参照向后扫描10或20条指令,并检查注释中的参数字符串。如果您使参数名称足够唯一,这应该可以很好地识别指令(这就是为什么我使用XYZ1而不是a1的原因)。

当然,
>
并不会真正帮助您。但是,如果func的大多数参数都是常量,则结果可能会更好。

评论


就此而言,PrevNotTail()和PrevHead()的问题在于它们不了解分支。他们以线性方式盲目向后走。为了快速浏览它们,它们可以很好地工作,但是要获得准确,可靠的结果,就需要分析整个基本块网络的某个功能。

– DarthGizka
2015年1月5日,12:48



这个答案不是专门针对64位吗?大多数32位程序使用__stdcall和__cdecl调用约定进行编译,并且参数很可能被压入堆栈。

–RoraZ
2015年1月5日13:33

@raz:是的,但是Ida将以相同的方式对参数进行注释。当然,如果您要查找注释,则主要会看到push 指令,因此这些指令甚至比上面的示例更不可用。

–贡特拉姆·布洛姆(Guntram Blohm)
2015年1月5日14:19

@DarthGizka始终正确地执行此操作可能是不可能的,相当于停止问题。诸如func(1,(a
–贡特拉姆·布洛姆(Guntram Blohm)
2015年1月5日14:27

“准确”不一定意味着“完美”。在许多情况下,“无能为力”是足够准确的答案。至少应通过检查外部参照来增强向后扫描,以识别何时跨越了基本块边界,在这种情况下,结果应标记为可疑(需要手动检查)。前提是错误的答案有时会比根本没有答案更糟糕。而且,在少量指令的有限模拟(在用于特定目的的特殊IDA脚本中)和完整的通用反编译器之间,范围非常广。

– DarthGizka
2015年1月5日14:55