While working on my netbook away from my desktop machine, I pondered how I could view my email clients display (claws-mail, a GTK app), without having to resort to something such as VNC.

I know this can be done using utilities such as xmove, which essentially act as a proxy X server that can detach a client from one display and move it to another. But this requires you to start xmove before the application, and to tell the application to launch with its display on xmove.

Nah. We need something cooler than this.

After using some Google fu it became apparent that the GTK library has a function named gtk_window_set_screen which is demonstrated in the gtk-demo application that comes with the GTK sources. This function allows the application to switch X11 display on the fly.. provided the application has a way to do so (which barely any do, apart from the GIMP, because most don't need to).

So I decided to look into ways of doing code injection in Linux, as a way of being able to call that function inside our target application. Turns out this is easy as pie using GDB.

First of all, we need to create a small library that contains our code. This will be loaded into memory and executed by us later. The code I hacked together looks like this (no guarantees whatsoever that this will work on your machine):

// Include the headers we will need
#include <stdio.h>
#include <gtk/gtk.h>

// Now write the function we will be executing.
// This one takes a string containing the display to connect to
void change_display(char *display_str)
{
  GdkDisplay *display = gdk_display_get_default();
  GtkWidget *toplevel = NULL;

  GdkWindow *window = NULL;
  GtkWidget *widget = NULL;

  // Not 100% necessary but gives you time before the line below this one runs
  sleep(5);

  // This line gets the GDK window below the cursor
  window = gdk_display_get_window_at_pointer(display, NULL, NULL);
  if (window) {
      gdk_window_get_user_data(window, (gpointer)&widget);

      toplevel = gtk_widget_get_toplevel(widget);

      GdkDisplay *new_display = gdk_display_open(display_str);
      if (new_display)
          gtk_window_set_screen(GTK_WINDOW(toplevel), gdk_display_get_screen(new_display, 0));
      else
          fprintf(stderr, "Could not connect to display \"%s\"\n", display_str);
  } else {
      fprintf(stderr, "No window found\n");
  }

  // By this point the window should have moved itself to the display you passed in
}

Cool. So now we just need to compile it and we're a step closer to winning:

gcc -shared -fPIC -o lib lib.c `pkg-config --libs --cflags gtk+-2.0`

Now we should be left with our library sitting in our current directory. Now we can actually inject the code. For this part, we will fire up GDB, attaching to the program we wish to move:

gdb attach `pidof claws-mail`

A bunch of lines will scroll by, which you can largely ignore for this exercise, but you should be left with a nice (gdb) prompt. Now it's time to inject our code!

(gdb) print (void*)dlopen("/path/to/your/current/directory/lib", 2)

Yep, GDB has now loaded our library into the memory space of the application. Now we can run our function :)

(gdb) print change_display("192.168.1.2:0")

If we now quickly move our cursor over to the window of the application. After a few seconds the window should successfully move to the proper display.

Pretty easy huh? :D There are plenty more things you can achieve with simple code injection like this, and for the purpose of this exercise, we could have written a GTK module instead. But this is just cool.


(Maybe) Related posts: