云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

华为云_企业邮箱注册申请需要钱吗_0元

小七 141 0

如何对Linux内核进行Monkey补丁

我有一个奇怪的装置。我输入Dvorak。但是,当我按住ctrl或alt键时,我的键盘会变为Qwerty。你看,经典的文本编辑热键,ctrl+Z,ctrl+X,ctrl+C和ctrl+V都位于Qwerty布局的最佳位置:在控制键旁边,当你用右手移动鼠标时,你的左手很容易够到。不幸的是,在Dvorak中,这些热键大多分散在键盘的右半部分,这使得它们不太方便。使用Dvorak打字,而Qwerty用于热键是一个不错的折衷方案。但是,在Linux/X上实现这一点的唯一方法是编写一个程序,使用X"grabs"截获关键事件并重写它们。这基本上没问题,直到最近,我的机器,未经通知,更新到韦兰。值得注意的是,我一开始都没注意到!但在某个时候,我意识到我的热键不能正常工作。你看,Wayland和X不同,实际上有一些合理的安全规则,因此,随机程序不能再仅仅捕获所有的键盘事件。破坏了我的计划。是的,没错,我就是那个人:来源:xkcd 1172那我该怎么办?我开始担心我需要直接在Wayland或Linux内核中修改键盘处理。维护我自己的分支核心系统基础设施(频繁变化)并不是一个吸引人的想法。绝望中,我问Cloudflare工程聊天频道是否有人知道更好的方法。就在那时,马雷克·克罗梅克前来营救:通过Marek的链接,我发现:#! /usr/bin/env stap公司#这是没有用的,但它证明了这一点#Systemtap可以修改运行内核中的变量。#用法:/keyhack.stp公司-g探查内核函数("kbd事件"){#将"m"更改为"b"。如果($event_code==50)$event_code=48}探头端{printf("\nDONE\n")}哦,天哪。这是什么?你说"这没用"是什么意思?这几乎就是我想要的!SystemTap:不只是为了调试?SystemTap是一个工具,它允许您探测Linux内核以进行调试。它允许您钩住任何内核函数(是的,任何在内核中定义的C函数)并记录参数值或其他系统状态。脚本是用一种特殊的语言编写的,旨在防止您做任何可能破坏系统的事情。但事实证明,你可以做的不仅仅是阅读:使用-g标志(代表"古鲁模式",在这种模式下,你可以为自己的行为承担责任),你不仅可以阅读,还可以修改。此外,您可以注入原始的C代码,从而摆脱SystemTap正常语言的限制。SystemTap的命令行工具stap将脚本编译成Linux内核模块并加载它。加载后,模块将找到您要探测的函数,并用跳转到您的探测代码覆盖它。探测代码执行您指定的操作,然后跳回原始函数体继续执行。当您终止stap时(例如通过命令行上的ctrl+C),它卸载模块,将探测的函数恢复到其原始状态。这意味着在任何时候向运行的系统中注入一个探针是很容易而且相对安全的。如果它不能满足您的需要,您可以安全地删除它,修改它,然后再试一次。不需要修改实际的内核代码,也不需要重新编译内核。您可以在不使用叉子的情况下进行更改。当然,这是动态编程语言中的一个众所周知的实践,在这种情况下通常要容易得多。我们称之为"猴子修补"。什么时候可以去猴子公园?"猴子补丁"常被用作贬义词。许多开发者一想到这个就畏缩不前。这是一次可怕的黑客攻击!千万别这样!实际上,在很多情况下,猴子修补是一个可怕的想法。在上一份工作中,我花了数周时间调试由我们的一个依赖项生成的一个坏的(但善意的)monkey补丁引起的问题。但是,通常情况下,一个小猴子补丁可以节省很多工作。通过monkey修补我的内核,我可以获得我想要的键盘行为,而无需永远维护fork,也无需花费数周时间开发一个值得向上游推进的特性。当我修理自己的机器时,除了我自己,我不能伤害任何人。我建议猴子修补的两条规则:只有环境的唯一拥有者才能对其进行修补。"所有者"是一个实体,他对猴子补丁可见的环境中存在的所有代码具有完全的判断力和控制权。对于一个精确指定其所有依赖项的自包含应用程序,可能允许应用程序开发人员在应用程序的运行时中对库进行修补,但是库和框架决不能应用monkey-patches。当我们谈论内核时,"所有者"是系统管理员。业主对由此造成的任何损坏承担全部责任。如果有什么地方出了问题,那就由主人来处理,或者放弃他们的补丁。在这种情况下,我是系统的所有者,因此我有权对其进行修补。如果我的monkey补丁坏了(比如说,因为我修补的内核函数在后来的内核版本中发生了变化),或者它破坏了我使用的其他程序,那就是我的问题,我会处理它。设置要使用SystemTap,必须安装内核头和调试符号。我发现我的Debian系统上的文档不太正确。我通过运行以下命令成功安装了所有设备:sudo apt install systemtap linux-headers-amd64 linux-image-amd64-dbg注意,调试符号是一个巨大的包(~500MB)。看来这就是你付出的代价。错误的开始从将"m"重新映射到"b"的示例脚本开始,似乎很明显如何继续。我将脚本保存到一个文件中,然后:sudo stap-g公司keyhack.stp公司但是…什么也没发生。我的"m"键仍键入"m"。为了调试,我添加了一些printf()语句(可以方便地打印到stap运行的终端)。但是,似乎键盘事件确实被捕捉到了。那为什么"m"还是"m"呢?结果,没人在听。kbd_event函数是文本模式终端支持的一部分。果不其然,如果我把虚拟终端切换到文本终端,那么密钥就会被重新映射。但是Wayland使用完全不同的代码路径来接收关键事件,/dev/输入设备。这些设备由evdev模块实现。查看evdev.c,首先evdev_event()作为探测点看起来很诱人:它与kbd_event()具有几乎相同的签名。不幸的是,驱动程序通常不调用此函数;相反,多事件版本evdev_events()通常是这样。但是这个版本需要一个数组,这看起来更乏味。进一步看,我遇到了优pass_event(),它是evdev_events()为每个事件调用的。它与kbd_event()略有不同,因为事件被封装在一个结构中,但至少一次只接受一个事件。这似乎是最容易探测的地方,所以我尝试了:#不起作用探测模块("evdev").function("\u pass_event"){#将"m"更改为"b"。如果($event->code==50)$event->code=48}唉,这不太管用。在运行stap时,我得到了:语义错误:检索不到"event"的位置属性这个错误似乎很奇怪。函数肯定有一个名为event的参数!问题是,uPass_event()是一个只从一个地方调用的静态函数。因此,编译器将其内联。当一个函数被内联时,它的参数通常不再在内存中有一个定义良好的位置,因此读取和修改它们变得不可行。SystemTap依赖于指定在何处查找参数的debug info表,但在本例中,这些表没有答案。工作版本唉,似乎我需要使用evdev_events()并处理数组。这个函数需要一个事件数组来同时传递,所以它的参数不是很方便。但是,它有多个调用位置,所以它没有内联。我只需要一个小循环:探测模块("evdev")。函数("evdev_events"){for(i=0;icode==50)$vals[i]>code=48}}成功!这个剧本很管用。我再也没有办法输入'm'。从这里开始,实现我想要的Dvorak Qwerty密钥重映射行为只需编写一些代码来跟踪修改器密钥状态和重新映射密钥。你可以在GitHub上找到我的完整脚本。