dll 注入:使用 opengl 绘制简单的游戏叠加层

dll injection: drawing simple game overlay with opengl

我正在尝试在 3d 桌面游戏中绘制自定义 opengl 叠加层(例如 steam 就是这样做的)。 这个覆盖图基本上应该能够显示用户需要的一些变量的状态 可以通过按一些键来影响。把它想象成一个游戏教练。 目标首先是在屏幕上的特定点绘制一些图元。后来我想在游戏window中有一个好看的"gui"组件。 游戏使用了GDI32.dll中的"SwapBuffers"方法。 目前我能够将自定义 DLL 文件注入游戏并挂钩 "SwapBuffers" 方法。 我的第一个想法是将覆盖图插入到该函数中。这可以通过将游戏的 3d 绘图模式切换为 2d,然后在屏幕上绘制 2d 叠加层并再次切换回来来完成,如下所示:

//SwapBuffers_HOOK (HDC)
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glOrtho(0.0, 640, 480, 0.0, 1.0, -1.0);

//"OVERLAY"
glBegin(GL_QUADS);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex2f(0, 0);
glVertex2f(0.5f, 0);
glVertex2f(0.5f, 0.5f);
glVertex2f(0.0f, 0.5f);
glEnd();

glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

SwapBuffers_OLD(HDC);

但是,这对游戏没有任何影响。

也欢迎任何类似的提示、源代码或教程。 顺便说一句,游戏是反恐精英1.6,我不打算在线作弊。

谢谢。

编辑:

通过使用 'derHass' 提议的新 opengl 上下文,我可以设法在游戏 window 中绘制一个简单的矩形。这是我所做的:

//1. At the beginning of the hooked gdiSwapBuffers(HDC hdc) method save the old context
GLboolean gdiSwapBuffersHOOKED(HDC hdc) {
    HGLRC oldContext =  wglGetCurrentContext();
//2. If the new context has not been already created - create it   
//(we need the "hdc" parameter for the current window, so the initialition 
//process is happening in this method - anyone has a better solution?)
//Then set the new context to the current one.
    if (!contextCreated) {
        thisContext = wglCreateContext(hdc);
        wglMakeCurrent(hdc, thisContext);
        initContext();
    }
    else {
        wglMakeCurrent(hdc, thisContext);
    }
    //Draw the quad in the new context and switch back to the old one.
    drawContext();
    wglMakeCurrent(hdc, oldContext);
    return gdiSwapBuffersOLD(hdc);
}

GLvoid drawContext() {
    glColor3f(1.0f, 0, 0);
    glBegin(GL_QUADS);
    glVertex2f(0,190.0f);
    glVertex2f(100.0f, 190.0f);
    glVertex2f(100.0f,290.0f);
    glVertex2f(0, 290.0f);
    glEnd();
}

GLvoid initContext() {
    contextCreated = true;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 640, 480, 0.0, 1.0, -1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glClearColor(0, 0, 0, 1.0);
}

结果如下: cs overlay example 它仍然很简单,但我会尝试添加更多细节、文本等。

谢谢。

如果游戏使用的是 OpenGL,原则上挂接到 SwapBuffers 是可行的方法。理论上,可能有几个不同的可绘制对象,您可能必须在交换缓冲区函数中决定哪些是正确的修改对象。

不过,此类 OpenGL 拦截存在一些问题:

  1. OpenGL 是一个状态机。应用程序可能已经修改了现有的任何 GL 状态变量。您提供的代码远未完成,无法保证绘制某些内容。例如,如果应用程序碰巧启用了着色器,则所有矩阵设置可能都没有效果,屏幕上真正显示的内容取决于着色器。 如果开启了深度测试,您的片段可能位于已绘制的内容后面。如果启用了多边形剔除,则您的图元可能会针对当前剔除模式错误缠绕。如果颜色遮罩设置为 GL_FALSE 或绘图缓冲区未设置为您期望的位置,则不会出现任何内容。

    另请注意,您对 "reset" 矩阵的尝试也是错误的。您似乎假设当前矩阵模式是 GL_MODELVIEW。但这不一定是这种情况。它也可以是 GL_PROJECTIONGL_TEXTURE。您还可以将 glOrtho 应用于当前投影矩阵,而无需先加载恒等式,因此仅此一项就是屏幕上不显示任何内容的充分理由。

  2. 由于 OpenGL 是一个状态机,您还必须恢复您所触及的所有状态。您已经使用矩阵堆栈 push/pop 尝试过此操作。但是,例如,您未能恢复精确的矩阵模式。正如您在 1 中看到的,将需要更多的状态更改,因此恢复它会更复杂。由于您使用旧版 OpenGL,glPushAttrib() 在这里可能会派上用场。

  3. SwapBuffers 不是 GL 函数,而是操作系统的 API 之一。它获取一个可绘制对象作为参数,并且仅间接引用任何 GL 上下文。 它可能在另一个 GL 上下文绑定到线程时被调用,或者根本没有 none。 如果你想安全地玩它,你还必须拦截GL上下文创建函数以及MakeCurrent。在最坏的(尽管不太可能)情况下,应用程序在调用 SwapBuffers 时将 GL 上下文绑定到另一个线程,因此挂钩函数中没有任何更改可以获取上下文。

将这些放在一起开辟了另一种选择:您可以创建自己的 GL 上下文,在挂钩 SwapBuffers 调用期间临时绑定它,然后再次恢复原始绑定。这样,您根本不会干扰应用程序的 GL 状态。您仍然可以增加应用程序呈现的图像内容,因为帧缓冲区是可绘制对象的一部分,而不是 GL 上下文。这样做可能会对性能产生负面影响,但它可能很小,您甚至都不会注意到它。

由于您只想对单个特定应用程序执行此操作,另一种方法是通过观察应用程序在 SwapBuffers 调用期间实际设置的 GL 状态来找出必要的最小状态更改。 apitrace 之类的工具可以帮助您。