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

时间:2021-08-06 13:49:18

I have written a C++ code for application, in which there are some variables that must have different values for every user will use it ( lets call it the variable X for simplicity)

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

X have different values for different user. This (X)should be not changed and also embedded in the exe itself ( so I can't read it from a file or any other similar solution)

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

I don't want to distribute the source code then compile. Instead, I want a method that makes me edit the final exe directly without need to compile ( it is just value of variable X which differs !) Is this possible ?

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

My idea to do this is if I can force this (X) at a constant memory location, I can then edit its value easily from Hex-editor as example. ( I mean the same ideas when hackers writes cheat tool for a certain game )

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

  1. Is the mechanism of fixed memory position possible?
  2. 固定记忆位置的机制是否可行?

  3. Is there any other idea to make what I want?
  4. 是否还有其他想法可以制作我想要的东西?

I hope my question is clear enough

我希望我的问题很清楚

2 个解决方案

#1


1  

In this answer I'll use Visual Studio 2017 Community Edition because I wanted to be sure to a have a development environment fully compatible with Windows.

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

I'll present five methods, from the most maintainable to the less. Of course the focus of this answer in strictly limited to the goal of "sharing" a C++ variable with an external tool.
Security of such an operation is a different topic and ultimately a futile attempt anyway.

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


Method 1 - Resources

Windows APIs1 and the PE2 support embedding resources in an executable3.
Resources are typically images, icons or localized strings but they can be anything - including raw binary data.

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

With Visual Studio is quite easy to add a resource: In the Solution Explorer > Resource files > Add > New item > Resource > Resource file (.rc)

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

This will open the Resource view, right-click on Resource.rc and select Add resource....
It's possible to create the standard resources but we need a Custom... type that we can call RAW.
This will create a new binary resource, gives it an ID and makes a few files in the solution.
Switching back to the Solution explorer we can see these new files and eventually edit the .bin file with a better hex editor than the VS's integrated one.
Of particular interest is the resource.h file that we can include to have the definition for the resource id, in my case it was IDR_RAW1.

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

After the bin file has been crafted we are ready to read it in the application, the pattern to use is the usual one - I don't feel like going over these API one more time a new answer so I'll link the Official documentation and provides a sample code:

在bin文件制作完成之后我们准备在应用程序中阅读它,使用的模式是常用的 - 我不想再多看一遍这些API新的答案所以我将链接官方文档并提供示例代码:

#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;
}

Resources are good because they allow for an easy integration with other automatic build tools: it's easy to add a build step before the resources are compiled to generate them on the fly.

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

It is also very easy to alter them after the exe file as been generated - CFF Explorer III is a simple and effective tools to edit a PE module's resources.
It's even possible to replace a resource entirely thereby not limiting ourselves to keeping the new resource the same size as the old one.

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

Just open the module in CFF, select Resource editor, browse to the raw resource and edit/replace it. Then save.

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

Method 2 - PE exports

Executable are ordinary PE module just like Dlls, the difference is really a batter of a bit.
Just like Dlls can exports functions and variables4 so can exes.

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

With VC++ the way to tag a symbol as exported is __declspec(dllexport):

使用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;
}

The C++ side of the matter is little affected.
The editing of the PE module is less user friendly but still very easy for everyone to follow.

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

With CFF open the export directory, all the exports will be listed.
C++ compilers have to mangle variables names when they can be shared due to the C++ features they support - so you won't find a nice name like var in the exports but something like ?var@@3PADA.
The export name doesn't really fulfil any goal in this context but you must be able to identify the correct export. This should be easy since it's very likely to be only one.
CFF will show you the function RVA, this is the RVA (relative to the image base) of the variable, you can easily convert it into a file offset or simply use the Address converted integrated in CFF.
This will open an hex editor and points you at the right bytes.

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

Method 3 - Map files

If you don't want to have a PE exports pointing right at your variable you can tell VS to generate a MAP file.

如果您不希望PE导出指向您的变量,您可以告诉VS生成MAP文件。

Map files will list all the symbols exported by an object file (note: an object file, not a PE module).
So you must make sure a variable, in this case, is exported by your translation unit - this is the default case for "global" variables but make sure to remember to not attach the static linkage modified to it and eventually make it volatile to prevent the compiler from eliminating it during the constants folding step.

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

#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;
}

A MAP file will be generated in the output dir, along with the exe, inside it's present a row like this one:

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

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

This gives you the VA of the variable (403018) that you can use in CFF Address translator.

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

Method 4 - PE scan

You can initialise the variable with an unique value.
To be able to do so the variable size must be big enough that the probability that a random sequence of bits of equal size end up with the same value is negligible.
For example, if the var is a QWORD the probability of finding, in the PE module, another QWORD with the same value is very low (one in 264) but if the var is a byte then the probability is just one in 256.

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

