CALL是如何炼成的 之二:实践篇

2024-05-16 02:38
文章标签 实践 之二 call 炼成

本文主要是介绍CALL是如何炼成的 之二:实践篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:遇到一个CALL应该如何写? 
 
这个是写一个内挂不可避免的问题.刚初学的朋友可能会不知道如何入手.想起刚学这方面的时候,绕过很多
弯路,现在把一些经验写出来给大家参考参考吧,不是很高深的东西,但我觉得对某些人很有帮助. 
 
写CALL的步骤! 
 
我之前说过写一个程序的CALL其实就2个步骤,第一:找到相关地址,第二:传入适当参数.只要这2点你做到
了 这个CALL便能调用了. 
 
其实写CALL就更简单了. 只要传入适当的参数调用这个地址便可以了. 
 
这里 地址我们找到了,所以我们所做的就是传入适当的参数. 
 
什么是适当的参数? 
 
就是CALL所需要的数据.  大家都知道,在WINDOW系统里,数据的传递是靠 寄存器和堆栈.其中寄存器用
的最多的指令就是 MOV 指令. 
 
而堆栈则是用PUSH指令.通过[esp+*] 或者先mov ebp,esp  然后[ebp+*] 指向堆栈数据. 
 
 
说了那么多其实本文就是围绕着一个中心点来讲解,那就是教你如何构
建一个CALL所需要的参数环境. 
 
例子一: 

 

这是某游戏的一个CALL.我们来看看CALL的内部

 

写一个CALL,首先要写堆栈.这里从调用CALL的图中我们可以看出只有一个堆栈,那么就是 
 
push ecx 
call 5FA410 
 
然后看调用的寄存器. 
 
如何看CALL内部调用了哪些寄存器呢? 
 
首先,明确一点,寄存器本身是空的.他并没有数据,只是一个用来存放数据的空间. 
 
假如我们直接调用这个CALL  那么,寄存器中,数据不是为0就是运行上一个CALL残留的数据.这些残留的
数据我们暂且把他看成0. 
 
 
那么 调用这个CALL的时候 寄存器 都是0了.  当然2个特殊的寄存器除外,一个是ESP 一个是EIP. 
好了 现在CPU执行 call 5FA410  后  
 
首先把当前的EIP压入堆栈, 也就是 call 5FA410 当前地址的下一条指令的地址.然后 JMP 5FA410  
 
这个时候,假设所有寄存器值都为0 
 
有哪些指令会读取寄存器的值呢?  最常见的就是mov  , push , lea , 其实很多汇编指令都会读取寄存器
 比如说 add ebx,eax 
 
读取EAX的值 加上EBX 所得的值放入EBX里. 
 
第一条指令为 push ebx 
 
我们刚刚说过push 也是传递寄存器的一种指令.它读取了EBX的值 开辟一个堆栈空间 也就是指令 sub
esp,4 然后存放. 
 
这里EBX是不是我们所说的CALL所需要寄存器呢? 
 
其实这里不是的.这里涉及到一个寄存器环境保护的机制. 
 
什么是寄存器环境保护机制? 
 
当ECX存放着一个重要的数据时候 ,这个时候需要运行一个CALL,而CALL的内部需要用ECX存放东西.
那么原有的ECX重要数据该怎么办? 
 
这个时候,肯定要找一个空间存放起来,等CALL内部临时用完寄存器后在放回. 
 
这就好像你家里有一个仓库, 堆满了玉米,但你邻居家要用你的仓库临时存放大米.你没办法,只好先将玉米
放入一个临时空间.然后给你邻居家使用.用完之后你在放回去. 
 
这里的临时空间就是指window系统里的堆栈.首先PUSH EBX 把数据保存到堆栈里,等下面的指令使用完
寄存器,然后POP EBX. 
 
在尾部我们可以看到 有POP EBX 对应上面的PUSH EBX. 
 
这里我们发现 下面也有几个PUSH 指令 
push ebx 
push ebp 
push esi 
push edi 
 
在CALL尾部我们可以看到 
 
pop edi 
pop esi  
pop ebp 
pop ebx 
 
 
这里的4个寄存器就是我上面指的 寄存器环境保护.所以 这4个寄存器就可以被排除了. 
 
然后是第二句,mov ebx,[esp+8] 
 
讲调用CALL之前的最后一个堆栈读取并存放到EBX.  呵呵 ,刚刚把寄存器里的数据存放这里就被用到了,
指令执行完后 原有的EBX数据被覆盖. 
 
所以这里的EBX 是被用来当做临时空间来使用的. 
 
mov esi,[ebx+a8] 
 
这里的EBX,上面那条指令已经赋值了,所以这里的EBX就不用理会了,[EBX+A8] 读取后存放到ESI,这里的
ESI也是用来当做临时空间使用. 
 
XOR EBP,EBP 
 
EBP置0. 即使没有上面的PUSH EBP  只要遇到这种指令 EBP也是被用来当做临时空间的而不是当做
参数传递的. 你都清0了 还怎么传递参数? 
 
下面就是一个对比,然后一个CALL了. 
 
这些都不是我们改理会的东西. 
 
最后我们来看看 MOV [ESP+14],EAX 
 
这一句调用了EAX的值.如果之前没有赋值的情况下,也就是我们假设等于0的情况下  ,那么EAX就是一
个参数传递的寄存器.而这里我们在上面发现了 
lea eax,[EBX+c]  执行完这条指令后 EAX的值便不是空的了.既然不是空的也就是不是参数传递的寄存
器. 
 
 
下面的MOV [ESP+14],ECX  也是如此. 
 
上面有一条指令给寄存器赋值了. 
 
