它看起来像这样:
.text:0000C0DE int __cdecl func(char* a1, int a2, int a3, int a4, int a5, int a6, char* a7)
大量外部参照(超过200个!)
是否有任何方法可以将对列表的每次调用的
a1
和a7
参数转储? /> 我知道我可以使用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