Linux 笔记本基于“敲打”的命令
扫描二维码
随时随地手机看文章
有史以来第一次您可以敲打一下计算机并得到有意义的响应!使用 Linux® 和 Hard Drive Active Protection System(硬盘活动保护系统HDAPS)内核驱动程序我们可以访问 Lenovo(以前称为 IBM®)ThinkPads 上的嵌入式加速器然后处理加速器的数据来读取特定 敲打 事件序列(也就是您使用关节敲打笔记本的事件序列)并基于这些敲打事件运行一些命令双击锁定屏幕然后敲入密码来解锁敲打显示屏一次就可以让 MP 播放器前进一个音轨这类可能事物是无穷无尽的
年IBM 开始发行集成了加速器和相关软件的 ThinkPad 笔记本以便在笔记本掉到地上时对硬盘进行保护来自 IBM 和其他地方的黑客已经为 Linux 内核开发了一些模块来利用这些传感器的优点屏幕显示方向桌面切换甚至是游戏控制和实时的笔记本倾斜度 D 模块现在都已经可以使用了本文将展示 敲打代码 这种新技术和一个简单程序该程序在检测到特定的敲打代码时会运行一些命令
使用带有 HDAPS 驱动的已更新的内核我们就可以用一个简单程序 knockAge 来生成敲打代码了我们也可以下载并使用一个 Perl 脚本来定制自己的敲打输入环境请参阅本文最后的 下载 和 参考资料 部分给出的链接其中包括了解 knockAge 操作的链接
硬件需求
容易实现
正如您从展示视频(请参见下面 参考资料 中的链接)可以看到的敲打操作是由一系列指节敲打构成的尽管 ThinkPad 的加速器是为保护它不受意外事件的影响但太强力的振荡仍然会对硬盘造成损坏因此我们必须小心
很多在 年以及这以后生产的 IBM(现在是 Lenovo)的 ThinkPads 中都有 HDAPS 硬件如果您不确定自己的硬件配置可以检查 Lenovo 的 Web 站点上关于您自己型号的机器的技术细节如果您的机器上没有 ThinkPad那么这段代码可能无法在您的笔记本上正常工作
本文是在 x 体系架构上编写的本文中的代码是在 ThinkPad Tp 的两个不同模块上进行开发和测试的有关 ThinkPad 硬件的链接请参阅 参考资料 部分
如果您有一台 Apple MacBook那么您可能也有这种加速器并且可以使用相同的方法通过内核访问它们然而本文中的代码并没有在 Apple 硬件上进行测试
软件要求
HDAPS 驱动程序必须包括在内核中才能启用对加速器的访问试图对现有内核增加补丁也不会获得成功因此我们建议从自己喜欢的镜像站点上下载最新的内核新内核发行版中已经包含了对 HDAPS 驱动程序的支持
启动内核配置选择程序并在配置中包含 HDAPS 驱动程序HDAPS 驱动程序位于Device Drivers > Hardware Monitoring Support > IBM Hard Drive Active Protection System (hdaps) 选项中更多的内核配置和安装过程已经超出了本文的范围但是在 Web 站点上有很多教程可以提供具体的帮助有关可以帮助我们入门的链接请参阅 参考资料 一节的内容
本文是在 版本的内核上进行开发和测试的
创建简单的敲打序列
从 下载 一节的链接中下载源代码并从中找到 knockAgepl 脚本这就是让我们可以创建敲打序列的主要 Perl 程序它还允许监听特殊的敲打序列并运行命令下面让我们来介绍一下这个用户空间程序的用法以及 knockAgepl 程序的配置然后再对这个函数进行回顾
使用下面的命令运行 knockAgepl 程序
perl knockAgepl c
这会启动 Perl 程序来监听敲打事件并记录下它们之间的间距以供将来使用一旦程序开始运行之后对笔记本进行的敲打操作就会产生效果我们并不需要在物理上移动自己的 ThinkPad 来注册敲打事件如果 ThinkPad 在一个平面上只要对其进行一些移动和滑行即可我建议您用左右握住 ThinkPad 左边接近连接轴的地方同时用右手在距离 LCD 底部 英寸的地方敲打显示屏即可请参阅 下载 部分给出的视频展示或参阅 参考资料 中用来创建敲打序列的例子
体验不同的敲打幅度和力度从而了解 knockAge 程序能够捕获的事件判断率对于创建复杂的敲打事件来说这非常重要
第一次真正尝试敲打应该非常简单两次双击之间停留 秒然后再次运行 perl knockAgepl c在看到 enter a knock sequence 时稳定地敲打 LCD 边上两次中间停留 秒在 秒之后会自动超时(这是可以配置的)您所敲打的序列会被打印出来这类似于下面的例子
_#_ (command here) _#_
让我们来分析一下这一行的内容敲打序列分隔符命令区分隔符最后是注释区我们的下一个步骤是将这行内容复制到 knockAgepl 程序使用的默认配置文件 {$HOME}/knockFile 中该配置文件也可能是 /home//knockFile 文件在使用上面的敲打序列行创建好 knockFile 文件之后就可以对这行进行修改来运行程序了将 (command here) 文本修改成 /bin/echo double tap并将注释区的内容修改成更有意义的内容例如
_#_ /bin/echo double tap _#_ Double tap event
现在我们已经修改好这个配置文件可以打印一条通知了接下来使用下面的命令在守护模式下运行 knockAge 脚本
perl knockAgepl
这个程序会在后台安静地监听 ~/knockFile 所罗列的事件请使用相同的间隔再次双击屏幕您会看到在屏幕上打印出了 double tap 消息如果我们希望更详细地了解 knockAgepl 脚本是如何工作的那么我们可以使用下面的命令在守护模式下运行它
perl knockAgepl v
使用 xscreensaver 锁定屏幕或打开屏幕
创建 password 序列
使用下面的命令在 create 模式下运行 knockAgepl 程序
perl knockAgepl c
现在我们需要创建一个解锁的密码序列我建议使用 刮脸和理发的动作请确保每次您都可以以一贯精确的方式执行这个动作尽管您可以通过修改参数来控制输入密码敲打操作所需要的精度但是这仍然很难匹配精确的时间刮脸和理发动作 除了可以提供稳定的击打顺序之外其复杂性和简单性对于屏保解锁密码来说也非常适合下面是一个刮脸和理发动作 的击打序列示例
_#_ /bin/echo shave the haircut _#_ two bits
在进行下一步操作之前您应该体验一下上面的命令和 ~/knockFile 配置文件中的双击命令这可以在屏保运行时提供很好的帮助它更难检测出敲打是否正确[!--empirenews.page--]
xscreensaver 所使用的命令配置
以下设置假设您已经登录到了窗口管理器中并且已经使用您的 userid 启动了xscreensaver 程序例如如果您正在运行 Fedora Core 并且使用 gdm 登录到 KDE 中那么 xscreensaver 就会自动启动因此要激活它则需要将双击命令从
/bin/echo double tap
修改为
xscreensavercommand activate &
现在每次识别出有 双击 事件发生时xscreensaver 程序都会使用所指定的内容来激活一旦 screensaver 被激活就可以通过输入密码(如果是这样配置的)对屏幕进行解锁不过我们真正希望的是自己的朋友也可以使用密码解锁代码来解除屏保因此我们需要在 ~/knockFile 文件中将下面的命令
/bin/echo shave the haircut
替换为
killall xscreensaver ; nohup xscreensaver nosplash >/dev/null >/dev/null &
这个命令会停止当前运行的所有 xscreensaver 程序然后在后台再重新启动 xscreensaver现在我们可以通过敲打屏幕边来重复加锁和解锁计算机屏保的过程这比蓝牙提供的近似度加锁更加安全或更方便吗?答案可能是否定的它更酷吗?当然!
更多例子
HDAPS 传感器和 knockAgepl 程序提供了另外一种用户输入设备我们可以使用它们以独特的方式进行输入例如
如果计划在一个基础上测试新的 X 配置文件可以将双击条目更改为重新启动配置好的 X 服务器这样就不需要敲任何其他键来强制重启了
在命令区中可以放上我们喜欢使用的任何 shell 脚本这样就可以使用双击来查看 email
以最新的组合节拍进行敲打让 ThinkLight 显示 WWII 代在 Kinakuta 的黄金存储设备的 Morse 密码位置
敲入 Morse 编码防止键盘输入被记录
请参阅 参考资料 部分给出的有关将 ThinkPad 的倾斜度用于游戏显示工具的例子或者直接跳过这部分内容将 Threshold 变量设置为 这样您使劲踢一脚 ThinkPad它就会自动重启了
knockAgepl 代码
历史和策略
Jeff Molofee 所编写的 hdapsglc 是 knockAgepl 代码的基础Hdapsglc 是一个非常好的展示程序可以展示如何使用倾斜传感器来实时地显示有关 ThinkPad 的方向的信息二者之间的区别是本例将时间上隔离的事件组织在一起创建了敲打事件同时提供了相关的代码来创建并监听敲打事件序列
参数配置
下面让我们来使用对时间和传感器敏感的一些参数来启动 knockAgepl
清单 主程序参数
require sys/syscallph; # for subsecond timing my $option = $ARGV[] || ; # simple option handling # filename for hdaps sensor reads my $hdapsFN = /sys/devices/platform/hdaps/position; my $UPDATE_THRESHOLD = ; # threshold of force that indicates a knock my $INTERVAL_THRESHOLD = ; # microseconds of time required between knock # events my $SLEEP_INTERVAL = ; # time to pause between hdaps reads my $MAX_TIMEOUT_LENGTH = ; # maximum length in seconds of knock pattern # length my $MAX_KNOCK_DEV = ; # maximum acceptable deviation between recorded # pattern values and knocking values my $LISTEN_TIMEOUT = ; # timeout value in seconds between knock # events when in listening mode
这些变量及其注释都非常简单它们的用法和配置选项在本文后面部分会进行解释下面是其余的一些全局变量及其描述
清单 敲打模式参数
my @baseKnocks = (); # contains knock intervals currently entered my %knockHash = (); # contains knock patterns associated commands my $prevInterval = ; # previous interval of time my $knockCount = ; # current number of knocks detected my $restX = ; # `resting positiong of X axis accelerometer my $restY = ; # `resting positiong of Y axis accelerometer my $currX = ; # current position of X axis accelerometer my $currY = ; # current position of Y axis accelerometer my $lastX = ; # most recent position of X axis accelerometer my $lastY = ; # most recent position of Y axis accelerometer my $startTime = ; # to manage timeout intervals my $currTime = ; # to manage timeout intervals my $timeOut = ; # perpetual loop variable my $knockAge = ; # count of knocks to cycle time interval
子程序
在我们的子程序清单中首先是一个简单的逻辑块用来检查是否有加速器可读
清单 检查加速器的子程序
sub checkAccelerometer() { my $ret; $ret = readPosition (); if( $ret ){ print no accelerometer data available tis bork edn; exit(); } }#checkAccelerometer
Jeff Molofee 编写的 hdapsglc 代码为 knockAgepl 中的所有代码提供了一个很好的起点在下面的 readPosition 子程序中我们可以看到他的注释这个子程序将打开一个文件从中读取当前的加速器数据然后关闭文件并返回不包含 (逗号) 字符的数据
清单 readPosition subroutine
## comments from Jeff Molofee in hdapsglc #* read_position read the (xy) position pair from hdaps #* #* We open and close the file on every invocation which is lame but due to #* several features of sysfs files: #* #* (a) Sysfs files are seekable #* (b) Seeking to zero and then rereading does not seem to work ## sub readPosition() { my ($posX $posY) = ; my $fd = open(FH $hdapsFN); while( ){ s/(//g; s/)//g; ($posX $posY) = split ; }# while read close(FH); return( $posX $posY ); }#readPosition
getEpochSeconds 和 getEpochMicroSeconds 提供了有关敲打模式状态的详细而精确的信息
清单 时间分隔器
sub getEpochMicroSeconds { my $TIMEVAL_T = LL; # LL for microseconds my $timeVal = pack($TIMEVAL_T ()); syscall(&SYS_gettimeofday $timeVal ) != or die micro seconds: $!; my @vals = unpack( $TIMEVAL_T $timeVal ); $timeVal = $vals[] $vals[]; $timeVal = substr( $timeVal ); my $padLen = length($timeVal); $timeVal = $timeVal x $padLen; return($timeVal); }#getEpochMicroSeconds sub getEpochSeconds { my $TIMEVAL_T = LL; # LL for microseconds my $start = pack($TIMEVAL_T ()); syscall(&SYS_gettimeofday$start ) != or die seconds: $!; return( (unpack($TIMEVAL_T $start))[] ); }#getEpochSeconds[!--empirenews.page--]
接下来是 knockListen 子程序前 行负责读取当前的加速器数据值并对基本的值读取进行调整如果加速器的数量在某一维度上大于更新上限值那么 checkKnock 变量就被设置为 为了调整这个程序使它只响应我们需要的敲打事件或类似的加速值我们需要扩大更新上限例如我们可以将 ThinkPad 放到自己的汽车中并让它在检测到硬加速(或减速)时更改 MP 播放列表
如果敲打笔记本的力度足够大并且大于了更新上限那么就会导致调用getEpochMicroSeconds 子程序然后 diffInterval 变量会在两次敲打事件之间被赋值这个值将很多击打力度大于更新上限的很多快速加速读取压缩到一个时间中如果没有间隔上限检查一次硬敲打就会被注册成很多事件就仿佛是加速器连续一段时间产生大量事件一样这种行为对于用户的视力和触觉来说都是无法感知到的但对于 HDAPS 来说显然并非如此如果已经达到了间隔上限那么敲打间隔会被记录在 baseKnocks 数组中然后将两次敲打之间的间隔重置
仔细修改这些变量可以帮助对程序进行优化从而识别出您特有的敲打风格缩小更新上限并扩大周期上限可以检测出更多间隔的轻微敲打机械敲打设备或特定的敲打方法可能会需要降低间隔上限从而识别出独特的敲打事件
清单 knockListen 子程序
sub knockListen() { my $checkKnock = ; ($currX $currY) = readPosition(); $currX = $restX; # adjust for rest data state $currY = $restY; # adjust for rest data state # require a high threshold of acceleration to ignore nonevents like # bashing the enter key or hitting the side with the mouse if( abs ($currX) > $UPDATE_THRESHOLD) { $checkKnock = ; } if( abs ($currY) > $UPDATE_THRESHOLD) { $checkKnock = ; } if( $checkKnock == ){ my $currVal = getEpochMicroSeconds(); my $diffInterval = abs($prevInterval $currVal); # hard knock events can create continuous acceleration across a large time # threshold requiring an elapsed time between knock events effectively # reduces what appear as multiple events according to sleep_interval and # update_threshold into a singular event if( $diffInterval > $INTERVAL_THRESHOLD ){ if( $knockCount == ){ $diffInterval = } if( $option ){ print Knock: $knockCount ## last: [$currVal] curr: [$prevInterval] ; print difference is: $diffIntervaln; } push @baseKnocks $diffInterval; $knockCount++; }# if the difference interval is greater than the threshold $prevInterval = $currVal; }#if checkknock passed }#knockListen
在创建敲打模式时该模式会被放入 ~/knockFile 文件中并使用下面的子程序进行读取
清单 读取敲打文件
sub readKnockFile { open(KNCKFILE$ENV{HOME}/knockFile) or die no knock file: $!; while(){ if( !/^#/ ){ my @arrLine = split _#_; $knockHash{ $arrLine[] }{ cmd } = $arrLine[]; $knockHash{ $arrLine[] }{ comment } = $arrLine[]; }#if not a comment line }#for each line in file close(KNCKFILE); }#readKnockFile
当 knockListen 获得敲打模式时它会将该模式与从 readKnockFile 中读取的敲打模式进行比较下面的 compareKnockSequences 子程序会对敲打之间的时间进行简单的区别检查注意敲打之间的差别并不是简单混合在一起的很多次敲打时的少量时间差别并不会累积成总体的匹配失效
第一个要比较的是敲打的次数因为我们没有必要将一个七次敲打的序列与一个两次敲打的序列进行比较如果敲打的次数与 ~/knockFile 中现有的敲打序列匹配每次敲打之间的差别也少于最大敲打偏差那么这次敲打就可以认为是完全匹配的在允许敲打序列进行不精确匹配时最大敲打偏差非常关键我们可以增大最大敲打偏差来使敲打节奏更加自由但是要注意这可能会导致敲打模式匹配不正确例如我们可以在所期望的时间之前或之后半秒钟允许自己的敲打模式发生偏离但这仍然可以匹配这样就可以有效地说明刮脸和理发 可以与 Mary 姓 Little Lamb 匹配因此在修改这个参数时一定要小心
如果完整的模式可以匹配就会运行 ~/knockFile 中指定的命令如果启用了冗余模式则会打印结果下一个步骤是如果没有找到匹配项就退出这个子程序如果找到了匹配项就重置所记录的敲打序列这个步骤会执行 compareKnockSequences 子程序
清单 比较敲打序列
sub compareKnockSequences { my $countMatch = ; # record how many knocks matched # for each knock sequence in the config file for( keys %knockHash ){ # get the timings between knocks my @confKnocks = split; # if the count of knocks match if( $knockCount eq @confKnocks ){ my $knockDiff = ; my $counter = ; for( $counter=; $counter<$knockCount; $counter++ ){ $knockDiff = abs($confKnocks[$counter] $baseKnocks[$counter]); my $knkStr = k $counter b $baseKnocks[$counter] c $confKnocks[$counter] d $knockDiffn; # if its an exact match increment the matching counter if( $knockDiff < $MAX_KNOCK_DEV ){ if( $option ){ print MATCH $knkStr } $countMatch++; # if the knocks dont match move on to the next pattern in the list }else{ if( $option ){ print DISSONANCE $knkStr } last; }# deviation check }#for each knock }#if number of knocks matches # if the count of knocks is an exact match run the command if( $countMatch eq @confKnocks ){ my $cmd = system( $knockHash{@confKnocks }{ cmd } ); if( $option ){ print $cmdn } last; # otherwise make the count of matches zero in order to not reset }else{ $countMatch = ; } }#for keys # if the match count is zero exit and dont reset variables so a longer # knock sequence can be entered and checked if( $countMatch == ){ return() } # if a match occurred reset the variables so it wont match another pattern $knockCount = ; @baseKnocks = (); }#compareKnockSequences[!--empirenews.page--]
主程序的逻辑
利用这些子程序主程序的逻辑允许用户创建敲打序列或在守护模式下监听敲打序列并执行命令第一部分是在用户指定 c 选项(用于创建模式)时执行的可以用简单的超时进程来结束敲打序列增大最大超时长度变量的值可以让两次敲打序列之间暂停 秒以上如果我们保留最大超时长度为 秒那么程序运行到这个时间时就会结束并打印当前输入的敲打序列
清单 创建序列主逻辑
if( $option eq c ){ print create a knock pattern:n; $startTime = getEpochSeconds(); # reset time out start while( $timeOut == ){ $currTime = getEpochSeconds(); # check if there has not been a knock in a while if( $currTime $startTime > $MAX_TIMEOUT_LENGTH ){ $timeOut = ; # exit the loop }else{ # if a knock has been entered before timeout reset timers so # more knocks can be entered if( $knockCount != $knockAge ){ $startTime = $currTime; # reset timer for longer delay $knockAge = $knockCount; # synchronize knock counts }# if a new knock came in }# if timer not reached knockListen(); select(undef undef undef $SLEEP_INTERVAL); }#timeOut = if( @baseKnocks ){ print place the following line in $ENV{HOME}/knockFilenn; for( @baseKnocks ){ print $_ } print _#_ (command here) _#_ nn; }#if knocks entered
第二部分是用来在一个无限循环中监听敲打序列的主逻辑它在一个循环中大约要睡眠/ 秒在这个循环中还使用了一个基于秒的超时在足够的延时之后重置敲打序列注意在这个例子中敲打监听超时时间为 秒而最大超时时间为 秒这样就提供了在敲打创建模式下进行简单测试设置的功能并为敲打序列的监听模式提供了一个快速重置选项
清单 主程序中的敲打监听代码
}else{ # main code loop to listen for knocking and run commands readKnockFile(); $startTime = getEpochSeconds(); while( $timeOut == ){ $currTime = getEpochSeconds(); if( $currTime $startTime > $LISTEN_TIMEOUT ){ $knockCount = ; @baseKnocks = (); $startTime = $currTime; if( $option ){ printlisten timeout resetting knocks n } }else{ if( $knockCount != $knockAge ){ $startTime = $currTime; # reset timer for longer delay $knockAge = $knockCount; # synchronize knock counts }# if a new knock came in compareKnockSequences(); }#if not reset timeout knockListen(); select(undefundef undef $SLEEP_INTERVAL); }#main knock listen loop }# if create or listen for knocks
警告安全性
knockAge 程序非常适合用于为系统提供一种额外的用户输入通道然而需要注意的是使用 knockAge 来做任何事都需要在系统上进行认证是的它可以防止密钥记录程序监听密码的问题但是很多与 敲打认证 有关的因素都表明在对安全性敏感的环境中使用这种技术还不够成熟敲打序列目前是以 到 个数字在 ~/knockFile 中进行存储的它们以毫秒为单位来表示延时这个 密码 文件非常容易读取并且通过尝试和匹配敲打模式可以获得对系统的访问权限排除毫秒值中一些精度是一种可用的方法但是这种练习就留给那些希望自行对系统风险进行评估的读者好了
在任何敏感环境中我们都应该进行一些研究判断用户是否有足够的应变能力并能够精确地重现敲打序列例如我们是否具有能力创建并连续输入可接受长度的敲打密码?具有普通智商的人是否可以直观地使用这种敲打序列?或者我们是否准备使用 刮脸和理发操作 来作为密码?