FPGA编译内核实现BadUSB

硬件安全导论
笔者认为硬件安全,主要可分为两大类。其一是外设安全,二是CPU安全(也叫可信执行环境)。其中外设安全主要讨论的是,类似键盘、鼠标、打印机、串口、网卡、内存、这类挂件对CPU指令程序流的一个影响。而CPU安全的核心目标,就是想办法通过用户层通杀现有操作系统和协议造成危害,通杀这个前提非常重要,如果你不牢记CPU安全研究的目标,很容易懵逼,不知道讨论这些的意义为何。
实验环境选择
主推的实验环境是使用fpga作为核心版,配合“集成电子模块”和“稀疏飞线”外设电路来进行实验。不推荐使用仿真软件,仿真软件会对复杂高速硬件进行大幅度抽象,偏离其实际功能,就比如在Proteus中CPU的代码无需引导就可以直接编写到内存里,与事实严重不符。
也不推荐绘制PCB打板进行实验,打板多用于投入生产的专项电路,打板后电路可塑性很低,主要优势是结实耐用,体积小,这些都不是实验阶段需要解决的问题。
虽然fpga核心电路设计总是比外设电路更加复杂,但软件定义网络总是会省很多事儿,因此要焊接外设电路所需要的装备比核心板多的多,清单如下:
焊接工具:电焊枪、焊锡、剥线钳、吸锡器(可选)、热风枪(多用于焊PCB,可选)
调试工具:直流可调电源、万用表、示波器(可暂时用fpga替代)
串接原件:导线、排针、接线端子、杜邦线、面包板
电子原件:电容、电阻、电感、三极管、二极管
集成原件:整流电路、数模转换芯片、关键接口底座
上面的原件在淘宝搜索【面包板电路】进入店铺一般都可以买到,工具设备都比较贵只能单独购买。不推荐一股脑全部购买,刚入门时只靠一个FPGA就足够了,购买的fpga就自带一套丰富的外设资源,加上丰富的IP核与片内资源,足够玩一段时间了。集成arm核和基本外设的zynq开发板在社区丰富性、最小系统编译速度上都有巨大优势,适合新手入门,只是随着研究的深入,zynq上的PS端口有的时候就显得不够灵活,然而这本质上还是资源瓶颈的问题,所有fpga都会有,fpga+arm的组合还是最为灵活的版本答案。
外设后门
主流PC机下,外设之间无法脱离CPU的控制直接通信,直白点说,最敏感的外设莫过于内存,其他任何外设都不能直接影响内存数据。如果想通过外设来设计硬件后门,最直接的办法是设计一个芯片来直连内存,但这会造成几个问题:
不那么致命的问题是外设电路相对简单,稍微学过点电路的人都能发现你在搞鬼。
另一个不那么致命的问题是存在总线竞争,你只能在CPU与内存交互的空隙进行数据修改
最致命的问题还是,由于CPU保护模式的存在,你无法通过用户层直接访问到你的后门设备,这意味着你的设备必须基于现有协议进行操作,综合来看是个吃力不讨好的事儿。
数据存储器逆向
外设里有三个扛把子非常特殊,分别是RAM、ROM和非易失存储器,从外设角度来看改变这三个角来影响CPU的指令执行是最靠谱的,虽然直接在外设上做手脚很容易被发现,但是如果能拿到硬件系统本身,分析这三者将有助黑客挖掘系统漏洞,保护模式造成的虚拟地址机制和掉电易失性使得RAM的分析相比其他两位更加困难且低效。AXI4-Slave接口使得内存映射和管理存在可能,封装一个操作系统来高仿内存不再是什么难事儿,其基本架构如图所示:

通过一个自定义Chip芯片对总线协议进行转义兼容AXI4协议,通过Control总线实现Chip与ARM的控制通信,防止AXI4协议访问到内核地址,造成操作系统崩溃。其最终实现的效果很类似于内核VT中的EPT内存管理,通过ARM对原操作系统的内存进行管理,当然这只是个理论上可行的系统,到了硬件层面体积、速度、时序约束都是很重要的问题,如何将该高仿系统连接到原始系统上是个比较头疼的问题。因此多数情况下,还是选择直接拆ROM和存储器芯片逆向里面的程序文件比较方便。
外设交互漏洞利用
这部分主要是针对现有通信协议进行攻击,最经典的案例就是hid(Human Interface Devices)伪装攻击,通过将USB-Slave接口伪造成一个键盘或鼠标操作设备,编写预制脚本短时间内在操作系统内执行大量操作。至于能否突破系统权限,其本质还是与软件安全息息相关,正常情况下用户也只有r3的权限,想要通过操作提权至r0,那么必须在软件层面具备条件,也就是存在漏洞。
可信执行环境
“处理器安全”的前身是“处理器开发”,我们常说的开源CPU就是公开了其电路设计思想的CPU,为了方便流通他们往往用硬件描述语言来定义CPU结构,可以理解为直接提供电路设计的原理图,可以根据自己的需求增删改,比如RISC-V就是开源处理器架构中的一员。
CPU开发的上下限很大,从简单内存读写、中断处理、浮点运算、顺序执行、乱序执行、预测执行、cache缓存涉及到CPU虚拟化等等,难度层层递增,非一日之功,笔者目前只做过极简CPU的开发,因此只能简单聊聊CPU后门的实现思路,具体到技术细节还有很多问题需要解决,比如CPU性能问题、电路时序问题、电磁干扰问题等等。
首先要理解处理器安全的攻击目标是“用户层通杀操作系统”,你绝对不能指望3环利用漏洞驱或是3环利用内核漏洞,虽然他们在安全攻击上可行,但本质上都属于软件安全,一个补丁就可以解决,处理器安全突破的是cpu保护模式,通杀一切操作系统,其目标本身就是在得不到内核权限的前提下突破操作系统保护,且这个漏洞无法通过软件修复。
当我们通过设计处理器后门实现的安全攻击时,其攻击流程看似和普通软件病毒或利用普通外设漏洞很像,无非是【投放钓鱼木马】、【在服务器交互端口注入payload】、【制作bad usb这种恶意外设】此类种种。
然而这其中却存在本质区别,处理器后门实现的核心思路是触发cpu内部潜在的“指令集”(而不是利用软件漏洞),从而改变cpu运行状态。最朴素的想法当然就是设计一些隐藏“指令”,然而这种做法还是容易被懂行的大佬们测试出来,早在几年之前就有人提出了针对cpu隐藏指令模糊测试方法,无需逆向电路即可通过黑盒测试爆破出后门指令。
然机缘巧合之下提出的硬件木马电路就改变了这一格局,一种基于【时序信号电荷累计】而进行状态切换的电路。这使得输如电流必须满足特定时序,且持续累进到目标阈值时才会触发状态改变,所达成的效果就类似于在游戏机上按【上下上下左右左BABA】就可以开挂一样。
换句话说cpu指令必须在特定输入组合配合之下才能造成系统异常。试想一下你随便在用户层写几个push,mov,and指令(按照既定顺序执行)就可以触发后门威胁操作系统,不满足目标顺序就无法触发后门,跟一个密码一样,这才是比较实用的hack方案。
这种方案导致在黑盒测试在概率意义上很难测试出这种后门,只能通过电路逆向来定位后门位置,配合上电路混淆技术可进一步提升后门的隐蔽性。

FPGA上搭建Linux内核调试环境
arm linux不同于x86下的操作系统,arm操作系统的定制化需求较高,CPU外设不能通过总线遍历自动获取,必须要手动配置设备树,因此很多参数不能够通用。
Vivado设计FPGA外设电路
开发板型号:Xilinx Zynq-7010
首先通过Vivado 来连接好arm cpu外设,并导出hdf硬件描述文件包,如何组织fpga电路以实现PS&PL端协同工作可以参考。

这里的hdf实际上是一个压缩文件,可用winrar打开,其中保存了PL端的硬件布局文件bitstream、c语言代码文件以及tcl脚本等

petalinux编译环境配置
下载并解压petalinux安装文件petalinux-v2018.3-final-installer.run。运行如下命令安装依赖工具
执行如下命令安装petalinux文件夹
运 行 sudo dpkg-reconfigure dash 命令,将默认终端解释器设置为bash
执行文件夹下的source settings.sh 初始化编译环境
运行如下命令设置终端别名
创建一个petalinux项目
使用 petalinux-config -c u-boot
初始化u-boot源码,并配置内核引导参数为:

上述步骤等价于在uboot命令行中执行如下命令(需根据自己的开发板进行定制):
详细配置原理可参考
。在项目根目录搜索 “env_default.h”文件定位U-boot源码位置,拷贝源码文件夹到任意目录如/home/benson/petalinux/v2018.3/MyZynqBoard/uboot。使用 petalinux-config -c kernel
初始化内核源码。初始化linux kgdb调试参数,使用串口模式调试Linux内核:
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Compile the kernel with debug info
[*] KGDB: kernel debugger --->
<*> KGDB: use kgdb over the serial console

项目根目录搜索 “hid.c”文件定位内核源码位置,拷贝源码文件夹到任意目录如/home/benson/petalinux/v2018.3/MyZynqBoard/kernel-source。
执行 petalinux-config
设置U-boot与内核源码到指定文件夹,同时设置跟文件系统存储类型为SD Card。
Linux Components Selection --->
u-boot (ext-local-src) --->
External u-boot local source settings --->
linux-kernel (ext-local-src) --->
External linux-kernel local source settings ---> Image Packaging Configuration ---> Root filesystem type (SD card) --->
内核编译与调试
运行如下命令编译内核
进入项目输出目录/images/linux下,通过如下命令,打包系统镜像
在输出目录下,执行调试命令加载符号,准备使用target命令连接并调试客户机
开发板上电进入系统,在终端执行,触发进入kgdb调试模式:
gdb调试器上执行如下命令进行断点测试,更多调试命令可参考
:
修复调试器寄存器交互协议bug
使用gdb调试内核时出现:
这是由于寄存器传输数量与调试器接受数量不匹配导致的,需要更改内核中kgdb的源码并重新编译,更改如下,原来程序使用的是DBG_MAX_REG_NUM作为最大寄存器数量,我们需要将其更改为GDB_MAX_REGS这个GDB的最大寄存器数量:
FPGA编译内核实现BadUSB
USB工作模式使能
通过查看
原理图可知,USB Slave端口与host共用一个OTG接口,并通过ID引脚进行USB工作模式使能,也就是说【Host】和【Slave】端口只能活一个,从电路接线方式不难看出【Slave】端口的优先级高于【Host】,同时接入两个端口的话,只有【Slave】会工作。这还是比较坑的。

因为参考《核心版电路图》可知,这个USB芯片驱动端口都是跑在PS端口上的,好歹放一个口到PL上啊,这样灵活性高一些,这样连的话想研究usb协议就得单独焊外设电路了,有点麻烦。

不过好在隔壁【领航者】更逆天,四个【Host】接口全都公用一个PS端的USB芯片,答案很明显“PL端不配拥有自己的USB口”。
参照芯片手册编址设备树
改写./MyZynqBoard/project-spec/meta-user/recipes-bsp/device-tree/files路径下的system-user.dtsi文件,以otg模式使能USB设备,可同时兼容USB Slave与USB Host。配置方法可参考Xilinx官方指导文档
Linux内核代码修改
执行如下命令修改linux内核配置
petalinux-config -c kernel
通过如下路径,开启”HID Gadget”功能,按下‘M’键设置内核模块化编译g_hid.ko驱动
[*] USB support --->
<*> USB Gadget Support --->
<*> USB Gadget Drivers (HID Gadget) --->
修改linux内核代码“kernel/drivers/usb/gadget/hid.c”, 在文件中增加以下代码后,重新编译内核,将内核镜像下载到设备中,在/dev目录下我们会看到设备节点”/dev/hidg0”,这个新的设备节点名就是我们刚刚加入的hid模拟键盘设备节点,接下来我们就可以针对该节点进行读写,就可以模拟HID键盘输出了。
USB OTG接口测试
重新编译内核后,运行如下命令,加载g_hid.ko驱动
使用arm-linux-gnueabihf-gcc编译测试程序hid_gadget_test,并执行如下命令,调用键盘设备实现按键输出
hid_gadget_test 的核心测试代码如下,详细代码可参考linux官方文档《Linux USB HID gadget driver》(https://www.kernel.org/doc/html/next/usb/gadget_hid.html):
接下来就是传统的c语言开发了,将测试代码改写为自动输入脚本即可实现bad usb效果。USB协议的详细数据流,可使用Bus Hound软件进行调试。
使用configfs实现灵活hid
现在主流配置usb gadget都是采用configfs通过用户空间进行配置,而不是像之前使用hardcode方式专门有个内核模块来配置,因为每次注册设备都要更改内核源码很不方便。然笔者个人认为改代码配置设备还是比较快的,配置文件需要改写的属性也很多,不见得方便,笔者自己也没用这套方案,如果想实现该效果可参考