5

我真的希望以前没有人问过这个问题,但是我的谷歌搜索没有任何结果,所以我想我会问。

我正在处理一个小脚本,我在其中将 DLL 注入游戏并编辑特定事件发生时显示的特定字符串。我找到了这个字符串在内存中的位置,并编写了一个基本的 sigscanning 函数来在运行时找到它。我现在的问题是,给定起始地址,我如何/可以用我自己的特定字符串替换这个字符串?

例如,字符串是“我喜欢煎饼”,我想将其替换为“煎饼很糟糕,华夫饼是最好的!”。一个人会怎么做呢?

谢谢!

哦,是的,如果重要的话,源中的字符串是一个 const char。我很确定它没有,但添加该信息并没有什么坏处!

4

2 回答 2

2

如果要替换它的字符串至少与替换它一样长,那么它是微不足道的:将其替换到位。否则,您无法可靠地表明您对煎饼的厌恶和对华夫饼的喜爱。

于 2012-11-14T03:43:01.380 回答
1

如您所述,引用字符串的汇编指令通常如下所示:

push offset aString

组装和链接后,这被解析为实际地址说:

push 0x00ABCDEF

这为您提供了两种选择:

  • 写入数据:修改aString(即指向的内存0x00ABCDEF)的内容
  • 编写代码:修改引用aString

写入数据

当源代码编译涉及标准 C 字符串字面量(内存中的不可变字符数组)时,在运行时该字符串通常映射到具有所有其他只读数据的某个只读页面。这些数据通常被连续打包以减少程序的内存占用。这是您尝试编写更大的字符串时遇到的问题。您将覆盖下一条数据,并且对该覆盖数据的任何引用现在都将指向大字符串的中间。

通过更改数据来编写更长的字符串并非易事,因为为了不丢失原始的功能行为,您必须在字符串之后移动所有数据。之后,您必须更新对移位数据的所有引用(其中一些可以使用指针算法动态计算)。正如我所说,这个过程并非微不足道 - 您试图在没有完整(如果有)符号信息的情况下重现链接器的重定位任务。

编写代码

最简单的方法是在任意位置写入新字符串。这可能是进程中未使用但已保留的内存(通常称为“代码洞穴”),或者它可能是您在注入 DLL 时映射的字符串文字。或者,您可以在注入后在运行时动态分配它。

下一步是查找所有引用aString并将它们替换为引用您的新字符串。

奖金方法:)

由于您在此级别研究逆向工程,因此您可能遇到了绕道/拦截/仪器的概念。在这里可以应用类似的方法来拦截所有引用并在运行时重定向它们。这将比上面概述的“编写代码”方法对性能造成更大的影响,但会保证所有访问都被捕获和重定向。

访问时的硬件断点设置为字符串指向的数据。当断点被触发时,一些寄存器将保存字符串的地址。在汇编中,这可能看起来像这样:

mov esi, 0x00ABCDEF
...

如果访问第一个字符,代码可能会这样做:

mov al, byte ptr ds:[esi]

当您的断点被命中时,您可以设置线程上下文(SetThreadContext在 Windows 上)以修改 的值esi以指向您的新字符串。

于 2012-12-27T21:37:37.103 回答