版本号 | 描述 | |
---|---|---|
文章日期 | 2023-03- | |
操作系统 | MacOS Big Sur 11.5 | |
Cheat Engine | 7.4.3 | |
第6步:指针:(PW=098712)
在上一步中,我解释了如何使用Code Finder来处理位置的变化。但仅使用该方法会使查找要设置的值的地址变得困难。
这就是为什么有指针存在的原因:
在底部,你会发现两个按钮。其中一个会更改值,另一个会更改值以及该值的位置。
对于这一步,你不需要真正了解汇编语言,但如果你了解的话,会有很大帮助。
首先找到该值的地址。当你找到它后,使用函数找出是什么访问了这个地址。再次更改该值,列表中会显示一个新的项。双击该项(或者选择并单击more info),将会打开一个新窗口,其中包含有关指令运行时发生的详细信息。
如果汇编指令在“[”和“]”之间没有任何东西,那么使用列表中的另一个项。
如果汇编指令中有“[”和“]”之间的内容,它将告诉你你需要的指针的值。然后回到主Cheat Engine窗口(如果你想保留这个额外的信息窗口,可以保留它,但是如果你关闭它,请记住“[”和“]”之间的内容),用十六进制的4个字节扫描额外信息中告诉你的值。
扫描完成后,它可能会返回1个或几百个地址。大多数时候,你需要的地址将是最小的地址。现在点击手动添加并选择指针复选框。
窗口将更改并允许你输入指针地址和偏移量。在地址中填入你刚才找到的地址。如果汇编指令的末尾有一个计算(例如:[esi+12]),则在末尾输入该值。否则,将其保留为0。如果它是一个更复杂的指令,请查看计算。
下面是更复杂指令的一个例子:
[EAX * 2 + EDX + 00000310] eax=4C and edx=00801234.
在这种情况下,EDX将是指针的值,EAX * 2 + 00000310将是偏移量,所以你需要填写的偏移量将是2 * 4C + 00000310 = 3A8。 (这都是十六进制的,可以使用windows中的calc.exe在科学模式下计算)
回到教程,点击“确定”按钮,地址将被添加。如果一切顺利,地址将显示为P->xxxxxxx,其中xxxxxxx是您找到的值的地址。如果不是这样,那么您可能做错了什么。
现在,使用您添加的指针在5000中更改该值并冻结它。然后点击“更改指针”,如果一切顺利,下一个按钮将变为可见。
额外信息:
您还可以使用指针扫描器查找到该地址的指针。
- 添加地址:
指针(Pointer)在编程中是一种非常重要的概念,指向内存中的某个位置
。可以将指针理解为一种变量类型
,它存储的是另一个变量的地址
,而不是实际的值。
指针可以用来传递函数参数、动态分配内存、数组操作和数据结构等方面。通过指针,我们可以直接访问和操作内存中的数据,从而实现高效的编程。
指针通常是以一个特定的数据类型为基础定义的,这个数据类型定义了指针可以指向的数据类型。例如,如果要定义一个指向整数类型的指针,可以使用以下语法:
int *ptr;
上述语句定义了一个名为ptr的指针,它可以指向一个整数类型的变量。在使用指针时,可以通过取地址运算符&来获取变量的地址,例如:
int num = 10;
int *ptr = # // ptr指向变量num的地址
现在,ptr指向了变量num的地址,可以使用解引用运算符*来访问该地址中存储的值,例如:
*ptr = 20; // 将num的值改为20
上述语句将内存地址中存储的值改为了20,也就是将num的值改为了20。
总之,指针是编程中的一个非常重要的概念,它可以让我们更加高效地访问和操作内存中的数据。在使用指针时,需要特别注意指针的数据类型和指针的解引用运算符。
多级指针是指指向指针的指针
,也称为“指向指针的指针”或“间接指针”。简单来说,它是一个指针变量的指针。
例如,一个指针变量可以指向一个整数,而一个指向指针变量的指针可以指向该指针变量,进而指向该整数。这种多级指针的使用通常涉及到复杂的数据结构或者动态内存分配。
在C语言中,可以使用星号(*)来声明指针变量,而多级指针则需要在变量名前面添加额外的星号。例如,一个指向整数类型的指针可以这样声明:
int *ptr;
而一个指向指针类型的指针可以这样声明:
int **ptr;
指针的级别可以随着需要而增加,例如:
int ***ptr;
这是一个指向指向指针的指针的指针,可以依此类推。在实际编程中,使用多级指针可以方便地访问复杂的数据结构,例如链表和树。同时,也需要注意指针的有效性和正确性,以避免出现错误和内存泄漏等问题。
我们模仿游戏中编写一个人物及背包的类。当访问
gRole
中第二个背包的容量时,就需要使用gRole->bag2->count
进行访问。
gRole本身就是个指针,该值为全局变量
,对于C++应用,将被编译到PE文件的data
节中,也就是上面提到的基址
,一旦编译好的软件,该值无论重启应用还是重启电脑,都是相对于该模块的固定偏移。对于本节,该值就是我们要找的目标指针。count
就是我们最终要找的血量
了。
ps: 下面示例代码是三级指针,而CE教程
中的是二级指针。
// 背包
class Bag
{
int type; // 背包类型
int index; // 索引,第几个背包
int count; // 容量
}
class Role
{
Bag *bag1;
Bag *bag2;
Bag *bag3;
}
Role *gRole = new Role();
int count = gRole->bag2->count;
除了上述讲述的方案,一级一级的搜索指针外,还有很多方案可以定位多级指针(而且,如果通过本方案在实际应用中,会搜到很多地址,这些地址很可能不是正确的,最终淹没在无尽的多级指针中,迷失方向)。
分析汇编代码
,根据堆栈,向上回溯定位基址。- 可以使用CE提供的
指针扫描器
查找到地址的指针,这个以后专门进行讲解。
**ps:**文章中内容仅用于技术交流,请勿用于违规违法行为。