×
单片机 > 单片机程序设计 > 详情

Arduino+Avr libc制作Badusb原理及示例讲解

发布时间:2020-06-08 发布时间:
|

一、 前言

2014年美国黑帽大会上研究人员JakobLell和Karsten Nohl展示了badusb的攻击方法后,国内与badusb相关的文章虽然有了一些,但是大部分人把相关文章都阅读后还是会有种“不明觉厉”的感觉,badusb仍有一层朦胧的面纱。经过一段时间的学习和研究后,笔者希望通过自己的一些心得体会可以帮助其他人更清晰地认识badusb,也希望这篇文章能够起到一定的启发。这篇文章主要分为五个部分——知识扫盲部分、badusb固件编写部分、badusb配置界面部分、技术展望部分和总结部分。

二、硬件准备

本文使用的硬件是Arduino Leonardo开发板,但是不难将Leonardo开发板替换为其它Arduino开发板。


三、知识扫盲

1. 虽然USB协议有一定的漏洞,但是不是任何USB设备都能制作badusb的。制作badusb有两个核心,一个是能够为USB设备编写相应的固件,另一个是能够将编写的固件烧录到USB设备中。要编写相应的固件就需要掌握USB设备中微控制器(或者说芯片)的指令规范文档(如果Intel不提供开发文档,那么除了Intel自己没有人能为Intel处理器编写程序);而要将固件烧录到USB设备中,要么使用相应的硬件编程器,要么就需要USB设备本身已经存在的bootloader来辅助进行烧录工作(bootloader是USB设备厂商在生产时就放在USB设备中的,网上某些优盘的优盘量产工具也是从厂商流出的,而不是第三方编写的)。要同时满足两个核心条件还是比较难的,即便是JakobLell和Karsten Nohl公布的badusb利用代码也是针对满足一定条件的优盘,因为流出的优盘量产工具有限(既然有优盘量产工具,可以推测相应的优盘中肯定有bootloader或者类似bootloader的固件存在,那么通过逆向优盘量产工具就可以掌握PC端软件和特定优盘的通信方式,进而实现自己的烧录工具)。

2. Arduino 和 teensy这类开发板之所以容易制作badusb,是因为它们采用的微控制器官方有详细的说明文档。最常用的是atmel公司的微控制器,atmel官网提供了各种开发文档和开发相关的库等。

3. Atmel厂商的微控制器整合了SRAM, FLASH和EEPROM。SRAM就好比电脑的内存,不用关心;FLASH高地址存放的是bootloader,低地址则存放用户烧录的固件,芯片加电时直接执行用户的固件,但是芯片复位时会先执行bootloader(这一点是烧录的关键),再执行用户的固件;EEPROM则主要用来存放数据,用户可以随意修改EEPROM中的数据,固件也可以从EEPROM里读取数据(本文的固件示例和PC端程序都利用了这一特性)。

4. Avr libc是一个开源项目,针对atmel厂商的各种微控制器开发C语言库、编译器、烧录工具等一系列辅助工具,还有针对Windows平台的WinAvr项目。Arduino ide的核心其实也是avr libc。

5. Arduino的开发板有相应的bootloader(在FLASH高地址)可以和avr libc项目中的avrdude.exe软件通信,实现固件的烧写和读取动作。在Arduino开发板复位时,会加载bootloader,这时候就可以利用avrdude.exe和bootloader通信。复位操作可以通过开发板上的复位按钮,或者编程实现软复位操作(如果开发板支持的话)。

6. Intel hex 是一种用于编程器的特殊的文件格式,正是因为这种格式,使得我们可以自由控制数据的存储地址。Intel hex的文件格式解析可以自行网上搜索。


四、知识获取途径

1. USB知识获取

如果只是为了简单了解USB设备为什么可以模拟键盘、鼠标等其它设备,网上有许多博客是关于USB规范详解的,也可以简单地看《USB开发大全》和《USB应用开发实例详解》前面关于USB通用协议部分,如果不是对硬件感兴趣没必要深究。

2. Arduino、Avr libc知识获取

这两样其实都是开源的,所以只要有足够的精力和实力,看源代码深入了解相关知识是没问题的。但是如果只是想体验一下制作badusb,可以只看一下Arduino的官方文档和avrdude.exe的相关文档,看这些文档时也没必要深究每个细节,能一定程度上“照葫芦画瓢”就可以了。


五、Badusb固件编写

这里给的固件示例在执行时会从EEPROM中指定的地址读取数据,根据读取的数据和制定的规则发送相应的按键响应给PC主机。


#include"Keyboard.h"


#include"EEPROM.h"


 


