craftwa.re

A walk outside the sandbox

Home Blog Cheat Sheets MacOS Tips Area 51 About

Hacking GNOME Minesweeper

|

Logo

Context

First I would like to give credit to the author of the picture I’ve used above, taken from pxleyes. It’s awesome, thank you! :pray:

After reading Game Hacking: WinXP Minesweeper I realised I wanted to do something similar but on a Linux system. This would be a great opportunity for me to learn some new skills, like debuging stripped GTK applications or becoming a GDB ninja (not!) while doing something interesting all along and with potential applications outside the gaming area.

My goal in this post is to reverse engineer the GNOME Minesweeper game and locate relevant areas in memory to patch. As a bonus, the result has to be as visually pleasing as possible! No pre-requisites are strictly necessary to follow along, but basic reversing knowledge and familiarity with GDB is always nice to have! Most important of course is a desire to learn. So let’s begin.

Debugging GTK applications

To perform debugging and inspection of GTK applications, we have two quite stable options: GTKInspector and gtkparasite (on which the GTKInspector is also based). I opted for the Parasite, which seems to be more established and has interesting features, like the ability to interact with GTK widgets from a Python shell, and apply CSS styles globally or individually per objects. A few things are needed to quickly get it working:

  • Download and install:
$ git clone git://github.com/chipx86/gtkparasite
$ cd gtkparasite
$ ./autogen.sh --with-gtk=3.0
$ make
$ sudo make install
  • Set the GTK_modules environment variable:
 export GTK_MODULES=gtkparasite:$GTK_MODULES
  • Make sure the application to be debugged is built using GTK3, because gtkparasite dropped support for GTK2. Check out which version of GTK gnome-mines was built against:
$ ldd `which gnome-mines` | grep gtk
    libgtk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007f2dc7f18000)
  • If you still get an error when trying to lunch GTK apps, make sure the libraries are placed in the correct location. The following workaround applies for Ubuntu 16.04:
$ gnome-calculator                                  
Gtk-Message: Failed to load module "gtkparasite"
$ find / -name  "libgtkparasite*.so" 2>/dev/null
/home/liv/Downloads/gtkparasite/src/.libs/libgtkparasite.so
/usr/lib/x86_64-linux-gnu/gtk-3.0/modules/libgtkparasite.so
/usr/local/lib/gtk-3.0/modules/libgtkparasite.so
$ sudo cp /usr/local/lib/gtk-3.0/modules/libgtkparasite.so /usr/lib/x86_64-linux-gnu/gtk-3.0/modules

Now that gtkparasire is up and running, let’s make sure we can perform some basic inspection of GUI widgets:

  • If everything worked fine, when launching the calculator (gnome-calc) you should also see also the gtk-parasite window.
  • Use the magnifying glass to select the text box.
  • In the right pane with the object’s properties, double-click on the buffer’s value.
  • Then navigate to the properties of text field and change it to whatever you like. Voila!

UI hacking

Static analysis

To be able to mess with objects in memory, we need to understand the internals of Minesweeper first. Luckily, the source code is available online. The goal for this section is to perform simple identification of data structures in memory, like obtaining the number of mines and details of the board (width, height) for example.

  • From gtkparasite we can see that the mine field is stored in a **MinefieldView_ class. This will be the starting point. In the source code (minefield.vala) we see however that all the interesting fields are stored in a **_minefield class:
public class Minefield : Object
{
    /* Size of map */
    public uint width = 0;
    public uint height = 0;

    /* Number of mines in map */
    public uint n_mines = 0;

    /* State of each location */
    protected Location[,] locations;
    [...]
  • After a bit of poking around with IDA Pro, we find the link between the MineFieldView object and the minefield class.
I have renamed the funtions and classes below (e.g. MFView, ERR_minefield_view_get_minefield) manually, identified using the available source code.
__int64 __fastcall get_minefield(__int64 MFView)
{
  __int64 result; // rax@2

  if ( MFView )
    result = *(_QWORD *)(*(_QWORD *)(MFView + 0x30) + 0x28LL);
  else
    result = ERR_minefield_view_get_minefield();
  return result;
}
  • This means that to obtain a pointer to the most important structure - minefield, we can perform the following steps in GDB using convenience variables:
gdb$ set $minefieldview = 0x816dd0
gdb$ set $minefield = *($minefieldview+0x30)+0x28
  • Going further, let’s locate where exactly in the minefield structure we have the width, height and n_mines fields. for the sake of brevity I won’t list all the steps to locate those fields, however, the minefield class looks like this after identifyng the key fields:
00000000 minefield_class struc ; (sizeof=0x44, mappedto_9)
00000000 field_0         dd ?
00000004 field_4         dd ?
00000008 field_8         dd ?
0000000C field_C         dd ?
00000010 field_10        dd ?
00000014 field_14        dd ?
00000018 _n_cleared      dd ?
0000001C _n_flags        dd ?
00000020 width           dd ?
00000024 height          dd ?
00000028 n_mines         dd ?
0000002C field_2C        dd ?
00000030 locations       dd ?
00000034 field_34        dd ?
00000038 _paused         dd ?
0000003C field_3C        dd ?
00000040 exploded        dd ?
  • Back in GDB, for an 8x8 table with 10 mines, we have:
set $width=*(*$minefield+0x20)
set $height=*(*$minefield+0x24)
set $n_mines=*(*$minefield+0x28)
gdb$ print $width
$1 = 0x8
gdb$ print $height
$2 = 0x8
gdb$ print $n_mines
$3 = 0xa

GDB Kung-Fu

