{"id":16714018,"url":"https://github.com/therealdreg/the-gtk-keylogger","last_synced_at":"2025-04-10T06:09:49.903Z","repository":{"id":64414263,"uuid":"117867105","full_name":"therealdreg/The-GTK-Keylogger","owner":"therealdreg","description":"The GTK Keylogger","archived":false,"fork":false,"pushed_at":"2023-08-11T06:21:10.000Z","size":7049,"stargazers_count":19,"open_issues_count":1,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T07:14:13.139Z","etag":null,"topics":["asm","c","gtk","hooking","keylogger","linux","x64","x86"],"latest_commit_sha":null,"homepage":"https://rootkit.es/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/therealdreg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["therealdreg"],"patreon":"dreg","custom":["https://www.paypal.me/therealdreg","https://www.paypal.me/therealdreg"]}},"created_at":"2018-01-17T17:09:42.000Z","updated_at":"2024-05-24T19:33:26.000Z","dependencies_parsed_at":"2025-02-16T19:43:38.023Z","dependency_job_id":null,"html_url":"https://github.com/therealdreg/The-GTK-Keylogger","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/therealdreg%2FThe-GTK-Keylogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/therealdreg%2FThe-GTK-Keylogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/therealdreg%2FThe-GTK-Keylogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/therealdreg%2FThe-GTK-Keylogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/therealdreg","download_url":"https://codeload.github.com/therealdreg/The-GTK-Keylogger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166925,"owners_count":21058481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asm","c","gtk","hooking","keylogger","linux","x64","x86"],"created_at":"2024-10-12T20:48:51.663Z","updated_at":"2025-04-10T06:09:49.889Z","avatar_url":"https://github.com/therealdreg.png","language":"C","funding_links":["https://github.com/sponsors/therealdreg","https://patreon.com/dreg","https://www.paypal.me/therealdreg"],"categories":[],"sub_categories":[],"readme":"The GTK Keylogger by:\n\nAbel Romero Pérez (aka D1W0U) @diw0u, abel@abelromero.com, http://www.abelromero.com , https://github.com/D1W0U\n\nDavid Reguera (aka Dreg) @therealdreg, http://www.fr33project.org , dreg@fr33project.org \n\nPlese, consider make a donation: https://github.com/sponsors/therealdreg\n\n--[ Table of contents\n\n1. Introduction  \n2. Table of Keyloggers  \n3. How the GTK keylogger works  \n\u0026nbsp;\u0026nbsp;3.1 The GTK+2 IM Contexts hooked  \n\u0026nbsp;\u0026nbsp;3.2 The GTK+3 IM Contexts hooked  \n\u0026nbsp;\u0026nbsp;3.3 The hooking technique  \n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.3.1 The hook in x86  \n\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3.3.2 The hook in x86_64  \n\u0026nbsp;\u0026nbsp;3.4 Infection  \n\u0026nbsp;\u0026nbsp;3.5 The problem with ld-config  \n4. Given support  \n5. How to test the GTK keylogger  \n6. Greetings  \n7. References  \n8. Appendix: code  \n\n--[ 1. Introduction\n\nThere're two scenarios that decides the content of a keylogger: the\nexecution space. They can be user-space or kernel-mode.\n\nWhile an LKM keylogger depends on the version of the Linux Kernel and the\nsupport to load modules, the most pieces of software for ring3 there're\npublic on Google are based on evdev.  Both have a problem that is about\ntranslating the input of the user to visible characters, or better said to\ncharacters there were really written.  Because the common scenario is to\ncatch scan codes, key codes and translate them to Unicode by getting and\ncombining the current keymap and lang. That process can be a pain in the\nass, but are the most used ways, what usually someone founds on Google by\nquering \"linux keylogger\".\n\nAlso, those ways of keylogging shouldn't work to catch the virtual\nkeyboard, while our method allows to catch directly the Unicode,\nautomatically in the language that were written and whatever the input\nmethod is installed and configured or by default.\n\nIf we do a leveled schema about what is more generic, at first we have the\nknown evdev keylogger, later the LKM one, and finally the GTK (or another\nGUI library) keylogger. Because with evedev we read a device, which brings\nus keycodes, with LKM we must take care about versions and support (but\nthose are usually supported and not hard to port), and in our keylogger we\ndepend on the binary code inside the libraries we hook. Because this method\nis all about of hooking the right place, and catch the desired text.\n\nThere are more than one place in GTK were you can hook and catch data, but\nthere's only 1 place that supports virtual keyboard, any charset and all\nthe input methods. And finding those places were really hard even having\nthe source code, as the whole source code is based just in give support to\nsome platforms: openSUSE, Debian \u0026 Ubuntu, and some versions (the actual\nwhen we developed it) of libgtk. Also, to catch special characteres there\nare another places. And the most hard thing is that we had to do for the\ntwo main versions of GTK available nowadays, which are v2 and v3.\n\nBoth are similar as they're a before release and a late one, but we must\nsay that in the latest one, the things are easier and better supported. For\nexample if we write in Arab inside the input text of a password field in\nFirefox, we'll catch rubbish but that's because the v2 of GTK is not well\nimplemented for that, and finally it's better, because then the user must\nuse an ascii password and we won't catch false data as it won't login the\nuser, when writing a password in Arab.\n\nIn the v3, the things are all well supported, and we can catch every\nUnicode character on any text input of the GTK applications.\n\nWe didn't gave support to Qt as the most supported is GTK, and even in KDE\nwe see that there aren't interesting applications where to catch\nsensitive data. The most cool are coded with GTK, v2 or v3. That's the way\nwe did the GTK keylogger, because all we catch is what is seen on the\ngraphical applications, and those like Chrome, Chromium and Firefox are\nhandling the most sensitive data an user can work with.\n\nA fail on our work, is that we can not catch KeePass passwords, because\nthat application is windows developed and ported to linux with some other\nlibraries different than GTK like libgdi. And as it was the only software\nwe found with those characteristics, we decided to finish our work by\ngiving full support to the versions in these days of GTK.\n\n\nWe must say that we developed this work for our job, we didn't spent a lot\nof time in making our disassemblers/assemblers or a better infection method,\nor persistence.\n\nThe true is that we are using capstone and keystone for this release, and a\nvery weak infection method, as we were doing it first functional and easy\nto face development deadlines.  So, ofcourse there are better ways of doing\nthis, and maybe you are more expert than us.  We reconize that we are just\ntwo researchers who decided to do this because we wanted that level of\nkeylogging and needed for our job, as I said, we are just showing you\nanother way of keylogging that is possible to do, and for sure some other\ncould do it better.  We want to share with you all our work and improve\nthat kind of tools, because we think it's very powerful.\n\n\n--[ 2. Table of keyloggers\n\nHere a table differentiating the kind of keyloggers we can use.\n\n Type           | Support           | Data             | Scope            |\n----------------|-------------------|------------------|------------------|\nEvdev keylogger | Completely generic| Catches keycodes | Keyboard events  |\nLKM keylogger   | Semi generic      | Catches keycodes | All input events |\nGTK keylogger   | Not generic       | Seen on GUI      | All using GTK    |\n\n\n--[ 3. How the GTK keylogger works\n\nFirst of all, we must patch the GTK shared object, making it to load ours.\nOur shared object is the keylogger, that once loaded looks for the right\nplaces where to hook, hooks and sets in the middle of the calls to those\nfunctions.\n\nIt saves into a file in a path, selected at compilation time, all the data that\ncatches. Those are the log files. It differences between user ids, pids\nand gids, to be safe and cleaner.\n\nThe method of hooking is thread-safe and is implemented both for x86 and\nx86_64.\n\nThe reason because it's not generic, is due to the machine code is very\ndifferent in every version tested of v2, and the final implementation of\ncatching special characters in both versions.  Seems that the IM Contexts\nare in development until the v3, were all the text is processed by the same\nroutine. Independently of the language, keymap or input method.\n\nThat requisite made us to do what I think is a well supported hooking,\nhaving in mind that in future versions the machine code can change. So we\nused a disassembler for C, called capstone, that helps us to identify\ndifferent opcodes before patch and then compose the correct hook code.\n\n\n--[ 3.1 The GTK+2 IM Contexts hooked\n\nThere're two places where to hook, because some text is logged in one place\nand other times, depending on the graphical control and language, is sent\nby the other, in GTKv2. \n\nText on method1: (EXPORTED) gtk_im_multicontext_set_context_id() -\u003e\ngtk_im_multicontext_set_slave() -\u003e gtk_im_multicontext_commit_cb()\n\nSpecial chars on method1: (EXPORTED) gtk_im_multicontext_new() -\u003e\nIA__gtk_im_multicontext_get_type() -\u003e\ngtk_im_multicontext_class_intern_init() -\u003e\ngtk_im_multicontext_filter_keypress()\n\nText on method2: (EXPORTED) gtk_im_context_simple_new() -\u003e\nIA__gtk_im_context_simple_get_type() -\u003e\ngtk_im_context_simple_class_intern_init() -\u003e -\u003e\ngtk_im_context_simple_filter_keypress() -\u003e\ngtk_im_context_simple_commit_char()\n\nSpecial chars on method2: (EXPORTED) gtk_im_context_simple_new() -\u003e\nIA__gtk_im_context_simple_get_type() -\u003e\ngtk_im_context_simple_class_intern_init() -\u003e -\u003e\ngtk_im_context_simple_filter_keypress()\n\n\n--[ 3.2 The GTK+3 IM Contexts hooked\n\nOn this version the IM Contexts are stable and better developed, everything\ngoes by one place and it's easier to do everything. So, making a keylogger\nwithout special characters and only for GTK+3 would make the keylogger more\ngeneric.\n\nUnicode: (EXPORTED) gtk_im_multicontext_set_context_id() -\u003e\ngtk_im_multicontext_set_slave() -\u003e gtk_im_multicontext_commit_cb()\n\nSupporting special characters: (EXPORTED) gtk_im_multicontext_new() -\u003e\nIA__gtk_im_multicontext_get_type() -\u003e\ngtk_im_multicontext_class_intern_init() -\u003e\ngtk_im_multicontext_filter_keypress()\n\n\n--[ 3.3 The hooking technique\n\nWe implemented hooking for x86 and x64, and it's decided on compilation\ntime.\n\nThe hooking method is based in three parts: routine patch, hook entry and\ntrampoline.\n\n\n--[ 3.3.1 The hook in x86\n\nThe patch is done with push + ret and filled with nops.\n\nThe hook entry is basicaly a prolog and epilog that saves the registers and\nthe flags before calling the C handler. Same as x64 but easier.\n\nIt's located on the file `src/hook_entry_x86.S`.\n\nThe first call which is located in the function to hook, always is a call\nto a function that moves to a register (ebx, eax, ecx, etc.) the value of\nthe retaddr of the next instruction on that call, to make a trick of\nrip-relative and be able to work with the following lea. So, we hardcoded\nthat instruction with a mov of the next address of the call.\n\nThe rest calls are rebuilded with a trick in ASM:\n\n```\njmp _call\n_push:\n  push $addr_of__call\n  ret\n_call: call _push\n```\n\nAs maybe you know, in x86_64 there're a difference from x86 when speaking\nabout pointing in code, which is called RIP relative addressing. That was\nrelatively easy to solve with capstone and the help of Dreg, but in x86 the\nmachine code is also using some RIP relative addressing, based on a call to\na function that just moves into a register the EIP and uses some other\nregister to point relatively to its value, as can be eax or any other.\nWhat we did is explained in the section 3.3.1, and it's to get the EIP and\nmove it to the register that the routine is using in libgtk to simulate\nthat way of getting EIP into EAX, EBX, ECX or EDX that is used nowadays in\nthe x86 machine code of Linux.\n\nHere you have a disassembly of some of the places we hook, in x86, with the\nexplained technique, and following the solution:\n\n```\n(gdb) disas gtk_im_multicontext_new\nDump of assembler code for function IA__gtk_im_multicontext_new:\n   0x00109d10 \u003c+0\u003e: push   %ebx\n   0x00109d11 \u003c+1\u003e: call   0x51730 \u003c__x86.get_pc_thunk.bx\u003e\n   0x00109d16 \u003c+6\u003e: add    $0x3e82ea,%ebx\n\n[...]\n\nEnd of assembler dump.\n(gdb) disas 0x51730\nDump of assembler code for function __x86.get_pc_thunk.bx:\n   0x00051730 \u003c+0\u003e: mov    (%esp),%ebx\n   0x00051733 \u003c+3\u003e: ret\nEnd of assembler dump.\n\nsnprintf(asm_code,\n  sizeof(asm_code),\n  \"mov %s, 0x%x\",\n  hardcoded_reg,\n  (unsigned long) insn[i + 1].address);\n\nprintf(\"%s\\n\", asm_code);\n\nasm_opcodes = Assemble(asm_code, \u0026opcodes_sz);\n```\n\n--[ 3.3.2 The hook in x64\n\nPatch of the routine to hook:\n\n```\npush rax\nmov rax, ADDR_hook_entry\njmp rax\npop rax \u003c-- ADDR return from\ntrampoline\n```\n\nThe push rax it's used to keep the value of the rax, after the use of this\nin the jmp to the hook entry.  The pop rax it's executed after the jmp from\nthe trampoline, because then we recover the value we had before.\n\nThe hook entry:\n\nWhen we do pop rax and after a push of all the registers, we keep the rax.\nAnd all the registers.\n\n```\npop rax\npushfq\npush REGISTERS\ncall handler_in_C\npop REGISTERS\npopfq\njmpq TRAMPOLINE_ADDRESS\n```\n\nThe trampoline in x64 rebuilds the lea, mov and push rip-relative.  And\nhas the following structure:\n\n```\nrepaired_opcodes\npush rax\nmov rax, ADDR_return_from_trampoline\njmp rax\n```\n\n--[ 3.4 Infection\n\nThe idea is to load our shared-object on each GTK app, so the best idea for\nkeeping it user-space we found, is to patch the libgtk shared-object,\nmaking it to load us on every load, because every GTK GUI loads this shared\nobject.\n\nWe found the tool patchelf in CPP which is on references, and we solved\nthis issue easily.\n\nThis tools what it does is to add a new NEEDED entry on the table in the\nlibgtk that the loader recognizes as a depdendency.  So as we did a\nkeylogger inside a shared-object, it adds our keylogger as a dependency of\ngtk.\n\nWhen the application loads gtk, gtk loads us. Then we are in the context of\nthe application.\n\nPatchELF can be found on GitHub and is on the references section.\n\n\n--[ 3.5 The problem with ld-config\n\nld-config checks the integrity of each shared-object installed, so after\nthe call of ld-config, which is called for example on an upgrade, we can\nsee problematic messages of library integrity, that can advice the root\nthat something is wrong.\n\nThe solution is to patch ld-config, hook the function that advises, and do\na simple logic:\n\n```\nif (strcmp(library, \"libgtk*\") == 0) {\n\t// nothing to do :)\n} else {\n\t// advise\n}\n```\n\n--[ 4. Given support\n\nThe supported versions of libgtk+2 are: Ubuntu: 2.24.30-1ubuntu1,\n2.24.30-4ubuntu2 and 2.24.30-4ubuntu3 Debian: 2.24.25-3+deb8u1 OpenSuse:\n2.24.31-12.1\n\nThe operating systems were it was tested are: OpenSUSE x86_64, Debian 8\nx86_64, Ubuntu 16.04 x86_64, Ubuntu 16.10 x86_64, Debian 8 x86 and Ubuntu\n16.04 x86.\n\nThe applications on it was tested to work are: Thunderbird, Firefox,\nChrome, Chromiun, LibreOffice, GUI LOGIN, GEDIT, VeraCrypt, Skype, Pidgin,\nIceweasel, Terminal X.\n\nThe development was done in the GNOME environment, but it should work on\nKDE applications that use GTK.\n\nThe languages tested were: Chineese (Pinyin way), Arab, English and\nSpanish.  But it should work with any language.\n\nThe input methods tested were all offered by Ubuntu. It worked on everyone.\n\nThe virtual keyboard (florence) is logged also.\n\n\n--[ 5. How-to test the GTK Keylogger\n\nFirst of all install it as root, by issuing the `install` script.\n\nOnce the keylogger is installed, open an instance of Firefox for example, or\na new terminal and write.\n\nAs you can see on the directory you choosed (/tmp for this case), there're\nnew .txt files.\n\n```\ndiwou@diwou-VirtualBox:~/shared_folder/ubuntu-x86$ ls /tmp\nconfig-err-gW7SKO\nfirefox.2595.2595.method1.log\nfirefox_diwou\nsystemd-private-c1ff4aea416542d38766daa829d3ec41-colord.service-IuWFjn\nsystemd-private-c1ff4aea416542d38766daa829d3ec41-rtkit-daemon.service-khr7wd\nunity_support_test.1\n\ndiwou@diwou-VirtualBox:~/shared_folder/ubuntu-x86$ cat\n/tmp/firefox.2595.2595.method1.log \nhotmai\n\nphrack@hotmail.om\n\npassword\n\ndiwou@diwou-VirtualBox:~/shared_folder/ubuntu-x86$ od --width=10 -c\n/tmp/firefox.2595.2595.method1.log \n0000000 357 273 277   g  \\b  \\b  \\b  \\b   h   o\n0000012   t   m   a   i  \\n  \\n   p   h   r   a\n0000024   c   k   @   h   o   t   m   a   i   l\n0000036   .   c   m  \\b  \\b   o   m  \\n  \\n   p\n0000050   a   s   s   w   o   r   d  \\n\n0000060\ndiwou@diwou-VirtualBox:~/shared_folder/ubuntu-x86$ \n```\n\nTo uninstall it just execute the uninstall script.\n\n```\nroot@diwou-VirtualBox:~/shared_folder/ubuntu-x86# ./uninstall \nUninstalling keylogger ...\ndone!\n```\n\n--[ 6. Greetings\n\ndiwou: Greetings to my brother LogicMan, to help me on my begginings and always\ngive me his support.\n\n\n--[ 7. References\n\n[1] https://nixos.org/patchelf.html  \n[2] https://github.com/NixOS/patchelf  \n[3] https://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15213-f03/www/ftrace/elf.c  \n[4] https://developer.gnome.org/gtk3/stable/gtk-building.html  \n[5] https://github.com/GNOME/gtk/tree/master  \n[6] https://developer.gnome.org/gtk3/stable/GtkIMContext.html#gtk-im-context-get-surrounding  \n[7] https://developer.gnome.org/pygtk/stable/class-gtkimcontextsimple.html  \n[8] https://developer.gnome.org/pygtk/stable/class-gtkimmulticontext.html  \n[10] https://github.com/GNOME/gtk/tree/gtk-2-0  \n[11] https://github.com/GNOME/gtk/blob/gtk-2-0/gtk/gtkimmulticontext.c  \n[12] https://github.com/GNOME/gtk/blob/gtk-2-0/gtk/gtkimcontextsimple.c  \n[13] https://github.com/GNOME/gtk/blob/gtk-3-0/gtk/gtkimmulticontext.c  \n[14] http://www.linuxquestions.org/questions/debian-26/dpkg-buildpackage-passing-configure-options-351791/  \n[15] https://github.com/kernc/logkeys  \n[16] http://www.securitybydefault.com/2013/09/listado-de-keyloggers-windows-linux.html  \n[17] https://packetstormsecurity.com/files/87139/Nux-Keylogger-0.0.1.html  \n[18] https://sourceforge.net/projects/lkl/  \n[19] https://github.com/zacscott/zedlog  \n[20] http://resources.infosecinstitute.com/keylogger/  \n[21] https://git.zx2c4.com/evdev-keylogger  \n[22] https://github.com/kwhat/libuiohook/tree/master/src/x11  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftherealdreg%2Fthe-gtk-keylogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftherealdreg%2Fthe-gtk-keylogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftherealdreg%2Fthe-gtk-keylogger/lists"}