> For the complete documentation index, see [llms.txt](https://wokough.gitbook.io/iot-firmware-aio/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wokough.gitbook.io/iot-firmware-aio/wiki/gu-jian-tiao-shi.md).

# 固件调试

跟传统安全思路一样，调试是安全分析中重要的一环，通过调试信息，可以直接操作硬件内部代码的执行，直观的看到各种数据的输入和输出。

### 一、串口调试

通过 UART 串口来直接与机器交互，通过串口输出输入信息，做动态调试。

通用异步收发传输器（Universal Asynchronous Receiver/Transmitter)，通常称作 UART ，是一种异步收发传输器，是电脑硬件的一部分。它将要传输的资料在串行通信与并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片， UART 通常被集成于其他通讯接口的连结上。

![](/files/hemUJImXMLERIUjPK0rp)

对于物联网硬件的串口调试，多数情况下指的就是通过 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口），但是要想连上这两种接口都要需要进行硬件接口转换和电平转换。

#### 1.1 **UART 串口调试**

UART 调试第一步需要先找到对应的四个PIN，在通电情况下，VCC 口可以不要接，判断 GND , RX, TX 三个引脚是调试的关键，找四个引脚可以先看 PCB上的印字。

![](/files/HmtR21ra8Uvt8ezni4Cu)

但多数厂商在量产前会去掉用于调试的串口印字，如果找不到对应引脚的印字，就需要先分析 PCB 的结构，一般 PCB 上有3、4 、5个并排或相距不远的焊点或通孔，就有可能是 UART 调试串口。

![](/files/j3qVL9XlizAjSzGjX9tB)

但 PCB 上可能存在多个这样的焊点或通孔，从多个口中找出真正的调试串口，就需要借助到万用表。

![](/files/8mutqbvnlBR8dolBvuwa)

万用表找串口首先需要找到 GND 口，就是接地口，在疑似串口的焊点处，通过测量电势差，可以判断出 GND 口，通过连接焊点和输入负极，如果电势为0，就可能是 GND 口，如果电势为最大值，例如 3.6V、5V 等，就可能是 VCC 口。然后通过 UART 转换器对应的4个口，引出导线，并设置好串口输出环境后，就可以依次尝试。也可以通过短接其中的两口，如果机器重启，就可以判断这两口为VCC和 GND 。 需要注意的是，在 TTL 电平模式下，UART 转换接口上的 RX、TX 口与上位设备，也就是 PCB 上的 UART 口的RX和TX是需要反接的。

![](/files/T3V2fHxs4oNgOLxtxWsL)

#### 1.2 **调试某智能摄像头**

通过万用表测量电势差之后，在靠近 CPU 的地方有三个通孔，有可能是 UART 串口，用导线连接之后，设置波特率为 115200。

![](/files/YoGOH1ze486DzTUlveES)

用 SecureCRT 连接串口，给机器通上电之后，串口立马输出了启动信息，并可以执行命令，说明串口正确，如果遇到无法输入的情况，首先检查接线是否松动，然后在 SecureCRT 中的， Session Options -> Connection -> Serial -> Flow Control，将原先选中的 RTS/CTS 取消掉，这是因为如果选中了 RTS/CTS ，则硬件上要有对应接口，软件上实现对应协议，才能实现此流控制。如果串口输出为乱码，则需要切换波特率，直至输出正常。

![](/files/PyQpgBWkNL6QKcq7KnC1)

#### 1.3 **调试某路由器**

在靠近cpu的地方有四个通孔，测量电势差后，利用导线探针，确定了三个 PIN，连接转换器。

![](/files/6obWQRcPlidPzZO65PTZ)

串口中输出调试信息，因波特率设置问题，初始输出为乱码，改为 38400 即可正常输出。

![](/files/r9N0UbWB4DNjLatjglvA)

#### 1.4 **调试某路由器**

在 PCB 上有四个焊点，先测量电势差，分出 GND 和VCC，在利用焊枪分别焊上导线，连接转换接口，测试出 TX 和 RX 口。

![](/files/jAlpfgONQfmDOkAm9T6o)

设置波特率为57600，串口输出正确，并可执行命令。

![](/files/ydc4oowDYDP67EIEgbV4)

### 二、**JTAG调试**

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 既支持在线调试，又能直接获取固件，对于芯片的调试和分析是非常具有帮助的。

![](/files/JtLLIcoPCOXJdRGlBgOg)

* **接口定义**

JTAG有 10pin 的、14pin 的和 20pin 的，尽管引脚数和引脚的排列顺序不同，但是其中有一些引脚是一样的，各个引脚的定义如下。

![h](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/872bc42054768e0f/5b3532c0a4b4a.png)

* **引脚定义**

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 的引脚名称与序号对应关系

![](/files/bDdPifbR0yOA8hOgK0fL)

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/872bc42054768e0f/5b352e6a43af4.png)

值得注意的是，不同的 IC 公司会自己定义自家产品专属的 Jtag 头，来下载或调试程序。 需要说明的是，上述 Jtag 头的管脚名称是对 IC 而言的。例如 TDI 脚，表示该脚应该与 IC 上的 TDI 脚相连，而不是表示数据从该脚进入 download cable。 实际上10针的只需要接4根线，4号是自连回路，不需要接，1，2接的都是1管脚，而8，10接的是 GND，也可以不接。

#### 2.1 JTAG调试

* **拆焊芯片**

首先用热风枪拆下智能锁主控芯片，该单片机型号为：Stm32F103R6。

![](/files/hKzUHgXyf8kT3i4ZehPm)

* **烧录座连接Jlink**

芯片第一脚对齐烧录座第一脚，然后把 Jlink 插入烧录座引出的 JTAG 接口。

![](/files/nmJNRMP0QXIvPv4PSSNU)

* **读取固件**

电脑上安装好 Jlink 驱动，打开 J-Flash 客户端，设置好参数，主要在配置栏选择正确的芯片型号，然后点击连接，在点击 Target->Read Back->Entire trip 即可读写固件。

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/872bc42054768e0f/5b3524ced11a4.jpg)

* **接口调试**

如果PCB上保留了厂商在研发过程中预留的 JTAG 接口，可直接通过飞线的方式，连上对应的引脚进行调试。

![](/files/uvAh0ApDJNKYsi37Z7dZ)

* **直连芯片 JTAG 引脚调试**

大部分厂商在生产环节会去掉外部引出的 JTAG 接口，因为多数量产芯片的封装格式，直接飞线难度较大，因此可以采用探针台直连芯片引脚进行调试。 在研究的某款智能锁，拆解发现采用的是 MSP432G2553 作为主控，下图红框位置。

![](/files/HjupI3yy29qIIoQjVdmf)

该款智能锁利用手机 app 产生开锁音频信号，进过外部 AD 转换后传输至芯片中进行解密开锁处理，厂商在生产过程中比较注重安全意识，PC 上的没有保留调试接口，进一步分析的话，需要对芯片进行固件提取和在线调试。

* **芯片分析**

查TI官方手册，MSP432G2553 芯片引脚定义如下，其支持四线 JTAG 和两线 SBW 的调试接口，随采用两线制 SBW 接口作为调试方式，其 16 引脚为 SBWTDIO 口，17脚为 SBWTCK 脚。

![](/files/qOpXKG25fq5tqvkel3OP)

#### **连接引脚**

两线制 SBW 对应 MSP430 仿真器上的14线排针接口，分别为 16 脚 SBWTDIO 口连仿真器第一脚 TDO，17脚 SBWTCK 连第7脚 TCK，最后需要连接 GND 脚，即芯片的第 20 脚连仿真器第9口，两线制 SBW 同时需要外部电源供电，仿真器接口定义如下图。

![](/files/daOcZWHVMI3piW9T1ets)

按照引脚说明，开始在探针台上连接引脚，需要注意 JTAG 和 SBW 调试，对连线的长度有严格要求，超过 20 厘米信号会大幅衰减，造成无法调试，因此在探针上利用夹子和铜导线缩小接线距离。

![](/files/3JVVngAOVmjJUjA5kK77)

* **在线调试**

连接上仿真器，启动 msp430-gdbproxy。 msp430-gdb 远程连接 target remote 192.168.1.196:2000

![](/files/r0eSXaYBiB7R57lehctW)

#### **固件提取**

仿真器环境配置好，然后打开Lite FET-Pro430，选择好芯片型号后，点击 read 即可读取固件，提取的固件分为 txt 和 hex 两种格式，也可以利用接口自己进行在线调试。

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/6d1723e1cd5f9f23/20180612190327.png)

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/6d1723e1cd5f9f23/20180615160857.png)

### 三、**远程调试**

在通过串口调试嵌入式设备时，每次需要通过接线和 USB 转换器连接才能进行，对设备操作的话，存在一定的不便，并且会占用电脑的 USB 口，接线也会造成一定的不稳定，因此可以通过串口命令开启 telnet 或者 ssh 服务，远程登陆设备。通过系统命令、程序的输出以及 gdb 进行 远程调试，提高调试的便捷性。

#### 3.1 **某路由器溢出漏洞调试**

基本函数结构分析结束后，使用 gdbserver 附加到 goahead 进程进行远程调试，验证我们的猜测是否正确。 telnet 连上路由器并使用 gdbserver 附加到 goahead 进程进行调试。

![](/files/PcaEojbZekIvHli732kF)

使用 ida 远程链接之后在 sub\_457ebc 入口出下断点，f9 键开始运行，brupsuit 发送 poc 之后断在了 sub\_457ebc 函数处。

![](/files/ZwIFchCDNR4BwmuMqmwW)

