C/C++程序中怎么会有静态地址?
Posted
技术标签:
【中文标题】C/C++程序中怎么会有静态地址?【英文标题】:How can there be static addresses in C/C++ programs? 【发布时间】:2014-12-19 22:01:06 【问题描述】:我一直在研究Cheat Engine,它允许您检查和操作 Windows 上正在运行的进程的内存:您根据变量的值扫描变量,然后您可以修改它们,例如在游戏中作弊。
为了编写机器人或类似的东西,您需要为要更改的变量找到一个静态地址 - 即,如果进程重新启动,该地址保持不变。其方法大致如下:
-
寻找你感兴趣的变量的地址,按值搜索
使用该地址查找代码,例如找到它所属的结构的地址(因为结构偏移是固定的)
寻找另一个指向该指针的指针,直到找到具有静态地址的指针(在作弊引擎中显示为绿色)
从我看过的教程来看,它似乎工作得很好,但我无法理解为什么它工作。
不是所有变量(包括全局静态变量)在运行时都获得相当随机的地址吗?
额外问题:
-
作弊引擎如何判断地址是否为静态(即重启时保持不变)?
一个教程提到了许多旧游戏和一些现代游戏(例如使命召唤 4)仅使用静态地址这一事实。这怎么可能?
【问题讨论】:
【参考方案1】:我将首先回答附加问题,因为它们介绍了一些您可能需要了解的概念才能理解主要问题的答案。
如果您知道可执行文件是如何工作的,那么回答第一个额外问题很容易:所有全局/静态变量都在 .data
部分内,其中 .exe 存储该部分的地址偏移量,因此 Cheat Engine 只检查如果变量在此地址范围内(从本节到下一节)。
对于第二个问题,可以只使用静态地址,但这对于游戏来说几乎是不可能的。甚至那些年长的。教程创建者可能想说的是,他想要的所有变量实际上都有一个指向它们的静态指针。但是仅仅因为你创建了一个局部变量,或者甚至将一个参数传递给一个函数,它们的值就被存储到了堆栈中。这就是为什么几乎不可能拥有“仅静态”程序的原因。即使你编译了一个实际上什么都不做的程序,它也可能会在堆栈中存储一些东西。
对于整个问题本身,并不是所有的动态地址变量都指向一个全局变量。这完全取决于程序员。例如,我可以创建一个局部变量,并且从不将其地址分配给 C 程序中的全局/静态指针。在这种情况下,找到该地址的唯一确定方法是真正知道变量第一次在堆栈中赋值时的代码。
有些变量具有动态地址,因为它们只是局部变量,它们在第一次被赋值时存储在堆栈中。
其他一些变量具有静态地址,因为它们被声明为编译器的全局变量或静态变量。这些变量有一个固定的地址偏移量,它是可执行文件中.data
部分的一部分。
可执行文件里面的每个节都有一个固定的偏移地址,.data
节也不例外。
但值得注意的是,可执行文件本身内部的偏移量是固定的。在操作系统中,事情可能会有所不同(所有随机地址),但这是操作系统的工作,为您抽象这类东西(在这种情况下创建可执行文件的虚拟地址空间)。所以看起来静态变量实际上是静态的,但只在可执行文件的内存空间内。 RAM 上的东西可能在任何地方。
最后,很难尝试向您解释这一点,因为您必须了解可执行文件的工作原理。一个好的开始是寻找一些关于低级编程的解释,比如堆栈框架、调用约定、汇编语言本身以及编译器如何使用一些众所周知的技术来管理函数(一般的作用域)、全局/静态/本地/constant 变量和内存系统(节、堆栈等),也许还有一些对 PE(甚至是 ELF)文件的研究。
【讨论】:
我的错,我用静态变量做了一些测试,看看它们的偏移量是否是固定的,看起来它们不是,但我在那里有一个错误:(所以是的,静态变量似乎是通常在固定的偏移量处,这回答了额外的问题。至于主要问题 - 一个所有数据都保存在堆栈/堆上而没有静态指针的程序呢?至少在 C++ 中这很典型。我怎么看,如果没有指向此数据的静态变量 somewhere,则无法为其获取静态地址 - 但它似乎适用于几乎所有游戏。 作弊引擎不仅会在您进行指针扫描时寻找“静态地址”。这实际上就是为什么您需要在游戏中的不同状态(以及不同的运行)中多次运行扫描以获取一个似乎可以满足您在进行扫描时通过的游戏状态的目的的地址。但是不能保证这个指针会一直持有你想要的地址。所以基本上它会搜索静态和本地指针。 但是为什么本地指针总是相同的偏移量呢?可能是因为它在堆栈中被分配得太早,以至于它停留在游戏早期打开的第一个作用域中,可能在主循环之前,这将保持有效,直到游戏关闭足够的作用域,然后使该指针持有垃圾内存。 我的意思是:从我看到的所有教程中,听起来好像总是人们可以使用静态地址来获取他们想要的数据,编写机器人和培训师。但我怀疑大多数游戏实际上都保留了指向玩家实例等的全局静态指针。那么这怎么可能呢? 这就是我的回答。这些不是静态地址,而只是一些早期分配的局部变量(例如main()
),它们在游戏运行时几乎保持活动状态。如果作用域没有被销毁,那么堆栈就不会增长,从而使地址有效,直到它实际关闭此函数的作用域。【参考方案2】:
据我了解,声明为静态的变量在程序数据中具有永久偏移量。这意味着当程序加载到 RAM 中时,变量的偏移量将始终相同。因为程序的起始地址是全局已知的,所以正如您所提到的,根据偏移量找到一个静态变量应该是一项微不足道的任务。因此,虽然指向静态变量的指针在事物方案中可能是随机的,但无论程序何时启动,它到程序存储器开头的偏移量都应该保持不变。所以Cheat Engine(虽然我不知道这个软件)很可能会存储静态变量的偏移量,然后当软件启动时,应用这个逻辑来找到那个变量。
至于它首先如何判断它是一个静态变量...嗯,这部分是猜测,但是当你在 C 中声明一个静态变量时,我假设编译器/链接器会放入某种标记以便操作系统知道它是一个静态变量。对于为某个目标系统编译的所有程序,也可能所有静态变量都以某种方式存储,或者存储在某个地址偏移处。同样,对此不太确定,但根据我对内存管理的理解,这似乎是最有意义的。有了这些假设,程序很可能只包含静态变量。不同之处在于内存是在程序运行时静态分配的,而不是动态分配的(如调用 malloc() 或类似方法)。如果变量是动态存储的,我相信有一种方法可以轻松找到它们,所以我认为变量是否为静态对于 Cheat Engine 并不重要。但是,正如我假设 Cheat Engine 想要在启动时修改游戏(就像旧的 GameSharks 过去......啊,怀念那些日子),修改静态变量可能更可靠,而不是尝试定位指针并反汇编代码等。
如果您有兴趣了解更多信息,我建议您查看this tutorial over at OSDev!
【讨论】:
以上是关于C/C++程序中怎么会有静态地址?的主要内容,如果未能解决你的问题,请参考以下文章