当前位置: 首页 > news >正文

动漫美女做爰视频网站/360优化大师官方版

动漫美女做爰视频网站,360优化大师官方版,快速搭建网站模板 下载,wordpress网代码显示图片eBPF 实时捕获键盘输入 本文将带你一步步实现一个基于eBPF kprobe的键盘记录功能,通过Go语言配合libbpfgo,你将学会如何无损地监控系统键盘输入,并从中获取实时数据,进一步提高系统安全和监控能力。 1. 说明 本文属于专栏 Go语言…

eBPF 实时捕获键盘输入

本文将带你一步步实现一个基于eBPF kprobe的键盘记录功能,通过Go语言配合libbpfgo,你将学会如何无损地监控系统键盘输入,并从中获取实时数据,进一步提高系统安全和监控能力。


1. 说明

本文属于专栏 Go语言+libbpfgo实战eBPF开发,示例代码目录为 040

如何下载并运行代码,请参考 专栏介绍。

注: 老学员可以直接 git pull 拉取最新代码。


2. 引言

在本篇文章中,我们将利用 eBPF 的 kprobe 技术捕捉键盘输入事件,实现一个简单的键盘记录功能。你将学习如何在内核层面监控 input_handle_event 函数,并将捕获到的按键事件传递到用户空间进行处理。整个过程高效且非侵入式,适用于实时监控场景。💡

eBPF 允许在内核中动态加载和执行代码。借助 kprobe,我们可以在 input_handle_event 函数入口处挂载探针,从而捕获关键事件。

  • 键盘事件类型:我们关注 EV_KEY 类型的按键事件,并仅记录按下(value=1)时的事件。
  • 事件传递方式:通过 bpf_perf_event_output 将事件数据发送到用户空间,用户空间程序使用 perf buffer 进行实时读取。

这样既能精准监控输入事件,又无需对系统进行侵入式修改。


3. 原理详解

3.1 当你按下一个键盘上的按键时,发生了什么?

流程图

硬件中断 → 键盘驱动 → 输入子系统 → TTY 行规程 → Shell 进程 → TTY 回显 → 终端渲染

在 Linux 系统中,从按下键盘到字符显示在终端上,涉及 硬件中断处理、内核子系统协作和用户空间进程交互。以下是详细流程和关键函数。

1. 硬件中断触发

  • 硬件层面:键盘控制器(如 PS/2 或 USB)检测到按键动作,生成 硬件中断(PS/2 键盘通常使用 IRQ 1)。
  • 中断控制器:将中断路由至 CPU,CPU 通过中断向量表调用对应的 中断处理程序

2. 内核中断处理

  • 中断处理函数:内核注册的键盘中断处理程序(irq_handler)被触发。
    • 关键函数request_irq() 注册中断处理函数,如 PS/2 键盘的 kbd_event 或 USB 键盘的 usb_kbd_irq
  • 读取扫描码:驱动从键盘控制器读取 扫描码(Scancode)(如 inb(0x60) 读取 PS/2 键盘数据端口)。
  • 转换为键码:扫描码转换为 键码(Keycode)(如 kbd_keycode 处理映射关系)。

3. 输入子系统(Input Subsystem)

  • 生成输入事件:键码通过输入子系统封装为 input_event 结构(包含时间戳、键值、动作等)。
    • 关键函数input_event()input_handle_event()
  • 传递事件:事件通过 /dev/input/eventX 设备节点传递,供用户空间程序(如终端)读取。
    • 关键结构struct input_handler 负责事件路由(如 evdev_handler)。

4. TTY 子系统处理

  • 绑定到 TTY:输入事件传递到当前活动的 TTY(如 /dev/tty1)。
    • 关键函数tty_insert_flip_char() 将字符写入 TTY 的 flip buffer。
  • 行规程(Line Discipline):处理特殊字符(如回车、退格)。默认行规程为 n_tty
    • 关键函数n_tty_receive_char() 解析字符并执行回显逻辑。
  • 刷新缓冲区:调用 tty_flip_buffer_push() 推送数据至 TTY 读队列。