intmodifier_key = -1;      //modifier_key不为-1说明Win键、Ctrl键或者Shift键按下,这个示例特指Win键


intkeycode;                //存放要发送的按键代码


unsignedint delay_time = 0;    //固件执行下条指令时延迟的时间


unsignedint address  = 0;  //从EEPROM读取数据的地址,这里要和EEPROM存放数据的地址一致


charnum_char[10] = {0};        //某个数字的字符串形式,这个数字用于delay()的参数,也就是作为睡眠的时间


int num= 0;


intindex = 0;


 


//change this to match your platform:


voidsetup() {


  // make pin 2 an input and turn on the


  // pullup resistor so it goes high unless


  // connected to ground:


  pinMode(2, INPUT_PULLUP);


  Keyboard.begin();


  delay(1000);                  //等待一段时间,让USB设备和PC主机通信完成初始化工作


  while((keycode = EEPROM.read(address++))!= 0){//循环从EEPROM中读取数据直到读到数值0(不是数字0),这个值是在向EEPROM中写入数据时在末尾添加的。


    if (keycode == '$') {            //如果从EEPROM中读到的数值等于'$'的ASCII码


      modifier_key = 131;            //将modifier_key的值设为Win键的键值,这个值会和后续的按键组合后发送给PC主机


      continue;


    }


/* 将两个'&'字符之间的数字作为后续一个按键发送后睡眠的时间,这里之所以要有这个控制值是因为种种因素都会影响按键按下到某个窗口弹出的时间,如果在窗口未弹出就发送了某个按键,这个按键就失效了,后续的按键组合起来也不完整了。比如在按下Win+R键后,还没等运行对话框弹出,固件就发送了"cmd"按键,那么这次启动cmd肯定就失败了。 */


    if (keycode == '&') {           


      while ((keycode = EEPROM.read(address++))!= '&') {


        num_char[index++] = keycode;


      }


      sscanf(num_char, "%d",&num);


      delay_time = num;          //将EEPROM中读取的数据转换为数值后保存到delay_time,控制下次发送按键后睡眠时间


      memset(num_char, 0, sizeof(num_char));//清空num_char为下次转换做准备


      continue;


    }


    if (keycode == ';') {    //如果从EEPROM中读取的数值等于';'的ASCII码,将发送一个回车键的按键码


      keycode = 176;     //176等于回车键的按键码


    }


     Write(keycode);     //实际发送按键的动作


  };


}


//


voidloop() {


  // do nothing:


  while (true);


}


 


voidWrite(int code)


{


  if (modifier_key != -1) {     //在这个例子中,modifier_key不等于-1就等于Win键的按键码


    Keyboard.press(modifier_key);    //按下Win键,不放


    Keyboard.press(code);            //按下另一个按键,不放


    Keyboard.releaseAll();       //同时放开Win键和另一个按键,发送Win+某个按键给PC主机


    modifier_key = -1;


    delay(delay_time);           //这个时间要么等于0,要么等于两个'&'字符之间指定的值


  } else {


    Keyboard.write(code);            //否则直接按下某个按键并放开


    delay(delay_time);           //这个时间要么等于0,要么等于两个'&'字符之间指定的值


  }


  delay_time = 0;


}


这里针对这个固件给几个例子帮助理解:


1.从EEPROM中依次读取到’$'、’r'($r)表示badusb会按下Win+R键。


2.从EEPROM中依次读取到’$'、’r'、’;'($r;)表示badusb会按下Win+R,然后按回车键。


3.从EEPROM中依次读取到’&’、’5′、’0′、’0′、’&’、’$'、’r'、’;'(&500&$r;)表示badusb会按下Win+R键,然后等待500毫秒(保证运行对话框弹出),再按下回车键。


4.从EEPROM中依次读取到(&500&$rpowershell&400&;Get-Date;)表示badusb先按下Win+R键,等待500毫秒后输入powershell,按回车键后等待400毫秒,再输入Get-Date,最后按下回车键。


六、Badusb配置界面

所谓的Badusb配置界面,就是avrdude.exe的UI界面精简版,再额外提供了控制Badusb执行的动作的功能(本质就是修改了EEPROM特定地址的数据,因为固件是根据EEPROM中的数据执行动作的)。

图1 PC程序界面


这个程序主要就是avrdude.exe的UI界面,只有executable处和address处是为了自定义Badusb动作设计的。partno的选项和programmer的选项是解析选定的avrdude.conf得到的,所以不选择avrdude.conf的话partno和programmer的下拉框将为空;端口号是通过注册表获取到的。


当点击upload按钮


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
浅谈AVR中定时器几种工作模式