歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

解析Linux筆記本基於“敲打”的命令

  史以來第一次,您可以敲打一下計算機並得到有意義的響應!使用 Linux? 和 Hard Drive Active Protection System(硬盤活動保護系統,HDAPS)內核驅動程序,我們可以訪問 Lenovo(以前稱為 IBM?)ThinkPads 上的嵌入式加速器,然後處理加速器的數據來讀取特定 “敲打” 事件序列(也就是您使用關節敲打筆記本的事件序列),並基於這些敲打事件運行一些命令。雙擊鎖定屏幕,然後敲入密碼來解鎖。敲打顯示屏一次就可以讓 mp3 播放器前進一個音軌。這類可能事物是無窮無盡的。

  2003 年,IBM 開始發行集成了加速器和相關軟件的 ThinkPad 筆記本,以便在筆記本掉到地上時對硬盤進行保護。來自 IBM 和其他地方的黑客已經為 Linux 內核開發了一些模塊來利用這些傳感器的優點。屏幕顯示方向、桌面切換、甚至是游戲控制和實時的筆記本傾斜度 3D 模塊現在都已經可以使用了。本文將展示 “敲打代碼” 這種新技術和一個簡單程序,該程序在檢測到特定的敲打代碼時會運行一些命令。

  使用帶有 HDAPS 驅動的已更新的內核,我們就可以用一個簡單程序 knockAge 來生成敲打代碼了。我們也可以下載並使用一個 Perl 腳本來定制自己的敲打輸入環境。

  硬件需求

  很多在 2003 年以及這以後生產的 IBM(現在是 Lenovo)的 ThinkPads 中都有 HDAPS 硬件。如果您不確定自己的硬件配置,可以檢查 Lenovo 的 Web 站點上關於您自己型號的機器的技術細節。如果您的機器上沒有 ThinkPad,那麼這段代碼可能無法在您的筆記本上正常工作。

  本文是在 x86 體系架構上編寫的。本文中的代碼是在 ThinkPad T42p 的兩個不同模塊上進行開發和測試的。有關 ThinkPad 硬件的鏈接。

  如果您有一台 Apple MacBook,那麼您可能也有這種加速器,並且可以使用相同的方法,通過內核訪問它們。然而,本文中的代碼並沒有在 Apple 硬件上進行測試。

  軟件要求

  HDAPS 驅動程序必須包括在內核中才能啟用對加速器的訪問。試圖對現有內核增加補丁也不會獲得成功,因此我們建議從自己喜歡的鏡像站點上下載最新的內核。新內核發行版中已經包含了對 HDAPS 驅動程序的支持。

  啟動內核配置選擇程序,並在配置中包含 HDAPS 驅動程序。HDAPS 驅動程序位於 Device Drivers > Hardware Monitoring Support > IBM Hard Drive Active Protection System (hdaps) 選項中。更多的內核配置和安裝過程已經超出了本文的范圍,但是在 Web 站點上有很多教程可以提供具體的幫助;有關可以幫助我們入門的鏈接。

  本文是在 2.6.15.1 版本的內核上進行開發和測試的。

  創建簡單的敲打序列

  從 下載 一節的鏈接中下載源代碼,並從中找到 knockAge.pl 腳本。這就是讓我們可以創建敲打序列的主要 Perl 程序,它還允許監聽特殊的敲打序列並運行命令。下面讓我們來介紹一下這個用戶空間程序的用法,以及 knockAge.pl 程序的配置,然後再對這個函數進行回顧。

  使用下面的命令運行 knockAge.pl 程序:

  perl knockAge.pl -c

  這會啟動 Perl 程序來監聽敲打事件並記錄下它們之間的間距以供將來使用。一旦程序開始運行之後,對筆記本進行的敲打操作就會產生效果。我們並不需要在物理上移動自己的 ThinkPad 來注冊敲打事件,如果 ThinkPad 在一個平面上,只要對其進行一些移動和滑行即可。我建議您用左右握住 ThinkPad 左邊接近連接軸的地方,同時用右手在距離 LCD 底部 3 英寸的地方敲打顯示屏即可。

  體驗不同的敲打幅度和力度,從而了解 knockAge 程序能夠捕獲的事件判斷率。對於創建復雜的敲打事件來說,這非常重要。

  第一次真正嘗試敲打應該非常簡單,兩次雙擊之間停留 0.5 秒,然後再次運行 perl knockAge.pl -c,在看到 “enter a knock sequence” 時穩定地敲打 LCD 邊上兩次,中間停留 0.5 秒。在 4 秒之後會自動超時(這是可以配置的),您所敲打的序列會被打印出來,這類似於下面的例子:

  0 540031 _#_ (command here) _#_ <comments here>

  讓我們來分析一下這一行的內容:敲打序列,分隔符,命令區,分隔符,最後是注釋區。我們的下一個步驟是將這行內容復制到 knockAge.pl 程序使用的默認配置文件 {$HOME}/.knockFile 中,該配置文件也可能是 /home/<username>/.knockFile 文件。在使用上面的敲打序列行創建好 .knockFile 文件之後,就可以對這行進行修改來運行程序了。將 (command here) 文本修改成 /bin/echo "double tap",並將注釋區的內容修改成更有意義的內容,例如:

  0 540031 _#_ /bin/echo "double tap" _#_ Double tap event

  現在我們已經修改好這個配置文件,可以打印一條通知了,接下來使用下面的命令在守護模式下運行 knockAge 腳本:

  perl knockAge.pl

  這個程序會在後台安靜地監聽 ~/.knockFile 所羅列的事件。請使用相同的間隔再次雙擊屏幕,您會看到在屏幕上打印出了 “double tap” 消息。如果我們希望更詳細地了解 knockAge.pl 腳本是如何工作的,那麼我們可以使用下面的命令在守護模式下運行它:

  perl knockAge.pl -v

  使用 xscreensaver 鎖定屏幕或打開屏幕