5. 用户空间进程读取输入

  • 前台进程:Shell(如 Bash)通过 read() 系统调用读取 TTY 设备的输入。
    • 关键路径read()tty_read()copy_from_read_buf()
  • 行编辑模式(Canonical Mode):启用时,输入缓存在内核直到用户按下回车。

6. 字符显示到终端

  • 回显(Echo):默认 n_tty 规程会自动回显字符到终端。
    • 关键函数n_tty_receive_char() 调用 echo_char() 进行回显。
  • 终端写入:字符通过 write() 系统调用发送至终端显示。
    • 关键路径write()tty_write()do_tty_write() → 终端驱动的写函数。
  • 终端渲染方式
    • 物理终端:通过显卡驱动(如 vt_console_print())直接输出。
    • 伪终端(PTY):如 SSH 或终端模拟器(如 GNOME Terminal),字符通过 PTY 主从设备传输,最终由终端模拟器渲染。

3.2 在哪里捕获键盘输入事件?

在 Linux 系统中,键盘输入事件可以在不同层次进行捕获,主要包括 内核态用户态,以下是常见的捕获位置及方法。

1. 内核态捕获

1.1 中断处理程序(IRQ 级别)

  • 最底层的捕获方式,在键盘触发 硬件中断 时,内核的 中断处理函数 被调用。
  • 相关代码位置:drivers/input/keyboard/atkbd.c(PS/2 键盘) 或 drivers/hid/usbhid/usbkbd.c(USB 键盘)。
  • 关键函数:
    • irq_handler_t kbd_event()(PS/2)
    • usb_kbd_irq()(USB)
    • 读取 扫描码 并传递至 输入子系统

1.2 输入子系统(Input Subsystem)

  • 内核的 input_event 机制封装了键盘输入事件。
  • 相关设备节点:/dev/input/eventX
  • 关键函数:
    • input_event() 生成键盘事件。
    • input_handle_event() 处理事件并分发至用户空间。
2. 用户态捕获

2.1 通过 /dev/input/eventX 捕获原始输入事件

  • 适用于读取底层输入设备(适用于键盘监听、按键统计等)。

  • 代码示例(使用 evdev 接口):

    #include <stdio.h>
    #include <fcntl.h>
    #include <linux/input.h>int main() {int fd = open("/dev/input/event2", O_RDONLY);if (fd < 0) {perror("open");return 1;}struct input_event ev;while (read(fd, &ev, sizeof(ev)) > 0) {if (ev.type == EV_KEY && ev.value == 1) {printf("Key %d pressed\n", ev.code);}}close(fd);return 0;
    }
    

2.2 通过 /dev/tty 读取终端输入

  • 适用于读取当前终端的键盘输入(受 TTY 规程控制)。

  • 代码示例(读取标准输入):

    #include <stdio.h>int main() {char c;while (1) {c = getchar();printf("Pressed: %c\n", c);}return 0;
    }
    

2.3 使用 libinput 监听键盘输入(Wayland 环境)

  • 适用于现代桌面环境(X11/Wayland)。

  • 监听系统级输入事件,适用于图形界面应用。

  • 代码示例(Python):

    from evdev import InputDevice, categorize, ecodesdev = InputDevice('/dev/input/event2')
    for event in dev.read_loop():if event.type == ecodes.EV_KEY:print(categorize(event))
    

