函数调用修改方法:
- 通过虚表访问类中的虚函数
- 通过hook虚表,更改原函数的调用
C++ 的封装是在C++语言层面的特性, 可以通过指针,直接操作内存.来规避C++的一些不可见的内存操作,已达到可以进行内存修改的目的.
原理:
对象的前4字节是虚表的地址,取出该地址. windows/arm 内存中是小端序.
Animal *animal = new Pig();
int virtab = *(int *) animal;
virtab 则为虚表(函数指针),他的值是一个数组.该虚表里面存储的都是函数的地址. 所以这里可以将虚表强转为int * p_vitab.
int *p_vitab = reinterpret_cast<int *>(virtab);
这里取出第一个函数的地址,将自己写的某个函数的地址,替换掉:
*p_vitab = reinterpret_cast<int >(test)
这里的test为函数名.
因为C++对虚表是有写保护,所以需要先将虚表的权限更改为可读写执行.
DWORD old = 0;
BOOL flag = VirtualProtect((void *) virtab, 4, PAGE_EXECUTE_READWRITE, &old);
内存大端序与小端序概念.
一个 4 字节的整数值 0x12345678 大端序是从低位到高位(按字节).
地址: 0x1000 0x1001 0x1002 0x1003
数据: 12 34 56 78
小端序是从高位到低位(按字节).
地址: 0x1000 0x1001 0x1002 0x1003
数据: 78 56 34 12
段的概念:
在 Windows 操作系统中,段(Segment)是内存管理的一个基本概念,尤其在早期的 x86 架构中。段用于将程序的不同部分分开,以便于管理和访问。虽然现代操作系统通常使用分页(paging)机制,段仍然在某些情况下发挥作用。 段是指一个连续的内存区域,可以包含代码、数据或其他信息。每个段都有其特定的用途和属性,允许操作系统或编译器更有效地管理内存。 在 Windows 中,主要有以下几种段:
-
代码段(Code Segment) :
- 存储程序的可执行代码。
- 通常是只读的,以防止程序在运行时被修改。
-
数据段(Data Segment) :
-
存储全局变量和静态变量。
-
可以分为多个部分:
- 初始化的数据段:存放已初始化的全局和静态变量。
- 未初始化的数据段(BSS段) :存放未初始化的全局和静态变量,通常初始化为0。
-
-
堆段(Heap Segment) :
- 动态分配内存的区域。
- 程序在运行时可以根据需要请求和释放内存。
-
栈段(Stack Segment) :
- 存储函数调用的局部变量、参数和返回地址。
- 采用后进先出(LIFO)方式管理。
-
资源段(Resource Segment) :
- 存储程序的资源,如图标、菜单、对话框等。
- 这些资源可以在运行时被访问和使用。
页
在 Windows 操作系统中,页(Page)是内存管理的基本单位,主要用于实现虚拟内存和物理内存之间的映射。分页机制使得操作系统能够高效管理内存,提高系统的性能和稳定性。
页表
操作系统维护一个 页表(Page Table),用于记录虚拟页到物理页的映射关系。每个进程都有自己的页表,系统通过页表来进行地址转换。
页的类型
在 Windows 中,页可以分为几种类型:
- 普通页:标准的 4 KB 页,存储程序的代码和数据。
- 大页:可以是 2 MB 或 1 GB 大的页,适用于需要大量连续内存的应用程序(如数据库和科学计算)。
计算机组成原理
- 寄存器
寄存器= N个带锁存的触发器. 触发器是电流经过,产生记录.存储在锁存里面. 例如现在8bit的线,每一根线上有一个触发器+锁存. 存储一个01101100的数据,实际上就是这8个触发器在这一个时钟下 锁存中的I/O值,当有电流经过则为1,无则为0. 寄存器就是由很多(锁存+触发器)组成的一个元器件.用于CPU的计算.
- 内存
内存是用于与寄存器进行数据交换的一个元器件.在内存中,分为地址线,控制线,数据线. 地址线用于寻址,地址线的大小代表该内存的大小,控制线用于对该地址的数据是进行合种操作.数据线用于寄存器与内存之间的数据交互.实际上内存笔者要复杂很多 内存是计算机系统中用于存储和读取数据的关键组成部分。它由一系列连续的存储单元组成,每个存储单元被分配一个唯一的地址,用于访问和操作数据。
在计算机中,内存通常是按照字节(byte)为最小单位进行寻址的。每个字节都有一个唯一的地址,可以通过地址访问和操作其中的数据。内存的大小通常以字节为单位进行表示,例如 1GB 内存表示具有 1,073,741,824 字节的存储空间。
-
内存单元:内存被划分为一系列的存储单元,每个存储单元通常是一个字节大小。每个存储单元都有一个唯一的地址,用于标识其在内存中的位置。
-
存储单元的访问:计算机可以通过内存地址来访问特定的存储单元。读取操作将获取存储单元中的数据,写入操作将向存储单元写入数据。这些操作可以通过处理器中的内存管理单元(Memory Management Unit,MMU)或者直接访问内存总线来完成。
-
内存层次结构:现代计算机系统通常采用多级缓存和虚拟内存等技术来组织内存。这样可以通过层次化的存储结构提供更高效的数据访问和管理。
-
内存寻址:内存的寻址方式可以是字节寻址或者按照其他数据单元(如字、双字)进行寻址。根据寻址方式和处理器的体系结构,内存的访问可以是对齐的(aligned)或者非对齐的(unaligned)。
-
内存映射:计算机系统将设备、寄存器和其他外部资源映射到内存地址空间中,以便通过读写内存地址来访问这些资源。这种内存映射的方式提供了一种统一的访问机制,使得对内存和外部设备的操作可以使用相同的指令和操作。
汇编相关:
汇编是对应的直接指令.汇编可以通过汇编器生成对应的指令,指令再交由CPU去执行.windows所有程序都必须遵循PE文件结构,否则无法执行该指令.