加密与解密 解密篇 逆向分析技术 (一) 栈/调用约定

news2024/11/25 0:54:53

目录

逆向是什么

32位软件逆向技术

1.启动函数

2.函数

函数的识别

函数的参数

利用栈进行传递

下面是通过esp来寻址

通过寄存器来传递参数

例子

例子

函数的返回值

例子

例子


逆向是什么

将可执行程序反汇编 通过分析反汇编代码来理解其代码功能(各个接口的数据结构)
用高级语言重述这个代码 逆向分析原始软件思路
 
这就是逆向分析

32位软件逆向技术

使用 VC6.0编译的32位程序

1.启动函数

在写32位程序的时候 源代码必须实现一个 WinMain函数

但是执行并不是从WinMain开始

首先执行的是启动函数的相关代码

是通过编译器生成

对于 Visual C++程序来说

它调用的是 C/C++运行时启动函数

该函数负责对C/C++运行库进行初始化

(Visual C++配有C运行库的函数 可以在 crt/src/crt0.c 文件中找到启动函数的源代码)

C/C++程序运行时 启动函数的作用基本相同

检索指向新进程的命令行指针

检索指向新进程的环境变量指针

全局变量初始化

内存栈初始化

等

所有的初始化完成后

 启动函数就会调用应用程序的入口函数(main、WinMain)

调用WinMain的函数大致如下

GetStartupInfo(&StartupInfo);
Int nMainRetVal = WinMain(GetModuleHandle(Null),Null,pszCommandLineAnsi,
\(StartupInfo.dwFlags&STARTF_USESHOWWINDOW)?StartupInfo.\wShowWindow:sw_SHOWEFAULT);

这里主要就是调用WinMain函数 然后返回值为一个 INT的 nMainRetVal

进入点返回时

启动函数便调用 C运行库的 exit函数,把返回值(nMainRetVal)传给他

进行必要的处理 然后调用 ExitProcess 退出

2.函数

每一个程序都是由不同功能的函数组成

所以在逆向中 重点是放在函数的识别和参数的传递

这样就可以把注意力集中在某一段代码中

函数是一个程序模块 用来实现特定功能

其包括了(函数名,入口参数,返回值,函数功能等部分)

函数的识别

函数通过调用程序来调用函数 然后在函数返回的时候继续执行程序

函数要知道如何返回的地址呢

实际上 大多数情况下都是使用call 和 ret来调用函数和保存返回地址

call指令和跳转指令功能类似

call:保存返回信息 即将之后的指令地址压入栈的顶部 

然后遇到ret 就返回该地址

该地址会和参数一起传给被调用函数


也就是说 call指令给出的地址 就是被调用函数的其实地址

ret则是用于结束函数的执行(并不是所有ret都是标志函数的结束)

 源代码是这个

 我们看看程序的执行

1.main函数

 2.call sub 就是 add函数

 函数的参数

这里就是主要的传递参数的方式

(1)栈方式:

需要定义参数在栈中的顺序 并且约定函数调用的栈平衡

(2)寄存器方式:

需要确定参数是通过那个寄存器进行传参


(3)通过全局变量进行隐含参数传递

利用栈进行传递

栈是 后进先出的存储区

指针esp指向栈的第一个可用的数据项

调用函数

先把参数入栈

然后调用函数

函数被调用后

在栈中取得数据

计算结束后

依照调用约定  平衡栈

在传递有一个很重要的事情

就是调用约定

这个是由不同语言 来确定的

1.栈的介绍-C语言调用函数(一)_双层小牛堡的博客-CSDN博客

1.栈的介绍-C语言调用函数(二)_双层小牛堡的博客-CSDN博客

这里给出例子

有一个test1(Par1,Par2,Par3)

汇编代码

_cdecl

pascal

stdcall

push par3;参数从右往左

push par2

push par1

call test1

add esp,0C ;栈平衡

push par1;参数从左往右

push par2

push par3

call test1

在函数内完成栈平衡

push par3;参数从右往左

push par2

push par1

call test1

在函数内完成栈平衡

这里就很清楚可以看见

_cdecl和stdcall是参数右往左进入栈   pascal则相反

在栈平衡上 _cdecl是自己加0c来保证栈平衡 则 pascal和stdcall相反

函数对参数和局部变量的取值都是通过栈来定义的

1. 调用者将函数执行完毕时返回地址和参数入栈

2.函数使用 ebp+偏移量 对栈中的参数进行寻址和去除

3.使用ret和retf返回  这个时候 eip是设置为栈中保存到地址

栈 只有一个出口 就是栈顶

这里给出一个例子 使用 stdcall约定来调用 test1