2.4 监听 X11 按键事件(X Window System)

  • 适用于 GUI 应用,使用 Xlib 监听按键。

  • 代码示例(Python + python-xlib):

    from Xlib import display, Xd = display.Display()
    r = d.screen().root
    r.grab_keyboard(True, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
    while True:event = r.display.next_event()if event.type == X.KeyPress:print("Key pressed")
    

3.3 选择合适的捕获方式

需求推荐方法
低级输入监控(扫描码)内核 input_event 或者 input_handle_event API
监听所有键盘事件读取 /dev/input/eventX
监听终端输入读取 /dev/tty
GUI 应用按键捕获Xliblibinput

不同场景下,可以选择合适的方式进行键盘输入捕获。

本文将演示通过 hook 内核函数 input_handle_event 实现键盘记录功能。


4. 代码详解

4.1 bpf代码

4.1.1 整体逻辑
  • 加载入口: 代码通过SEC("kprobe/input_handle_event")挂载到内核的input_handle_event函数。
  • 事件过滤: 判断传入的type是否为EV_KEYvalue是否等于1,只在按键按下时进行处理。
  • 数据发送: 如果满足条件且按键码小于MAX_KEYS,则利用bpf_perf_event_output将事件数据发送到用户空间。
4.1.2 代码细节解析
  • 头文件与宏定义:

    • 包含了vmlinux.hbpf_helpers.hbpf_tracing.hbpf_core_read.h等头文件,保证代码能调用内核相关的API。
    • 定义了EV_KEYMAX_KEYS,分别代表按键事件类型和允许的最大按键数量。
  • 事件结构体:

    struct event_t {u32 type;u32 code;u32 value;
    };
    

    该结构体用于保存按键事件数据,包括事件类型、按键码和按键状态。

  • kprobe函数:

    SEC("kprobe/input_handle_event")
    int BPF_KPROBE(hook_input_handle_event, struct input_dev *dev, unsigned int type, unsigned int code, int value)
    {struct event_t event = { 0, };event.type = type;event.code = code;event.value = value;if (type == EV_KEY && value == 1){if(code < MAX_KEYS) {bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));} }return 0;
    }
    
    • 整体逻辑: 在每次input_handle_event调用时,将输入数据封装到event中;判断条件满足时,将事件通过perf event发送。
    • 细节说明:
      • SEC("kprobe/input_handle_event"):声明该函数为kprobe处理函数。
      • BPF_KPROBE宏用于自动生成探针入口。
      • bpf_perf_event_output负责将数据传递到用户空间,不涉及错误处理代码。

4.2 go代码

4.2.1 main.go
整体逻辑
  • 信号注册: 利用signal.NotifyContext注册系统信号(如SIGINTSIGTERM),确保程序能优雅退出。
  • 加载BPF程序: 调用BpfLoadAndAttach加载并附加BPF程序文件bpf.o
  • 创建perf buffer: 初始化perf buffer,通过eventsChannellostChannel接收事件数据及丢失事件统计。
  • 事件处理: 启动一个事件处理循环,实时解析并打印键盘事件,直到收到退出信号。
代码细节解析
  • 日志设置: 使用logrus设置日志级别和时间格式,使调试信息更直观。
  • 资源管理: 利用defer语句确保BPF模块和perf buffer在程序结束时被正确关闭。
  • 事件循环:
    for {// 循环接收事件select {// 接收到事件时打印事件信息case data := <-eventsChannel:var event Eventerr := event.Parse(data)if err != nil {log.Printf("parse event error: %v", err)} else {log.Println(event.String())}}
    }
    
    该循环实时响应来自内核空间的事件数据。

4.2.2 event.go
整体逻辑
  • 数据结构: 定义了Event结构体,用于与内核传来的event_t数据一一对应。
  • 数据解析: Parse方法利用binary.Read将接收到的字节流转化为结构体数据。
  • 数据展示: String方法调用keyStr函数,将按键码转换为对应的按键名称,便于直观展示。
