const结构内存修补声明

时间:2012-09-28 21:03:21

标签: c++ c hook code-injection

我正在做反向引擎的东西,并通过DLL修补游戏的内存。通常我坚持使用相同的旧方法在一个或多个函数中修补所有内容。但是感觉可以通过使用结构数组来更好地实现它,该结构数组定义了需要进行的内存写入并一次性循环遍历它们。更容易管理,IMO。

但是,我想让它保持不变。所以数据都是一次性的(在.rdata中)而不是为每个补丁动态分配内存,这是一个带有'bytesize'数据的简单任务,例如:

struct struc_patch
{
    BYTE val[8];    // max size of each patch (usually I only use 5 bytes anyway for call and jmp writes)
                    // I can of course increase this if really needed
    void *dest;
    char size;
} patches[] =
{
    // simply write "01 02 03 04" to 0x400000
    {{0x1, 0x2, 0x3, 0x4}, (void*)0x400000, 4},
};
//[...]
for each(struc_patch p in patches)
{
    memcpy(p.dest, p.val, p.size);
}

但是当我想要获得更好的类型时,我发现无法指定像“0x90909090”这样的整数作为字节数组“90 90 90 90”。所以这不起作用:

struct struc_patch
{
    BYTE val[8];    // max size of each patch (usually I only use 5 bytes anyway for call and jmp writes)
                    // I can of course increase this if really needed
    void *dest;
    char size;
} patches[] =
{
    // how to write "jmp MyHook"? Here, the jmp offset will be truncated instead of overlapping in the array. Annoying.
    {{0xE9, (DWORD)&MyHook - 0x400005}, (void*)0x400000, 5},
};

当然主要的问题是& MyHook必须由编译器解决。是否有其他方法可以获得所需的结果并将其保持为常量?

说实话,我对STL没什么经验。因此,如果有使用该解决方案的解决方案,我可能需要对其进行详细解释才能正确理解代码。我是一个很大的C / C ++ / WinAPI迷瘾大声笑,但这是一个类似性质的游戏,所以它适合。

3 个答案:

答案 0 :(得分:2)

我认为STL中的任何内容都不会帮助您,而不是在编译时。 使用宏可能有一种奇特的方式来处理模板。 (用逗号分隔字节)

但我建议做一些像这样简单的事情:

struct jump_insn
{
    unsigned char opcode;
    unsigned long addr;
} jump_insns[] = {
    {0xe9, (unsigned long)&MyHook - 0x400005}
};

struct mem
{
   unsigned char val[8];
} mems[] = {
    {1,2,3,4}
};

struct struc_patch
{
    unsigned char *val;    // max size of each patch (usually I only use 5 bytes anyway for call and jmp writes)
                    // I can of course increase this if really needed
    void *dest;
    char size;
} patches[] =
{
    // simply write "01 02 03 04" to 0x400000
    {(unsigned char*)(&mems[0]), (void*)0x400000, 4},

    // how to write "jmp MyHook"? Here, the jmp offset will be truncated instead of overlapping in the array. Annoying.
    {(unsigned char*)(&jump_insns[0]), (void*)0x400000, 5},
};

你不能内联所有内容,你需要新类型的不同类型的补丁,但它们可以任意长(不只是8个字节),一切都将在.rodata。

答案 1 :(得分:0)

处理这种情况的更好方法是动态计算地址差异。例如(source):

#define INST_CALL    0xE8

void InterceptLocalCode(BYTE bInst, DWORD pAddr, DWORD pFunc, DWORD dwLen)
{
    BYTE *bCode = new BYTE[dwLen];
    ::memset(bCode, 0x90, dwLen);
    DWORD dwFunc = pFunc - (pAddr + 5);

    bCode[0] = bInst;
    *(DWORD *)&bCode[1] = dwFunc;
    WriteBytes((void*)pAddr, bCode, dwLen);

    delete[] bCode;
}

void PatchCall(DWORD dwAddr, DWORD dwFunc, DWORD dwLen)
{
    InterceptLocalCode(INST_CALL, dwAddr, dwFunc, dwLen);
}

dwAddr是放入调用指令的地址,dwFunc是要调用的函数,dwLen是要替换的指令的长度(主要用于计算要放入多少NOP)。

答案 2 :(得分:0)

总结一下,我的解决方案(感谢Nicolas的建议):

#pragma pack(push)
#pragma pack(1)
#define POFF(d,a) (DWORD)d-(a+5)
struct jump_insn
{
    const BYTE opcode = 0xE9;
    DWORD offset;
};

struct jump_short_insn
{
    const BYTE opcode = 0xEB;
    BYTE offset;
};

struct struc_patch
{
    void *data;
    void *dest;
    char size;
};
#pragma pack(pop)

并在使用中:

// Patches
jump_insn JMP_HOOK_LoadButtonTextures = {POFF(&HOOK_LoadButtonTextures, 0x400000)};

struc_patch patches[] =
{
    {&JMP_HOOK_LoadButtonTextures, IntToPtr(0x400000)},
};

使用类成员const我可以更容易和更清晰地定义所有内容,它可以简单地全部记忆。 pack pragma当然需要确保memcpy不会复制BYTE操作码和DWORD值之间的3个对齐字节。

谢谢大家,帮助我使修补方法更加健壮。