跳转至

放置和移除跳转钩子

由于你只是用钩子找设备,只需要调用它一次。获得设备后,你会移除跳转钩子,把执行恢复到 EndScene() 的开头,让绘制循环继续工作。信不信由你,这让你的生活容易得多。由于代码会立即恢复,你的蹦床不需要执行被跳转替换的命令,也不需要给跳转填充 NOP。你只需存储原始字节并放置钩子。为此,你使用清单 8-11 中跳转钩子代码的略微调整版本:

unsigned char* hookWithJump(DWORD hookAt, DWORD 
newFunc)
{
    DWORD newOffset = newFunc - hookAt - 5;
  auto oldProtection = protectMemory<BYTE[5]>(hook
At, PAGE_EXECUTE_READWRITE);
char[5];
    for (int i = 0; i < 5; i++)
      originals[i] = readMemory<unsigned 
char>(hookAt + i);
  writeMemory<BYTE>(hookAt, 0xE9);
    writeMemory<DWORD>(hookAt + 1, newOffset);
    protectMemory<BYTE[5]>(hookAt, oldProtection);
    return originals;
}

与清单 8-11 中的函数一样,这个函数使内存可写①,放置钩子③,并恢复内存保护。放置钩子前,它分配一个名为 originals 的 5 字节缓冲区②,并用原始字节填充。放置钩子后把 originals 返回给调用函数。

到移除钩子的时候,把 originals 传给以下函数:

void unhookWithJump(DWORD hookAt, unsigned char* 
originals)
{
    auto oldProtection = protectMemory<BYTE[5]>(hoo
kAt, PAGE_EXECUTE_READWRITE);
    for (int i = 0; i < 5; i++)
        writeMemory<BYTE>(hookAt + i, 
originals[i]);
    protectMemory<BYTE[5]>(hookAt, oldProtection);
    delete [] originals;
}

这段代码只是遍历 originals,把那 5 个字节放回原处,这样执行返回 EndScene() 函数时一切如常。到时候,你可以用两行代码放置和移除实际钩子,像这样:

auto originals = hookWithJump(EndSceneAddress, 
(DWORD)&endSceneTrampoline);
unhookWithJump(EndSceneAddress, originals);

有了 hookWithJump() 和 unhookWithJump() 函数,是时候准备回调并找到设备了。