創建 “passWord” 序列

  使用下面的命令在 “create” 模式下運行 knockAge.pl 程序:

  perl knockAge.pl -c

  現在我們需要創建一個解鎖的密碼序列;我建議使用 “刮臉和理發的動作”。請確保每次您都可以以一貫精確的方式執行這個動作。盡管您可以通過修改參數來控制輸入密碼敲打操作所需要的精度,但是這仍然很難匹配精確的時間。“刮臉和理發動作” 除了可以提供穩定的擊打順序之外,其復雜性和簡單性對於屏保解鎖密碼來說也非常適合。下面是一個 “刮臉和理發動作” 的擊打序列示例:

  0 564025 1185795 621350 516038 960035 444421 _#_ /bin/echo "shave the haircut" _#_ two bits

  在進行下一步操作之前,您應該體驗一下上面的命令和 ~/.knockFile 配置文件中的雙擊命令。這可以在屏保運行時提供很好的幫助,它更難檢測出敲打是否正確。

  xscreensaver 所使用的命令配置

  以下設置假設您已經登錄到了窗口管理器中,並且已經使用您的 userid 啟動了xscreensaver 程序。例如,如果您正在運行 Fedora Core 4,並且使用 gdm 登錄到 KDE 中,那麼 xscreensaver 就會自動啟動。因此,要激活它,則需要將雙擊命令從:

  /bin/echo "double tap"

  修改為:

  xscreensaver-command -activate &

  現在,每次識別出有 “雙擊” 事件發生時,xscreensaver 程序都會使用所指定的內容來激活。一旦 screensaver 被激活,就可以通過輸入密碼(如果是這樣配置的)對屏幕進行解鎖。不過我們真正希望的是自己的朋友也可以使用密碼解鎖代碼來解除屏保。因此,我們需要在 ~/.knockFile 文件中將下面的命令:

  /bin/echo "shave the haircut"

  替換為:

  killall xscreensaver ; nohup xscreensaver -nosplash >/dev/null 2>/dev/null &

  這個命令會停止當前運行的所有 xscreensaver 程序,然後在後台再重新啟動 xscreensaver。現在我們可以通過敲打屏幕邊來重復加鎖和解鎖計算機屏保的過程。這比藍牙提供的近似度加鎖更加安全或更方便嗎?答案可能是否定的。它更酷嗎?當然!

  更多例子

  HDAPS 傳感器和 knockAge.pl 程序提供了另外一種用戶輸入設備,我們可以使用它們以獨特的方式進行輸入。例如:

