函数调用之mov edi, edi

很多函数前面几个指令都是下面这样:
mov     edi, edi
push    ebp
mov     ebp, esp

第一行指令,是不是没用呢?


你还会在上面发现5个没用的nop

这样的做法是方便做hook,比如你要挂钩这个函数,你可以用一个2字节的短jmp(eb xx)转移到那5个nop那里,然后在那5个nop那里做一个大的jmp(e9 xx xx xx xx) 跳到任意地址完成你的jmp

至于为什么是mov edi,edi而不是2个nop,因为mov edi,edi比2个nop执行起来快

-------------------------------------------
补充一下,这样做还可以防止多线程引起的意外,比如是2个nop或者不用这种方法直接修改5个字节来hook的话,有可能引起这样的问题。在你修改了其中一部分,而剩下的那部分未完成的情况下,另一个线程执行到了这里,就会因为错误的代码导致程序崩溃或者产生预期之外的结果。

### Ubuntu 环境下函数调用的相关指令及示例 在 Ubuntu 环境中,函数调用通常涉及高级语言(如 C/C++)、低级汇编以及跨语言调用等多种场景。以下是几种常见情况及其对应的指令和示例。 #### 1. 高级语言中的函数调用 在 C 或 C++ 编程中,函数调用是最基本的操作之一。以下是一个简单的 C++ 函数调用示例: ```cpp #include <iostream> void greet(const char* name) { std::cout << "Hello, " << name << "!" << std::endl; } int main() { greet("World"); // 函数调用 return 0; } ``` 上述代码展示了如何定义并调用一个名为 `greet` 的简单函数[^5]。通过命令行可以将其编译并运行: ```bash g++ example.cpp -o example_program && ./example_program ``` #### 2. 使用汇编分析函数调用过程 对于更深层次的理解,可以通过反汇编查看函数调用的具体实现细节。例如,在 x86_64 架构下,GCC 可以将 C++ 文件转换为汇编代码以便观察栈帧的构建方式[^4]。 假设有一个简单的 C++ 文件 `func_call.cpp` 如下所示: ```cpp int add(int a, int b) { return a + b; } int main() { return add(3, 4); } ``` 使用 GCC 将其转为汇编代码: ```bash g++ -S func_call.cpp -masm=intel ``` 生成的汇编文件可能类似于以下内容: ```asm add: mov eax, edi ; 将第一个参数 (a) 移动到寄存器 EAX add eax, esi ; 对第二个参数 (b) 进行加法操作 ret ; 返回结果 main: sub rsp, 8 ; 调整堆栈指针 mov edi, 3 ; 设置第一个参数为 3 mov esi, 4 ; 设置第二个参数为 4 call add ; 调用 add 函数 xor ecx, ecx ; 清零 ECX 寄存器 add rsp, 8 ; 恢复堆栈指针 ret ; 结束程序 ``` 这段汇编代码清晰地显示了函数调用过程中使用的寄存器传递参数的方式。 #### 3. Linux 系统调用中的函数调用 Linux 提供了一些核心系统调用接口,比如 `fork()` 和 `exec()` 家族函数。这些函数允许进程创建子进程或者替换当前进程映像。以下是一段演示 `fork()` 和 `execl()` 功能的小型程序[^2]: ```c #include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(void) { pid_t child_pid; /* 创建一个新的子进程 */ child_pid = fork(); if (child_pid == 0) { // 子进程中执行的部分 execl("/bin/ls", "ls", "-l", NULL); // 替换当前进程图像 perror("execl failed"); _exit(EXIT_FAILURE); } else if (child_pid > 0) { // 父进程中执行的部分 printf("Child process created with PID %d\n", child_pid); } return 0; } ``` 该程序利用 `fork()` 来派生新的子进程,并通过 `execl()` 加载另一个可执行文件 `/bin/ls` 列出目录内容。 #### 4. 跨语言函数调用 如果需要从一种语言调用另一种语言的功能,则可以借助工具库完成这一目标。例如,Pybind11 支持在 C++ 中嵌入 Python 解释器从而调用 Python 方法或类实例化对象[^3]。 下面给出一个基于 Pybind11 实现的例子: ```cpp #define PY_SSIZE_T_CLEAN #include <pybind11/pybind11.h> namespace py = pybind11; // 声明要绑定至Python环境的一个C++函数 double multiply(double value, double factor) { return value * factor; } PYBIND11_MODULE(example_module, m) { m.def("multiply", &multiply, "A function which multiplies two numbers."); } ``` 接着按照官方文档说明安装依赖项之后即可测试模块加载效果: ```bash python3 -m pip install . python3 -c 'import example_module; print(example_module.multiply(7, 9))' ``` 以上介绍了不同层次上的函数调用机制及相关指令应用案例。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值