从 exe 的 .data 和 .rdata 读取值。字节对齐

Reading values from .data and .rdata of exe. Bytes alignment

我有一个从这段代码编译的非常小的 exe(调试,未优化):

struct some_data
{
    unsigned long long a;
    unsigned long long b;
}

int main ()
{
    constexpr some_data {0x1234567890abcdef, 0xabcdabcdabcdabcd}
    cout << some_data.a << endl;
    return 0;
}

现在我想制作另一个程序,在第一个 exe 中找到值 0x1234567890abcdef 并将其替换为 0x111111111111111 然后保存 exe,所以下次我 运行 我得到 "injected" 0x111111111111111作为输出。

我制作了一个简单的程序,可以按照以下方式执行某些操作:

uint64_t key = 0x1234567890abcdef;
uint64_t value;
fstream file ("file", ios::in| ios::out | ios::binary);
if (file.is_open())
{
while (not_end_of_file) {
    file.read (&value, sizeof(value));
    if (value == key) {
        //code that overwrittes the old value with new
    }
}

它确实工作了一两次让我很高兴,但是在我向第一个项目添加一些代码并重建它之后,替换(第二个项目)突然停止工作。在停止输入代码的 if (value == key) 部分。我认为发生这种情况的原因是在添加一些代码后,.data 部分中的结构在文件中向前移动了几个字节,现在当读取 8 字节的块时,它不再像这样在一个块中(little_endianess)

XX XX XX ef cd ab 90 78        one read    operation   
56 34 12 XX XX XX XX XX        the next read operation

你觉得是这样吗?我可以期望 .data 和 .rdata 部分以任何可靠的方式对齐吗?您将如何实施将这种转变考虑在内的检查?或者,也许有人对如何轻松地以编程方式更改此值有完全不同的想法。

此致 马辛

看来您不能依赖数据对齐,或者至少不能像我以前那样依赖。

注意 如果您想复制实现,您最好使用 取消引用者的答案 中的代码,因为它更清晰。我留下了关于数据对齐的信息和实现它的替代方法的答案。

这是我想出的代码,它逐个加载字节并检查最后 8 个字节。这是为了小端,对于大我相信你需要改变字节是 "value" 引用,并改变移位方向。

#include <iostream>
#include <fstream>

using namespace std;

using magic_bytes_t = uint64_t;
const magic_bytes_t search_key{ 0x1234567890abcdef };
const magic_bytes_t replacemenet_key{ 0x1212121212121212 };

struct data_struct
{
    const magic_bytes_t value_to_replace;
};

union parsing_union
{
    magic_bytes_t value;
    uint8_t bytes[sizeof(magic_bytes_t)];
};

int main()
{
    std::fstream myFile("C:\Users\crazy_path\x64\Debug\SampleApplication.exe", ios::in | ios::out | ios::binary);
    if (!myFile) {
        cout << "Couldn't open file" << endl;
        return EXIT_FAILURE;
    } else {

        parsing_union parsing_helper{ 0x0000000000000000 };
        uint8_t &single_byte_value = parsing_helper.bytes[7];

        while (myFile.read((char*)&single_byte_value, sizeof(single_byte_value)))
        {
            if (parsing_helper.value == search_key)
            {
                uint64_t control_key_start_position = myFile.tellg();
                control_key_start_position -= sizeof(parsing_helper.value);

                myFile.seekp(control_key_start_position);
                myFile.write(reinterpret_cast<const char*>(&replacemenet_key), sizeof(replacemenet_key));
                cout << "GOT EM !! GOT EM GOOD" << endl;;
                break;
            } else {
                parsing_helper.value >>= (sizeof(single_byte_value) * 8);
            }
        }
    }

    system("pause");
    return EXIT_SUCCESS;
}

这可能看起来有点贫民窟,但您可以将整个文件加载到字节数组中,然后将字节取消引用到 uint64_t,如下所示:

(是的,抱歉,我没有包含完整的代码,但这应该会给你一个 idea/be 清楚)

byte* bytes = new byte[filesizeinbytes];
//read to the byte array
then:
DWORD offset = 0;
while( *(uint64_t*)((DWORD)bytes+offset) != 0x1234567890abcdef )
{
    offset++;
}
//We found the right address, so we can replace the bytes/address/offset
*(uint64_t*)((DWORD)bytes+offset) = 0x111111111111111;

//write the entire file, starting from bytes till the end of the file

delete[] bytes; // free the allocated memory
bytes = 0;