# Linux下 fuzz 图形界面程序的方法总结 之前有分享过windows下用autoit辅助fuzz图形界面程序。由于Linux下大多都是开源软件因此fuzz图形界面程序的方法一般是利用源码写harness,但也有一些编译环境复杂或者不提供源码的,本文分享几个Linux下较为通用的闭源图形界面程序fuzz方法: ## virtual display + xdotool Linux下的虚拟显示器(Virtual Display)是一种模拟物理显示器的技术,它可以在没有实际显示器的情况下运行应用程序、桌面环境或其他图形界面。最常见的使用方式是用Xvfb创建虚拟显示器: ```shell Xvfb:2 -screen 0 1024x768x16 ``` 之后可以通过环境变量`DISPLAY`将目标应用程序连接到创建的显示器中: ```shell DISPLAY=:2 deepin-reader ``` 为确保目标应用程序确实在使用虚拟显示器,并在那里渲染它的内容,可以使用imagemagick工具的如下命令截取屏幕截图: ```shell import -display :2 -window root image.png display image.png ``` 或者使用xwininfo来列出虚拟显示器的窗口id来确认: ```shell ivan@ivan-PC:~$ DISPLAY=:2 xwininfo -tree -root xwininfo: Window id: 0x2d4 (the root window) (has no name) Root window id: 0x2d4 (the root window) (has no name) Parent window id: 0x0 (none) 3 children: 0x20000a "Document Viewer": ("deepin-reader" "deepin-reader") 1924x1030+-450+-124 +-450+-124 1 child: 0x200006 "Document Viewer": ("deepin-reader" "deepin-reader") 1920x1012+2+2 +-448+-122 0x200008 (has no name): () 1x1+0+0 +0+0 0x200004 (has no name): () 3x3+0+0 +0+0 ``` 之后编写一个后台监控脚本,使用xdotool定时关闭目标程序窗体: ```shell ivan@ivan-PC:~$ cat watchdog.sh #!/bin/bash while true ; do sleep 3.5 DISPLAY=:2 xdotool windowactivate --sync 0x20000a key --clearmodifiers ALT+F4 done ``` `windowactivate --sync 0x20000a` 用于将窗口激活,其中 `--sync` 参数表示等待窗口完全激活后再执行下一个命令,`0x20000a` 是窗口 ID,代表要激活的窗口。`key --clearmodifiers ALT+F4` 用于模拟按下 `ALT+F4` 组合键关闭当前激活的窗口,其中 `--clearmodifiers` 参数用于清除任何已按下的修饰键(如 Shift、Control 等),以确保只有 `ALT+F4` 被模拟按下。 ## 环境变量+glib注入 之前在浏览github项目时偶然看到了使用环境变量关闭qt图形界面的例子[2] : ```c qputenv("QT_QPA_PLATFORM", "offscreen"); ``` 问一下OpenAI: ``` Q: 介绍 QT_QPA_PLATFORM 环境变量 A: QT_QPA_PLATFORM环境变量是Qt应用程序中一个重要的环境变量,用于设置Qt平台插件。Qt平台插件是为了支持不同的操作系统和设备而设计的。每个平台插件提供一个与操作系统本地窗口系统进行交互的接口,从而提供了一个跨平台的窗口系统。 该环境变量被用来设置Qt程序所使用的平台插件。该变量的值通常为“xcb”、“windows”、“cocoa”或“eglfs”,具体取决于所使用的操作系统以及Qt的配置选项。例如,在Linux上,如果想要使用Qt的X11平台插件,则需要将QT_QPA_PLATFORM环境变量设置为“xcb”。 通过设置QT_QPA_PLATFORM环境变量,可以控制Qt应用程序在不同平台下的运行方式,从而确保程序能够在不同的操作系统和设备上正确运行。 Q: 介绍 QT_QPA_PLATFORM=offscreen A: 当QT_QPA_PLATFORM环境变量的值设置为“offscreen”时,Qt应用程序将在一个虚拟的屏幕上运行,而不是在实际的物理屏幕上显示。这种模式通常用于一些特殊的场景,例如在服务器环境中运行Qt应用程序时,因为在服务器环境下往往没有可用的物理屏幕。 使用“offscreen”平台插件还可以提高Qt应用程序的性能,因为它避免了用于绘制和渲染的昂贵的图形硬件加速功能。然而,在某些情况下,这可能会导致应用程序的外观和行为与在实际的物理屏幕上运行时不同。 总之,通过设置QT_QPA_PLATFORM环境变量为“offscreen”,可以让Qt应用程序在虚拟屏幕上运行,从而适应特定的场景和需求。 ``` 猜测GTK中也有类似的环境变量: ``` Q: gtk中有类似QT_QPA_PLATFORM的环境变量参数吗 A: GTK中也有类似于QT_QPA_PLATFORM的环境变量参数,它是GDK_BACKEND。 GDK_BACKEND环境变量用于控制GTK使用哪个后端。GTK支持多个后端,如X11、Wayland、Broadway等。通过设置GDK_BACKEND环境变量,您可以指定GTK使用哪个后端。 例如,如果您想要使用Wayland后端,则可以将GDK_BACKEND设置为wayland: export GDK_BACKEND=wayland 如果您想要使用X11后端,则可以将GDK_BACKEND设置为x11: export GDK_BACKEND=x11 请注意,某些版本的GTK可能不支持所有的后端。如果您尝试使用一个不受支持的后端,GTK可能会自动回退到可用的后端。 Q: GDK_BACKEND=offscreen A: GDK_BACKEND=offscreen是一种特殊的后端,它将GTK的渲染目标设置为Offscreen surface而不是实际的屏幕。这意味着GTK可以在没有屏幕或没有显示器的环境中运行,例如在无头服务器上或通过SSH连接到远程主机。 当您将GDK_BACKEND设置为offscreen时,GTK将使用一个虚拟的、内存中的屏幕来渲染界面。由于没有屏幕可用,GTK将无法显示图形界面,但您仍然可以使用GTK API创建和操作UI元素。这使得您可以在不需要图形界面的地方运行GTK应用程序。 请注意,如果您将GDK_BACKEND设置为offscreen,则可能会导致某些GTK功能无法正常工作,因为它们依赖于与实际屏幕相关的功能,例如窗口管理和输入事件处理。 ``` 后续验证中发现GDK_BACKEND似乎不存在offscreen这一参数,OpenAI会口胡一些根本不存在的东西,在使用时需要对其答案加以甄别。 振哥通过限制 `g_main_context_iteration`函数执行次数通过LD_PRELOAD注入目标程序的方式达到fuzz图形界面程序的目的,代码非常精简: ```c static unsigned int i = 0; static unsigned int iteration_count = 10; __attribute__((constructor(5))) void __auto_exit_init(void) { char * p = getenv("iteration_count"); if (p) iteration_count = atoi(p); } int g_main_context_iteration(void * x, int y) { //fprintf(stdout, "Hook g_main_context_iteration.\n"); int (*real_func)(void *, int); real_func = dlsym(RTLD_NEXT, "g_main_context_iteration"); i = i + 1; if (i > iteration_count) { _exit(0); } return (*real_func)(x, y); } ``` `g_main_context_iteration()` 函数是 GLib 库中的函数,用于运行指定上下文中挂起的事件循环。如果一个图形界面程序使用了 GLib 库进行事件处理,那么它可能会调用 `g_main_context_iteration()` 函数。经过调试可见使用QT和GTK图形界面开发框架的程序均用到了GLib库且使用了该函数进行事件处理: gnome-calculator: ```bash test@test-VMware-Virtual-Platform:~$ gdb /usr/bin/gnome-calculator (gdb) b *g_main_context_iteration Breakpoint 2 at 0x7ffff7ec4180: file ../../../glib/gmain.c, line 4336. (gdb) c Continuing. [New Thread 0x7ffff25ff6c0 (LWP 3143)] [New Thread 0x7ffff1dfe6c0 (LWP 3144)] [New Thread 0x7ffff15fd6c0 (LWP 3145)] [Switching to Thread 0x7ffff1dfe6c0 (LWP 3144)] Thread 3 "gmain" hit Breakpoint 2, g_main_context_iteration (context=0x555555657e80, may_block=may_block@entry=1) at ../../../glib/gmain.c:4336 4336 { (gdb) bt #0 g_main_context_iteration (context=0x555555657e80, may_block=may_block@entry=1) at ../../../glib/gmain.c:4336 #1 0x00007ffff7ec4201 in glib_worker_main (data=) at ../../../glib/gmain.c:6455 #2 0x00007ffff7ef1361 in g_thread_proxy (data=0x555555658040) at ../../../glib/gthread.c:831 #3 0x00007ffff6c8f18a in start_thread (arg=) at ./nptl/pthread_create.c:444 #4 0x00007ffff6d1dbd0 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 (gdb) i thread Id Target Id Frame 1 Thread 0x7ffff37f7d40 (LWP 3140) "gnome-calculato" clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:62 2 Thread 0x7ffff25ff6c0 (LWP 3143) "pool-spawner" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 * 3 Thread 0x7ffff1dfe6c0 (LWP 3144) "gmain" g_main_context_iteration (context=0x555555657e80, may_block=may_block@entry=1) at ../../../glib/gmain.c:4336 4 Thread 0x7ffff15fd6c0 (LWP 3145) "gnome-calculato" clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:62 (gdb) ``` deepin-image-viewer: ```bash gdb-peda$ bt #0 0x00007ffff5beb2f0 in g_main_context_iteration () at /lib/x86_64-linux-gnu/libglib-2.0.so.0 #1 0x00007ffff6b0e897 in QEventDispatcherGlib::processEvents(QFlags) () at /lib/x86_64-linux-gnu/libQt5Core.so.5 #2 0x00007ffff6abc2bb in QEventLoop::exec(QFlags) () at /lib/x86_64-linux-gnu/libQt5Core.so.5 #3 0x00007ffff690be66 in QThread::exec() () at /lib/x86_64-linux-gnu/libQt5Core.so.5 #4 0x00007ffff623a545 in () at /lib/x86_64-linux-gnu/libQt5DBus.so.5 #5 0x00007ffff6915a57 in () at /lib/x86_64-linux-gnu/libQt5Core.so.5 #6 0x00007ffff7addfa3 in start_thread (arg=) at pthread_create.c:486 #7 0x00007ffff65ed63f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 gdb-peda$ i thread Id Target Id Frame 1 Thread 0x7ffff1bd2c00 (LWP 20154) "deepin-image-vi" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 2 Thread 0x7ffff0d0c700 (LWP 20159) "QXcbEventReader" 0x00007ffff65e2989 in __GI___poll (fds=0x7ffff0d0b8b8, nfds=0x1, timeout=0xffffffff) at ../sysdeps/unix/sysv/linux/poll.c:29 * 3 Thread 0x7fffebfff700 (LWP 20160) "QDBusConnection" 0x00007ffff5beb2f0 in g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0 ``` ## 参考链接 [1] https://blog.gypsyengineer.com/en/security/fuzzing-gui-applications-abiword.html [2] https://github.com/xfuzzable/dde-fuzz/blob/master/fuzz-dde-clipboard/main.cpp#L14