固件调试
Last updated
Last updated
跟传统安全思路一样,调试是安全分析中重要的一环,通过调试信息,可以直接操作硬件内部代码的执行,直观的看到各种数据的输入和输出。
通过 UART 串口来直接与机器交互,通过串口输出输入信息,做动态调试。
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作 UART ,是一种异步收发传输器,是电脑硬件的一部分。它将要传输的资料在串行通信与并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片, UART 通常被集成于其他通讯接口的连结上。
对于物联网硬件的串口调试,多数情况下指的就是通过 UART 串口进行数据通讯, 但是我们经常搞不清楚它和COM口的区别, 以及 RS232, TTL 等关系, 实际上 UART 、COM 指的物理接口形式(硬件), 而 TTL、RS-232 是指的电平标准(电信号). UART 有4个pin(VCC, GND , RX, TX), 用的TTL电平, 低电平为0(0V)、高电平为1(3.3V或以上), UART 串口的 RXD、TXD 等一般直接与处理器芯片的引脚相连,而RS232串口的 RXD、TXD 等一般需要经过电平转换(通常由 Max232 等芯片进行电平转换)才能接到处理器芯片的引脚上,否则这么高的电压很可能会把芯片烧坏。 在调试的时候, 多数情况下我们只引出 rx、tx、 GND 即可,但是 UART 的数据要传到电脑上分析就要匹配电脑的接口,通常我们电脑使用接口有COM口和USB口(最终在电脑上是一个虚拟的COM口),但是要想连上这两种接口都要需要进行硬件接口转换和电平转换。
UART 调试第一步需要先找到对应的四个PIN,在通电情况下,VCC 口可以不要接,判断 GND , RX, TX 三个引脚是调试的关键,找四个引脚可以先看 PCB上的印字。
但多数厂商在量产前会去掉用于调试的串口印字,如果找不到对应引脚的印字,就需要先分析 PCB 的结构,一般 PCB 上有3、4 、5个并排或相距不远的焊点或通孔,就有可能是 UART 调试串口。
但 PCB 上可能存在多个这样的焊点或通孔,从多个口中找出真正的调试串口,就需要借助到万用表。
万用表找串口首先需要找到 GND 口,就是接地口,在疑似串口的焊点处,通过测量电势差,可以判断出 GND 口,通过连接焊点和输入负极,如果电势为0,就可能是 GND 口,如果电势为最大值,例如 3.6V、5V 等,就可能是 VCC 口。然后通过 UART 转换器对应的4个口,引出导线,并设置好串口输出环境后,就可以依次尝试。也可以通过短接其中的两口,如果机器重启,就可以判断这两口为VCC和 GND 。 需要注意的是,在 TTL 电平模式下,UART 转换接口上的 RX、TX 口与上位设备,也就是 PCB 上的 UART 口的RX和TX是需要反接的。
通过万用表测量电势差之后,在靠近 CPU 的地方有三个通孔,有可能是 UART 串口,用导线连接之后,设置波特率为 115200。
用 SecureCRT 连接串口,给机器通上电之后,串口立马输出了启动信息,并可以执行命令,说明串口正确,如果遇到无法输入的情况,首先检查接线是否松动,然后在 SecureCRT 中的, Session Options -> Connection -> Serial -> Flow Control,将原先选中的 RTS/CTS 取消掉,这是因为如果选中了 RTS/CTS ,则硬件上要有对应接口,软件上实现对应协议,才能实现此流控制。如果串口输出为乱码,则需要切换波特率,直至输出正常。
在靠近cpu的地方有四个通孔,测量电势差后,利用导线探针,确定了三个 PIN,连接转换器。
串口中输出调试信息,因波特率设置问题,初始输出为乱码,改为 38400 即可正常输出。
在 PCB 上有四个焊点,先测量电势差,分出 GND 和VCC,在利用焊枪分别焊上导线,连接转换接口,测试出 TX 和 RX 口。
设置波特率为57600,串口输出正确,并可执行命令。
JTAG(Joint Test Action Group;联合测试工作组)是一种国际标准测试协议(IEEE 1149.1兼容),主要用于芯片内部测试。现在多数的高级器件都支持 JTAG 协议,如 DSP、FPGA 器件等。标准的 JTAG 接口是4线:TMS、TCK、TDI、TDO,分别为模式选择、时钟、数据输入和数据输出线,另外 ARM 还提供了 SWD 的调试接口,比 JTAG 所需要的线更少,高速模式下更稳稳定,部分厂商如TI,还支持2线制 JTAG 协议进行调试,称为 SBW 接口。 常见支持上述协议进行调试和仿真的设备如:Jlink、Ulink、ST-link 和 MSP430 仿真器等,jtag 既支持在线调试,又能直接获取固件,对于芯片的调试和分析是非常具有帮助的。
接口定义
JTAG有 10pin 的、14pin 的和 20pin 的,尽管引脚数和引脚的排列顺序不同,但是其中有一些引脚是一样的,各个引脚的定义如下。
引脚定义
Test Clock Input (TCK) -----强制要求1 TCK 在 IEEE1149.1 标准里是强制要求的。TCK 为 TAP 的操作提供了一个独立的、基本的时钟信号,TAP 的所有操作都是通过这个时钟信号来驱动的。
Test Mode Selection Input (TMS) -----强制要求2 TMS 信号在 TCK 的上升沿有效。TMS 在 IEEE1149.1 标准里是强制要求的。TMS信号用来控制 TAP 状态机的转换。通过TMS信号,可以控制 TAP 在不同的状态间相互转换。
Test Data Input (TDI) -----强制要求3 TDI 在 IEEE1149.1 标准里是强制要求的。TDI 是数据输入的接口。所有要输入到特定寄存器的数据都是通过 TDI 接口一位一位串行输入的(由 TCK 驱动)。
Test Data Output (TDO) -----强制要求4 TDO在IEEE1149.1标准里是强制要求的。TDO 是数据输出的接口。所有要从特定的寄存器中输出的数据都是通过 TDO 接口一位一位串行输出的(由 TCK 驱动)。
Test Reset Input (TRST) ----可选项1 这个信号接口在 IEEE 1149.1 标准里是可选的,并不是强制要求的。TRST 可以用来对 TAPController 进行复位(初始化)。因为通过TMS也可以对TAP Controll 进行复位(初始化)。所以有四线 JTAG 与五线 JTAG 之分。
(VTREF) -----强制要求5 接口信号电平参考电压一般直接连接 Vsupply 。这个可以用来确定 ARM 的 JTAG 接口使用的逻辑电平(比如3.3V还是5.0V)
Return Test Clock ( RTCK) ----可选项2 可选项,由目标端反馈给仿真器的时钟信号,用来同步 TCK 信号的产生,不使用时直接接地。
System Reset ( nSRST)----可选项3 与目标板上的系统复位信号相连,可以直接对目标系统复位。同时可以检测目标系统的复位情况,为了防止误触发应在目标端加上适当的上拉电阻。
USER IN:用户自定义输入。可以接到一个 IO 上,用来接受上位机的控制。
USER OUT:用户自定义输出。可以接到一个 IO 上,用来向上位机的反馈一个状态
由于 JTAG 经常使用排线连接,为了增强抗干扰能力,在每条信号线间加上地线就出现了这种 20 针的接口。但事实上,RTCK、USER IN、USER OUT 一般都不使用,于是还有一种14针的接口。
SWD 引脚定义
VRef:目标板参考电压信号。用于检查目标板是否供电,直接与目标板 VDD 联,并不向外输出电压; GND:公共地信号; SWDIO:串行数据输入输出,作为仿真信号的双向数据信号线,建议上拉; SWCLK:串行时钟输入,作为仿真信号的时钟信号线,建议下拉; SWO:串行数据输出引脚,CPU 调试接口可通过 SWO 引脚输出一些调试信息。该引脚是可选的; RESET:仿真器输出至目标 CPU 的系统复位信号。 虽然RESET是可选的信号,但一般都建议接上,使得仿真器能够在连接器件前对器件进行复位,以获得较理想的初始状态,便于后续调试操作。
对应关系
20、14、10pin JTAG 的引脚名称与序号对应关系
值得注意的是,不同的 IC 公司会自己定义自家产品专属的 Jtag 头,来下载或调试程序。 需要说明的是,上述 Jtag 头的管脚名称是对 IC 而言的。例如 TDI 脚,表示该脚应该与 IC 上的 TDI 脚相连,而不是表示数据从该脚进入 download cable。 实际上10针的只需要接4根线,4号是自连回路,不需要接,1,2接的都是1管脚,而8,10接的是 GND,也可以不接。
拆焊芯片
首先用热风枪拆下智能锁主控芯片,该单片机型号为:Stm32F103R6。
烧录座连接Jlink
芯片第一脚对齐烧录座第一脚,然后把 Jlink 插入烧录座引出的 JTAG 接口。
读取固件
电脑上安装好 Jlink 驱动,打开 J-Flash 客户端,设置好参数,主要在配置栏选择正确的芯片型号,然后点击连接,在点击 Target->Read Back->Entire trip 即可读写固件。
接口调试
如果PCB上保留了厂商在研发过程中预留的 JTAG 接口,可直接通过飞线的方式,连上对应的引脚进行调试。
直连芯片 JTAG 引脚调试
大部分厂商在生产环节会去掉外部引出的 JTAG 接口,因为多数量产芯片的封装格式,直接飞线难度较大,因此可以采用探针台直连芯片引脚进行调试。 在研究的某款智能锁,拆解发现采用的是 MSP432G2553 作为主控,下图红框位置。
该款智能锁利用手机 app 产生开锁音频信号,进过外部 AD 转换后传输至芯片中进行解密开锁处理,厂商在生产过程中比较注重安全意识,PC 上的没有保留调试接口,进一步分析的话,需要对芯片进行固件提取和在线调试。
芯片分析
查TI官方手册,MSP432G2553 芯片引脚定义如下,其支持四线 JTAG 和两线 SBW 的调试接口,随采用两线制 SBW 接口作为调试方式,其 16 引脚为 SBWTDIO 口,17脚为 SBWTCK 脚。
两线制 SBW 对应 MSP430 仿真器上的14线排针接口,分别为 16 脚 SBWTDIO 口连仿真器第一脚 TDO,17脚 SBWTCK 连第7脚 TCK,最后需要连接 GND 脚,即芯片的第 20 脚连仿真器第9口,两线制 SBW 同时需要外部电源供电,仿真器接口定义如下图。
按照引脚说明,开始在探针台上连接引脚,需要注意 JTAG 和 SBW 调试,对连线的长度有严格要求,超过 20 厘米信号会大幅衰减,造成无法调试,因此在探针上利用夹子和铜导线缩小接线距离。
在线调试
连接上仿真器,启动 msp430-gdbproxy。 msp430-gdb 远程连接 target remote 192.168.1.196:2000
仿真器环境配置好,然后打开Lite FET-Pro430,选择好芯片型号后,点击 read 即可读取固件,提取的固件分为 txt 和 hex 两种格式,也可以利用接口自己进行在线调试。
在通过串口调试嵌入式设备时,每次需要通过接线和 USB 转换器连接才能进行,对设备操作的话,存在一定的不便,并且会占用电脑的 USB 口,接线也会造成一定的不稳定,因此可以通过串口命令开启 telnet 或者 ssh 服务,远程登陆设备。通过系统命令、程序的输出以及 gdb 进行 远程调试,提高调试的便捷性。
基本函数结构分析结束后,使用 gdbserver 附加到 goahead 进程进行远程调试,验证我们的猜测是否正确。 telnet 连上路由器并使用 gdbserver 附加到 goahead 进程进行调试。
使用 ida 远程链接之后在 sub_457ebc 入口出下断点,f9 键开始运行,brupsuit 发送 poc 之后断在了 sub_457ebc 函数处。
单步查看 websGetVar 的返回值 0x48d4a8 处的值正是我们发送的poc。
运行至 bs_setmanpwd 查看参数如下,a0 的值为格式化之后的 json 对象的指针,a1 的值为当前栈帧中一个长度为 400 字节的缓冲区的指针。
继续运行,查看 nvram_bufget 的返回值v0为我们传入的 routepwd 字段的值。
继续向下,fprintf 格式化字符串,a0 为当前栈帧上一个长度为 200 字节的字符串的地址。
可以看到此处并没有对 nvram_bufget 获取到的值进行长度验证就格式化到缓冲区上造成了缓冲区溢出。
继续向下可以看到 bl_do_system 把刚才格式化好的字符串当作指令执行了,因此此处对 “chpasswd admin %s” 进行截断之后可以追加任意命令且以管理员权限执行。
继续向下执行到“lw $ra,0x2a0+ra($sp)”之后,ra的值被覆盖成 0x61616161 ,我们在此处劫持了函数的返回地址,经过计算此处距离缓冲区起始地址为 618 个字节,至此可以执行任意代码。
在对某个摄像头进行漏洞分析时需要调试 recorder 进程。
直接附加上去之后,本地 arm-none-eabi-gdb 连接,下断点,提示:Cannot access memory at address 0xb695de04 发现内存地址不可读,查看发现已经变为僵尸进程,又新起了一个。
猜测应该有另外一个守护进程,附加的时候会停止程序,查看进程
直接 kill 掉发现摄像头不能正常使用,看来这个进程还有一些其他的功能,不能直接 kill,只能去掉对目标进程的守护功能,载入 ida 字符串搜索 recorder 。
直接替换掉 recorder 即可,例如替换为相同长度的 aaaaaaaa 。
为了方便,可以把原程序修改,然后重刷进去,具体可以参考后面 2.6 章节,这里不涉及到硬件修改,简单的把守护进程放到可写目录。 目前的调试流程为:
修改 as9ipcwatchdog,替换所有的 recorder
停止 as9ipcwatchdog,recorder 。
下载 as9ipcwatchdog 到tmp目录,启动 as9ipcwatchdog 。
启动 recorder
APP 端删除当前的设备,重新搜索添加 。
断点调试
此时已经可以正常调试,同样以修改设备的时间来做测试,在 Linux 操作时间函数 stime 下断点 。
点击保存
成功断点,可以做一些后续的漏洞分析。
来源:物联网安全百科