2

我需要处理发送到笔记本电脑视频显示器的图像,并且需要使用 C++ 或 shell 程序将键盘输入发送到我的 Linux 系统。

我的目标是处理属于 FPS 游戏一部分的图像,然后根据这些图像在该游戏中采取行动(因此是键盘输入)。我没有试图理解(如果可能的话)如何使用一些 API 与游戏 X 或 Y 交互,而是认为这是与任何游戏交互的最快方式,以某种方式劫持 Linux 输入和输出。

有没有任何方法可以在没有任何内核或设备驱动程序黑客攻击的情况下做到这一点?我之前使用 recordmydesktop 将我的桌面录制为视频,我想我可以破解它的代码并尝试从中进行逆向工程。还有其他想法吗?我在 Ubuntu 11 上。

相关问题

4

3 回答 3

5

你不需要做任何像内核或设备驱动程序那样低级的事情来做到这一点。

例如,您可以使用XTest X11 扩展程序以编程方式伪造输入事件(来自此贴文,还有另一个键盘示例)。

#include <X11/extensions/XTest.h>  
#include <unistd.h>  

int main ()  
{  
  Display *dpy = NULL;  
  XEvent event;  

  dpy = XOpenDisplay (NULL);  

  /* Get the current pointer position */  
  XQueryPointer (dpy, RootWindow (dpy, 0),  
        &event.xbutton.root, &event.xbutton.window,  
        &event.xbutton.x_root, &event.xbutton.y_root,  
        &event.xbutton.x, &event.xbutton.y,  
        &event.xbutton.state);  

  /* Fake the pointer movement to new relative position */  
  XTestFakeMotionEvent (dpy, 0, event.xbutton.x + 100,  
        event.xbutton.y + 50, CurrentTime);  
  XSync(dpy, 0);  
  XCloseDisplay (dpy);  
  return 0;  
}   

要捕获图像,最简单的方法是使用函数 interposition (via LD_PRELOAD) 来“拦截”对 的调用glXSwapBuffers,一旦绘制了每一帧,就会调用它。从那里你可以复制帧缓冲区的内容,使用glReadPixels它并做你想做的事。

例如,用于拦截 OpenGL 帧的未经测试的轮廓:

// Function pointer to the *real* glXSwapBuffers
static void (*glx_fptr)(Display*, GLXDrawable) = NULL;

// Make sure init gets called when the shared object is loaded. GCC specific.
static void init(void)  __attribute__((constructor));

static void init(void) {
    dlerror();
    // find the real glXSwapBuffers
    glx_fptr = dlsym(RTLD_NEXT, "glXSwapBuffers");
    if (NULL == glx_fptr)
        fprintf(stderr, "[glvidcap] %s\n", dlerror());
}

void glXSwapBuffers(Display *dpy, GLXDrawable drawable) {
    unsigned int w = 0;
    unsigned int h = 0;
    static int x,y;
    static Window win;
    static unsigned int border,depth;
    // Find the window size. (You could skip this and make it all static if you
    // Trust the window not to change size
    XGetGeometry(dpy, drawable, &win, &x, &y, &w, &h, &border, &depth);

    // Assuming frame is some memory you want the frame dumped to:
    glReadPixels(0,0,w,h,GL_BGR,GL_UNSIGNED_BYTE, frame);

    // Call the real function:
    assert(glx_fptr);
    glx_fptr(dpy, drawable);
}

然后,您想LD_PRELOAD在运行您正在查看的任何游戏之前将其编译为共享对象和共享对象。

如果它碰巧是一个 SDL 应用程序,您可以拦截对SDL_FlipSDL_UpdateRect酌情的调用。

于 2011-08-28T14:40:51.473 回答
1

感谢@awoodland的回答,我找了相关资源,发现了这个

http://bzr.sesse.net/glcapture/glcapture.c

我将此代码编译为

gcc -shared -fPIC -o glcapture.so glcapture.c -ldl

并在脚本中为 FPS 游戏 Urban Terror 加载它

LD_PRELOAD=`pwd`/glcapture.so [DIR]/UrbanTerror/ioUrbanTerror.i386

运行此脚本的那一刻,游戏已加载。出于某种原因,游戏图形不显示,但我可以稍后改进。当我退出游戏时,我看到控制台上打印了 gettimeofday 消息,这告诉我钩子起作用了。在我继续处理此代码时,我将提供更多详细信息。

为了发送按键,我点击了链接

http://bharathisubramanian.wordpress.com/2010/03/14/x11-fake-key-event-generation-using-xtest-ext/

在我在 Ubuntu 上安装了必要的软件包之后

sudo apt-get install libxtst-dev

然后 fakeKey.c 编译并没有问题地工作。

注意:我为此在 Github 上启动了一个项目,欢迎任何人 fork、帮助等。

于 2011-08-28T19:46:38.637 回答
0

我终于有了解决办法。我相信 UrT 会自行加载 OpenGL,因此无法进行墙黑客等操作。那么剩下的最好的选择就是截取 X 个屏幕截图。即使使用像 Python 这样的脚本语言,它的工作速度也非常快。以下代码获取连续的屏幕截图并通过 OpenCV 将它们显示为动画。当然,您需要以最小化模式启动 UrT。其余的细节在我的项目中

import gtk.gdk
import PIL
from opencv.cv import *
from opencv.highgui import *
from opencv.adaptors import PIL2Ipl

w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz

size_x = 600
size_y = 400
start_x = 0
start_y = 100
end_x = start_x+size_x
end_y = start_y+size_y
box = (start_x, start_y, start_x+size_x, start_y+size_y)

while True:
    pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
    pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
    width,height = pb.get_width(),pb.get_height()
    im = PIL.Image.fromstring("RGB",(width,height),pb.get_pixels())
    im = im.crop(box)
    cv_img = PIL2Ipl(im)
    cvNamedWindow("fps")
    cvShowImage("fps", cv_img)
    cvWaitKey(30) 

另外,为了向游戏发送密钥,上面的方法不起作用,我必须使用xdotool才能将前进步行密钥发送到 UrT,

xdotool search --name ioUrbanTerror  windowactivate keydown W
于 2011-10-11T12:58:13.927 回答