可以这么说 ,在假设寄存器都是空的情况下,  CALL内部调用了空的寄存器那么这个寄存器就是参数传递
的指令. 
 
从这里我们可以看出,整个CALL的内部都没有调用寄存器.也就是没有用寄存器传递参数,故这个CALL的
写法就是 
 
 
 
push ecx 
call 5FA410 
 
 
 
==================================================================== 

例子二:

从图中我们可以看出这个CALL是一个比较好写的CALL 
 
首先我们来看堆栈部分.拿到一个CALL,首先处理堆栈,从图中我们可以看出这个CALL只有一个堆栈. 
 
从 mov al,[esp+8]我们可以看出 这里的 [ESP+8]指向了  CALL上面压入的堆栈PUSH ECX 
 
处理完堆栈后,我们来看看 寄存器的处理. 
 
首先 PUSH ESI   我们可以看到尾部有POP ESI 相对应. 所以这个ESI是环境数据保护的寄存器,用来被
CALL内部作为临时寄存器使用. 
 
下面的MOV ESI,EAX  中的EAX则是上面一个CALL的返回值.所以不必要做考虑 
 
而 mov [esi+2],al  中 EAX则是由上面的指令mov al,[esp+8]  赋值.所以这里是临时寄存器. 
 
而 mov ecx,[ecx+20]  中的ECX则由上面的 指令赋值了.所以这里也不用考虑. 
 
从上面的分析得到,这里我们不需要任何寄存器. 
 
然后我们来看看,CALL尾部 ,是RETN,  后面没有跟随数字,这里没有自动平衡堆栈.所以这里需要我们来加
上恢复指令恢复堆栈 
 
所以这个CALL的写法是 
push ecx 
call   5BD150 
add esp,4 
 
我们来看看ECX的值,1615d00 
 
那么是不是就是 push 1615d00呢 ? 
 
的确,压入这个值CALL是不会崩溃,但这个绝对不是我想要的答案. 
记住:调用CALL就是传入适当的参数.也就是需要我们构造一个CALL所需要的参数环境 
 
这里我们只有一个参数.我们来看看  CALL内部中调用这个参数的指令 
 
mov al,[esp+8] 
 
al 大家看过汇编都知道,这个是 EAX的低位 1个字节.也就是最大数字是 FF 
 
从这句话我们可以得知,这里读取了 PUSH ECX 的低位数的1个字节的数据! 
 
既然只调用了一个字节的数据 ,我们为何要压入4字节的数据呢? 
 
所以这里ECX数据是 1615d00  低位1字节数据也就是 00 
 
所以写成CALL就是  
 
push 0 
call   5BD150 
add esp,4 

这篇关于CALL是如何炼成的 之二:实践篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993644

相关文章

Docker集成CI/CD的项目实践

《Docker集成CI/CD的项目实践》本文主要介绍了Docker集成CI/CD的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、引言1.1 什么是 CI/CD?1.2 docker 在 CI/CD 中的作用二、Docke

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

Prometheus与Grafana在DevOps中的应用与最佳实践

Prometheus 与 Grafana 在 DevOps 中的应用与最佳实践 随着 DevOps 文化和实践的普及,监控和可视化工具已成为 DevOps 工具链中不可或缺的部分。Prometheus 和 Grafana 是其中最受欢迎的开源监控解决方案之一,它们的结合能够为系统和应用程序提供全面的监控、告警和可视化展示。本篇文章将详细探讨 Prometheus 和 Grafana 在 DevO

springboot整合swagger2之最佳实践

来源:https://blog.lqdev.cn/2018/07/21/springboot/chapter-ten/ Swagger是一款RESTful接口的文档在线自动生成、功能测试功能框架。 一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务,加上swagger-ui,可以有很好的呈现。 SpringBoot集成 pom <!--swagge

AutoGen Function Call 函数调用解析(一)

目录 一、AutoGen Function Call 1.1 register_for_llm 注册调用 1.2 register_for_execution 注册执行 1.3 三种注册方法 1.3.1 函数定义和注册分开 1.3.2 定义函数时注册 1.3.3  register_function 函数注册 二、实例 本文主要对 AutoGen Function Call

vue2实践:el-table实现由用户自己控制行数的动态表格

需求 项目中需要提供一个动态表单,如图: 当我点击添加时,便添加一行;点击右边的删除时,便删除这一行。 至少要有一行数据,但是没有上限。 思路 这种每一行的数据固定,但是不定行数的,很容易想到使用el-table来实现,它可以循环读取:data所绑定的数组,来生成行数据,不同的是: 1、table里面的每一个cell,需要放置一个input来支持用户编辑。 2、最后一列放置两个b

【HarmonyOS】-TaskPool和Worker的对比实践

ArkTS提供了TaskPool与Worker两种多线程并发方案,下面我们将从其工作原理、使用效果对比两种方案的差异,进而选择适用于ArkTS图片编辑场景的并发方案。 TaskPool与Worker工作原理 TaskPool与Worker两种多线程并发能力均是基于 Actor并发模型实现的。Worker主、子线程通过收发消息进行通信;TaskPool基于Worker做了更多场景化的功能封装,例

vue2实践:第一个非正规的自定义组件-动态表单对话框

前言 vue一个很重要的概念就是组件,作为一个没有经历过前几代前端开发的我来说,不太能理解它所带来的“进步”,但是,将它与后端c++、java类比,我感觉,组件就像是这些语言中的类和对象的概念,通过封装好的组件(类),可以通过挂载的方式,非常方便的调用其提供的功能,而不必重新写一遍实现逻辑。 我们常用的element UI就是由饿了么所提供的组件库,但是在项目开发中,我们可能还需要额外地定义一