  • Using similar logic as before, we can map all the needed information related to the position of the mines. The has_mine function queries the locations array:
public bool has_mine (uint x, uint y)
{
    return locations[x, y].has_mine
}
  • Back to IDA Pro, the disassembly of the function shows the offset shows the offsets inside the minefield class:
__int64 __fastcall has_mine(minefield_class *minefield, int x, int y)
{
  if ( minefield )
    return *(unsigned int *)(*(_QWORD *)(*(_QWORD *)&minefield->locations
                                       + 8LL * (unsigned int)(minefield->field_3C * x + y))
                           + 0x20LL);
  g_return_if_fail_warning(0LL, "minefield_has_mine", "self != NULL");
  return 0LL;
}
  • Using the information from above, we can construct a query in GDB to verify whether the location at [x,y] has a mine or not:
set $x=1
set $y=1
set $has_mine_xy = *(*(*(*$minefield+0x30) +8*(*(*$minefield+0x3c)*$x+$y))+0x20)
print $has_mine_xy
$20 = 0x0
The mines are actually placed on the map on the first attempt to clear a spot. So run the previous procedure only after a few spots have already been revealed.
  • Below is the full GDB script that can be used to reveal all the mines. While debugging, stope the execution (Ctrl+C) and define the following function:
define discover_minefield    
    #
    # Initialise MiefieldView with the value from gtkparasite
    # Usage: discover_minefield 0x816dd0
    #
    set $minefieldview=$arg0       
    # Reconnaissance
    set $minefield=*($minefieldview+0x30)+0x28
    set $width=*(*$minefield+0x20)
    printf "Minefield width: %d\n", $width
    set $height=*(*$minefield+0x24) 
    printf "Minefield height: %d\n", $height
    set $n_mines=*(*$minefield+0x28)
    printf "Number of mines: %d\n", $n_mines
    set $x=0
    set $y=0
    set $mf = (char*)malloc(100)
    set $saved_mf = $mf
    set $mine = "X"
    set $clear = "-"
    set $mines_discovered = 0
    
    # Style for discovered mines
    set $gtk_style = (char*)malloc(2048)
    set $saved_gtk_style = $gtk_style
    set $child_num = 1
    set $buf = (char*)malloc(64)   
    
    # Go from left to right, line by line, so we can build the matrix string   
    while $y<$height
        # Convert address to (QWORD*)
        set $has_mine_xy = *(long*)(*(*$minefield+0x30) +8*(*(*$minefield+0x3c)*$x+$y))+0x20
        #print $has_mine_xy        
        if *$has_mine_xy==1
            #printf "[%d,%d] %s\n", $x, $y, $mine
            # Saved the result of the function to avoid printing
            set $unused = strncpy($mf++, $mine, 1)            
    
            # Transpose the child number
            set $x_child = $child_num % $width
            set $y_child = $child_num / $width + 1
            if $x_child ==0
                set $x_child = $width
                set $y_child = $y_child - 1 
            end
            
            set $child_num_trans = $height * ($x_child-1) + $y_child
            set $unused = sprintf($buf, ".tile:nth-child(%3d){ background: pink; }\n", $child_num_trans)
            set $unused = strcpy($gtk_style, $buf)
            set $gtk_style = $gtk_style + strlen($buf)
            set $mines_discovered = $mines_discovered + 1
        else
            #printf "[%d,%d] %s\n", $x, $y, $clear
            set $unused = strncpy($mf++, $clear, 1)        
        end
        
        set $child_num = $child_num + 1
        set $x=$x+1
        if $x==$width
            set $x=0
            set $y=$y+1
            set $unused = strncpy($mf++, "\n", 1)        
        end    
    end 
    
    set $unused = strncpy($mf, "\0", 1)        
    printf "Mines discovered: %d/%d\n", $mines_discovered, $n_mines
    printf "%s\n\n", $saved_mf    
        
    # Log css to file
    set logging on
    set logging file gtk.css    
    set logging redirect on             # Log only to file not stdout
    printf "%s\n", $saved_gtk_style
    set logging off        
    printf "CSS style saved to gtk.css. Apply the style to view mines.\n" 
end
  • The first parameter to the function is the pointer to the MineFieldView object, obtained from gtkparasite window:
gdb$ discover_minefield 0x816dd0
Minefield width: 8
Minefield height: 8
Number of mines: 10
Mines discovered: 10/10
X---X---
X---X---
--------
X-------
-X-X---X
---X----
--------
----X---                  
CSS style saved to gtk.css. Apply the style to view mines.

CSS beautification

A very nice feature of gtk-parasite is that it allows us to apply CSS styles globally per application or individually to each of its elements. GNOME developer center provides a good overview of CSS features supported in GTK+.

  • In this case we need a way to apply a style to an invidual Tile, identified by its [x,y] coordinates. For this we need to identify which child number each tile has, then apply the style to n-th child. For example to apply a CSS to the element at [0, 1], we would do the following:
.button:nth-child(1) {
    background: pink; 
}
  • After correctly identifying the status of each square, with some CSS hacking we assign colours individually. For the example above, the stylesheet (which is saved to gtk.css file) is:
.tile:nth-child(  1){ background: pink; }
.tile:nth-child( 33){ background: pink; }
.tile:nth-child(  2){ background: pink; }
.tile:nth-child( 34){ background: pink; }
.tile:nth-child(  4){ background: pink; }
.tile:nth-child( 13){ background: pink; }
.tile:nth-child( 29){ background: pink; }
.tile:nth-child( 61){ background: pink; }
.tile:nth-child( 30){ background: pink; }
.tile:nth-child( 40){ background: pink; }
  • The final product after applying the styles looks like this: Mines with CSS

Next steps

If instead you’re interested in a Windows based reverse engineering ransomeware hands-on tutorial and want learn to reverse and decrypt some ransomware along the way, check out this course! Very recommended for intermediate and advanced. Or, if you’re interested to cover the basics, see this one.