push par2
push par1
call test2{
    push ebp                     ;保护栈
    mov ebp,esp                  ;设置新的ebp 指向栈顶
    mov eax,dword ptr [ebp+0c]  ;取得par2
    mov edx, dword ptr [ebp+08]  ;取得par1
    sub esp,8                    ;如果要使用局部变量 就要预留空间 
    .........
    add esp,8                    ;释放局部变量的栈
    pop ebp                      ;恢复现场的的ebp
    ret 8h                       ;返回(相当于 ret;add esp,8)
                                  ret 后面为参数个数x4h
}
给出建立的过程

因为esp为栈指针 所以ebp来存取栈

1.此函数具有两个参数   假设执行函数前 esp=k

2.依据stdcall调用 从右至左入栈 先将Par2入栈 esp=K-04h

3.将Par1入栈   esp为 K-08h

4.参数入栈了 现在就是调用函数  call

5.call 函数 把返回地址入栈   esp=K-0ch

6.现在已经在子程序(函数)中了   开始使用ebp来调用参数 但是为了恢复之前的栈

我们先需要 push ebp来保护  这个时候 esp=K-10h

7.执行mov ebp,esp  ebp是用来寻找调用者压入栈的参数 这个时候 [ebp+8]就是Par1 [ebp+c]就是参数2

8.sub esp,8表示在栈中定义局部变量  局部变量1和局部变量2的地址分别是[ebp-4] [ebp-8]

调用结束使用 add esp,8 来释放  也就是说 调用结束后 局部变量就消失了

9. 调用 ret 8 来平衡栈   

此外 使用 enter和leave 也可以帮助栈的维护

enter 就是
push ebp 
mov ebp,esp
sub esp,xxx


leave就是
add esp,xxx
pop ebp


所以 上面的程序 可以修改为

enter xxxx,0   0表示创建 xxxx空间来放置局部变量
......

leave 
ret 8

在许多时候 编译器会选择最优化的方式来编译程序

栈寻址方式会有点不一样

下面是通过esp来寻址

是通过 visual 6.0 的 "Maximize Speed"的优化选项

push par2
push par1
call test1
{
    mov eax,dword ptr [esp+04]          ;参数1
    mov ecx,dword ptr [esp+08]          ;参数2
    .......
    ret 8
}


1.假设 esp=K
2.依据stdcall调用 从右至左入栈 Par2先进去 esp=K-04h
3.Par1 入栈 esp=K-08h
4.参数入栈 开始执行call call把返回地址压入栈 esp=K-0Ch
5.通过esp来选择参数

通过寄存器来传递参数

利用寄存器传参并没有具体的要求 虽然没有要求

但是都会在不声明的情况下进行遵守 Fastcall规范

Visual C++ Fastcall规范

左边的两个不大于4字节(dword)的参数分别存放在ecx edx中

寄存器就要用栈,将其余的参数依然使用从右往左的方式入栈,被调用函数在返回前清理栈

注意 浮点值 _int64 远指针都是通过栈来传递
Delphi/C++ Fastcall规范

左边的3个不大于4字节 (dword)参数分别放在 eax edx ecx 中

寄存器用完后 通过从左至右的Pascal方式入栈
特别的编译器 Watcom C 

总是通过寄存器来传递参数

1. eax
2. edx
3. ebx
4.ecx

如果寄存器用完 就是用栈来存入参数
例子

 我们分析一下汇编代码

 这里就是用Fastcall 来调用

push 4
push 3   从右往左 先把后两个压入栈
mov edx,2
mov cl,1 (char 大小为8个字节 用8位寄存器即可)

然后就是调用函数

接下来我们看看add函数

push ebp
mov ebp,esp            保护栈
sub esp,8              开辟局部变量空间
mov [ebp+var_8],edx    edx存放的是2 2先进入局部变量[ebp-8]的地方
mov [ebp+var_4],cl     cl存放的是1  1进入局部变量[ebp-4]
movsx eax,[ebp+var_4]  将字符型整数 扩展到双位 就是把cl存放的8位 扩充到16位
add eax,[ebp+var_8]    把左边两个参数相加 即 1+2
add eax,[ebp+arg_0]    把3+(1+2)
add eax,[ebp+arg_4]    把4+((1+2)+3)
mov esp,ebp            清空栈 返回
pop ebp
retn 8

从这里看出 寄存器需要先存入局部变量地址 然后通过局部变量读取出来进行计算

还存在一个调用规范 也是通过寄存器传值

thiscall

非静态的类成员函数调用

对象的每个函数隐含接收到this参数

采用this约定的时候
寄存器按照从右往左入栈

