我认为你误解了作弊引擎的目标。
CE 允许您修改以持久方式存储在内存中的值。例如,在堆上,或者在程序的静态数据中。
例如,C++ 对象以确定性方式分配,因此它们永远不会移动。这就是为什么它们可以通过在对象的整个生命周期中保持不变的指针来引用。该对象有时由另一个对象拥有。如果你找到一个指向所有者对象的指针,你就找到了所谓的基指针.
例如 :
class Object
{
bool dummy;
int someField;
Object* child;
};
现在假设你有一棵包含 3 个元素的嵌套树Object
。意味着你有根Object
(n°1),其child
是另一个Object
(n°2),其child
是另一个Object
(n°3)。想象一下你做了这样的事情:
int main(int argc, char** argv)
{
Object root; // n°1
root.child = new Object(); // n°2
root.child->child = new Object(); // n°3
return 0;
}
你有兴趣搞乱 n°3someField
价值。你知道地址someField
,相对于Object
, is of +sizeof(bool) = 1
.
So (void*)&(object n°3) + 1
是一个指向someField
你要。
现在,如何找到指向对象 n°3 的指针?
知道相对地址child
is +sizeof(bool)+sizeof(int) = 5
。我们知道指向对象 n°3 的指针是(void*)&(object n°2) +
5.
对象 n°2 的地址也是如此,我将其作为练习。
但是对象 n°1 呢?它没有分配在堆上。它在堆栈上。废话。所以我们必须找到另一种方法来找到对象n°1存储的地址。
局部变量存储在堆栈中。在汇编中,它们通过相对于寄存器的偏移量来识别EBP
(or ESP
如果函数不改变堆栈)。EBP
是堆栈的顶部,而ESP
是堆栈的底部。
在这个例子中:
function foo()
{
int a;
double b;
}
当调用 foo 时,堆栈将增加刚好足以容纳 a 和 b,即 sizeof(int) + sizeof(double),即 12 个字节。 a 将存储在EBP - sizeof(int) = EBP - 4
(与...一样ESP + 8
) 和 b 将被存储在EBP - sizeof(int) - sizeof(double) = EBP - 12
(与...一样ESP
). 注意力!编译器可以更改此顺序,因此变量的声明顺序在内存中不一定相同。优化也可以彻底改变这一点。但让我们保持简单好吗?
回到我们的主要例子。我们有哪些局部变量?仅根。因此根将位于EBP - 9
直接地。但这仅当 main 是调用堆栈顶部的函数时才有效。没有调试器,你就无法做到这一点。
让我们假设我们的EBP
当 main 被调用时(取自新编译的 C 程序),值为 0028FF28。
根位于 (0x0028FF28 - 9) = 0x0028FF1F;
指向的指针root.child
位于 (0x0028FF1F + 5) = (0x0028FF24);
所以,root.child
位于*0x0028FF24。
指向的指针root.child->child
位于 (*0x0028FF24 + 5) = (假设 10000)
然后root.child->child
为 *10000。
最后,root.child->child.someField
为 *10000 + 3。
总结一下:你只需要找到root的静态地址即可找到其余的。
root 不在堆或任何类型的持久内存上,但它在 main 的堆栈上,该堆栈几乎在整个程序中持续存在,因此它几乎就像是永久的。
CE通过扫描整个进程内存空间帮助您找到静态地址
考虑到所有这些,您应该能够计算hp
的相对地址在堆栈上并找到一个指向它的静态指针(main 非常非常非常有可能在每次启动程序时都有一个静态帧地址)。如果您需要帮助,请使用调试器!我推荐免疫调试器。