代码细节解析
package mainimport ("bytes""encoding/binary"
)type Event struct {Type  uint32Code  uint32Value uint32
}// 解析event数据
func (e *Event) Parse(data []byte) error {err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, e)if err != nil {return err}return nil
}// 转换成字符串
func (e *Event) String() string {return keyStr(int(e.Code))
}const MAX_KEYS = 256var keyNames = [MAX_KEYS]string{0: "RESERVED",1: "ESC",2: "1", 3: "2", 4: "3", 5: "4", 6: "5", 7: "6", 8: "7", 9: "8", 10: "9", 11: "0",12: "MINUS", 13: "EQUAL", 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: "LEFTBRACE", 27: "RIGHTBRACE", 28: "ENTER", 29: "LEFTCTRL",30: "A", 31: "S", 32: "D", 33: "F", 34: "G", 35: "H", 36: "J", 37: "K", 38: "L", 39: "SEMICOLON",40: "APOSTROPHE", 41: "GRAVE", 42: "LEFTSHIFT", 43: "BACKSLASH",44: "Z", 45: "X", 46: "C", 47: "V", 48: "B", 49: "N", 50: "M", 51: "COMMA", 52: "DOT", 53: "SLASH",54: "RIGHTSHIFT", 55: "KPASTERISK", 56: "LEFTALT", 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: "SCROLLLOCK", 71: "KP7", 72: "KP8", 73: "KP9", 74: "KPMINUS", 75: "KP4", 76: "KP5", 77: "KP6", 78: "KPPLUS",79: "KP1", 80: "KP2", 81: "KP3", 82: "KP0", 83: "KPDOT",85: "ZENKAKUHANKAKU", 86: "102ND", 87: "F11", 88: "F12", 89: "RO", 90: "KATAKANA", 91: "HIRAGANA", 92: "HENKAN", 93: "KATAKANAHIRAGANA", 94: "MUHENKAN",95: "KPJPCOMMA", 96: "KPENTER", 97: "RIGHTCTRL", 98: "KPSLASH", 99: "SYSRQ", 100: "RIGHTALT", 101: "LINEFEED",102: "HOME", 103: "UP", 104: "PAGEUP", 105: "LEFT", 106: "RIGHT", 107: "END", 108: "DOWN", 109: "PAGEDOWN", 110: "INSERT", 111: "DELETE",112: "MACRO", 113: "MUTE", 114: "VOLUMEDOWN", 115: "VOLUMEUP", 116: "POWER", 117: "KPEQUAL", 118: "KPPLUSMINUS", 119: "PAUSE",120: "SCALE", 121: "KPCOMMA", 122: "HANGEUL", 123: "HANJA", 124: "YEN", 125: "LEFTMETA", 126: "RIGHTMETA", 127: "COMPOSE",128: "STOP", 129: "AGAIN", 130: "PROPS", 131: "UNDO", 132: "FRONT", 133: "COPY", 134: "OPEN", 135: "PASTE", 136: "FIND", 137: "CUT",138: "HELP", 139: "MENU", 140: "CALC", 141: "SETUP", 142: "SLEEP", 143: "WAKEUP", 144: "FILE", 145: "SENDFILE", 146: "DELETEFILE", 147: "XFER",148: "PROG1", 149: "PROG2", 150: "WWW", 151: "MSDOS", 152: "COFFEE", 153: "ROTATE_DISPLAY", 154: "CYCLEWINDOWS", 155: "MAIL", 156: "BOOKMARKS",157: "COMPUTER", 158: "BACK", 159: "FORWARD", 160: "CLOSECD", 161: "EJECTCD", 162: "EJECTCLOSECD", 163: "NEXTSONG", 164: "PLAYPAUSE",165: "PREVIOUSSONG", 166: "STOPCD", 167: "RECORD", 168: "REWIND", 169: "PHONE", 170: "ISO", 171: "CONFIG", 172: "HOMEPAGE", 173: "REFRESH",174: "EXIT", 175: "MOVE", 176: "EDIT", 177: "SCROLLUP", 178: "SCROLLDOWN", 179: "KPLEFTPAREN", 180: "KPRIGHTPAREN", 181: "NEW", 182: "REDO",183: "F13", 184: "F14", 185: "F15", 186: "F16", 187: "F17", 188: "F18", 189: "F19", 190: "F20", 191: "F21", 192: "F22", 193: "F23", 194: "F24",200: "PLAYCD", 201: "PAUSECD", 202: "PROG3", 203: "PROG4", 204: "ALL_APPLICATIONS", 205: "SUSPEND", 206: "CLOSE", 207: "PLAY", 208: "FASTFORWARD",209: "BASSBOOST", 210: "PRINT", 211: "HP", 212: "CAMERA", 213: "SOUND", 214: "QUESTION", 215: "EMAIL", 216: "CHAT", 217: "SEARCH", 218: "CONNECT",219: "FINANCE", 220: "SPORT", 221: "SHOP", 222: "ALTERASE", 223: "CANCEL", 224: "BRIGHTNESSDOWN", 225: "BRIGHTNESSUP", 226: "MEDIA",227: "SWITCHVIDEOMODE", 228: "KBDILLUMTOGGLE", 229: "KBDILLUMDOWN", 230: "KBDILLUMUP", 231: "SEND", 232: "REPLY", 233: "FORWARDMAIL", 234: "SAVE",235: "DOCUMENTS", 236: "BATTERY", 237: "BLUETOOTH", 238: "WLAN", 239: "UWB", 240: "UNKNOWN", 241: "VIDEO_NEXT", 242: "VIDEO_PREV",243: "BRIGHTNESS_CYCLE", 244: "BRIGHTNESS_AUTO", 245: "DISPLAY_OFF", 246: "WWAN", 247: "RFKILL", 248: "MICMUTE",
}func keyStr(code int) string {if code < 0 || code >= MAX_KEYS {return "UNKNOWN"}name := keyNames[code]if name == "" {return "UNKNOWN"}return name
}
  • Parse方法:
    • 通过binary.LittleEndian格式解析数据,确保与内核传输的数据格式一致。
  • String方法与键值映射:
    • 利用预定义的keyNames数组映射按键码到具体按键名称。
    • keyStr函数通过判断码值范围返回对应的字符串,如果不存在则返回"UNKNOWN"