被调用函数在返回前清空栈

并且仅仅通过ecx寄存器传递额外的参数

this指针
例子

 main

push ebp
mov ebp,esp
push ecx            保护ecx 先暂存
push 2
push 1              从右至左入栈
lea ecx,[ebp+var_4] 此处是通过ecx传递this指针

push ebp
mov ebp,esp
push ecx
mov [ebp+var_4],ecx  把局部变量ebp-4的位置存入this指针
mov eax,[ebp+arg_0]  把1存入eax中
add eax,[ebp+arg_4]  把2和1相加
mov esp,ebp
pop ebp
retn 8

函数的返回值

return返回值

函数返回值一般情况下存放在eax中
如果超过了eax的容量
那么高32位会存放在edx寄存器中
例子
MyAdd(int x,int y)
{
    int temp;
    temp=x+y;
    return temp;
}

汇编代码
主函数        MyAdd      

push x

push y

call MyAdd

mov ....,eax

push ebp

mov ebp,esp

sub esp,4

mov ebx,[ebp+0ch]

mov ecx,[ebp+08h]

add ebx,ecx

mov [ebp-4],ebx   ;结果存入局部变量中

mov eax,[ebp-4]    ;存入eax用来返回

mov esp,ebp

add esp,4

ret

通过参数按传引用方式返回值

给函数传递参数的方式有两个

一个是传值

一个是传引用

在传递引用的 时候 修改参数值的复本不会影响的原本参数的值

传引用调用 允许调用函数修改原始变量的值

例子

 main函数

sub esp,8     开辟局部变量空间  设esp为K
lea eax,[esp+8-4]        为 K-4
lea ecx,[esp+8-8]        为 K-8
push eax                 指向参数B的字符指针入栈
push ecx                 指向参数A的字符指针入栈
call                     调用函数
mov edx,[esp+8]          利用esp+8返回值

 max函数

mov eax,[esp+8]      eax 就是指向B的指针
mov ecx,[esp+4]      ecx 就是指向A的指针
mov eax,dword ptr [eax] B的值存入eax
mov edx,dword ptr [ecx] A的值存入edx
cmp edx,eax
jge                  如果小于 就不跳转
mov [ecx],eax        把较大的值存入A的地址中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/754221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

慢速减压控制技术在预防同步辐射光源和原位透射电镜氮化硅窗口膜真空中破裂的应用

摘要:氮化硅薄膜窗口广泛应用于同步辐射光源中的扫描透射软X射线显微镜和原位透射电镜,但氮化硅薄膜只有几百纳米的厚度,很容易因真空抽取初期的快速压差变化造成破裂。为此,本文提出了线性缓变压力控制解决方案,即控制…

【小数据处理】从日志中获取json数据的处理

写在头上 本次分析的数据来源是SpringBoot服务输出的logback日志。具体配置参考: 处理工具:Notepad v7.6。处理的日志内容不易过大,Notepad能打开电脑不卡最好(100M以内吧)。如果实在过大,先从日志源头进行截取&…

【javaEE面试题(五)在JMM(Java Memory Model (Java 内存模型))下谈volatile的作用】【保证内存可见 和 指令有序】

volatile的作用 JMM下volatile作用 volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性”. 代码在写入 volatile 修饰的变量的时候 改变线程工作内存中volatile变量副本的值将改变后的副本的值从工作内存刷新到主内存 代码在读取 volatile 修饰的变量的时…

微信小程序上线与发布图文步骤操作

1.上传代码 打开微信小程序,在微信开发者工具的工具栏中单击“上传”按钮,页面中弹出提示框,根据提示填写相应的信息,然后单击“上传”按钮,即可上传代码。 2.查看上传代码之后的版本 登录微信小程序管理后台&…

【docker】部署svn服务器,docker安装部署svn服务器

话不多说直接上步骤! 1.下载镜像,创建容器 # 下载镜像 docker pull elleflorio/svn-server # 创建svn仓库目录,进入svn仓库目录 mkdir -p /var/svn # 创建svn服务容器,把容器中的svn仓库映射到本机,并映射3690端口 d…

基于深度学习的高精度安全帽及背心检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要:基于深度学习的高精度安全帽及背心检测识别系统可用于日常生活中或野外来检测与定位安全帽及背心目标,利用深度学习算法可实现图片、视频、摄像头等方式的安全帽及背心目标检测识别,另外支持结果可视化与图片或视频检测结果的导出。本系…

动手学深度学习——线性回归(原理解释+代码详解)

