CTF-RE 从0到N:汇编层函数调用
windows
在 Windows 平台上的汇编语言中,调用函数的方式通常遵循特定的调用约定(Calling Convention)。最常见的调用约定包括:
- cdecl: C 默认调用约定,调用者清理堆栈。
- stdcall: Windows API 默认调用约定,被调用者清理堆栈。
- fastcall: 使用寄存器传递前两个参数,其余参数通过堆栈传递。
- x64: 在 64 位系统上,使用寄存器传递前四个参数,其余参数通过堆栈传递。
1. cdecl 调用约定
在 cdecl 调用约定中,函数参数从右到左推入堆栈,函数返回后由调用者清理堆栈。
; 函数原型: int add(int a, int b);
push dword ptr [b] ; 将参数 b 推入堆栈
push dword ptr [a] ; 将参数 a 推入堆栈
call add ; 调用函数 add
add esp, 8 ; 调用者清理堆栈 (2 * 4 bytes)
2. stdcall 调用约定
在 stdcall 调用约定中,函数参数同样从右到左推入堆栈,但由被调用者清理堆栈。
; 函数原型: int add(int a, int b);
push dword ptr [b] ; 将参数 b 推入堆栈
push dword ptr [a] ; 将参数 a 推入堆栈
call add ; 调用函数 add
; 不需要清理堆栈,因为被调用者会清理堆栈
3. fastcall 调用约定
在 fastcall 调用约定中,前两个参数通过寄存器传递(ECX 和 EDX),其余参数通过堆栈传递。
; 函数原型: int add(int a, int b);
mov ecx, dword ptr [a] ; 将参数 a 放入 ECX 寄存器
mov edx, dword ptr [b] ; 将参数 b 放入 EDX 寄存器
call add ; 调用函数 add
; 不需要清理堆栈,因为被调用者会清理寄存器
4. x64 调用约定
在 64 位系统的 Windows 上,前四个参数通过寄存器传递,后续参数通过堆栈传递。惯例使用的寄存器是:RCX、RDX、R8 和 R9。
; 函数原型: int add(int a, int b, int c, int d);
mov ecx, dword ptr [a] ; 将参数 a 放入 RCX 寄存器
mov edx, dword ptr [b] ; 将参数 b 放入 RDX 寄存器
mov r8d, dword ptr [c] ; 将参数 c 放入 R8 寄存器
mov r9d, dword ptr [d] ; 将参数 d 放入 R9 寄存器
call add ; 调用函数 add
; 返回值在 RAX 寄存器中
具体例子
假设我们有一个简单的 C 函数:
int add(int a, int b) {return a + b;
}
使用 cdecl 调用约定的实现:
section .dataa dd 5b dd 10section .textextern addglobal _start_start:push dword [b] ; 推入参数 bpush dword [a] ; 推入参数 acall add ; 调用函数 addadd esp, 8 ; 清理堆栈mov ebx, eax ; 将返回值存入 EBX(假设退出码)mov eax, 1 ; 系统调用号 (sys_exit)int 0x80 ; 调用内核
使用 x64 调用约定的实现:
section .dataa dd 5b dd 10section .textextern addglobal _start_start:mov ecx, dword [a] ; 将参数 a 放入 RCXmov edx, dword [b] ; 将参数 b 放入 RDXcall add ; 调用函数 add; 返回值在 RAX 寄存器中mov edi, eax ; 将返回值存入 EDI(假设退出码)mov eax, 60 ; 系统调用号 (sys_exit)syscall ; 调用内核
linux
基本
在汇编语言中,调用函数的过程涉及多个步骤,包括参数传递、调用指令、堆栈管理和返回值处理。下面详细解释在x86架构的汇编语言中,调用函数的过程,特别是基于C语言的函数调用约定。
函数调用约定
函数调用约定定义了如何在汇编中传递参数、管理堆栈以及返回值。不同系统和编译器有不同的调用约定,如cdecl
、stdcall
、fastcall
等。我们以最常见的cdecl
调用约定为例。
函数调用的主要步骤
-
参数压栈:
- 在调用函数前,参数按照从右到左的顺序压入栈中。
- 这意味着第一个参数最后压栈,最后一个参数最先压栈。
-
调用指令
call
:- 使用
call
指令调用函数。call
指令会做两件事:- 将下一条指令(即函数调用后紧跟的指令地址)压入栈中,作为返回地址。
- 跳转到被调用函数的地址。
- 使用
-
函数内部处理:
- 进入函数后,通常会创建一个新的栈帧,通过
push ebp
保存调用者的栈帧指针,然后mov ebp, esp
设置当前栈帧指针。 - 函数从栈中读取参数,并执行操作。
- 进入函数后,通常会创建一个新的栈帧,通过
-
返回值:
- 返回值通常存储在
eax
寄存器中。
- 返回值通常存储在
-
清理栈:
- 函数返回后,调用者负责清理栈上压入的参数。对于
cdecl
调用约定,调用者负责清理栈。 - 对于
stdcall
调用约定,函数自己清理参数。
- 函数返回后,调用者负责清理栈上压入的参数。对于
汇编代码中的调用过程
我们通过一个具体的汇编调用过程来解释:
push arg2 ; 第二个参数压栈
push arg1 ; 第一个参数压栈
call function_name ; 调用函数
add esp, 8 ; 调用者清理栈上的参数 (两个参数,每个参数4字节,总共8字节)
具体例子
假设我们有一个简单的 C 函数:
int add(int a, int b) {return a + b;
}int main() {int result = add(3, 4);return 0;
}
对应的汇编代码(简化版)可能如下:
; 在 main 函数中:
push 4 ; 第二个参数 b = 4 压栈
push 3 ; 第一个参数 a = 3 压栈
call add ; 调用 add 函数
add esp, 8 ; 清理栈(2个参数,每个4字节)
mov [ebp-4], eax ; 将结果存入局部变量 result; 在 add 函数中:
add procpush ebp ; 保存调用者的栈帧mov ebp, esp ; 设置当前栈帧mov eax, [ebp+8] ; 加载第一个参数 amov ecx, [ebp+12] ; 加载第二个参数 badd eax, ecx ; 执行加法pop ebp ; 恢复调用者的栈帧ret ; 返回,结果在 eax 中
add endp
详细步骤说明
-
参数压栈:
push 4
和push 3
将两个参数4
和3
压入栈,按照从右到左的顺序。
-
调用函数:
call add
调用add
函数。call
指令会将下一条指令的地址压入栈中,作为返回地址,并跳转到add
函数的地址执行。
-
函数栈帧设置:
- 在
add
函数中,push ebp
将调用者的栈帧指针保存到栈中。 mov ebp, esp
设置当前函数的栈帧指针,使得栈指针esp
成为当前函数的栈帧基准。mov eax, [ebp+8]
加载第一个参数a
,它位于栈中距离ebp
8 个字节的位置(4字节是保存的ebp
,再往上是返回地址)。mov ecx, [ebp+12]
加载第二个参数b
,它位于ebp+12
位置。
- 在
-
执行加法:
add eax, ecx
将参数a
和b
相加,结果存入eax
寄存器。
-
函数返回:
pop ebp
恢复调用者的栈帧。ret
从函数返回,eax
中的值作为返回值传递回调用者。
-
调用者清理栈:
add esp, 8
调用者负责清理栈上压入的两个参数,共8个字节。
总结
函数调用在汇编中的实现依赖于:
- 参数压栈:参数按从右到左顺序压入栈中。
- 调用函数:
call
指令将返回地址压栈并跳转到函数地址。 - 栈帧管理:函数通常会创建自己的栈帧,使用
ebp
寄存器管理。 - 返回值传递:返回值通常存储在
eax
寄存器中。 - 清理栈:根据调用约定,调用者或被调用者负责清理栈上的参数。
通过这个过程,我们可以理解函数在低级汇编语言中的调用机制如何运作。
拥有可变参数的函数
对于可变参数函数(例如 C 语言中的 printf
),在汇编中调用可变参数函数的过程与普通函数调用稍有不同,因为需要处理不确定数量的参数。stdarg.h
中的宏(例如 va_start
, va_arg
, va_end
)用于处理这些可变参数。
我们以 int a(char *Format, ...)
这种可变参数函数为例,来说明汇编中如何处理可变参数函数。
可变参数的处理
当一个函数定义为可变参数(即包含 ...
),在汇编中,通常需要根据栈的布局和函数的调用约定手动管理这些参数。x86 调用约定(例如 cdecl
)中,可变参数会和固定参数一起压入栈中。
以 int a(char *Format, ...)
为例,它的汇编调用流程与普通函数类似,但需要特别处理可变参数。
函数调用过程
参数压栈
调用函数时,已知的参数(如 Format
)按照普通参数顺序压栈,之后才压入可变参数。可变参数通过栈传递。
例如,假设我们调用如下的 C 代码:
int a(char *Format, ...) {// 实现略
}int main() {a("%d %s", 42, "example");
}
在汇编中,参数传递的顺序如下(以 cdecl
调用约定为例):
push offset string_example ; 可变参数 "example"
push 42 ; 可变参数 42
push offset format_string ; 固定参数 Format
call a ; 调用可变参数函数
add esp, 12 ; 清理栈(3个参数,每个4字节,共12字节)
函数内部处理
函数在内部使用 va_start
等宏来访问可变参数,这在汇编中是通过对栈指针的操作来完成的。
以下是可能的汇编流程:
-
设置栈帧:
- 和普通函数一样,先保存调用者的栈帧,设置当前栈帧:
push ebp mov ebp, esp
- 和普通函数一样,先保存调用者的栈帧,设置当前栈帧:
-
访问固定参数:
- 在栈中,
Format
是第一个参数,位于[ebp + 8]
处。mov eax, [ebp + 8] ; 获取 Format 参数
- 在栈中,
-
初始化可变参数列表:
- 使用
lea
(Load Effective Address)指令计算可变参数列表的起始地址。通常第一个可变参数位于[ebp + 12]
,即在第一个固定参数之后:lea ecx, [ebp + 12] ; 计算可变参数列表的地址
- 使用
-
调用处理函数:
- 将
Format
和可变参数列表的地址传递给实际处理函数,类似于vprintf
这样的函数。push ecx ; 压入可变参数地址 push eax ; 压入 Format 字符串 call vprintf ; 调用变参处理函数
- 将
-
清理栈并返回:
- 函数执行完毕后,需要恢复栈指针并返回。
pop ebp ret
- 函数执行完毕后,需要恢复栈指针并返回。
完整的汇编示例
假设我们要实现一个简单的 int a(char *Format, ...)
函数,它只将格式化字符串和可变参数传递给 vprintf
。以下是它的汇编实现的简化版本:
a procpush ebp ; 保存调用者的栈帧mov ebp, esp ; 设置当前栈帧mov eax, [ebp + 8] ; 获取第一个参数 Format(位于栈中第一个固定参数位置)lea ecx, [ebp + 12] ; 获取可变参数列表的地址push ecx ; 将可变参数地址压栈push eax ; 将 Format 压栈call vprintf ; 调用 vprintf 函数pop ebp ; 恢复栈帧ret ; 返回a endp
对应的 C 实现
对应的 C 实现如下所示,使用 va_start
、va_end
等标准宏来处理可变参数:
#include <stdio.h>
#include <stdarg.h>int a(const char *Format, ...) {va_list args;va_start(args, Format); // 初始化可变参数列表int result = vprintf(Format, args); // 将 Format 和可变参数传递给 vprintfva_end(args); // 清理可变参数列表return result;
}int main() {a("%d %s\n", 42, "example");return 0;
}
总结
在可变参数函数的汇编实现中:
-
参数传递:
- 固定参数(如
Format
)和可变参数按顺序压入栈中。 - 可变参数通过栈传递,函数内部通过栈指针访问这些参数。
- 固定参数(如
-
处理可变参数:
- 函数通过计算堆栈中的地址来获取可变参数列表的起始位置(通常通过
[ebp + offset]
来计算)。
- 函数通过计算堆栈中的地址来获取可变参数列表的起始位置(通常通过
-
调用内部的处理函数:
- 使用汇编中的
lea
指令计算可变参数列表的地址,并传递给诸如vprintf
这样的变参处理函数。
- 使用汇编中的
相关文章:
CTF-RE 从0到N:汇编层函数调用
windows 在 Windows 平台上的汇编语言中,调用函数的方式通常遵循特定的调用约定(Calling Convention)。最常见的调用约定包括: cdecl: C 默认调用约定,调用者清理堆栈。stdcall: Windows API 默认调用约定࿰…...
雷池社区版compose配置文件解析-mgt
在现代网络安全中,选择合适的 Web 应用防火墙至关重要。雷池(SafeLine)社区版免费切好用。为网站提供全面的保护,帮助网站抵御各种网络攻击。 compose.yml 文件是 Docker Compose 的核心文件,用于定义和管理多个 Dock…...
无人机避障——4D毫米波雷达Octomap从点云建立三维栅格地图
Octomap安装 sudo apt-get install ros-melodic-octomap-ros sudo apt-get install ros-melodic-octomap-msgs sudo apt-get install ros-melodic-octomap-server sudo apt-get install ros-melodic-octomap-rviz-plugins # map_server安装 sudo apt-get install ros-melodic-…...
Python(数据结构2)
常见数据结构 队列 队列(Queue),它是一种运算受限的线性表,先进先出(FIFO First In First Out) Python标准库中的queue模块提供了多种队列实现,包括普通队列、双端队列、优先队列等。 1 普通队列 queue.Queue 是 Python 标准库 queue 模块中的一个类…...
深入解析HTTP与HTTPS的区别及实现原理
文章目录 引言HTTP协议基础HTTP响应 HTTPS协议SSL/TLS协议 总结参考资料 引言 HTTP(HyperText Transfer Protocol)超文本传输协议是用于从Web服务器传输超文本到本地浏览器的主要协议。随着网络安全意识的提高,HTTPS(HTTP Secure…...
Java IO 模型
I/O 何为 I/O? I/O(Input/Output) 即输入/输出 。 我们先从计算机结构的角度来解读一下 I/O。 根据冯.诺依曼结构,计算机结构分为 5 大部分:运算器、控制器、存储器、输入设备、输出设备。 输入设备(比…...
安装双系统后ubuntu无法联网(没有wifi标识)网卡驱动为RTL8852BE
安装双系统后ubuntu没有办法联网,(本篇博客适用的版本为ubuntu20.04)且针对情况为无线网卡驱动未安装的情况 此时没有网络,可以使用手机数据线连接,使用USB共享网络便可解决无法下载的问题。 打开终端使用命令lshw -C …...
Sqoop的安装配置及使用
Sqoop安装前需要检查之前是否安装了Tez,否则会产生版本或依赖冲突,我们需要移除tez-site.xml,并将hadoop中的mapred-site.xml配置文件中的mapreduce驱动改回成yarn,然后分发到其他节点,hive里面配置的tez也要移除,然后…...
R语言机器学习算法实战系列(十三)随机森林生存分析构建预后模型 (Random Survival Forest)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程加载R包案例数据数据预处理数据描述构建randomForestSRC模型评估模型C-indexBrier score特征重要性构建新的随机森林生存模型风险打分高低风险分组的生存分析时间依赖的ROC(Ti…...
三款计算服务器配置→如何选择科学计算服务器?
科学计算在众多领域都扮演着关键角色,无论是基础科学研究还是实际工程应用,强大的计算能力都是不可或缺的。而选择一台合适的科学计算服务器,对于确保科研和工作的顺利进行至关重要。 首先,明确自身需求是重中之重。要仔细考虑计算…...
Oracle 19c RAC删除多余的PDB的方式
文章目录 一、删除PDB并删除数据文件二、删除PDB并保留数据文件三、插拔PDB 一、删除PDB并删除数据文件 所删除的pdb必须是mount的状态才可以删除: #1、关闭pdb alter pluggable database pdb_name close immediate instancesall; #2、删除pdb以及数据文件 drop p…...
什么是云渲染?云渲染有什么用?一篇看懂云渲染意思
你知道云渲染是怎么回事吗? 其实就是把3D模型变成2D图像的过程,只不过这个过程是在云端完成的。我们在本地啥都不用做,只需要等结果就行。 现在云渲染主要有两种类型:一种是物理机房云渲染,另一种是服务器机房云渲染。…...
MATLAB中 exist函数用法
目录 语法 说明 示例 检查工作区变量是否存在 检查文件夹是否存在 检查 MATLAB 函数是否为内置函数 exist函数的功能是检查变量、脚本、函数、文件夹或类的存在情况。 语法 exist name exist name searchType A exist(___) 说明 exist name 以数字形式返回 name 的类…...
在银河麒麟系统中Qt连接达梦数据库
解决在银河麒麟系统中使用Qt连接达梦数据库提示:project Error library odbc is not defined问题 一、编译ODBC 下载解压unixODBC(http://www.unixodbc.org/unixODBC-2.3.1.tar.gz) 打开终端,切换到unixODBC-2.3.1目录下&#x…...
nodejs 服务器实现负载均衡
server.js const express require(express); const { createProxyMiddleware } require(http-proxy-middleware); const axios require(axios);const app express();// 定义后端服务列表 const services [{ target: http://localhost:3001 },{ target: http://localhost:…...
今日总结10.29
常见序列化协议有哪些 序列化(serialization)是将对象序列化为二进制形式(字节数组),一般也将序列化称为编码(Encode),主要用于网络传输、数据持久化等。常见的序列化协议包括以下几…...
使用 FastGPT 工作流实现 AI 赛博算卦,一键生成卦象图
最近那个男人写的汉语新解火遍了全网,那个男人叫李继刚,国内玩 AI 的同学如果不知道这个名字,可以去面壁思过了。 这个汉语新解的神奇之处就在于它只是一段几百字的提示词,效果却顶得上几千行代码写出来的应用程序。 这段提示词…...
vue3+ts实时播放视频,视频分屏
使用vue3以及播放视频组件Jessibuca Jessibuca地址 使用循环个数来实现分屏 效果图,四屏 九屏 dom代码 <div class"icon"><div class"icon-box"><span class"text">分屏:</span><el-icon …...
【网页设计】学成在线案例
Demo 典型的企业级网站,目的是为了整体感知企业级网站的布局流程,复习以前知识。 集合代码见文章最后。 5.1 准备素材和工具 学成在线 PSD 源文件。开发工具 PS(切图) sublime(代码) chrome࿰…...
一篇文章总结 SQL 基础知识点
1. 官方文档 MySQL:https://dev.mysql.com/doc/refman/8.4/en/ SQL Server:What is SQL Server? - SQL Server | Microsoft Learn Oracle:https://docs.oracle.com/en/database/oracle/oracle-database/23/lnpls/loe.html 2. 术语 SQL S…...
vue Element U 解决表格数据不更新问题
最近在使用 Vue 和 Element UI 开发后台管理系统时,操作表单数据重新请求表格接口后遇到表格数据不更新的问题。后面查阅了些资料,这通常是由于 Vue 的响应式系统没有检测到数据的变化,或者数据更新后没有正确地触发视图的重新渲染。以下是一…...
PeView 命令行PE文件解析工具
PeView 是一款基于C/C开发的命令行版PE文件解析工具,专门用于解析Windows可执行文件并提供详尽的文件结构和交互式查询功能,帮助用户理解和分析目标程序的内部构成,是逆向分析和软件调试中的重要工具,本次分享工具源代码及使用方法…...
微信小程序25__实现卡片变换
先看效果图 实现代码如下: <view class"page" style"filter:hue-rotate({{rotation}}deg)"><view class"prev" catchtap"toPrev">《《《</view><view class"next" catchtap"toNext&q…...
使用Git进行团队协作开发
使用Git进行团队协作开发 Git简介 安装Git 在Windows上安装Git 在macOS上安装Git 在Linux上安装Git 设置Git用户信息 创建Git仓库 基本Git命令 添加文件 提交更改 查看状态 克隆仓库 推送更改 获取更改 分支管理 创建分支 切换分支 合并分支 删除分支 解决合并冲突 检查冲突…...
期货跟单、量化交易模拟演示系统
一、跟单下单 在“排行榜”中选择要跟单的用户,合约可以跟全部,也可以指定跟该用户的某一合约操作,选定跟单的倍数(操作手数的倍数)/手数(指定手数,可以不是对方的倍数),…...
Python小白学习教程从入门到入坑------第十八课 异常模块与包【下】(语法基础)
一、内置全局变量__name__ 在Python中,有一些内置的全局变量和特殊变量,它们是由Python解释器预定义的,可以在代码的任何地方直接使用。 这些变量通常用于提供关于当前解释器状态的信息,或者用于控制解释器的行为 在Python中&a…...
arcgis pro 3.3.1安装教程
一、获取方式: http://dt4.8tupian.net/2/29913a61b1500.pg3二、软件目录: 三、安装步骤: (1)安装软件运行环境windowsdesktop-runtime 8.0.4; (2)选中安装文件arcgispro_33zh_cn_190127.exe&…...
Spring 获取Cookie/Session
获取Cookie/Session Cookie & Session获取Cookie传统方法获取使用 Spring 获取 Cookie 获取Session传统方法使用 SpringBoot 获取 Session简洁获取 Session HTTP协议 自身是属于 无状态协议(默认情况下 HTTP 协议的客户端与服务端的这次通信,和下次的通信之间没…...
小红书接口数据查询优化指南
小红书,作为分享生活、购物心得与美妆护肤经验的热门社交媒体平台,对开发者而言,其数据接口具有极高的实用价值。本指南将聚焦于如何通过接口高效查询小红书数据,并提供清晰的步骤与示例代码。 一、前置准备 在深入查询之前&…...
数据结构 - 图
文章目录 一、图的基本概念二、图的储存结构1、邻接矩阵2、邻接表 三、图的遍历1、广度优先遍历2、深度优先遍历 四、最小生成树1、概念2、Kruskal算法3、Prim算法 五、最短路径问题1、单源最短路径--Dijkstra算法2、单源最短路径--Bellman-Ford算法3、多源最短路径--Floyd-War…...
怎么做商业服务网站/百度官方推广平台
这两天接受了一个新任务,就是学在iphone和android平台上编译openSSL,因为我对Apple知之甚少,所以在做的过程中遇到了一些困难和问题,经过学习和尝试,终于弄出来了,网上的好多教程有问题,所以自己…...
搜索引擎wordpress/seo网站建设优化
1.下面是Sping技术栈所包含的技术框架图 2.Spring Boot的一些知识点 3.Spring Boot 推荐的基础 POM 文件 名称说明spring-boot-starter核心 POM,包含自动配置支持、日志库和对 YAML 配置文件的支持。spring-boot-starter-amqp通过 spring-rabbit 支持 AMQP。spring-boot…...
怎样做网站建设方案/网络销售员每天做什么
基于 转载于:https://www.cnblogs.com/qcjd/p/9324863.html...
网站分析模板/安卓优化大师官方版
暴力就好了 #include<bits/stdc.h> using namespace std; int a,b,c; int main(){cin>>a>>b>>c;for(int i1;;i){if(i%23a&&i%233b&&i%2333c){cout<<i;return 0;}} }...
宽屏网站背景/网络营销推广软件
Map JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象&am…...
榆次做网站/百度首页优化排名
文章底部随意打赏作者,获取公号所有整理的案例资源!当前视频讲解Scratch融合卡通、动画、音效等多媒体的运用和直观拖拽式的编程方式,生动有趣,可以编写各种类型程序,游戏、动画、互动美术、实物模拟、数学模拟等&…...