Eventually, add a marker variable (I'd use a random array of 16 bytes) before the variable to mark it (i.e. act as the unique value).

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

To modify the PE use an hex editor to look for that unique value, this will give you the offset of the var to edit.

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

Method 5 - Reverse engineering

After each release, reverse engine the application (this is easy as you can debug it with VS along with the sources) and look where the compiler allocated the variable.
Take note of the RVA (nota bene: RVA not VA, the VA is variable) and then use CFF to edit the exe.

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

This requires a reverse engineering analysis each time a new release is built.

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


1 To be correct, "Win32" APIs.
2 I strongly advice the reader to be at least accustomized with the PE file format as I must assume so to keep this answer in topic and short. Having no understanding of the PE file format will likely result in no understanding of the question as a whole.
3 Actually, in any PE module.
4 Symbols in general.

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

#2


-1  

This will work, as you can find the offset in the exe where the variable is located and change the binary bytes. Until you ship executables digitally signed. Also, do you plan to test the changed exe-s after the change?

这将起作用,因为您可以在变量所在的exe中找到偏移量并更改二进制字节。直到您发送数字签名的可执行文件。另外,您是否计划在更改后测试更改的exe-s?

More flexible is to ship protected (signed, etc) per-user config files with required info.

更灵活的是发送带有所需信息的受保护(签名等)的每用户配置文件。

#1


1  

In this answer I'll use Visual Studio 2017 Community Edition because I wanted to be sure to a have a development environment fully compatible with Windows.

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

I'll present five methods, from the most maintainable to the less. Of course the focus of this answer in strictly limited to the goal of "sharing" a C++ variable with an external tool.
Security of such an operation is a different topic and ultimately a futile attempt anyway.

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


Method 1 - Resources

Windows APIs1 and the PE2 support embedding resources in an executable3.
Resources are typically images, icons or localized strings but they can be anything - including raw binary data.

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

With Visual Studio is quite easy to add a resource: In the Solution Explorer > Resource files > Add > New item > Resource > Resource file (.rc)

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

This will open the Resource view, right-click on Resource.rc and select Add resource....
It's possible to create the standard resources but we need a Custom... type that we can call RAW.
This will create a new binary resource, gives it an ID and makes a few files in the solution.
Switching back to the Solution explorer we can see these new files and eventually edit the .bin file with a better hex editor than the VS's integrated one.
Of particular interest is the resource.h file that we can include to have the definition for the resource id, in my case it was IDR_RAW1.

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

After the bin file has been crafted we are ready to read it in the application, the pattern to use is the usual one - I don't feel like going over these API one more time a new answer so I'll link the Official documentation and provides a sample code:

在bin文件制作完成之后我们准备在应用程序中阅读它,使用的模式是常用的 - 我不想再多看一遍这些API新的答案所以我将链接官方文档并提供示例代码:

#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;
}

Resources are good because they allow for an easy integration with other automatic build tools: it's easy to add a build step before the resources are compiled to generate them on the fly.

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

It is also very easy to alter them after the exe file as been generated - CFF Explorer III is a simple and effective tools to edit a PE module's resources.
It's even possible to replace a resource entirely thereby not limiting ourselves to keeping the new resource the same size as the old one.

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

Just open the module in CFF, select Resource editor, browse to the raw resource and edit/replace it. Then save.

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

Method 2 - PE exports

Executable are ordinary PE module just like Dlls, the difference is really a batter of a bit.
Just like Dlls can exports functions and variables4 so can exes.

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

With VC++ the way to tag a symbol as exported is __declspec(dllexport):

使用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;
}

The C++ side of the matter is little affected.
The editing of the PE module is less user friendly but still very easy for everyone to follow.

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

With CFF open the export directory, all the exports will be listed.
C++ compilers have to mangle variables names when they can be shared due to the C++ features they support - so you won't find a nice name like var in the exports but something like ?var@@3PADA.
The export name doesn't really fulfil any goal in this context but you must be able to identify the correct export. This should be easy since it's very likely to be only one.
CFF will show you the function RVA, this is the RVA (relative to the image base) of the variable, you can easily convert it into a file offset or simply use the Address converted integrated in CFF.
This will open an hex editor and points you at the right bytes.

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

Method 3 - Map files

If you don't want to have a PE exports pointing right at your variable you can tell VS to generate a MAP file.

如果您不希望PE导出指向您的变量,您可以告诉VS生成MAP文件。

Map files will list all the symbols exported by an object file (note: an object file, not a PE module).
So you must make sure a variable, in this case, is exported by your translation unit - this is the default case for "global" variables but make sure to remember to not attach the static linkage modified to it and eventually make it volatile to prevent the compiler from eliminating it during the constants folding step.

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

#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;
}

A MAP file will be generated in the output dir, along with the exe, inside it's present a row like this one:

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

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

This gives you the VA of the variable (403018) that you can use in CFF Address translator.

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

Method 4 - PE scan

You can initialise the variable with an unique value.
To be able to do so the variable size must be big enough that the probability that a random sequence of bits of equal size end up with the same value is negligible.
For example, if the var is a QWORD the probability of finding, in the PE module, another QWORD with the same value is very low (one in 264) but if the var is a byte then the probability is just one in 256.

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

Eventually, add a marker variable (I'd use a random array of 16 bytes) before the variable to mark it (i.e. act as the unique value).

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

To modify the PE use an hex editor to look for that unique value, this will give you the offset of the var to edit.

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

Method 5 - Reverse engineering

After each release, reverse engine the application (this is easy as you can debug it with VS along with the sources) and look where the compiler allocated the variable.
Take note of the RVA (nota bene: RVA not VA, the VA is variable) and then use CFF to edit the exe.

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

This requires a reverse engineering analysis each time a new release is built.

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


1 To be correct, "Win32" APIs.
2 I strongly advice the reader to be at least accustomized with the PE file format as I must assume so to keep this answer in topic and short. Having no understanding of the PE file format will likely result in no understanding of the question as a whole.
3 Actually, in any PE module.
4 Symbols in general.

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

#2


-1  

This will work, as you can find the offset in the exe where the variable is located and change the binary bytes. Until you ship executables digitally signed. Also, do you plan to test the changed exe-s after the change?

这将起作用,因为您可以在变量所在的exe中找到偏移量并更改二进制字节。直到您发送数字签名的可执行文件。另外,您是否计划在更改后测试更改的exe-s?

More flexible is to ship protected (signed, etc) per-user config files with required info.

更灵活的是发送带有所需信息的受保护(签名等)的每用户配置文件。