dll注入:使用opengl绘制简单的游戏覆盖图

dll injection: drawing simple game overlay with opengl

本文关键字:简单 游戏 覆盖图 绘制 opengl 注入 使用 dll      更新时间:2023-10-16

我正试图在一个3d桌面游戏中绘制一个自定义的opengl覆盖(例如steam)。这个覆盖基本上应该能够显示用户可以通过按下某些键进行影响。把它想象成一个游戏教练。目标首先是在屏幕上的特定点绘制一些图元。稍后,我想在游戏窗口中有一个好看的"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);

然而,这对游戏没有任何影响。

  • 我的方法正确合理吗(还考虑到我的3d到2d切换代码)
  • 我想知道在挂钩函数中设计和显示自定义覆盖的最佳方法是什么。(我应该使用类似windows窗体的东西,还是应该用opengl函数组装我的组件——线条、四边形…?)
  • SwapBuffers方法是绘制覆盖图的最佳方法吗

任何类似的提示、源代码或教程也将不胜感激。顺便说一句,游戏是反击1.6,我不打算在网上作弊。

谢谢。

编辑:

我可以通过使用"derHass"提出的新的opengl上下文,在游戏窗口中绘制一个简单的矩形。以下是我所做的:

//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覆盖示例它仍然很简单,但我会尝试添加一些更多的细节,文本等。

谢谢。

如果游戏使用OpenGL,那么原则上挂接SwapBuffers是可行的。理论上,可能有几种不同的可提取项,您可能必须在交换缓冲区函数中决定修改哪一个是正确的。

不过,这种OpenGL拦截有几个问题:

  1. OpenGL是一个状态机。应用程序可能已经修改了任何GL状态变量。您提供的代码还远远不能保证某些东西是绘制的。例如,如果应用程序恰好启用了着色器,则所有矩阵设置可能都没有效果,屏幕上实际显示的内容取决于着色器。如果进行深度测试,您的碎片可能位于已经绘制的碎片后面。如果多边形剔除处于启用状态,则基本体可能会错误地缠绕到当前剔除模式。如果颜色遮罩设置为GL_FALSE,或者绘制缓冲区未设置为预期位置,则不会显示任何内容。

    还要注意,您试图"重置"矩阵也是错误的。您似乎认为当前的矩阵模式是GL_MODELVIEW。但事实并非如此。它也可以是GL_PROJECTIONGL_TEXTURE。您还可以将glOrtho应用于当前投影矩阵,而无需首先加载标识,因此仅此一点就是屏幕上不显示任何内容的充分理由。

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

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

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

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