单步查看 websGetVar 的返回值 0x48d4a8 处的值正是我们发送的poc。

![](/files/DejVUfA7z6ybc3YPo90c)

运行至 bs\_setmanpwd 查看参数如下,a0 的值为格式化之后的 json 对象的指针，a1 的值为当前栈帧中一个长度为 400 字节的缓冲区的指针。

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/5b390ea6a2580.png)

继续运行，查看 nvram\_bufget 的返回值v0为我们传入的 routepwd 字段的值。

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/5b390eae51d41.png)

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/5b390eb587d58.png)

继续向下，fprintf 格式化字符串，a0 为当前栈帧上一个长度为 200 字节的字符串的地址。

![](/files/MTLkcy2MKQWT2I7G7Fjh)

可以看到此处并没有对 nvram\_bufget 获取到的值进行长度验证就格式化到缓冲区上造成了缓冲区溢出。

![](/files/CTK9tK3ke4S67W9LebKx)

继续向下可以看到 bl\_do\_system 把刚才格式化好的字符串当作指令执行了，因此此处对 “chpasswd admin %s” 进行截断之后可以追加任意命令且以管理员权限执行。

![](/files/E8A5JoFL9WDiL4wNcwXn)

继续向下执行到“lw $ra,0x2a0+ra($sp)”之后，ra的值被覆盖成 0x61616161 ，我们在此处劫持了函数的返回地址，经过计算此处距离缓冲区起始地址为 618 个字节，至此可以执行任意代码。

![](/files/y5Z2k7FI0Z9RhRXGYYUZ)

#### 3.2 **某摄像头远程调试**

在对某个摄像头进行漏洞分析时需要调试 recorder 进程。

```
[root@xxx /tmp]$ ftpget   192.168.1.111   gdbserver-7.7.1-armel-eabi5-v1-sysv
[root@xxx /tmp]$ chmod   777   gdbserver-7.7.1-armel-eabi5-v1-sysv
[root@xxx /tmp]$  ./gdbserver-7.7.1-armel-eabi5-v1-sysv  :9999  --attach  2394
Attached; pid = 2394
Listening on port 9999
```

直接附加上去之后，本地 arm-none-eabi-gdb 连接，下断点，提示:Cannot access memory at address 0xb695de04 发现内存地址不可读，查看发现已经变为僵尸进程，又新起了一个。

```
[root@xxx ~]$ ps   |  grep  re
    2 root       0:00 [kthreadd]
 2394 root       0:24 [recorder]
 2651 root       0:02 recorder
```

猜测应该有另外一个守护进程，附加的时候会停止程序，查看进程

```
494 root 0:01 /mvs/apps/as9ipcwatchdog /mnt/mtd
```

直接 kill 掉发现摄像头不能正常使用，看来这个进程还有一些其他的功能，不能直接 kill，只能去掉对目标进程的守护功能，载入 ida 字符串搜索 recorder 。

![](/files/8O8RA0KhlH6iWTFhCCR3)

直接替换掉 recorder 即可，例如替换为相同长度的 aaaaaaaa 。

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/5b388c79eea01.jpg)

为了方便，可以把原程序修改，然后重刷进去，具体可以参考后面 2.6 章节，这里不涉及到硬件修改，简单的把守护进程放到可写目录。 目前的调试流程为:

* 修改 as9ipcwatchdog，替换所有的 recorder
* 停止 as9ipcwatchdog，recorder 。
* 下载 as9ipcwatchdog 到tmp目录，启动 as9ipcwatchdog 。
* 启动 recorder
* APP 端删除当前的设备，重新搜索添加 。
* 断点调试

```
(gdb) target  remote 192.168.1.136:9999
Remote debugging using 192.168.1.136:9999
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0xb69d3e04 in ?? ()
(gdb) x  /5i  $pc
=> 0xb69d3e04:    mov    r7, r0
   0xb69d3e08:    mov    r0, r12
   0xb69d3e0c:    bl    0xb6a19b74
   0xb69d3e10:    mov    r0, r7
   0xb69d3e14:    pop    {r7, lr}
```

此时已经可以正常调试，同样以修改设备的时间来做测试，在 Linux 操作时间函数 stime 下断点 。

![](https://img-1253984064.cos.ap-guangzhou.myqcloud.com/5b388d5a744d5.jpg)

点击保存

![](/files/wI4vKI2AtzHbuCINU2EI)

```
(gdb) b *0x0000D3DC
Breakpoint 1 at 0xd3dc
(gdb) info  b
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x0000d3dc
(gdb) c
Continuing.
[New Thread 1925.2202]
[New Thread 1925.2201]
[Switching to Thread 1925.2202]

Thread 28 "" hit Breakpoint 1, 0x0000d3dc in ?? ()
```

成功断点，可以做一些后续的漏洞分析。

来源：[***物联网安全百科***](https://iot-security.wiki/)
