5
votes

I'm trying to write some code to detect a wallhack for a game. Basically, some hacks exist which create a windows aero transparent window, and they draw the hack onto this external window, so it can't be detected by taking a screenshot of the game itself.

My approach at the moment is to - 1. take a screenshot of the game window. 2. take a screenshot of the windows desktop for the same coordinates. 3. perform image analysis to compare screenshot 1 to screenshot 2 to see if there is a difference.

My problem is that screenshot 1 and screenshot 2 are not performed simultaneously so new game frames can be drawn between the two screenshots, causing false positives when the images are compared.

I want to know if there is a way to coordinate the screenshots so they occur at exactly the same time ? or somehow stop the screen drawing any new frames until my screenshots are finished?

This is the code I use for taking screenshots. Note, I have even tried to take the 2 screenshots in parallel by queuing two work items. However, even this doesn't result in the screenshots happening at exactly the same time. So I wonder if there is some way to stop any further updates to screen from the graphics card until my screenshots finish? Or any other way I can do this?

    public void DoBitBlt(IntPtr dest, int width, int height, IntPtr src)
    {
        GDI32.BitBlt(dest, 0, 0, width, height, src, 0, 0, GDI32.SRCCOPY);
    }

    public struct Windows
    {
        public Bitmap window;
        public Bitmap desktop;
    }
    public Windows CaptureWindows(IntPtr window, IntPtr desktop, User32.RECT coords)
    {
        Windows rslt = new Windows();
        // get te hDC of the target window
        IntPtr hdcSrcWindow = User32.GetWindowDC(window);
        IntPtr hdcSrcDesktop = User32.GetWindowDC(desktop);

        // get the size
        int width = coords.right - coords.left;
        int height = coords.bottom - coords.top;

        // create a device context we can copy to
        IntPtr hdcDestWindow = GDI32.CreateCompatibleDC(hdcSrcWindow);
        IntPtr hdcDestDesktop = GDI32.CreateCompatibleDC(hdcSrcDesktop);
        // create a bitmap we can copy it to,
        // using GetDeviceCaps to get the width/height
        IntPtr hBitmapWindow = GDI32.CreateCompatibleBitmap(hdcSrcWindow, width, height);
        IntPtr hBitmapDesktop = GDI32.CreateCompatibleBitmap(hdcSrcDesktop, width, height);
        // select the bitmap object
        IntPtr hOldWindow = GDI32.SelectObject(hdcDestWindow, hBitmapWindow);
        IntPtr hOldDesktop = GDI32.SelectObject(hdcDestDesktop, hBitmapDesktop);
        // bitblt over
        var handle1 = new ManualResetEvent(false);
        var handle2 = new ManualResetEvent(false);
        Action actionWindow = () => { try { DoBitBlt(hdcDestWindow, width, height, hdcSrcWindow); } finally { handle1.Set(); } };
        Action actionDesktop = () => { try { DoBitBlt(hdcDestDesktop, width, height, hdcSrcDesktop); } finally { handle2.Set(); } };
        ThreadPool.QueueUserWorkItem(x => actionWindow());
        ThreadPool.QueueUserWorkItem(x => actionDesktop());
        WaitHandle.WaitAll(new WaitHandle[] { handle1, handle2 });

        rslt.window = Bitmap.FromHbitmap(hBitmapWindow);
        rslt.desktop = Bitmap.FromHbitmap(hBitmapDesktop);

        // restore selection
        GDI32.SelectObject(hdcDestWindow, hOldWindow);
        GDI32.SelectObject(hdcDestDesktop, hOldDesktop);
        // clean up
        GDI32.DeleteDC(hdcDestWindow);
        GDI32.DeleteDC(hdcDestDesktop);
        User32.ReleaseDC(window, hdcSrcWindow);
        User32.ReleaseDC(desktop, hdcSrcDesktop);
        // free up the Bitmap object
        GDI32.DeleteObject(hBitmapWindow);
        GDI32.DeleteObject(hBitmapDesktop);
        return rslt;
    }
1
I don't believe what you are doing is ethical. Your 'wallhack' detector is gathering information that a user may not wish to divulge. Why would a user consent to you taking screen captures of their desktop whilst they play a game? The behaviour you're trying to implement comes under the classification of trojan horse. That is of course if you're not lying about the purpose of your program. <Moved here at Romuku's suggestion>Daniel Lane
This might be an appropriate use of Parallel.Foreach. The ThreadPool does not guarantee parallelism.Dustin Kingen
Suppose I have your game open in a window, but then open a web browser over that window to (google something|check my bank account|browse Facebook|do something illegal) and you take a screenshot of that. You'll record a false-positive - that I am cheating when I'm not, and if you decide that you should upload the "faked" screenshot for proof/verification, you could capture my private details accidentally.Tim S.
Tim S. no, that won't happen, the check only runs if the game window is the active window. So if you have the browser open, nothing will happen.DaManJ
PunkBuster does this, with the users consent of course. So if you guys are complaining that its not ethical, its already been done by others for the same reason as DaManJ claims he is doing it for.Nicolas Tyler

1 Answers

2
votes

You are not going to be able to have both screenshots simultaneously, unless you resource to some graphic accelarators, meaning that that will not work in every computer...

About stopping rendering, as it is a game, I think this is not so good idea... you want your game to run smoothly.

Instead I would like to suggest to store recently rendered images of your game in memory, and when you take the screenshot compare it to them. If you can add some visual clue to decide which of the recent frames to compare to then it will work much better, because otherwise you will have to compare the screenshot to all of them and that will certainly eat some CPU/GPU time.

Are you using GDI to render? if so, what you want is to store the frames of your game in DIBs (Device Independent Bitmaps) to be able to compare them.

As for the clue to decide which image to use, I would go for some sort of time representation on screen, maybe a single pixel that changes color. If so, you will read the color of that pixel, use it to find the right frame, and them proced to compare the whole picture.