C ++强制变量到固定的内存位置

问题描述 投票:-2回答:1

我已经为应用程序编写了一个C ++代码,其中有一些变量必须具有不同的值,每个用户都会使用它(为简单起见,我们称之为变量X)

X对于不同的用户具有不同的值。这个(X)不应该被改变并且也嵌入在exe本身中(因此我无法从文件或任何其他类似的解决方案中读取它)

我不想分发源代码然后编译。相反,我想要一个方法,让我直接编辑最终的exe而不需要编译(它只是变量X的值,它不同!)这可能吗?

我的想法是,如果我可以将此(X)强制在一个恒定的内存位置,那么我可以从Hex编辑器中轻松编辑它的值作为示例。 (我的意思是当黑客为特定游戏编写作弊工具时的相同想法)

  1. 固定记忆位置的机制是否可行?
  2. 有没有其他想法可以做出我想要的东西?

我希望我的问题很清楚

c++ pointers assembly memory exe
1个回答
1
投票

在这个答案中,我将使用Visual Studio 2017 Community Edition,因为我希望确保一个开发环境与Windows完全兼容。

我将介绍五种方法,从最易维护到最少。当然,这个答案的重点仅限于与外部工具“共享”C ++变量的目标。 这种操作的安全性是一个不同的主题,最终无论如何都是徒劳的尝试。


Method 1 - Resources

Windows API1和PE2支持在可执行文件3中嵌入资源。 资源通常是图像,图标或本地化字符串,但它们可以是任何东西 - 包括原始二进制数据。

使用Visual Studio很容易添加资源:在解决方案资源管理器>资源文件>添加>新项>资源>资源文件(.rc)中

这将打开Resource视图,右键单击Resource.rc并选择Add resource .... 可以创建标准资源,但我们需要一个Custom ...类型,我们可以调用RAW。 这将创建一个新的二进制资源,为其提供一个ID并在解决方案中生成一些文件。 切换回Solution explorer,我们可以看到这些新文件,并最终使用比VS的集成编辑器更好的十六进制编辑器编辑.bin文件。 特别感兴趣的是resource.h文件,我们可以包含它来定义资源id,在我的例子中它是IDR_RAW1

在bin文件制作完成后,我们准备在应用程序中读取它,使用的模式是常用的 - 我不想再次使用这些API一个新的答案所以我将链接Official documentation和提供示例代码:

#include <Windows.h>
#include "resource.h"

int WINAPI WinMain(HMODULE hModule, HMODULE hPrevModule, LPSTR lpCmdLine, int showCmd)
{
    //Get an handle to our resource
    HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_RAW1), "RAW");

    //Load the resource (Compatibility reasons make this use two APIs)
    HGLOBAL hResData = LoadResource(hModule, hRes);
    LPVOID ptrData = LockResource(hResData);

    /*
      ptrData is out binary content. Here is assumed it was a ASCIIZ string
    */
    MessageBox(NULL, (LPCSTR)ptrData, "Title", MB_ICONINFORMATION);

    return 0;
}

资源很好,因为它们允许与其他自动构建工具轻松集成:在编译资源以便动态生成资源之前,可以轻松添加构建步骤。

在生成exe文件之后更改它们也很容易--CFF Explorer III是编辑PE模块资源的简单而有效的工具。 甚至可以完全替换资源,从而不限制自己保持新资源与旧资源的大小相同。

只需在CFF中打开模块,选择资源编辑器,浏览到原始资源并编辑/替换它。然后保存。

Method 2 - PE exports

可执行程序是普通的PE模块就像Dlls一样,差别确实是一点点。 就像Dlls可以导出函数和变量4那样可以。

使用VC ++,将符号标记为导出的方式是__declspec(dllexport)

#include <Windows.h>

__declspec(dllexport) char var[30] = "Hello, cruel world!";

int WINAPI WinMain(HMODULE hModule, HMODULE hPrevModule, LPSTR lpCmdLine, int showCmd)
{
    MessageBox(NULL, var, "Title 2", MB_ICONINFORMATION);

    return 0;
}

问题的C ++方面几乎没有受到影响。 PE模块的编辑对用户不太友好,但每个人都很容易遵循。

使用CFF打开导出目录,将列出所有导出。 由于它们支持的C ++特性,C ++编译器必须在可以共享时修改变量名称 - 所以你不会在导出中找到像var这样的好名字,而是像?var@@3PADA。 导出名称在此上下文中并未真正实现任何目标,但您必须能够识别正确的导出。这应该很容易,因为它很可能只有一个。 CFF将向您展示函数RVA,这是变量的RVA(相对于图像库),您可以轻松地将其转换为文件偏移量或仅使用在CFF中集成的地址转换。 这将打开一个十六进制编辑器,并指向正确的字节。

Method 3 - Map files

如果您不希望PE出口指向您的变量,您可以告诉VS to generate a MAP file

映射文件将列出目标文件导出的所有符号(注意:目标文件,而不是PE模块)。 所以你必须确保一个变量,在这种情况下,由你的翻译单元导出 - 这是“全局”变量的默认情况,但请务必记住不要附加修改后的static链接,最终使它成为volatile以防止编译器在常量折叠步骤中消除它。

#include "Windows.h"

//extern is redundant, I use it only for documenting the intention
//volatile is a hack to prevent constant folding in this simple case
extern volatile int var2 = 3;


int WINAPI WinMain(HMODULE hModule, HMODULE hPrevModule, LPSTR lpCmdLine, int showCmd)
{
    //A simple use of an int
    return var2;
}

将在输出目录中生成一个MAP文件以及exe,在它内部会出现如下行:

 0003:00000018       ?var2@@3HC                 00403018     Source.obj

这为您提供了可以在CFF地址转换器中使用的变量(403018)的VA。

Method 4 - PE scan

您可以使用唯一值初始化变量。 为了能够这样做,可变大小必须足够大,以使得相同大小的随机序列比特以相同值结束的概率可以忽略不计。 例如,如果var是QWORD,则在PE模块中找到具有相同值的另一个QWORD的概率非常低(264中的一个),但如果var是一个字节,那么概率只是256中的一个。

最后,在变量之前添加一个标记变量(我使用16字节的随机数组)来标记它(即充当唯一值)。

要修改PE,请使用十六进制编辑器查找该唯一值,这将为您提供要编辑的var的偏移量。

Method 5 - Reverse engineering

在每次发布之后,反向引擎应用程序(这很简单,因为您可以使用VS和源代码调试它)并查看编译器分配变量的位置。 注意RVA(nota bene:RVA不是VA,VA是可变的),然后使用CFF编辑exe。

每次构建新版本时,都需要进行逆向工程分析。


1要正确,“Win32”API。 2我强烈建议读者至少习惯使用PE文件格式,因为我必须假设这样才能在主题和简短的内容中保留这个答案。不了解PE文件格式可能导致不能理解整个问题。 3实际上,在任何PE模块中。 4一般符号。

© www.soinside.com 2019 - 2024. All rights reserved.