如果計劃在一個基礎上測試新的 X 配置文件,可以將雙擊條目更改為重新啟動配置好的 X 服務器。這樣就不需要敲任何其他鍵來強制重啟了。 在命令區中可以放上我們喜歡使用的任何 shell 腳本,這樣就可以使用雙擊來查看 e-mail。 以最新的組合節拍進行敲打,讓 ThinkLight 顯示 WWII 代在 Kinakuta 的黃金存儲設備的 Morse 密碼位置。 敲入 Morse 編碼,防止鍵盤輸入被記錄。

  給出的有關將 ThinkPad 的傾斜度用於游戲、顯示工具的例子。或者直接跳過這部分內容,將 Threshold 變量設置為 15,這樣您使勁踢一腳 ThinkPad,它就會自動重啟了。

  knockAge.pl 代碼

  歷史和策略

  Jeff Molofee 所編寫的 hdaps-gl.c 是 knockAge.pl 代碼的基礎。Hdaps-gl.c 是一個非常好的展示程序,可以展示如何使用傾斜傳感器來實時地顯示有關 ThinkPad 的方向的信息。二者之間的區別是本例將時間上隔離的事件組織在一起創建了敲打事件,同時提供了相關的代碼來創建並監聽敲打事件序列。

  參數配置

  下面讓我們來使用對時間和傳感器敏感的一些參數來啟動 knockAge.pl:

  清單 1. 主程序參數

require 'sys/syscall.ph'; # for subsecond timing my $option = $ARGV[0] ""; # simple option handling # filename for hdaps sensor reads my $hdapsFN = "/sys/devices/platform/hdaps/position"; my $UPDATE_THRESHOLD = 4; # threshold of force that indicates a knock my $INTERVAL_THRESHOLD = 100000; # microseconds of time required between knock # events my $SLEEP_INTERVAL = 0.01; # time to pause between hdaps reads my $MAX_TIMEOUT_LENGTH = 4; # maximum length in seconds of knock pattern # length my $MAX_KNOCK_DEV = 100000; # maximum acceptable deviation between recorded # pattern values and knocking values my $LISTEN_TIMEOUT = 2; # timeout value in seconds between knock # events when in listening mode

  這些變量及其注釋都非常簡單。它們的用法和配置選項在本文後面部分會進行解釋。下面是其余的一些全局變量及其描述。

  清單 2. 敲打模式參數

my @baseKnocks = (); # contains knock intervals currently entered my %knockHash = (); # contains knock patterns, associated commands my $prevInterval = 0; # previous interval of time my $knockCount = 0; # current number of knocks detected my $restX = 0; # `resting' positiong of X axis accelerometer my $restY = 0; # `resting' positiong of Y axis accelerometer my $currX = 0; # current position of X axis accelerometer my $currY = 0; # current position of Y axis accelerometer my $lastX = 0; # most recent position of X axis accelerometer my $lastY = 0; # most recent position of Y axis accelerometer my $startTime = 0; # to manage timeout intervals my $currTime = 0; # to manage timeout intervals my $timeOut = 0; # perpetual loop variable my $knockAge = 0; # count of knocks to cycle time interval 子程序

  在我們的子程序清單中首先是一個簡單的邏輯塊,用來檢查是否有加速器可讀:

  清單 3. 檢查加速器的子程序

sub checkAccelerometer() { my $ret; $ret = readPosition (); if( $ret ){ print "no accelerometer data available - tis bork ed\n"; exit(1); } }#checkAccelerometer   Jeff Molofee 編寫的 hdaps-gl.c 代碼為 knockAge.pl 中的所有代碼提供了一個很好的起點。在下面的 readPosition 子程序中,我們可以看到他的注釋。這個子程序將打開一個文件,從中讀取當前的加速器數據,然後關閉文件,並返回不包含 “,(逗號)” 字符的數據。

  清單 4. readPosition subroutine

## comments from Jeff Molofee in hdaps-gl.c #* read_position - read the (x,y) 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( <FH> ){ s/\(//g; s/\)//g; ($posX, $posY) = split ","; }# while read close(FH); return( $posX, $posY ); }#readPosition   getEpochSeconds 和 getEpochMicroSeconds 提供了有關敲打模式狀態的詳細而精確的信息。

  清單 5. 時間分隔器

