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

相关文章

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

springboot依靠security实现digest认证的实践

《springboot依靠security实现digest认证的实践》HTTP摘要认证通过加密参数(如nonce、response)验证身份,避免明文传输,但存在密码存储风险,相比基本认证更安全,却因... 目录概述参数Demopom.XML依赖Digest1Application.JavaMyPasswo

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

Java 结构化并发Structured Concurrency实践举例

《Java结构化并发StructuredConcurrency实践举例》Java21结构化并发通过作用域和任务句柄统一管理并发生命周期,解决线程泄漏与任务追踪问题,提升代码安全性和可观测性,其核心... 目录一、结构化并发的核心概念与设计目标二、结构化并发的核心组件(一)作用域(Scopes)(二)任务句柄

Java中的Schema校验技术与实践示例详解

《Java中的Schema校验技术与实践示例详解》本主题详细介绍了在Java环境下进行XMLSchema和JSONSchema校验的方法,包括使用JAXP、JAXB以及专门的JSON校验库等技术,本文... 目录1. XML和jsON的Schema校验概念1.1 XML和JSON校验的必要性1.2 Sche

SpringBoot集成WebService(wsdl)实践

《SpringBoot集成WebService(wsdl)实践》文章介绍了SpringBoot项目中通过缓存IWebService接口实现类的泛型入参类型,减少反射调用提升性能的实现方案,包含依赖配置... 目录pom.XML创建入口ApplicationContextUtils.JavaJacksonUt

MyCat分库分表的项目实践

《MyCat分库分表的项目实践》分库分表解决大数据量和高并发性能瓶颈,MyCat作为中间件支持分片、读写分离与事务处理,本文就来介绍一下MyCat分库分表的实践,感兴趣的可以了解一下... 目录一、为什么要分库分表?二、分库分表的常见方案三、MyCat简介四、MyCat分库分表深度解析1. 架构原理2. 分

Java 中的 equals 和 hashCode 方法关系与正确重写实践案例

《Java中的equals和hashCode方法关系与正确重写实践案例》在Java中,equals和hashCode方法是Object类的核心方法,广泛用于对象比较和哈希集合(如HashMa... 目录一、背景与需求分析1.1 equals 和 hashCode 的背景1.2 需求分析1.3 技术挑战1.4

k8s搭建nfs共享存储实践

《k8s搭建nfs共享存储实践》本文介绍NFS服务端搭建与客户端配置,涵盖安装工具、目录设置及服务启动,随后讲解K8S中NFS动态存储部署,包括创建命名空间、ServiceAccount、RBAC权限... 目录1. NFS搭建1.1 部署NFS服务端1.1.1 下载nfs-utils和rpcbind1.1