dll injection: drawing simple game overlay with op

2019-05-20 10:01发布

I'm trying to draw a custom opengl overlay (steam does that for example) in a 3d desktop game. This overlay should basically be able to show the status of some variables which the user can affect by pressing some keys. Think about it like a game trainer. The goal is in the first place to draw a few primitives at a specific point on the screen. Later I want to have a little nice looking "gui" component in the game window. The game uses the "SwapBuffers" method from the GDI32.dll. Currently I'm able to inject a custom DLL file into the game and hook the "SwapBuffers" method. My first idea was to insert the drawing of the overlay into that function. This could be done by switching the 3d drawing mode from the game into 2d, then draw the 2d overlay on the screen and switch it back again, like this:

//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);

However, this does not have any effect on the game at all.

  • Is my approach correct and reasonable (also considering my 3d to 2d switching code)?
  • I would like to know what the best way is to design and display a custom overlay in the hooked function. (should I use something like windows forms or should I assemble my component with opengl functions - lines, quads ...?)
  • Is the SwapBuffers method the best place to draw my overlay?

Any hint, source code or tutorial to something similiar is appreciated too. The game by the way is counterstrike 1.6 and I don't intend to cheat online.

Thanks.

EDIT:

I could manage to draw a simple rectangle into the game's window by using a new opengl context as proposed by 'derHass'. Here is what I did:

//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);
}

Here is the result: cs overlay example It is still very simple but I will try to add some more details, text etc. to it.

Thanks.

1条回答
我欲成王,谁敢阻挡
2楼-- · 2019-05-20 10:44

If the game is using OpenGL, then hooking into SwapBuffers is the way to go, in principle. In theory, there might be sevaral different drawables, and you might have to decide in your swap buffer function which one(s) are the right ones to modify.

There are a couple of issues with such kind of OpenGL interceptions, though:

  1. OpenGL is a state machine. The application might have modified any GL state variable there is. The code you provided is far from complete to guarantee that something is draw. For example, if the application happens to have shaders enabled, all your matrix setup might be without effect, and what really would appear on the screen depends on the shaders. If depth testing is on, your fragments might lie behind what already was drawn. If polygon culling is on, your primitive might be incorrectly winded for the currect culling mode. If the color masks are set to GL_FALSE or the draw buffer is not set to where you expect it, nothing will appear.

    Also note that your attempt to "reset" the matrices is also wrong. You seem to assume that the current matrix mode is GL_MODELVIEW. But this doesn't have to be the case. It could as well be GL_PROJECTION or GL_TEXTURE. You also apply glOrtho to the current projection matrix without loading identity first, so this alone is a good reason for nothing to appear on the screen.

  2. As OpenGL is a state machine, you also must restore all the state you touched. You already try this with the matrix stack push/pop. But you for example failed to restore the exact matrix mode. As you have seen in 1, a lot more state changes will be required, so restoring it will be more comples. Since you use legacy OpenGL, glPushAttrib() might come handy here.

  3. SwapBuffers is not a GL function, but one of the operating system's API. It gets a drawable as parameter, and does only indirectly refer to any GL context. It might be called while another GL context is bound to the thread, or with none at all. If you want to play it safe, you'll also have to intercept the GL context creation function as well as MakeCurrent. In the worst (though very unlikely) case, the application has the GL context bound to another thread while it is calling the SwapBuffers, so there is no change for you in the hooked function to get to the context.

Putting this all together opens up another alternative: You can create your own GL context, bind it temporarily during the hooked SwapBuffers call and restore the original binding again. That way, you don't interfere with the GL state of the application at all. You still can augment the image content the application has rendered, since the framebuffer is part of the drawable, not the GL context. Doing so might have a negative impact on performance, but it might be so small that you never would even notice it.

Since you want to do this only for a single specific application, another approach would be to find out the minimal state changes which are necessary by observing what GL state the application actually set during the SwapBuffers call. A tool like apitrace can help you with that.

查看更多
登录 后发表回答