sub getEpochMicroSeconds { my $TIMEVAL_T = "LL"; # LL for microseconds my $timeVal = pack($TIMEVAL_T, ()); syscall(&SYS_gettimeofday, $timeVal, 0) != -1 or die "micro seconds: $!"; my @vals = unpack( $TIMEVAL_T, $timeVal ); $timeVal = $vals[0] . $vals[1]; $timeVal = substr( $timeVal, 6); my $padLen = 10 - length($timeVal); $timeVal = $timeVal . "0" x $padLen; return($timeVal); }#getEpochMicroSeconds sub getEpochSeconds { my $TIMEVAL_T = "LL"; # LL for microseconds my $start = pack($TIMEVAL_T, ()); syscall(&SYS_gettimeofday, $start, 0) != -1 or die "seconds: $!"; return( (unpack($TIMEVAL_T, $start))[0] ); }#getEpochSeconds   接下來是 knockListen 子程序,前 5 行負責讀取當前的加速器數據值,並對基本的值讀取進行調整。如果加速器的數量在某一維度上大於更新上限值,那麼 checkKnock 變量就被設置為 1。為了調整這個程序,使它只響應我們需要的敲打事件或類似的加速值,我們需要擴大更新上限。例如,我們可以將 ThinkPad 放到自己的汽車中,並讓它在檢測到硬加速(或減速)時更改 MP3 播放列表。

  如果敲打筆記本的力度足夠大,並且大於了更新上限,那麼就會導致調用 getEpochMicroSeconds 子程序。然後 diffInterval 變量會在兩次敲打事件之間被賦值。這個值將很多擊打力度大於更新上限的很多快速加速讀取壓縮到一個時間中。如果沒有間隔上限檢查,一次硬敲打就會被注冊成很多事件,就仿佛是加速器連續一段時間產生大量事件一樣。這種行為對於用戶的視力和觸覺來說都是無法感知到的,但對於 HDAPS 來說顯然並非如此。如果已經達到了間隔上限,那麼敲打間隔會被記錄在 baseKnocks 數組中,然後將兩次敲打之間的間隔重置。

  仔細修改這些變量可以幫助對程序進行優化,從而識別出您特有的敲打風格。縮小更新上限並擴大周期上限可以檢測出更多間隔的輕微敲打。機械敲打設備或特定的敲打方法可能會需要降低間隔上限,從而識別出獨特的敲打事件。

  清單 6. knockListen 子程序

sub knockListen() { my $checkKnock = 0; ($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 non-events like # bashing the enter key or hitting the side with the mouse if( abs ($currX) > $UPDATE_THRESHOLD) { $checkKnock = 1; } if( abs ($currY) > $UPDATE_THRESHOLD) { $checkKnock = 1; } if( $checkKnock == 1 ){ 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 == 0 ){ $diffInterval = 0 } if( $option ){ print "Knock: $knockCount ## last: [$currVal] curr: [$prevInterval] "; print "difference is: $diffInterval\n"; } push @baseKnocks, $diffInterval; $knockCount++; }# if the difference interval is greater than the threshold $prevInterval = $currVal; }#if checkknock passed }#knockListen   在創建敲打模式時,該模式會被放入 ~/.knockFile 文件中,並使用下面的子程序進行讀取:

  清單 7. 讀取敲打文件

sub readKnockFile { open(KNCKFILE,"$ENV{HOME}/.knockFile") or die "no knock file: $!"; while(<KNCKFILE>){ if( !/^#/ ){ my @arrLine = split "_#_"; $knockHash{ $arrLine[0] }{ cmd } = $arrLine[1]; $knockHash{ $arrLine[0] }{ comment } = $arrLine[2]; }#if not a comment line }#for each line in file close(KNCKFILE); }#readKnockFile   當 knockListen 獲得敲打模式時,它會將該模式與從 readKnockFile 中讀取的敲打模式進行比較。下面的 compareKnockSequences 子程序會對敲打之間的時間進行簡單的區別檢查。注意,敲打之間的差別並不是簡單混合在一起的:很多次敲打時的少量時間差別並不會累積成總體的匹配失效。

  第一個要比較的是敲打的次數,因為我們沒有必要將一個七次敲打的序列與一個兩次敲打的序列進行比較。如果敲打的次數與 ~/.knockFile 中現有的敲打序列匹配,每次敲打之間的差別也少於最大敲打偏差,那麼這次敲打就可以認為是完全匹配的。在允許敲打序列進行不精確匹配時,最大敲打偏差非常關鍵。我們可以增大最大敲打偏差來使敲打節奏更加自由,但是要注意,這可能會導致敲打模式匹配不正確。例如,我們可以在所期望的時間之前或之後半秒鐘允許自己的敲打模式發生偏離,但這仍然可以匹配。這樣就可以有效地說明 “刮臉和理發” 可以與 “Mary 姓 Little Lamb” 匹配,因此在修改這個參數時一定要小心。

  如果完整的模式可以匹配,就會運行 ~/.knockFile 中指定的命令,如果啟用了冗余模式,則會打印結果。下一個步驟是如果沒有找到匹配項,就退出這個子程序;如果找到了匹配項,就重置所記錄的敲打序列。這個步驟會執行 compareKnockSequences 子程序:

  清單 8. 比較敲打序列

sub compareKnockSequences { my $countMatch = 0; # 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 = 0; my $counter = 0; for( $counter=0; $counter<$knockCount; $counter++ ){ $knockDiff = abs($confKnocks[$counter] - $baseKnocks[$counter]); my $knkStr = "k $counter b $baseKnocks[$counter] ". "c $confKnocks[$counter] d $knockDiff\n"; # if it's an exact match, increment the matching counter if( $knockDiff < $MAX_KNOCK_DEV ){ if( $option ){ print "MATCH $knkStr" } $countMatch++; # if the knocks don't 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 "$cmd\n" } last; # otherwise, make the count of matches zero, in order to not reset }else{ $countMatch = 0; } }#for keys # if the match count is zero, exit and don't reset variables so a longer # knock sequence can be entered and checked if( $countMatch == 0 ){ return() } # if a match occurred, reset the variables so it won't match another pattern $knockCount = 0; @baseKnocks = (); }#compareKnockSequences   主程序的邏輯

  利用這些子程序,主程序的邏輯允許用戶創建敲打序列,或在守護模式下監聽敲打序列並執行命令。第一部分是在用戶指定 -c 選項(用於創建模式)時執行的。可以用簡單的超時進程來結束敲打序列。增大最大超時長度變量的值可以讓兩次敲打序列之間暫停 4 秒以上。如果我們保留最大超時長度為 4 秒,那麼程序運行到這個時間時就會結束,並打印當前輸入的敲打序列。

  清單 9. 創建序列主邏輯

if( $option eq "-c" ){ print "create a knock pattern:\n"; $startTime = getEpochSeconds(); # reset time out start while( $timeOut == 0 ){ $currTime = getEpochSeconds(); # check if there has not been a knock in a while if( $currTime - $startTime > $MAX_TIMEOUT_LENGTH ){ $timeOut = 1; # 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 =0 if( @baseKnocks ){ print "place the following line in $ENV{HOME}/.knockFile\n\n"; for( @baseKnocks ){ print "$_ " } print "_#_ (command here) _#_ <comments here>\n\n"; }#if knocks entered   第二部分是用來在一個無限循環中監聽敲打序列的主邏輯,它在一個循環中大約要睡眠 1/100 秒。在這個循環中還使用了一個基於秒的超時,在足夠的延時之後重置敲打序列。注意,在這個例子中,敲打監聽超時時間為 2 秒,而最大超時時間為 4 秒。這樣就提供了在敲打創建模式下進行簡單測試設置的功能,並為敲打序列的監聽模式提供了一個快速重置選項。

  清單 10. 主程序中的敲打監聽代碼

}else{ # main code loop to listen for knocking and run commands readKnockFile(); $startTime = getEpochSeconds(); while( $timeOut == 0 ){ $currTime = getEpochSeconds(); if( $currTime - $startTime > $LISTEN_TIMEOUT ){ $knockCount = 0; @baseKnocks = (); $startTime = $currTime; if( $option ){ print "listen 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(undef, undef, undef, $SLEEP_INTERVAL); }#main knock listen loop }# if create or listen for knocks

  警告:安全性

  knockAge 程序非常適合用於為系統提供一種額外的用戶輸入通道。然而,需要注意的是使用 knockAge 來做任何事都需要在系統上進行認證。是的,它可以防止密鑰記錄程序監聽密碼的問題,但是很多與 “敲打認證” 有關的因素都表明在對安全性敏感的環境中使用這種技術還不夠成熟。敲打序列目前是以 4 到 9 個數字在 ~/.knockFile 中進行存儲的,它們以毫秒為單位來表示延時。這個 “密碼” 文件非常容易讀取,並且通過嘗試和匹配敲打模式,可以獲得對系統的訪問權限。排除毫秒值中一些精度是一種可用的方法,但是這種練習就留給那些希望自行對系統風險進行評估的讀者好了。

  在任何敏感環境中,我們都應該進行一些研究,判斷用戶是否有足夠的應變能力並能夠精確地重現敲打序列。例如,我們是否具有能力創建並連續輸入可接受長度的敲打密碼?具有普通智商的人是否可以直觀地使用這種敲打序列?或者我們是否准備使用“刮臉和理發操作”來作為密碼?




Copyright © Linux教程網 All Rights Reserved