有史以來第一次您可以敲打一下計算機并得到有意義的響應(yīng)!使用 Linux® 和 Hard Drive Active Protection System(硬盤活動保護系統(tǒng)HDAPS)內(nèi)核驅(qū)動程序我們可以訪問 Lenovo(以前稱為 IBM®)ThinkPads 上的嵌入式加速器然后處理加速器的數(shù)據(jù)來讀取特定 敲打 事件序列(也就是您使用關(guān)節(jié)敲打筆記本的事件序列)并基于這些敲打事件運行一些命令雙擊鎖定屏幕然后敲入密碼來解鎖敲打顯示屏一次就可以讓 MP 播放器前進一個音軌這類可能事物是無窮無盡的
年IBM 開始發(fā)行集成了加速器和相關(guān)軟件的 ThinkPad 筆記本以便在筆記本掉到地上時對硬盤進行保護來自 IBM 和其他地方的黑客已經(jīng)為 Linux 內(nèi)核開發(fā)了一些模塊來利用這些傳感器的優(yōu)點屏幕顯示方向桌面切換甚至是游戲控制和實時的筆記本傾斜度 D 模塊現(xiàn)在都已經(jīng)可以使用了本文將展示 敲打代碼 這種新技術(shù)和一個簡單程序該程序在檢測到特定的敲打代碼時會運行一些命令
使用帶有 HDAPS 驅(qū)動的已更新的內(nèi)核我們就可以用一個簡單程序 knockAge 來生成敲打代碼了我們也可以下載并使用一個 Perl 腳本來定制自己的敲打輸入環(huán)境請參閱本文最后的 下載 和 參考資料 部分給出的鏈接其中包括了解 knockAge 操作的鏈接
硬件需求
容易實現(xiàn)
正如您從展示視頻(請參見下面 參考資料 中的鏈接)可以看到的敲打操作是由一系列指節(jié)敲打構(gòu)成的盡管 ThinkPad 的加速器是為保護它不受意外事件的影響但太強力的振蕩仍然會對硬盤造成損壞因此我們必須小心
很多在 年以及這以后生產(chǎn)的 IBM(現(xiàn)在是 Lenovo)的 ThinkPads 中都有 HDAPS 硬件如果您不確定自己的硬件配置可以檢查 Lenovo 的 Web 站點上關(guān)于您自己型號的機器的技術(shù)細節(jié)如果您的機器上沒有 ThinkPad那么這段代碼可能無法在您的筆記本上正常工作
本文是在 x 體系架構(gòu)上編寫的本文中的代碼是在 ThinkPad Tp 的兩個不同模塊上進行開發(fā)和測試的有關(guān) ThinkPad 硬件的鏈接請參閱 參考資料 部分
如果您有一臺 Apple MacBook那么您可能也有這種加速器并且可以使用相同的方法通過內(nèi)核訪問它們?nèi)欢疚闹械拇a并沒有在 Apple 硬件上進行測試
軟件要求
HDAPS 驅(qū)動程序必須包括在內(nèi)核中才能啟用對加速器的訪問試圖對現(xiàn)有內(nèi)核增加補丁也不會獲得成功因此我們建議從自己喜歡的鏡像站點上下載最新的內(nèi)核新內(nèi)核發(fā)行版中已經(jīng)包含了對 HDAPS 驅(qū)動程序的支持
啟動內(nèi)核配置選擇程序并在配置中包含 HDAPS 驅(qū)動程序HDAPS 驅(qū)動程序位于Device Drivers > Hardware Monitoring Support > IBM Hard Drive Active Protection System (hdaps) 選項中更多的內(nèi)核配置和安裝過程已經(jīng)超出了本文的范圍但是在 Web 站點上有很多教程可以提供具體的幫助有關(guān)可以幫助我們?nèi)腴T的鏈接請參閱 參考資料 一節(jié)的內(nèi)容
本文是在 版本的內(nèi)核上進行開發(fā)和測試的
創(chuàng)建簡單的敲打序列
從 下載 一節(jié)的鏈接中下載源代碼并從中找到 knockAgepl 腳本這就是讓我們可以創(chuàng)建敲打序列的主要 Perl 程序它還允許監(jiān)聽特殊的敲打序列并運行命令下面讓我們來介紹一下這個用戶空間程序的用法以及 knockAgepl 程序的配置然后再對這個函數(shù)進行回顧
使用下面的命令運行 knockAgepl 程序
perl knockAgepl c
這會啟動 Perl 程序來監(jiān)聽敲打事件并記錄下它們之間的間距以供將來使用一旦程序開始運行之后對筆記本進行的敲打操作就會產(chǎn)生效果我們并不需要在物理上移動自己的 ThinkPad 來注冊敲打事件如果 ThinkPad 在一個平面上只要對其進行一些移動和滑行即可我建議您用左右握住 ThinkPad 左邊接近連接軸的地方同時用右手在距離 LCD 底部 英寸的地方敲打顯示屏即可請參閱 下載 部分給出的視頻展示或參閱 參考資料 中用來創(chuàng)建敲打序列的例子
體驗不同的敲打幅度和力度從而了解 knockAge 程序能夠捕獲的事件判斷率對于創(chuàng)建復(fù)雜的敲打事件來說這非常重要
第一次真正嘗試敲打應(yīng)該非常簡單兩次雙擊之間停留 秒然后再次運行 perl knockAgepl c在看到 enter a knock sequence 時穩(wěn)定地敲打 LCD 邊上兩次中間停留 秒在 秒之后會自動超時(這是可以配置的)您所敲打的序列會被打印出來這類似于下面的例子
_#_ (command here) _#_
讓我們來分析一下這一行的內(nèi)容敲打序列分隔符命令區(qū)分隔符最后是注釋區(qū)我們的下一個步驟是將這行內(nèi)容復(fù)制到 knockAgepl 程序使用的默認配置文件 {$HOME}/knockFile 中該配置文件也可能是 /home//knockFile 文件在使用上面的敲打序列行創(chuàng)建好 knockFile 文件之后就可以對這行進行修改來運行程序了將 (command here) 文本修改成 /bin/echo double tap并將注釋區(qū)的內(nèi)容修改成更有意義的內(nèi)容例如
_#_ /bin/echo double tap _#_ Double tap event
現(xiàn)在我們已經(jīng)修改好這個配置文件可以打印一條通知了接下來使用下面的命令在守護模式下運行 knockAge 腳本
perl knockAgepl
這個程序會在后臺安靜地監(jiān)聽 ~/knockFile 所羅列的事件請使用相同的間隔再次雙擊屏幕您會看到在屏幕上打印出了 double tap 消息如果我們希望更詳細地了解 knockAgepl 腳本是如何工作的那么我們可以使用下面的命令在守護模式下運行它
perl knockAgepl v
使用 xscreensaver 鎖定屏幕或打開屏幕
創(chuàng)建 password 序列
使用下面的命令在 create 模式下運行 knockAgepl 程序
perl knockAgepl c
現(xiàn)在我們需要創(chuàng)建一個解鎖的密碼序列我建議使用 刮臉和理發(fā)的動作請確保每次您都可以以一貫精確的方式執(zhí)行這個動作盡管您可以通過修改參數(shù)來控制輸入密碼敲打操作所需要的精度但是這仍然很難匹配精確的時間刮臉和理發(fā)動作 除了可以提供穩(wěn)定的擊打順序之外其復(fù)雜性和簡單性對于屏保解鎖密碼來說也非常適合下面是一個刮臉和理發(fā)動作 的擊打序列示例
_#_ /bin/echo shave the haircut _#_ two bits
在進行下一步操作之前您應(yīng)該體驗一下上面的命令和 ~/knockFile 配置文件中的雙擊命令這可以在屏保運行時提供很好的幫助它更難檢測出敲打是否正確[!--empirenews.page--]
xscreensaver 所使用的命令配置
以下設(shè)置假設(shè)您已經(jīng)登錄到了窗口管理器中并且已經(jīng)使用您的 userid 啟動了xscreensaver 程序例如如果您正在運行 Fedora Core 并且使用 gdm 登錄到 KDE 中那么 xscreensaver 就會自動啟動因此要激活它則需要將雙擊命令從
/bin/echo double tap
修改為
xscreensavercommand activate &
現(xiàn)在每次識別出有 雙擊 事件發(fā)生時xscreensaver 程序都會使用所指定的內(nèi)容來激活一旦 screensaver 被激活就可以通過輸入密碼(如果是這樣配置的)對屏幕進行解鎖不過我們真正希望的是自己的朋友也可以使用密碼解鎖代碼來解除屏保因此我們需要在 ~/knockFile 文件中將下面的命令
/bin/echo shave the haircut
替換為
killall xscreensaver ; nohup xscreensaver nosplash >/dev/null >/dev/null &
這個命令會停止當(dāng)前運行的所有 xscreensaver 程序然后在后臺再重新啟動 xscreensaver現(xiàn)在我們可以通過敲打屏幕邊來重復(fù)加鎖和解鎖計算機屏保的過程這比藍牙提供的近似度加鎖更加安全或更方便嗎?答案可能是否定的它更酷嗎?當(dāng)然!
更多例子
HDAPS 傳感器和 knockAgepl 程序提供了另外一種用戶輸入設(shè)備我們可以使用它們以獨特的方式進行輸入例如
如果計劃在一個基礎(chǔ)上測試新的 X 配置文件可以將雙擊條目更改為重新啟動配置好的 X 服務(wù)器這樣就不需要敲任何其他鍵來強制重啟了
在命令區(qū)中可以放上我們喜歡使用的任何 shell 腳本這樣就可以使用雙擊來查看 email
以最新的組合節(jié)拍進行敲打讓 ThinkLight 顯示 WWII 代在 Kinakuta 的黃金存儲設(shè)備的 Morse 密碼位置
敲入 Morse 編碼防止鍵盤輸入被記錄
請參閱 參考資料 部分給出的有關(guān)將 ThinkPad 的傾斜度用于游戲顯示工具的例子或者直接跳過這部分內(nèi)容將 Threshold 變量設(shè)置為 這樣您使勁踢一腳 ThinkPad它就會自動重啟了
knockAgepl 代碼
歷史和策略
Jeff Molofee 所編寫的 hdapsglc 是 knockAgepl 代碼的基礎(chǔ)Hdapsglc 是一個非常好的展示程序可以展示如何使用傾斜傳感器來實時地顯示有關(guān) ThinkPad 的方向的信息二者之間的區(qū)別是本例將時間上隔離的事件組織在一起創(chuàng)建了敲打事件同時提供了相關(guān)的代碼來創(chuàng)建并監(jiān)聽敲打事件序列
參數(shù)配置
下面讓我們來使用對時間和傳感器敏感的一些參數(shù)來啟動 knockAgepl
清單 主程序參數(shù)
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
這些變量及其注釋都非常簡單它們的用法和配置選項在本文后面部分會進行解釋下面是其余的一些全局變量及其描述
清單 敲打模式參數(shù)
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 子程序中我們可以看到他的注釋這個子程序?qū)⒋蜷_一個文件從中讀取當(dāng)前的加速器數(shù)據(jù)然后關(guān)閉文件并返回不包含 (逗號) 字符的數(shù)據(jù)
清單 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 提供了有關(guān)敲打模式狀態(tài)的詳細而精確的信息
清單 時間分隔器
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 子程序前 行負責(zé)讀取當(dāng)前的加速器數(shù)據(jù)值并對基本的值讀取進行調(diào)整如果加速器的數(shù)量在某一維度上大于更新上限值那么 checkKnock 變量就被設(shè)置為 為了調(diào)整這個程序使它只響應(yīng)我們需要的敲打事件或類似的加速值我們需要擴大更新上限例如我們可以將 ThinkPad 放到自己的汽車中并讓它在檢測到硬加速(或減速)時更改 MP 播放列表
如果敲打筆記本的力度足夠大并且大于了更新上限那么就會導(dǎo)致調(diào)用getEpochMicroSeconds 子程序然后 diffInterval 變量會在兩次敲打事件之間被賦值這個值將很多擊打力度大于更新上限的很多快速加速讀取壓縮到一個時間中如果沒有間隔上限檢查一次硬敲打就會被注冊成很多事件就仿佛是加速器連續(xù)一段時間產(chǎn)生大量事件一樣這種行為對于用戶的視力和觸覺來說都是無法感知到的但對于 HDAPS 來說顯然并非如此如果已經(jīng)達到了間隔上限那么敲打間隔會被記錄在 baseKnocks 數(shù)組中然后將兩次敲打之間的間隔重置
仔細修改這些變量可以幫助對程序進行優(yōu)化從而識別出您特有的敲打風(fēng)格縮小更新上限并擴大周期上限可以檢測出更多間隔的輕微敲打機械敲打設(shè)備或特定的敲打方法可能會需要降低間隔上限從而識別出獨特的敲打事件
清單 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
在創(chuàng)建敲打模式時該模式會被放入 ~/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
當(dāng) knockListen 獲得敲打模式時它會將該模式與從 readKnockFile 中讀取的敲打模式進行比較下面的 compareKnockSequences 子程序會對敲打之間的時間進行簡單的區(qū)別檢查注意敲打之間的差別并不是簡單混合在一起的很多次敲打時的少量時間差別并不會累積成總體的匹配失效
第一個要比較的是敲打的次數(shù)因為我們沒有必要將一個七次敲打的序列與一個兩次敲打的序列進行比較如果敲打的次數(shù)與 ~/knockFile 中現(xiàn)有的敲打序列匹配每次敲打之間的差別也少于最大敲打偏差那么這次敲打就可以認為是完全匹配的在允許敲打序列進行不精確匹配時最大敲打偏差非常關(guān)鍵我們可以增大最大敲打偏差來使敲打節(jié)奏更加自由但是要注意這可能會導(dǎo)致敲打模式匹配不正確例如我們可以在所期望的時間之前或之后半秒鐘允許自己的敲打模式發(fā)生偏離但這仍然可以匹配這樣就可以有效地說明刮臉和理發(fā) 可以與 Mary 姓 Little Lamb 匹配因此在修改這個參數(shù)時一定要小心
如果完整的模式可以匹配就會運行 ~/knockFile 中指定的命令如果啟用了冗余模式則會打印結(jié)果下一個步驟是如果沒有找到匹配項就退出這個子程序如果找到了匹配項就重置所記錄的敲打序列這個步驟會執(zhí)行 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--]
主程序的邏輯
利用這些子程序主程序的邏輯允許用戶創(chuàng)建敲打序列或在守護模式下監(jiān)聽敲打序列并執(zhí)行命令第一部分是在用戶指定 c 選項(用于創(chuàng)建模式)時執(zhí)行的可以用簡單的超時進程來結(jié)束敲打序列增大最大超時長度變量的值可以讓兩次敲打序列之間暫停 秒以上如果我們保留最大超時長度為 秒那么程序運行到這個時間時就會結(jié)束并打印當(dāng)前輸入的敲打序列
清單 創(chuàng)建序列主邏輯
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
第二部分是用來在一個無限循環(huán)中監(jiān)聽敲打序列的主邏輯它在一個循環(huán)中大約要睡眠/ 秒在這個循環(huán)中還使用了一個基于秒的超時在足夠的延時之后重置敲打序列注意在這個例子中敲打監(jiān)聽超時時間為 秒而最大超時時間為 秒這樣就提供了在敲打創(chuàng)建模式下進行簡單測試設(shè)置的功能并為敲打序列的監(jiān)聽模式提供了一個快速重置選項
清單 主程序中的敲打監(jiān)聽代碼
}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 程序非常適合用于為系統(tǒng)提供一種額外的用戶輸入通道然而需要注意的是使用 knockAge 來做任何事都需要在系統(tǒng)上進行認證是的它可以防止密鑰記錄程序監(jiān)聽密碼的問題但是很多與 敲打認證 有關(guān)的因素都表明在對安全性敏感的環(huán)境中使用這種技術(shù)還不夠成熟敲打序列目前是以 到 個數(shù)字在 ~/knockFile 中進行存儲的它們以毫秒為單位來表示延時這個 密碼 文件非常容易讀取并且通過嘗試和匹配敲打模式可以獲得對系統(tǒng)的訪問權(quán)限排除毫秒值中一些精度是一種可用的方法但是這種練習(xí)就留給那些希望自行對系統(tǒng)風(fēng)險進行評估的讀者好了
在任何敏感環(huán)境中我們都應(yīng)該進行一些研究判斷用戶是否有足夠的應(yīng)變能力并能夠精確地重現(xiàn)敲打序列例如我們是否具有能力創(chuàng)建并連續(xù)輸入可接受長度的敲打密碼?具有普通智商的人是否可以直觀地使用這種敲打序列?或者我們是否準備使用 刮臉和理發(fā)操作 來作為密碼?