├── README.md ├── loader.c ├── loader.h ├── plugins ├── linux_screenshot_xwindows.py └── linux_xwindows.py └── xorg-structures.png /README.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | 3 | The goal of this volatility plugin is to extract a screenshot of all 4 | open X windows from a memory dump. 5 | 6 | ## Previous work 7 | 8 | The volatility plugin `linux_xwindows` realised by Adam Bridge 9 | (https://github.com/bridgeythegeek/linux_xwindows) extracts various 10 | metadata from the window objects registered to X, which are identified 11 | by walking the data structures of the X server. For example, for each 12 | window it lists the window coordinates, the width and height 13 | dimensions, and the window identifier. Adam's 14 | [README](https://github.com/bridgeythegeek/linux_xwindows) has a good 15 | discussion on these details, so we suggest to read them if you are 16 | interested! 17 | 18 | As a reference, here you can find a figure representing the core 19 | structures of X and their inter-dependencies: 20 | ![Alt text](https://github.com/pagabuc/xfore/blob/master/xorg-structures.png?raw=true "Xorg-mappings") 21 | 22 | ## Overview 23 | 24 | To extract these screenshot we re-use part of the X server code. 25 | 26 | The plugin first dumps the X server memory mappings. These mappings 27 | are then given in input to a C program (`loader`), along with the 28 | output of Adam's plugin. This C program `mmap`s (with the `MAP_FIXED` 29 | flag) the memory mappings of Xorg into its own address space. In this 30 | way we recereate the address space of Xorg, ensuring that no reference 31 | between code and data is broken. The loader then calls a function 32 | (`compGetImage`) contained in the Xorg code and saves the result to 33 | file. We identified this "magic" function by looking at how X serves a 34 | request for screenshot during normal usage. Fortunately, this function 35 | can always be found accurately since `struct _Screen` contains a 36 | pointer to it. 37 | 38 | Another good feature of this approach is that it is able to retreive 39 | the screenshot even if the screen was locked at the time of dumping, 40 | and even of windows hidden behind other windows! 41 | 42 | ## Usage 43 | 44 | After cloning this repository, compile the loader: 45 | 46 | `gcc -Wall -o loader loader.c` 47 | 48 | To use the plugins: 49 | 50 | `vol.py --plugins=$PWD/plugins/ --profile=XXX -f ./vbox.dmp linux_screenshot_xwindows --out-dir /tmp/xwds/` 51 | 52 | To convert the results from `xwd` to `png`: 53 | 54 | `find /tmp/xwds/ -type f -name "*.xwd" -exec convert {} {}.png \;` 55 | 56 | ## Tested version of Xorg 57 | 58 | We successuly retrived the screenshot from the following setups: 59 | 60 | 61 | 1) Ubuntu 14.04 LTS - X.Org X Server 1.15.1 (Release Date: 2014-04-13) 62 | 63 | 2) Ubuntu 16-04 LTS - X.Org X Server 1.18.4 (Release Date: 2016-07-19) 64 | 65 | 3) Debian 9 Testing - X.Org X Server 1.19.6 (Release Date: 2017-12-20) 66 | 67 | 4) Kubuntu 18.04 LTS - X.Org X Server 1.19.6 (Release Date: 2017-12-20) 68 | 69 | 70 | ## Notes 71 | A few random but important notes: 72 | 73 | 1) In order to limit the process to only “drawable” images we select 74 | in the volatility plugin only the images with reasonable size. 75 | 76 | 2) This tools works *only* if Xorg uses software rendering. This is 77 | not usually the case on physical machines but it was used by default 78 | on the VirtualBox machines we tested. 79 | 80 | 3) Don't forget that you are seamlessly running code extracted from a 81 | memory dump. It is probably not difficult for an attacker to tamper 82 | the dump and gain code execution on your box. So take the necessary 83 | precautions. 84 | 85 | 86 | ## Authors 87 | This project has been completed during a Semester Project in Fall 2017 88 | at Eurecom, and realised by two Eurecom students: Hamdi Ammar and 89 | Ahmed Mkadem. It was supervised by [Fabio 90 | Pagani](http://s3.eurecom.fr/~pagabuc/) and [Davide 91 | Balzarotti](http://s3.eurecom.fr/~balzarot/) 92 | 93 | -------------------------------------------------------------------------------- /loader.c: -------------------------------------------------------------------------------- 1 | #include "loader.h" 2 | 3 | 4 | char map_file[4096]; 5 | 6 | int load_mapping(char *dir, char *file){ 7 | int pid; 8 | long long address; 9 | 10 | sscanf(file, "task.%d.%llx", &pid, &address); 11 | 12 | memset(map_file, 0, 4096); 13 | sprintf(map_file, "%s/%s", dir, file); 14 | 15 | int fd = open(map_file,O_RDWR); 16 | if(fd < 0){ 17 | printf("[-] loader: opening '%s' failed, aborting!\n", map_file); 18 | exit(-1); 19 | } 20 | 21 | struct stat stats; 22 | fstat(fd,&stats); 23 | int filesize = stats.st_size; 24 | unsigned long long res = (unsigned long long)mmap((void *) address, filesize, 25 | PROT_READ|PROT_WRITE|PROT_EXEC, 26 | MAP_PRIVATE|MAP_FIXED, fd, 0); 27 | if(res < 1){ 28 | printf("[-] loader: mmap failed, aborting!\n"); 29 | exit(-1); 30 | } 31 | return 0; 32 | } 33 | 34 | void dump_to_fd(FILE *fd, char *output_buf,int w){ 35 | int i = 0; 36 | int k; 37 | k=w*3; 38 | char *zero = malloc(k); 39 | memset(zero, 0, k); 40 | int written = 0; 41 | while(((unsigned int *)(output_buf))[i/4] != 0x0 ){ 42 | fwrite(output_buf+i, 1, 3, fd); 43 | written += 3; 44 | i+=4; 45 | if (written == k){ 46 | fwrite(zero, 1,w, fd); 47 | written = 0; 48 | } 49 | } 50 | return; 51 | } 52 | 53 | 54 | compGetImage_t compGetImage = 0x00; 55 | 56 | void compGetImage_wrapper(DrawablePtr pDrawable, 57 | int sx, int sy, int w, int h, 58 | unsigned int format, unsigned long planemask, char *pdstLine,int OUTPUT_SIZE){ 59 | memset(pdstLine, 0, OUTPUT_SIZE); 60 | compGetImage(pDrawable, sx, sy, w, h, format, planemask, pdstLine); 61 | } 62 | 63 | 64 | void 65 | _swaplong (register char *bp, register unsigned n) 66 | { 67 | register char c; 68 | register char *ep = bp + n; 69 | 70 | while (bp < ep) { 71 | c = bp[3]; 72 | bp[3] = bp[0]; 73 | bp[0] = c; 74 | c = bp[2]; 75 | bp[2] = bp[1]; 76 | bp[1] = c; 77 | bp += 4; 78 | } 79 | } 80 | 81 | void 82 | _swapshort (register char *bp, register unsigned n) 83 | { 84 | register char c; 85 | register char *ep = bp + n; 86 | 87 | while (bp < ep) { 88 | c = *bp; 89 | *bp = *(bp + 1); 90 | bp++; 91 | *bp++ = c; 92 | } 93 | } 94 | static int Get24bitDirectColors(XColor **colors) 95 | { 96 | int i , ncolors = 256 ; 97 | XColor *tcol ; 98 | 99 | *colors = tcol = (XColor *)malloc(sizeof(XColor) * ncolors) ; 100 | 101 | for(i=0 ; i < ncolors ; i++) 102 | { 103 | tcol[i].pixel = i << 16 | i << 8 | i ; 104 | tcol[i].red = tcol[i].green = tcol[i].blue = i << 8 | i ; 105 | } 106 | 107 | return ncolors ; 108 | } 109 | 110 | int write_header(DrawablePtr draw,VisualPtr vis,unsigned short border_width,int imageByteOrder,int bitmapScanlineUnit,int bitmapScanlinePad,int bitmapBitOrder,FILE *fd){ 111 | CARD32 header_size; 112 | XWDFileHeader header; 113 | XWDColor xwdcolor; 114 | size_t win_name_size; 115 | int ncolors; 116 | XColor *colors; 117 | char win_name[] = "xwdump"; 118 | int i; 119 | 120 | win_name_size = strlen(win_name) + sizeof(char); 121 | header_size = SIZEOF(XWDheader) + (CARD32) win_name_size; 122 | 123 | /* 124 | * Write out header information. 125 | */ 126 | header.header_size = (CARD32) header_size; 127 | header.file_version = (CARD32) XWD_FILE_VERSION; 128 | header.pixmap_format = (CARD32) 2; //format zpixmap 129 | header.pixmap_depth = (CARD32) (*draw).depth; 130 | header.pixmap_width = (CARD32) (*draw).width; 131 | header.pixmap_height = (CARD32) (*draw).height; 132 | header.xoffset = (CARD32) 0; /* Bitmap x offset, normally 0 */ 133 | header.byte_order = (CARD32) imageByteOrder; /* of image data: MSBFirst, LSBFirst */ 134 | header.bitmap_unit = (CARD32) bitmapScanlineUnit;/* bitmap_unit applies to bitmaps (depth 1 format XY) only It is the number of bits that each scanline is padded to. */ 135 | header.bitmap_bit_order = (CARD32) bitmapBitOrder; /* bitmaps only: MSBFirst, LSBFirst */ 136 | header.bitmap_pad = (CARD32) bitmapScanlinePad;/* 8 bitmap_pad applies to pixmaps (non-bitmaps) only.*/ 137 | header.bits_per_pixel = 24; //(CARD32) (*draw).bitsPerPixel; 138 | header.bytes_per_line = (CARD32)bitmapScanlinePad* (*draw).width / 8; //bytes_per_line is pixmap_width padded to bitmap_unit (bitmaps) 139 | header.visual_class = (CARD32) (*vis).class; // 5 or 4 140 | header.red_mask = (CARD32) (*vis).redMask; 141 | header.green_mask = (CARD32) (*vis).greenMask; 142 | header.blue_mask = (CARD32) (*vis).blueMask; 143 | header.bits_per_rgb = (CARD32) (*vis).bitsPerRGBValue; 144 | header.colormap_entries = (CARD32) (*vis).ColormapEntries; 145 | 146 | ncolors = Get24bitDirectColors(&colors) ; 147 | header.ncolors = (CARD32) (*vis).ColormapEntries;//256;//ncolors; 148 | header.window_width = (CARD32) (*draw).width; 149 | header.window_height = (CARD32) (*draw).height; 150 | header.window_x = (*draw).x; 151 | header.window_y = (*draw).y; 152 | header.window_bdrwidth = (CARD32) border_width; 153 | 154 | _swaplong((char *) &header, sizeof(header)); 155 | for (i = 0; i < ncolors; i++) { 156 | _swaplong((char *) &colors[i].pixel, sizeof(CARD32)); 157 | _swapshort((char *) &colors[i].red, 3 * sizeof(short)); 158 | } 159 | 160 | if (fwrite((char *)&header, SIZEOF(XWDheader), 1, fd) != 1 || 161 | fwrite(win_name, win_name_size, 1, fd) != 1) { 162 | perror("xwd"); 163 | exit(1); 164 | } 165 | 166 | for (i = 0; i < ncolors; i++) { 167 | xwdcolor.pixel = colors[i].pixel; 168 | xwdcolor.red = colors[i].red; 169 | xwdcolor.green = colors[i].green; 170 | xwdcolor.blue = colors[i].blue; 171 | xwdcolor.flags = colors[i].flags; 172 | if (fwrite((char *) &xwdcolor, SIZEOF(XWDColor), 1, fd) != 1) { 173 | perror("xwd"); 174 | exit(1); 175 | } 176 | } 177 | 178 | return 0; 179 | } 180 | 181 | 182 | int dump_screenshot(unsigned long long pdrawableAddr, unsigned long long pvisualAddr, unsigned short border_width, 183 | int imageByteOrder, int bitmapScanlineUnit, int bitmapScanlinePad, int bitmapBitOrder, char *out_dir){ 184 | 185 | DrawablePtr pDrawable = (DrawablePtr) pdrawableAddr; 186 | VisualPtr pVisual = (VisualPtr)pvisualAddr; 187 | char filename[256]; 188 | /* __asm__("int $3;"); */ 189 | if ((pVisual->class!=4) && (pVisual->class!=5)){ 190 | return 0; 191 | } 192 | 193 | int OUTPUT_SIZE=((bitmapScanlinePad* (*pDrawable).width / 8) * (*pDrawable).height);; 194 | char *output_buf = (char *)malloc(OUTPUT_SIZE); 195 | compGetImage_wrapper(pDrawable, 0, 0, (*pDrawable).width, (*pDrawable).height, /*ZPixmap*/ 2, 4294967295, output_buf, OUTPUT_SIZE); 196 | 197 | if (strlen(output_buf) == 0) 198 | return 0; 199 | 200 | snprintf(filename, 256, "%s/screenshot_%x.xwd", out_dir, pDrawable->id); 201 | 202 | FILE *fd = fopen(filename, "w+"); 203 | write_header(pDrawable, pVisual, border_width, imageByteOrder, bitmapScanlineUnit, bitmapScanlinePad, bitmapBitOrder, fd); 204 | dump_to_fd(fd, output_buf, (*pDrawable).width); 205 | printf("[+] loader: saving xwd image %s for ID : %x\n", filename, pDrawable->id); 206 | 207 | fclose(fd); 208 | free(output_buf); 209 | return 0; 210 | } 211 | 212 | int main(int argc, char **argv){ 213 | DIR *p; 214 | struct dirent *pp; 215 | char *dir = argv[9]; 216 | printf("[+] loader: Loading mappings from %s\n", argv[9]); 217 | p = opendir(dir); 218 | if (p != NULL){ 219 | while ((pp = readdir (p)) != NULL) { 220 | int length = strlen(pp->d_name); 221 | if (strncmp(pp->d_name + length - 4, ".vma", 4) == 0) { 222 | load_mapping(dir, pp->d_name); 223 | } 224 | } 225 | closedir (p); 226 | } 227 | compGetImage = (compGetImage_t)atoll(argv[8]); 228 | dump_screenshot(atoll(argv[1]), atoll(argv[2]), atoi(argv[3]), atoi(argv[4]), 229 | atoi(argv[5]), atoi(argv[6]), atoi(argv[7]), argv[10]); 230 | 231 | return 0; 232 | } 233 | -------------------------------------------------------------------------------- /loader.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | # if defined (_LP64) || defined(__LP64__) || \ 12 | defined(__alpha) || defined(__alpha__) || \ 13 | defined(__ia64__) || defined(ia64) || \ 14 | defined(__sparc64__) || \ 15 | defined(__s390x__) || \ 16 | defined(__amd64__) || defined(amd64) || \ 17 | defined(__powerpc64__) 18 | # if !defined(__ILP32__) /* amd64-x32 is 32bit */ 19 | # define LONG64 /* 32/64-bit architecture */ 20 | # endif /* !__ILP32__ */ 21 | # endif 22 | 23 | # define _SIZEOF(x) sz_##x 24 | # define SIZEOF(x) _SIZEOF(x) 25 | #define XWD_FILE_VERSION 7 26 | #define sz_XWDheader 100 27 | #define sz_XWDColor 12 28 | #define lowbit(x) ((x) & (~(x) + 1)) 29 | #define TrueColor 4 30 | #define DirectColor 5 31 | # define B32 32 | # define B16 33 | # ifdef LONG64 34 | typedef long INT64; 35 | typedef int INT32; 36 | # else 37 | typedef long INT32; 38 | # endif 39 | typedef short INT16; 40 | typedef signed char INT8; 41 | # ifdef LONG64 42 | typedef unsigned long CARD64; 43 | typedef unsigned int CARD32; 44 | # else 45 | typedef unsigned long long CARD64; 46 | typedef unsigned long CARD32; 47 | # endif 48 | typedef unsigned short CARD16; 49 | typedef unsigned char CARD8; 50 | typedef CARD32 BITS32; 51 | typedef CARD16 BITS16; 52 | 53 | 54 | typedef struct _xwd_file_header { 55 | 56 | CARD32 header_size B32; 57 | CARD32 file_version B32; /* = XWD_FILE_VERSION above */ 58 | CARD32 pixmap_format B32; /* ZPixmap or XYPixmap */ 59 | CARD32 pixmap_depth B32; /* Pixmap depth */ 60 | CARD32 pixmap_width B32; /* Pixmap width */ 61 | CARD32 pixmap_height B32; /* Pixmap height */ 62 | CARD32 xoffset B32; /* Bitmap x offset, normally 0 */ 63 | CARD32 byte_order B32; /* of image data: MSBFirst, LSBFirst */ 64 | 65 | /* bitmap_unit applies to bitmaps (depth 1 format XY) only. 66 | * It is the number of bits that each scanline is padded to. */ 67 | CARD32 bitmap_unit B32; 68 | CARD32 bitmap_bit_order B32; /* bitmaps only: MSBFirst, LSBFirst */ 69 | 70 | /* bitmap_pad applies to pixmaps (non-bitmaps) only. 71 | * It is the number of bits that each scanline is padded to. */ 72 | CARD32 bitmap_pad B32; 73 | CARD32 bits_per_pixel B32; /* Bits per pixel */ 74 | 75 | /* bytes_per_line is pixmap_width padded to bitmap_unit (bitmaps) 76 | * or bitmap_pad (pixmaps). It is the delta (in bytes) to get 77 | * to the same x position on an adjacent row. */ 78 | CARD32 bytes_per_line B32; 79 | CARD32 visual_class B32; /* Class of colormap */ 80 | CARD32 red_mask B32; /* Z red mask */ 81 | CARD32 green_mask B32; /* Z green mask */ 82 | CARD32 blue_mask B32; /* Z blue mask */ 83 | CARD32 bits_per_rgb B32; /* Log2 of distinct color values */ 84 | CARD32 colormap_entries B32; /* Number of entries in colormap; not used? */ 85 | CARD32 ncolors B32; /* Number of XWDColor structures */ 86 | CARD32 window_width B32; /* Window width */ 87 | CARD32 window_height B32; /* Window height */ 88 | CARD32 window_x B32; /* Window upper left X coordinate */ 89 | CARD32 window_y B32; /* Window upper left Y coordinate */ 90 | CARD32 window_bdrwidth B32; /* Window border width */ 91 | } XWDFileHeader; 92 | 93 | typedef struct { 94 | CARD32 pixel B32; 95 | CARD16 red B16; 96 | CARD16 green B16; 97 | CARD16 blue B16; 98 | CARD8 flags; 99 | CARD8 pad; 100 | } XWDColor; 101 | 102 | 103 | /* taken from xorg-server/include/pixmapstr.h */ 104 | typedef unsigned int XID; 105 | #pragma pack(1) 106 | struct _Visual { 107 | int vid; 108 | short class; 109 | short bitsPerRGBValue; 110 | short ColormapEntries; 111 | short nplanes; 112 | int nplanes11; 113 | unsigned long redMask; 114 | unsigned long greenMask; 115 | unsigned long blueMask; 116 | int offsetRed; 117 | int offsetGreen; 118 | int offsetBlue; 119 | } ; 120 | //visual structure was modified in order to get the right values !! 121 | typedef struct _Visual *VisualPtr; 122 | struct _Drawable { 123 | unsigned char type; /* DRAWABLE_ */ 124 | unsigned char class; /* specific to type */ 125 | unsigned char depth; 126 | unsigned char bitsPerPixel; 127 | XID id; /* resource id */ 128 | short x; /* window: screen absolute, pixmap: 0 */ 129 | short y; /* window: screen absolute, pixmap: 0 */ 130 | unsigned short width; 131 | unsigned short height; 132 | unsigned long long *pScreen; 133 | unsigned long serialNumber; 134 | }; 135 | 136 | 137 | typedef struct { 138 | unsigned long pixel; /* pixel value */ 139 | unsigned short red, green, blue; /* rgb values */ 140 | char flags; /* DoRed, DoGreen, DoBlue */ 141 | char pad; 142 | } XColor; 143 | 144 | typedef struct _Drawable *DrawablePtr; 145 | typedef int (*compGetImage_t)(DrawablePtr, int, int, int, int, unsigned int, unsigned long, char *); 146 | -------------------------------------------------------------------------------- /plugins/linux_screenshot_xwindows.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Fabio Pagani, Hamdi AMMAR, Ahmed MKADEM 3 | @license: GNU General Public License 2.0 4 | @contact: pagani@eurecom.fr 5 | @organization: Eurecom (FR) 6 | """ 7 | import volatility.debug as debug 8 | import volatility.plugins.linux.pslist as linux_pslist 9 | import volatility.plugins.linux.dump_map as linux_dump_map 10 | import volatility.plugins.linux.proc_maps as linux_proc_maps 11 | import volatility.plugins.linux_xwindows as linux_xwindows 12 | import volatility.plugins.linux.common as linux_common 13 | import subprocess 14 | import StringIO 15 | import tempfile 16 | import os.path 17 | 18 | 19 | class linux_screenshot_xwindows(linux_common.AbstractLinuxCommand): 20 | def __init__(self, config, *args, **kwargs): 21 | 22 | linux_common.AbstractLinuxCommand.__init__(self, config, *args, **kwargs) 23 | config.add_option('OUT-DIR', short_option='', default=None, help='Output dir', action='store', type='str') 24 | 25 | 26 | def calculate(self): 27 | if (not self._config.OUT_DIR or 28 | not os.path.isdir(self._config.OUT_DIR)): 29 | debug.error("Please specify an existing output dir (--out-dir)") 30 | 31 | if (not os.path.isfile("./loader")): 32 | debug.error("Cannot find 'loader' inside current working directory, compile it or `cd` in the right directory") 33 | 34 | # Extract mappings, kind of ugly but i did not find another way to call the modules.. 35 | for p in linux_pslist.linux_pslist(self._config).calculate(): 36 | pname = str(p.comm) 37 | if pname == 'X' or pname == 'Xorg': 38 | break 39 | 40 | tmp_dir = tempfile.mkdtemp() 41 | self._config.add_option("PID") 42 | self._config.add_option("DUMP_DIR") 43 | self._config.PID = str(p.pid) 44 | self._config.DUMP_DIR = tmp_dir 45 | out = StringIO.StringIO() 46 | data = linux_proc_maps.linux_proc_maps(self._config).calculate() 47 | print("[+] Dumping Xorg memory mappings in %s" % tmp_dir) 48 | linux_dump_map.linux_dump_map(self._config).render_text(out, data) 49 | 50 | # Extract screenshots 51 | xw = linux_xwindows.linux_xwindows(self._config) 52 | for msg, screen_windows, screen_info in xw.calculate(): 53 | for screen_id, win in screen_windows: 54 | # print('{} on Screen {}\n'.format(str(win), screen_id)) 55 | if win.drawable.type==0 and win.drawable.width > 150 and win.drawable.height > 150: 56 | out = StringIO.StringIO() 57 | xw.visit_property(win.optional.dereference().userProps.dereference(), 58 | out) 59 | 60 | loader = ["./loader", 61 | str(win.drawable), str(win.drawable.pScreen.visuals), 62 | str(win.borderWidth), str(screen_info.imageByteOrder), 63 | str(screen_info.bitmapScanlineUnit), str(screen_info.bitmapScanlinePad), 64 | str(screen_info.bitmapBitOrder), str(win.drawable.pScreen.GetImage), 65 | str(tmp_dir), str(self._config.OUT_DIR)] 66 | print("[+] Calling %s" % ' '.join(loader)) 67 | subprocess.check_call(loader) 68 | 69 | 70 | def render_text(self, outfd, data): 71 | return 72 | -------------------------------------------------------------------------------- /plugins/linux_xwindows.py: -------------------------------------------------------------------------------- 1 | """ 2 | @author: Adam Bridge (bridgeythegeek) 3 | @license: GNU General Public License 2.0 4 | @contact: bridgeythegeek@gmail.com 5 | @organization: 6 | """ 7 | 8 | import volatility.obj as obj 9 | import volatility.debug as debug 10 | import volatility.plugins.linux.pslist as linux_pslist 11 | import volatility.utils as utils 12 | 13 | import struct 14 | 15 | 16 | xwindows_vtypes_x64 = { 17 | 18 | # typedef struct _PixmapFormat { 19 | # unsigned char depth; 20 | # unsigned char bitsPerPixel; 21 | # unsigned char scanlinePad; 22 | # } PixmapFormatRec; 23 | 24 | 'PixmapFormatRec' : [ 0x03, { 25 | 'depth': [0x00, ['char']], 26 | 'bitsPerPixel': [0x01, ['char']], 27 | 'scanlinePad': [0x02, ['char']] 28 | }], 29 | 30 | # typedef struct _ScreenInfo { 31 | # int imageByteOrder; 32 | # int bitmapScanlineUnit; 33 | # int bitmapScanlinePad; 34 | # int bitmapBitOrder; 35 | # int numPixmapFormats; 36 | # PixmapFormatRec formats[MAXFORMATS]; 37 | # int numScreens; 38 | # ScreenPtr screens[MAXSCREENS]; 39 | # int numGPUScreens; 40 | # ScreenPtr gpuscreens[MAXGPUSCREENS]; 41 | # int x; /* origin */ 42 | # int y; /* origin */ 43 | # int width; /* total width of all screens together */ 44 | # int height; /* total height of all screens together */ 45 | # } ScreenInfo; 46 | 47 | 'ScreenInfo': [ 328, { 48 | 'imageByteOrder': [0x00, ['int']], 49 | 'bitmapScanlineUnit': [0x04, ['int']], 50 | 'bitmapScanlinePad': [0x08, ['int']], 51 | 'bitmapBitOrder': [0x0c, ['int']], 52 | 'numPixmapFormats': [0x10, ['int']], 53 | 'formats': [0x14, ['array', 8, ['PixmapFormatRec']]], 54 | 'numScreens': [0x2c, ['int']], 55 | 'screens': [0x30, ['array', 16, ['pointer', ['ScreenPtr']]]], 56 | 'numGPUScreens': [0xb0, ['int']], 57 | 'gpuScreens': [0xb8, ['array', 16, ['address']]], # Why at 0xb8 and not 0xb4??? 58 | 'x': [0x138, ['int']], 59 | 'y': [0x13c, ['int']], 60 | 'width': [0x140, ['int']], 61 | 'height': [0x144, ['int']] 62 | }], 63 | 64 | # typedef struct _Screen { 65 | # int myNum; /* index of this instance in Screens[] */ 66 | # ATOM id; 67 | # short x, y, width, height; 68 | # short mmWidth, mmHeight; 69 | # short numDepths; 70 | # unsigned char rootDepth; 71 | # DepthPtr allowedDepths; 72 | # unsigned long rootVisual; 73 | # unsigned long defColormap; 74 | # short minInstalledCmaps, maxInstalledCmaps; 75 | # char backingStoreSupport, saveUnderSupport; 76 | # unsigned long whitePixel, blackPixel; 77 | # GCPtr GCperDepth[MAXFORMATS + 1]; 78 | # PixmapPtr PixmapPerDepth[1]; 79 | # void *devPrivate; 80 | # short numVisuals; 81 | # VisualPtr visuals; 82 | # WindowPtr root; 83 | # ... 84 | 85 | 'ScreenPtr': [0xb8, { 86 | 'myNum': [0x00, ['int']], 87 | 'id': [0x04, ['unsigned int']], 88 | 'x': [0x08, ['short']], 89 | 'y': [0x0a, ['short']], 90 | 'width': [0x0c, ['short']], 91 | 'height': [0x0e, ['short']], 92 | 'mmWidth': [0x10, ['short']], 93 | 'mmHeight': [0x12, ['short']], 94 | 'numDepths': [0x14, ['short']], 95 | 'rootDepth': [0x16, ['short']], #unsigned char? 96 | 'allowedDepths': [0x18, ['address']], 97 | 'rootVisual': [0x20, ['unsigned long']], 98 | 'defColormap': [0x28, ['unsigned long']], 99 | 'minInstalledCmaps': [0x30, ['short']], 100 | 'maxInstalledCmaps': [0x32, ['short']], 101 | 'backingStoreSupport': [0x34, ['char']], #short? 102 | 'saveUnderSupport': [0x36, ['char']], #short? 103 | 'whitePixel': [0x38, ['unsigned long']], 104 | 'blackPixel': [0x40, ['unsigned long']], 105 | 'GCperDepth': [0x48, ['array', 9, ['address']]], 106 | 'PixmapPerDepth': [0x90, ['array', 1, ['address']]], 107 | 'devPrivate': [0x98, ['pointer', ['void']]], 108 | 'numVisuals': [0xa0, ['short']], 109 | 'visuals': [0xa8, ['address']], 110 | 'root': [0xb0, ['pointer', ['WindowPtr']]], 111 | 'GetImage': [0x298, ['pointer', ['void']]] 112 | }], 113 | 114 | 'DrawableRec': [0x20, { 115 | 'type': [0x00, ['unsigned char']], 116 | 'class': [0x01, ['unsigned char']], 117 | 'depth': [0x02, ['unsigned char']], 118 | 'bitsPerPixel': [0x03, ['unsigned char']], 119 | 'id': [0x04, ['unsigned int']], 120 | 'x': [0x08, ['short']], 121 | 'y': [0x0a, ['short']], 122 | 'width': [0x0c, ['unsigned short']], 123 | 'height': [0x0e, ['unsigned short']], 124 | 'pScreen': [0x10, ['pointer', ['ScreenPtr']]], 125 | 'serialNumber': [0x18, ['unsigned long']] 126 | }], 127 | 128 | 'WindowPtr': [0xd0, { 129 | 'drawable': [0x00, ['DrawableRec']], 130 | 'devPrivates': [0x20, ['pointer', ['void']]], 131 | 'parent': [0x28, ['pointer', ['WindowPtr']]], 132 | 'nextSib': [0x30, ['pointer', ['WindowPtr']]], 133 | 'prevSib': [0x38, ['pointer', ['WindowPtr']]], 134 | 'firstChild': [0x40, ['pointer', ['WindowPtr']]], 135 | 'lastChild': [0x48, ['pointer', ['WindowPtr']]], 136 | 'clipList': [0x50, ['RegionRec']], 137 | 'borderClip': [0x60, ['RegionRec']], 138 | 'valdata': [0x70, ['pointer', ['void']]], 139 | 'winSize': [0x78, ['RegionRec']], 140 | 'borderSize': [0x88, ['RegionRec']], 141 | #'origin': [0x98, ['DDXPointRec']], 142 | 'borderWidth': [0x9c, ['unsigned short']], 143 | 'deliverableEvents': [0x9e, ['unsigned short']], 144 | 'eventMask': [0xa0, ['unsigned int']], 145 | #'background': [0xa8, ['PixUnion']], 146 | #'border': [0xac, ['PixUnion']], 147 | 'optional': [0xb8, ['pointer', ['WindowOpt']]] 148 | }], 149 | 150 | 'AtomNode': [0x20, { 151 | 'left': [0x00, ['pointer', ['AtomNode']]], 152 | 'right': [0x08, ['pointer', ['AtomNode']]], 153 | 'a': [0x10, ['unsigned int']], 154 | 'fingerPrint': [0x14, ['unsigned int']], 155 | 'string': [0x18, ['pointer', ['String', dict(length=1024)]]] 156 | }], 157 | 158 | #(gdb) ptype screenInfo.screens[0].root.optional 159 | #type = struct _WindowOpt { 160 | # CursorPtr cursor; 8 161 | # VisualID visual; 4 162 | # Colormap colormap; 4 163 | # Mask dontPropagateMask; 4 164 | # Mask otherEventMasks; 4 165 | # struct _OtherClients *otherClients; 8 166 | # struct _GrabRec *passiveGrabs; 8 167 | # PropertyPtr userProps; 8 168 | # CARD32 backingBitPlanes; 169 | # CARD32 backingPixel; 170 | # RegionPtr boundingShape; 171 | # RegionPtr clipShape; 172 | # RegionPtr inputShape; 173 | # struct _OtherInputMasks *inputMasks; 174 | # DevCursorList deviceCursors; 175 | #} * 176 | 177 | 'WindowOpt': [0x30, { 178 | 'userProps': [0x28, ['pointer', ['Property']]] 179 | }], 180 | 181 | #(gdb) ptype screenInfo.screens[0].root.optional.userProps 182 | #type = struct _Property { 183 | # struct _Property *next; 184 | # ATOM propertyName; 185 | # ATOM type; 186 | # uint32_t format; 187 | # uint32_t size; 188 | # void *data; 189 | # PrivateRec *devPrivates; 190 | #} * 191 | 192 | 'Property': [0x28, { 193 | 'next_': [0x00, ['pointer', ['Property']]], 194 | 'propertyName': [0x08, ['unsigned int']], 195 | 'type': [0x0c, ['unsigned int']], 196 | 'format': [0x10, ['unsigned int']], 197 | 'size_': [0x14, ['unsigned int']], 198 | 'data': [0x18, ['pointer', ['void']]], 199 | 'devPrivates': [0x20, ['pointer', ['void']]] 200 | }] 201 | } 202 | 203 | 204 | class ScreenInfo(obj.CType): 205 | 206 | def __str__(self): 207 | """String representation of ScreenInfo""" 208 | 209 | return '<{0}(offset={6:#x}, numScreens={1}, x={2}, y={3}, width={4}, height={5})>'.format( 210 | self.__class__.__name__, self.numScreens, self.x, self.y, self.width, self.height, self.v()) 211 | 212 | 213 | class ScreenPtr(obj.CType): 214 | 215 | def __str__(self): 216 | """String representation of ScreenPtr""" 217 | 218 | return '<{0}(offset={8:#x}, myNum={1}, id={2}, x={3}, y={4}, width={5}, height={6}, root={7:#x})>'.format( 219 | self.__class__.__name__, self.myNum, self.id, self.x, self.y, self.width, self.height, self.root, self.v()) 220 | 221 | 222 | class WindowPtr(obj.CType): 223 | 224 | def __str__(self): 225 | """String representation of WindowPtr""" 226 | 227 | return '<{}(offset={:#x}, id={:#x}, x={}, y={}, width={}, height={}, parent={:#x}, firstChild={:#x}, lastChild={:#x}, nextSib={:#x}, prevSib={:#x})>'.format( 228 | self.__class__.__name__, self.v(), self.drawable.id, self.drawable.x, self.drawable.y, self.drawable.width, self.drawable.height, 229 | self.parent, self.firstChild, self.lastChild, self.nextSib, self.prevSib) 230 | 231 | 232 | class AtomNode(obj.CType): 233 | 234 | def __str__(self): 235 | """String representation of AtomNode""" 236 | 237 | return '<{}(offset={:#x}, a={:#x}, string={})>'.format( 238 | self.__class__.__name__, self.v(), self.a, self.string.dereference()) 239 | 240 | 241 | class WindowOpt(obj.CType): 242 | 243 | def __str__(self): 244 | """String representation of WindowOpt""" 245 | 246 | return '<{}(offset={:#x}, userProps={:#x})>'.format( 247 | self.__class__.__name__, self.v(), self.userProps) 248 | 249 | 250 | class Property(obj.CType): 251 | 252 | __type_string = ['STRING', '_NET_DESKTOP_NAMES', 'WM_CLASS', '_NET_WM_NAME', '_NET_WM_ICON_NAME'] 253 | __type_uint32 = ['ATOM', 'CARDINAL', '_NET_WM_PID'] 254 | __type_int32 = ['INTEGER'] 255 | 256 | def read_data(self, vm, type): 257 | """Read the property's data. Each type needs to be implemented.""" 258 | 259 | # Let's not go crazy! 260 | max_size = self.size_ 261 | truncated = False 262 | if max_size > 0x200: # 512 bytes 263 | max_size = 0x200 264 | truncated = True 265 | 266 | if type in self.__type_string: 267 | # Null separated strings 268 | l = [x for x in vm.zread(self.data, max_size).split('\0') if x] 269 | temp = ','.join(l) 270 | return '{}... '.format(temp, 271 | self.size_ - max_size) if max_size < self.size_ else temp 272 | elif type in self.__type_uint32: 273 | # unsigned int32 274 | temp = ','.join([str(x) for x in struct.unpack('{}I'.format(int(max_size)), vm.zread(self.data, int(max_size) * 4))]) 275 | return '{}... '.format(temp, 276 | self.size_ - max_size) if max_size < self.size_ else temp 277 | elif type in self.__type_int32: 278 | # signed int32 279 | temp = ','.join([str(x) for x in struct.unpack('{}i'.format(int(max_size)), vm.zread(self.data, int(max_size) * 4))]) 280 | return '{}... '.format(temp, 281 | self.size_ - max_size) if max_size < self.size_ else temp 282 | 283 | return None # Don't know how to read 284 | 285 | def __str__(self): 286 | """String representation of Property""" 287 | 288 | return '<{}(offset={:#x}, propertyName={:#x}, type={:#x}, format={}, size={:#x}, data={:#x})>'.format( 289 | self.__class__.__name__, self.v(), self.propertyName, self.type, self.format, self.size_, self.data) 290 | 291 | 292 | class linux_xatoms(linux_pslist.linux_pslist): 293 | """Lists the Atoms from each X server process""" 294 | 295 | xatoms_classes = { 296 | 'AtomNode' : AtomNode 297 | } 298 | 299 | def __init__(self, config, *args, **kwargs): 300 | linux_pslist.linux_pslist.__init__(self, config, *args, **kwargs) 301 | self._config.add_option('PID', 302 | short_option='p', 303 | default=None, 304 | help='PIDs to analyse (comma separated)', 305 | action='store', 306 | type='str' 307 | ) 308 | 309 | def seek_atom_root(self, task, proc_maps): 310 | """Find the root Atom node; it's in the heap somewhere""" 311 | 312 | _atom_root = None 313 | 314 | debug.info("Seeking atomRoot.") 315 | 316 | for proc_map in proc_maps: 317 | (fname, major, minor, ino, pgoff) = proc_map.info(task) 318 | if fname == '[heap]': # The atoms are on the heap 319 | debug.info('Heap: {} {:#x} {:#x}'.format(str(proc_map.vm_flags), proc_map.vm_start, proc_map.vm_end)) 320 | offset = proc_map.vm_start 321 | while offset < proc_map.vm_end: 322 | if self._current_vm.is_valid_address(offset): 323 | atom_root = obj.Object('AtomNode', vm=self._current_vm, offset=offset) 324 | if self.is_atom_root_sane(atom_root): 325 | debug.info(atom_root) 326 | return atom_root 327 | offset += 8 328 | 329 | return None # Didn't find the root Atom node 330 | 331 | def is_atom_root_sane(self, atom_root): 332 | """Validate a candidate root Atom node""" 333 | 334 | if not self._current_vm.is_valid_address(atom_root.string): 335 | return False 336 | 337 | return atom_root.a == 0x01 and str(atom_root.string.dereference()) == 'PRIMARY' 338 | 339 | def visit_atomNode(self, atomNode): 340 | 341 | if atomNode.v() in self._seen_atoms: 342 | debug.info('Atom referenced more than once! Offset {:#x}.'.format(atomNode.v())) 343 | else: 344 | self._atoms[int(atomNode.a)] = atomNode 345 | self._seen_atoms.add(atomNode.v()) 346 | 347 | if atomNode.left and self._current_vm.is_valid_address(atomNode.left): 348 | self.visit_atomNode(atomNode.left.dereference()) 349 | 350 | if atomNode.right and self._current_vm.is_valid_address(atomNode.right): 351 | self.visit_atomNode(atomNode.right.dereference()) 352 | 353 | def calculate(self): 354 | 355 | # Apply the correct vtypes for the profile 356 | addr_space = utils.load_as(self._config) 357 | addr_space.profile.object_classes.update(linux_xatoms.xatoms_classes) 358 | addr_space.profile.vtypes.update(xwindows_vtypes_x64) 359 | addr_space.profile.compile() 360 | 361 | # Build a list of tasks 362 | tasks = linux_pslist.linux_pslist.calculate(self) 363 | if self._config.PID: 364 | pids = [int(p) for p in self._config.PID.split(',')] 365 | the_tasks = [t for t in tasks if t.pid in pids] 366 | else: 367 | # Find the X Windows task 368 | the_tasks = [] 369 | for task in tasks: 370 | task_offset, dtb, ppid, uid, gid, start_time = self._get_task_vals(task) 371 | task_name = str(task.comm) 372 | task_pid = int(task.pid) 373 | if task_name == 'X' or task_name == 'Xorg': 374 | the_tasks.append(task) 375 | 376 | # In case no appropriate processes are found 377 | if len(the_tasks) < 1: 378 | return 379 | 380 | for task in the_tasks: 381 | 382 | # These need to be here so that they're reset for each X/Xorg process. 383 | self._atoms = {} # Holds the atoms, per X process 384 | self._seen_atoms = set() # Holds a list of atom offsets for avoiding circular referencing 385 | 386 | self._current_vm = task.get_process_address_space() 387 | msg = 'Working with \'{0}\' (pid={1}).'.format(str(task.comm), task.pid) 388 | debug.info(msg) 389 | proc_maps = task.get_proc_maps() 390 | atom_root = self.seek_atom_root(task, proc_maps) 391 | if atom_root: 392 | self.visit_atomNode(atom_root) 393 | debug.info('Found {:,} atom(s).'.format(len(self._atoms))) 394 | yield msg, self._atoms 395 | 396 | def render_text(self, outfd, data): 397 | for msg, atoms in data: 398 | outfd.write('{}\n{}\n'.format('*' * 70, msg)) 399 | for atom_id in sorted(atoms): 400 | outfd.write('{}\n'.format(str(self._atoms[atom_id]))) 401 | 402 | 403 | class linux_xwindows(linux_pslist.linux_pslist): 404 | """Lists the windows known to each X server process""" 405 | 406 | xwindows_classes = { 407 | 'ScreenInfo' : ScreenInfo, 408 | 'ScreenPtr' : ScreenPtr, 409 | 'WindowPtr' : WindowPtr, 410 | 'WindowOpt' : WindowOpt, 411 | 'Property' : Property, 412 | 'AtomNode' : AtomNode 413 | } 414 | 415 | def __init__(self, config, *args, **kwargs): 416 | linux_pslist.linux_pslist.__init__(self, config, *args, **kwargs) 417 | self._config.add_option('PID', 418 | short_option='p', 419 | default=None, 420 | help='PIDs to analyse (comma separated)', 421 | action='store', 422 | type='str' 423 | ) 424 | self._config.add_option('ATOMS', 425 | default=False, 426 | help='Dump the Atom Table', 427 | action='store_true', 428 | dest='dump_atoms' 429 | ) 430 | 431 | def seek_screen_info(self, task, proc_maps): 432 | 433 | _screen_info = None 434 | 435 | debug.info("Seeking screenInfo. (This can take a while!)") 436 | bss_start = -1 437 | for proc_map in proc_maps: 438 | (fname, major, minor, ino, pgoff) = proc_map.info(task) 439 | if fname.endswith('/{0}'.format(str(task.comm))) and str(proc_map.vm_flags) == 'rw-': 440 | bss_start = proc_map.vm_end 441 | elif fname.endswith('/{0}'.format('Xorg')) and str(proc_map.vm_flags) == 'rw-': 442 | # This is because in some cases,e.g. OpenSUSE Leap 42.3, the process is called 'X' but the mapped binary is 'Xorg' 443 | bss_start = proc_map.vm_end 444 | elif str(proc_map.vm_flags) == 'rw-' and proc_map.vm_start == bss_start: 445 | debug.info('Anonymous section (BSS): {} {:#x} {:#x}'.format(str(proc_map.vm_flags), proc_map.vm_start, proc_map.vm_end)) 446 | offset = 0 447 | while bss_start < proc_map.vm_end: 448 | if self._current_vm.is_valid_address(bss_start+offset): 449 | screen_info = obj.Object('ScreenInfo', vm=task.get_process_address_space(), offset=bss_start+offset) 450 | if self.is_screen_info_sane(screen_info): 451 | debug.info(screen_info) 452 | return screen_info 453 | offset += 8 454 | 455 | def is_screen_info_sane(self, screen_info): 456 | """Validate a candidate screenInfo struct""" 457 | 458 | if screen_info.numScreens < 1 or screen_info.numScreens > 16: 459 | return False 460 | 461 | if screen_info.x < 0 or screen_info.x > 10000: 462 | return False 463 | 464 | if screen_info.y < 0 or screen_info.y > 10000: 465 | return False 466 | 467 | if screen_info.width < 640 or screen_info.width > 10000: 468 | return False 469 | 470 | if screen_info.height < 480 or screen_info.height > 10000: 471 | return False 472 | 473 | if not self._current_vm.is_valid_address(screen_info.screens[0]): 474 | return False 475 | 476 | if not self._current_vm.is_valid_address(screen_info.screens[0].dereference().root): 477 | return False 478 | 479 | root_window = screen_info.screens[0].dereference().root.dereference() 480 | if root_window.drawable.x != 0 or root_window.drawable.y != 0 or root_window.parent != 0: 481 | return False 482 | 483 | return True 484 | 485 | def seek_atom_root(self, task, proc_maps): 486 | 487 | _atom_root = None 488 | 489 | debug.info("Seeking atomRoot.") 490 | 491 | for proc_map in proc_maps: 492 | (fname, major, minor, ino, pgoff) = proc_map.info(task) 493 | if fname == '[heap]': 494 | debug.info('Heap: {} {:#x} {:#x}'.format(str(proc_map.vm_flags), proc_map.vm_start, proc_map.vm_end)) 495 | offset = proc_map.vm_start 496 | while offset < proc_map.vm_end: 497 | if self._current_vm.is_valid_address(offset): 498 | atom_root = obj.Object('AtomNode', vm=self._current_vm, offset=offset) 499 | if self.is_atom_root_sane(atom_root): 500 | debug.info(atom_root) 501 | return atom_root 502 | offset += 8 503 | 504 | return None 505 | 506 | def is_atom_root_sane(self, atom_root): 507 | 508 | if not self._current_vm.is_valid_address(atom_root.string): 509 | return False 510 | 511 | return atom_root.a == 0x01 and str(atom_root.string.dereference()) == 'PRIMARY' 512 | 513 | def lookup_atom(self, atom_id): 514 | """Get an Atom's string from its ID""" 515 | 516 | atom_id = int(atom_id) 517 | if atom_id in self._atoms: 518 | return str(self._atoms[atom_id].string.dereference()) 519 | else: 520 | return "" 521 | 522 | def visit_atomNode(self, atomNode): 523 | 524 | if atomNode.v() in self._seen_atoms: 525 | debug.info('Atom referenced more than once! Offset {:#x}.'.format(atomNode.v())) 526 | else: 527 | self._atoms[int(atomNode.a)] = atomNode 528 | self._seen_atoms.add(atomNode.v()) 529 | 530 | if atomNode.left and self._current_vm.is_valid_address(atomNode.left): 531 | self.visit_atomNode(atomNode.left.dereference()) 532 | 533 | if atomNode.right and self._current_vm.is_valid_address(atomNode.right): 534 | self.visit_atomNode(atomNode.right.dereference()) 535 | 536 | def parse_screenInfo(self, screen_info): 537 | 538 | debug.info('Parsing the {} ScreenPtr structure(s).'.format(screen_info.numScreens)) 539 | for screen_ptr in screen_info.screens: 540 | if screen_ptr and self._current_vm.is_valid_address(screen_ptr): 541 | screen = screen_ptr.dereference() 542 | debug.info(screen) 543 | debug.info('Parsing the windows.') 544 | if self._current_vm.is_valid_address(screen.root): 545 | self.visit_window(screen.myNum, screen.root.dereference()) 546 | 547 | def visit_window(self, screen_id, win): 548 | 549 | if win.v() in self._seen_windows: 550 | debug.info('Window referenced more than once! Offset {:#x}. (Skipped)'.format(win.v())) 551 | else: 552 | self._windows.append((screen_id, win)) 553 | self._seen_windows.add(win.v()) 554 | 555 | if win.firstChild and self._current_vm.is_valid_address(win.firstChild): 556 | self.visit_window(screen_id, win.firstChild.dereference()) 557 | 558 | if win.nextSib and self._current_vm.is_valid_address(win.nextSib): 559 | self.visit_window(screen_id, win.nextSib.dereference()) 560 | 561 | def visit_property(self, prop, outfd): 562 | 563 | for atom_id in self._atoms: 564 | if prop.propertyName == atom_id: 565 | 566 | prop_name = str(self._atoms[atom_id].string.dereference()) 567 | prop_type = int(prop.type) 568 | 569 | type_atom = self.lookup_atom(prop.type) 570 | 571 | outfd.write('{}\n'.format('~' * 50)) 572 | outfd.write('{}({}): {}\n'.format(prop_name, type_atom, str(prop))) 573 | 574 | d = prop.read_data(self._current_vm, type_atom) 575 | if d: 576 | if isinstance(d, str): 577 | outfd.write('{}{}'.format(d, '' if d.endswith('\n') else '\n')) 578 | elif isinstance(d, int): 579 | outfd.write('{}\n'.format(d)) 580 | 581 | if prop.next_ and self._current_vm.is_valid_address(prop.next_): 582 | self.visit_property(prop.next_.dereference(), outfd) 583 | 584 | def calculate(self): 585 | 586 | addr_space = utils.load_as(self._config) 587 | 588 | # Check the profile: we only support 64-bit Linux 589 | meta = addr_space.profile.metadata 590 | if not (meta['os'] == 'linux' and meta['memory_model'] == '64bit'): 591 | debug.error('Sorry, currently only 64-bit Linux is supported.') 592 | 593 | # Apply the correct vtypes for the profile 594 | addr_space.profile.object_classes.update(linux_xwindows.xwindows_classes) 595 | addr_space.profile.vtypes.update(xwindows_vtypes_x64) 596 | addr_space.profile.compile() 597 | 598 | # Build a list of tasks 599 | tasks = linux_pslist.linux_pslist.calculate(self) 600 | if self._config.PID: 601 | pids = [int(p) for p in self._config.PID.split(',')] 602 | the_tasks = [t for t in tasks if t.pid in pids] 603 | else: 604 | # Find the X Windows task 605 | the_tasks = [] 606 | for task in tasks: 607 | task_offset, dtb, ppid, uid, gid, start_time = self._get_task_vals(task) 608 | task_name = str(task.comm) 609 | task_pid = int(task.pid) 610 | if task_name == 'X' or task_name == 'Xorg': 611 | the_tasks.append(task) 612 | 613 | # In case no appropriate processes are found 614 | if len(the_tasks) < 1: 615 | return 616 | 617 | for task in the_tasks: 618 | 619 | # These need to be here so that they're reset for each X/Xorg process. 620 | self._windows = [] # Stores a list of WindowPtr objects; one for each window found 621 | self._seen_windows = set() # Holds a list of window offsets for avoiding circular referencing 622 | self._atoms = {} # Stores a dictionary of AtomNode objects, indexed by atom id 623 | self._seen_atoms = set() # Holds a list of atom offsets for avoiding circular referencing 624 | self._current_vm = task.get_process_address_space() 625 | 626 | msg = 'Working with \'{0}\' (pid={1}).'.format(str(task.comm), task.pid) 627 | debug.info(msg) 628 | proc_maps = task.get_proc_maps() 629 | screen_info = self.seek_screen_info(task, proc_maps) 630 | atom_root = self.seek_atom_root(task, proc_maps) 631 | if atom_root: 632 | self.visit_atomNode(atom_root) 633 | debug.info('Found {:,} atom(s).'.format(len(self._atoms))) 634 | self.parse_screenInfo(screen_info) 635 | debug.info('Found {:,} window(s).'.format(len(self._windows))) 636 | yield msg, self._windows, screen_info 637 | 638 | # This function never seems to get called?? 639 | # def generator(self, data): 640 | # pass 641 | 642 | def render_text(self, outfd, data): 643 | for msg, screen_windows, screen_info in data: # Iterate the windows/atoms for each X process 644 | 645 | outfd.write('{}\n{}\n'.format('*' * 70, msg)) 646 | 647 | if self._config.dump_atoms: 648 | for atom_id in sorted(self._atoms): 649 | outfd.write('{}\n'.format(str(self._atoms[atom_id]))) 650 | outfd.write('{}\n'.format('+' * 60)) 651 | 652 | for screen_id, win in screen_windows: 653 | outfd.write('{}\n'.format('=' * 60)) 654 | outfd.write('{} on Screen {}\n'.format(str(win), screen_id)) 655 | 656 | self.visit_property(win.optional.dereference().userProps.dereference(), outfd) 657 | -------------------------------------------------------------------------------- /xorg-structures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eurecom-s3/linux_screenshot_xwindows/971c0432a11336c9be92f2d7506f980e105f1664/xorg-structures.png --------------------------------------------------------------------------------