目录 1、线性回归2、线性回归模型2.1 线性模型2.2 损失函数2.2.1 平方差损失函数2.2.2 整个数据集上的损失函数 2.3 随机梯度下降2.4 用模型进行预测 3、线性回归的简单实现3.1 生成数据集3.2 读取数据集3.3 初始化模型参数3.4 定义模型3.5 定义损失函数3.6 定义优化算法3.7 训…

H3C-Cloud Lab实验-OSPF配置实验

一、实验拓扑图 实验需求: 1、按照图示配置 IP 地址 2、按照图示分区域配置 OSPF ,实现全网互通 3、为了路由结构稳定,要求路由器使用环回口作为 Router-id,ABR 的环回口宣告进骨干区域 4、掌握OSPF初始化流程、路由表学习的过…

基于linux下的高并发服务器开发(第一章)- GDB调试(2)1.14

(1)执行 gcc test.c -o test -g ,生成test文件 (2)gdb test (3)list 查看当前文件代码 list/l (从默认位置显示) (4)l 20 list/l 行号 &#xf…

kafka(一)

一:kafka架构介绍 1. Brokers kafka集群包括一个或者多个服务器,服务器的节点叫做broker。 2. Topic 类似于数据库中的table。物理上不通的topic会分开存储。一个topic的消息会存储在多个broker上。但是在读取的时候,只要选择好topic&…

Centos Stream9安装Neovim的详细过程

Centos Stream9下的Neovim的安装步骤: 安装neovim Index of /pub/epelhttps://dl.fedoraproject.org/pub/epel/ yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm yum install -y neovim python3-neovim yum install -y …

【MySQL】不允许你不会全文本搜索

🎬 博客主页:博主链接 🎥 本文由 M malloc 原创,首发于 CSDN🙉 🎄 学习专栏推荐:LeetCode刷题集 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正&#xff0…

【ShenYu系列】ShenYu网关条件匹配的设计及原理分析

ShenYu网关中用到了很多有趣的设计,我对其中的条件匹配的实现尤其感兴趣,所以研究一下具体实现的原理。我这边用到的shenyu版本是2.6.0-SNAPSHOT。 应用入口 原理拆解 AbstractShenyuPlugin#execute,获取到SelectorData集合,进行…

Inkscape扩展脚本入门

Inkscape官网 https://inkscape.org/ 建议下载1.3版本 官方插件脚本文档 https://inkscape-extensions-guide.readthedocs.io/en/latest/index.html 但这个文档似乎和当前版本不符合,直接按照其内的方法写脚本会有问题 Inkscape插件加载目录 默认情况下&…

LVGL开发:配置模拟器学习LVGL V8.3

文章目录 模拟器配置常用控件学习基本知识WidgetEvents 输入设备ImagesScreen 参考 模拟器配置 LVGL支持多种IDE下配置模拟器: 在WINDOWS下面,大家最常使用的是VS2019,为了和大家保持一致,这里也使用VS2019进行配置。 首先&…

Matlab export_fig 输出高清图片和部分运行错误问题

Matlab export_fig 输出占空间较小的矢量高清图和部分运行错误问题 Matlab export_fig 的安装与运行错误export_fig 配置:安装后一直提示加载Ghostscript运行错误export_fig输出pdf的本质过程export_fig介绍几种生成图片的大小 Matlab export_fig 的安装与运行错误 …

mysql数据库备份与还原、索引、视图

一、备份与还原 /***************************样例表***************************/ CREATE DATABASE booksDB; use booksDB; CREATE TABLE books ( bk_id INT NOT NULL PRIMARY KEY, bk_title VARCHAR(50) NOT NULL, copyright YEAR NOT NULL …

Transaction事务使用了解

1.功能概述 ​ 在wiki的解释中,事务是一组单元化的操作,这组操作可以保证要么全部成功,要么全部失败(只要有一个失败的操作,就会把其他已经成功的操作回滚)。 ​ 这样的解释还是不够直观,看下…

利用BP网络输电线路故障诊断(Python代码,压缩包带有数据集和代码,解压缩可直接运行)

1.数据集介绍 将故障区分为具体的不同类型:单相短路故障、两相接地短路故障、两相相间故障、三相相间短路故障。这里随意举出每种类别的两个样本进行展示。 GCBAIaIbIcVaVbVc1001-151.2918124-9.67745156385.800162260.400749853-0.132934945-0.2678149071001-336…

git下载源码及环境搭建之前端(三)

学习目标: vue 新项目的 前端环境搭建 vue 项目在 使用 Visual Studio Code 开发前端项目环境的搭建及 相关文件的配置 操作步骤: 前端: 下图所示为开发时前端所用的编辑器 注意:在配置时 有时候 localhost 可能 不太好用&…