Linux
输入子系统是 Linux
内核用于管理各种输入设备的。从上到下由三层实现,分别为:输入子系统事件处理层、输入子系统核心层和输入子系统设备驱动层。
设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。
/dev/input
目录下显示的是已经注册在内核中的设备编程接口,用户通过 open
这些设备文件来打开不同的输入设备进行硬件操作。
我们需要监控键盘,那么可以通过读取驱动程序得到键盘的活动。
用 C 语言编写一个 linux 键盘记录器
- 执行
cat /proc/bus/input/devices
查看键盘活动的对应驱动,内容较多,我们可以使用grep
筛选一下。-A 5
表示显示后面5
行,-C 1
表示显示前后1
行。
- 现在我们知道,我们需要读取的驱动文件是
/dev/input/event4
,那么open
之,然后read
之。读取时这个驱动每次都会给我们返回一个struct input_event
,我们分析这个结构体就知道键盘活动了。
struct input_event event; |
- 测试代码。将代码编译后使用
root
权限执行,因为需要保证能够读取驱动文件。最后生成的log.txt
也需要使用root
权限查看。
我们可以看到,在大写和 shift
情况下,均能够正常工作。在执行 sudo ./keylogger
时我使用 ctrl + C
结束程序但是这两个按键没有出现在我们的 log.txt
中的原因是我使用了外接键盘,需要监听的驱动程序不同。
用 python 语言编写一个 linux 键盘记录器
参考链接,linux 下的键盘记录程序。这里都讲的很清楚了,有几个坑。
- 在安装模块的时候,需要执行
sudo pip install evdev
,因为后面我们的程序是在sudo
权限下执行,如果安装在了用户空间会报找不到模块的错误。个人理解,反正在执行的时候我报错了。 - 作者是使用
python2
写的代码,不过现在已经到了python3
时代了。没有什么很大的需要调整的,只需要把print
函数加个括号。dev.fn
需要修改为dev.path
。 re.search('eyboard', dev.name)
我们可以修改为re.search('keyboard', dev.name)
,如果没有修改,在我的机器上找到的Keyboard
,从而没有办法得到结果。可能是外接键盘的锅。- 注意,作者用了个
select
函数,这个是为了I/O
多路复用,避免程序阻塞,造成性能浪费。由内核监控dev
,当dev
的状态发生变化时,内核通知我们的程序来处理。多路复用(select
、poll
和epoll
)在处理多个I/O
操作时非常有用,假设我们有100
个I/O
操作,如果都阻塞线程,那么对性能是一种极大的浪费。
执行结果如下:
使用 keylogger 命令
- 安装这个工具。
git clone https://github.com/kernc/logkeys.git |
- 使用
logkeys
来监控键盘,通过-s
选项开启,通过-k
选项关闭。
我们可以看到,日志文件记录下来我们系统的所有的键盘行为。
- 通过
sudo make uninstall
将这个工具从我们的系统中移除。
思考与总结
event x
可以读,那么event x
事件也可以写,这样就可以实现模拟敲击键盘。
void write_device(const char *dev) { |
不断模拟按键和松开键,得到的结果如下,event.code = 2
对应的按键就是 1
:
- 阅读
logkeys
源码,分析实现原理与过程。
从下面的截图我们可以看出,核心方案也是先通过命令,得到和键盘相关的驱动。然后读取驱动文件,得到 struct input_event
结构体。然后处理结构体,将键盘监控信息写入文件。