截取挂钩OpenGL应用程序的屏幕截图

本文关键字:屏幕截图 应用程序 OpenGL 截取 | 更新日期: 2023-09-27 17:57:27

我有一个小问题。我想拍一张Genymotion的屏幕截图。由于这不是那么容易,并且需要OpenGL Hooking,我有一个通过Easyhook注入的DLL。

现在DLL挂接方法wglSwapBuffers()来处理缓冲区,截取屏幕截图,然后将缓冲区返回到genymotion。问题是,一旦尝试使用任何OpenGL方法,它就会崩溃应用程序(Kernelbase.dll)和/或mscorlib。

当调用SwapBuffers_Hooked时,删除它们并只打印文本效果很好。

这是注入进程的dll

   public class Main : EasyHook.IEntryPoint
{

    GlMon.GlMonInterface Interface;
    Stack<String> Queue = new Stack<String>();
    public LocalHook CreateBufferHook = null;
    Bitmap bitmap = null;
    public Main(
        RemoteHooking.IContext InContext,
        String InChannelName)
    {

        // connect to host...
        Interface = RemoteHooking.IpcConnectClient<GlMon.GlMonInterface>(InChannelName);
        Interface.Ping();
    }
    public void Run(
        RemoteHooking.IContext InContext,
        String InChannelName)
    {
        // install hook...
        try
        {
            CreateBufferHook = LocalHook.Create(LocalHook.GetProcAddress("opengl32.dll", "wglSwapBuffers"), new DwglSwapBuffers(SwapBuffers_Hooked), this);
            CreateBufferHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
        }
        catch (Exception ExtInfo)
        {
            Interface.ReportException(ExtInfo);
            return;
        }
        Interface.IsInstalled(RemoteHooking.GetCurrentProcessId());
        RemoteHooking.WakeUpProcess();
        try
        {
            while (true)
            {
                Thread.Sleep(500);
                // transmit newly monitored file accesses...
                if (this.bitmap != null)
                {
                    Interface.onSwapBuffer(RemoteHooking.GetCurrentProcessId(), "bitmap received. size=" + bitmap.Size);
                    this.bitmap = null;
                }
                else
                {
                    Interface.onSwapBuffer(RemoteHooking.GetCurrentProcessId(), "ping..");
                    Interface.Ping();
                }
            }
        }
        catch
        {
            // Ping() will raise an exception if host is unreachable
        }
        // wait for host process termination...
    }
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
    delegate IntPtr DwglSwapBuffers(IntPtr hdc);

    [DllImport("opengl32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    static extern IntPtr wglSwapBuffers(IntPtr hdc);

    // this is where we are intercepting all file accesses!
    static IntPtr SwapBuffers_Hooked(IntPtr hdc)
    {
        Main This = (Main)HookRuntimeInfo.Callback;
        try
        {
            /** apitrace dump is 
             *                                      
                11288 wglSwapBuffers(hdc = 0xb0013e0a) = TRUE
                11289 glFlush()
                11290 wglMakeCurrent(hdc = 0xf601374b, hglrc = 0x10004) = TRUE
                11291 glDisable(cap = GL_SCISSOR_TEST)
                11292 glClear(mask = GL_COLOR_BUFFER_BIT)
                11293 glEnable(cap = GL_SCISSOR_TEST)
                11294 glDisable(cap = GL_BLEND)
                11295 glBindTexture(target = GL_TEXTURE_2D, texture = 20)
                11296 glPushClientAttrib(mask = GL_CLIENT_VERTEX_ARRAY_BIT)
                11297 glPushAttrib(mask = GL_TRANSFORM_BIT)
                11298 glMatrixMode(mode = GL_PROJECTION)
                11299 glPushMatrix()
                11300 glLoadIdentity()
                11301 glGetIntegerv(pname = GL_VIEWPORT, params = {0, 0, 860, 720})
                11302 glOrtho(left = 0, right = 860, bottom = 0, top = 720, zNear = 0, zFar = -1)
                11303 glMatrixMode(mode = GL_TEXTURE)
                11304 glPushMatrix()
                11305 glLoadIdentity()
                11306 glMatrixMode(mode = GL_MODELVIEW)
                11307 glPushMatrix()                                     * **/
              Bitmap bitmap = new Bitmap(860, 720);
            Rectangle bounds = new Rectangle(0, 0, 860, 720);
            BitmapData bmpData = bitmap.LockBits(bounds, ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            wgl.glReadBuffer((uint)wgl.DrawBufferMode.GL_FRONT_AND_BACK);
            wgl.glReadPixels(0, 0, bounds.Width, bounds.Height, (uint)wgl.PixelFormat.GL_RGB, (uint)wgl.DataType.GL_UNSIGNED_BYTE, bmpData.Scan0);
            bitmap.UnlockBits(bmpData);
            bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
            int i = 0;
            while (File.Exists("images/test_" + i + ".png"))
            {
                i++;
            }
            bitmap.Save("images/test_" + i + ".png");
        return wgl.SwapBuffers(hdc);

        }
        catch
        {
        }
        // call original API...
        return wglSwapBuffers(hdc);
    }

崩溃的错误是

Problemereignisname:    APPCRASH
Anwendungsname: player.exe
Anwendungsversion:  0.0.0.0
Anwendungszeitstempel:  54edc8da
Fehlermodulname:    KERNELBASE.dll
Fehlermodulversion: 6.3.9600.17415

调试器显示的另一个异常是System.FileNotFoundexception,但我无法确定找不到哪个文件。

更新:我已经更新了代码。这个代码在大多数情况下都有效,并且图像是存储的。不管怎样,它们有令人讨厌的颜色。看起来有些颜色不见了和/或红色变少了。是什么原因导致了这个问题?如果我选择另一个PixelFormat,它会再次崩溃。与GL_RGB相同。RGBA似乎崩溃了。如果我增加DataType,它也会崩溃。你知道为什么这些颜色是卷曲的吗?

截取挂钩OpenGL应用程序的屏幕截图

代码片段

IntPtr hglrc = wgl.GetCurrentDC();
[...]
wgl.MakeCurrent(hdc, hglrc);

肯定不会起作用(这可能是崩溃的原因)。wglGetCurrentDC()返回设备上下文,而不是GL上下文的句柄。您可以通过glGetCurrentContext()查询总账上下文。然而,由于您需要GL上下文句柄的唯一操作似乎是设置当前上下文,因此这对您毫无帮助。要么,应用程序将上下文绑定,那么您的操作根本不必要,要么,它将其解除绑定或使用几个不同的上下文-然后,您必须挂接到wglMakeCurrent,并实际跟踪哪个上下文将呈现给哪个DC,这样您就可以在特定的SwapBuffers调用中找到需要从哪个上下文获取缓冲区数据的上下文。

您的代码还有另一个问题:您要交换缓冲区两次:一次是在读取数据之前,一次是之后。在缓冲区swao之后,后缓冲区的内容将变为未定义。因此,有两种策略可以遵循:

  1. 获取后台缓冲区的内容,然后交换,或者
  2. 交换,并获取前缓冲区的内容

但永远不要交换两次。

我认为GRB问题的原因是Genymotion的数据是Big endian,而你的hook程序是Little endian。您应该反转字节数组。