IDA Pro 使用
常用快捷键
Navigate
-
G : 跳转地址
-
Esc:往回跳转
-
X/Ctrl+X: 查看谁调用了当前选中的函数、变量
-
书签
- Alt+M: 添加书签
- Ctrl+M: 查看书签
-
Alt+T:搜索内容
-
Ctrl+T:继续查找下一个
-
Ctrl+L:跳转到指定名称处
-
Tab,F5:反汇编
-
空格:切换视图(文本视图,图形视图)
注释,变量名
-
冒号:单行注释
-
封号:基本等同于常规注释,但有一个小区别:它们会在任何引用原始注释位置的地方重复出现。
-
insert:行前注释
-
N:增加、修改变量名
数据转换
-
Edit->operand type
-
Q: 十六进制
-
H:十进制
-
R: 字符
-
A:普通字符串
-
Alt+D: 选择 Q word 或者 TByte
-
Alt+A:字符串(C 或者 pascal 格式)
-
选中,按 C,标记为 code
-
选中,按 U,取消
偏移(Offset)
-
O,Ctrl+O:改为偏移 label
-
Ctrl+R:相对偏移
-
M:改为符号常量
新建结构体
- shift+f1,按 insert,新建 C 语言结构体,双击后修改各个 element 的数据格式(sync, yes)
- H:十进制
- Q:十六进制
- A:字符串
-
T:把偏移改为结构体
-
Alt+Q:修改数据类型为结构体类型
数组
-
星号,新建数组
-
Alt+L,批量选择(选择后,再按星号,改为数组)
函数
-
P:创建函数
-
Y:修改函数 header
插件
IDA 常见命名意义
IDA 经常会自动生成假名字。他们用于表示子函数,程序地址和数据。根据不同的类型和值假名字有不同前缀
sub * 指令和子函数起点
locret* 返回指令
loc * 指令
off* 数据,包含偏移量
seg * 数据,包含段地址值
asc* 数据,ASCII 字符串
byte * 数据,字节(或字节数组)
word* 数据,16 位数据(或字数组)
dword * 数据,32 位数据(或双字数组)
qword* 数据,64 位数据(或 4 字数组)
flt * 浮点数据,32 位(或浮点数组)
dbl* 浮点数,64 位(或双精度数组)
tbyte * 浮点数,80 位(或扩展精度浮点数)
stru* 结构体 (或结构体数组)
algn * 对齐指示
unk* 未处理字节
IDA 中有常见的说明符号,如 db、dw、dd 分别代表了 1 个字节、2 个字节、4 个字节
IDA 反编译代码中的各种 call
在 IDA 和其他反汇编、编译器工具中,__stdcall、__thiscall、__cdecl 等调用约定 (calling conventions) 定义了函数调用时参数的传递方式、调用者和被调用者的职责等。以下是常见的调用约定及其含义:
__cdecl
-
含义: C declaration
-
参数传递: 参数从右到左推入栈中。
-
返回值传递: 通过寄存器
EAX返回。 -
栈清理: 调用者负责清理栈。
-
用途: 标准的 C 函数调用约定,多用于 C 语言编译的函数。
__stdcall
-
含义: Standard call
-
参数传递: 参数从右到左推入栈中。
-
返回值传递: 通过寄存器
EAX返回。 -
栈清理: 被调用者负责清理栈。
-
用途: 多用于 WinAPI 函数调用。
__fastcall
-
含义: Fast call
-
参数传递: 前两个参数通过寄存器 (
ECX和EDX) 传递,剩余参数从右到左推入栈中。 -
返回值传递: 通过寄存器
EAX返回。 -
栈清理: 被调用者负责清理栈。
-
用途: 用于需要高效调用的函数。
__thiscall
-
含义: This call
-
参数传递:
this指针通过寄存器ECX传递,其他参数从右到左推入栈中。 -
返回值传递: 通过寄存器
EAX返回。 -
栈清理: 被调用者负责清理栈。
-
用途: 用于 C++ 类的成员函数调用。
__vectorcall
-
含义: Vector call
-
参数传递: 首先通过寄存器(包括 SIMD 寄存器)传递,剩余参数通过栈传递。
-
返回值传递: 通过寄存器返回。
-
栈清理: 被调用者负责清理栈。
-
用途: 用于需要高效传递 SIMD 类型参数的函数。
__syscall
-
含义: System call
-
参数传递: 根据特定操作系统的系统调用约定传递参数。
-
返回值传递: 通过寄存器返回。
-
栈清理: 操作系统内核负责栈清理。
-
用途: 用于系统调用。
__usercall
__usercall 是一个用于定义自定义调用约定的标识符。这意味着函数的调用和参数传递方式是由用户定义的,而不是由标准调用约定(如 __stdcall、__cdecl 等)决定的。使用 __usercall 可以帮助分析器识别参数和返回值的位置。
例如,假设你有一个函数,它接受两个参数并返回一个值,但这些参数和返回值的位置与标准调用约定不同:
int __usercall my_function<eax>(int param1<ecx>, int param2<edx>);
在这个例子中,my_function 接受两个参数 param1 和 param2,并且这些参数分别通过 ecx 和 edx 寄存器传递,返回值通过 eax 寄存器返回。
__userpurge
__userpurge 与 __usercall 类似,但它还包括调用方负责清理堆栈的约定。这通常用于类似于 __stdcall 的约定,其中被调用者负责清理堆栈,但在 __userpurge 中,由调用者清理堆栈。
例如,假设你有一个函数,它接受两个参数,参数传递方式是自定义的,并且调用方负责清理堆栈:
int __userpurge my_function<eax>(int param1<ecx>, int param2<edx>);
在这个例子中,函数的参数通过寄存器传递,并且调用者在调用函数后负责清理堆栈。
__usercall 与 __userpurge 区别
-
__usercall允许定义自定义的调用约定,但不指定堆栈清理责任。 -
__userpurge也是定义自定义的调用约定,但明确指定调用者负责堆栈清理。
如何在 IDA 中识别调用约定
在 IDA 中,你可以通过反汇编代码和调用图识别调用约定。一般来说,观察函数的 prologue 和 epilogue 可以帮助你确定调用约定。例如:
-
__cdecl: 函数结束时有类似add esp, N的栈清理代码。 -
__stdcall: 函数结束时没有add esp, N,栈指针由ret N指令自动调整。 -
__thiscall: 函数的第一个参数是this指针,传递通过ECX寄存器。