5. 总结

本文通过Go语言结合libbpfgo演示了如何利用eBPF实现键盘记录功能,具体如下:

  • 实时性: 直接利用内核kprobe捕捉键盘事件,无需轮询,实时性极佳⏱️。
  • 高效性: 通过perf buffer将数据高效传递到用户空间,确保系统性能不受影响。

该方案不仅适用于键盘事件监控,还可以拓展到其他系统监控和安全检测场景。动手实践后,你也可以根据业务需求调整代码,实现更多高级功能。


6. 练习题

  1. 按键过滤: 修改代码使其只记录特定按键(如ESCENTER)的事件,并在用户空间进行特别处理。
  2. 数据统计: 增加功能,对每种按键的出现次数进行统计,并定时输出统计结果。
  3. 扩展应用: 在BPF程序中增加其他类型事件(如鼠标事件)的监控,探索更多内核事件的捕捉方式。

🚀 动手试试吧!遇到问题欢迎留言交流,一起进步!

http://www.lryc.cn/news/580923.html

相关文章:

  • 哪种网站开发简单/襄阳网站推广优化技巧
  • 共同建设网站协议/google seo教程
  • 凡科建站步骤/杭州产品推广服务公司
  • 关于网络的网站怎么做/音乐接单推广app平台
  • 个人网站做企业备案/今日百度关键词排名
  • 电商网站 网站服务内容/推广app的软文案例
  • 企业网站托管方式/seo泛目录培训
  • wordpress图片模糊/搜索引擎优化的重要性
  • wordpress 炫酷插件/seo排名优化表格工具
  • 宠物网站制作内容/新闻软文推广案例
  • 家里电脑做网站/整站优化外包服务
  • 网站开发前端和后端怎么连接/电子商务网站推广策略
  • 陕西安康网站建设/网站设计方案
  • we建站/黑龙江最新疫情
  • 知名网站开发哪家好/百度广告服务商
  • 安徽网站建设/百度推广登录入口登录
  • 网站建设费算费用还是固定资产/西安seo盐城
  • 网站做电源/李飞seo
  • 管理咨询公司怎么开展业务/网站seo外链平台
  • 韩国男女真人做视频网站/百度seo搜搜
  • 北京网站优化快速排名/晋城seo
  • html5 后台网站模板/网站平台推广
  • 廊坊网站建设-商昊网络/流量精灵app
  • 用织梦做的网站怎么管理/国内优秀网页设计赏析
  • wordpress兼容php版本/seort什么意思
  • asp做旅游网站毕业论文/杭州seo薪资水平
  • 怎么给网站做访问量/百度官网app下载安装
  • java实现网站建设/seo是指
  • 网站建设可以帮助企业/seo服务公司招聘
  • 网站界面尺寸/seo应该怎么做