【物联网安全】走进MIPS的奇妙世界

随着物联网(IOT)日益增长,面向IOT硬件的新一波恶意软件如期而至,不过对于安全人员来说,这些恶意软件所面向的系统架构貌似有点生疏:MIPS架构。为了帮助读者熟悉这一架构,本文将详细介绍如何编译、分析和调试基于MIPS的二进制文件。

简介

MIPS是一种精简指令集计算机(RISC),问世于二十世纪八十年代初期,常用于家用网关路由器和交换机等设备。在本文中,我们将为读者详细演示如何在x86系统上编译、运行、分析和调试用C语言编写的MIPS示例程序。虽然这里使用的代码并非恶意软件,但本文中介绍的技术和方法同样适用于分析基于MIPS的二进制文件/恶意软件。

在x86系统上交叉编译MIPS ELF文件

这里,假设使用的虚拟机安装的是Kali Linux 2018系统。为了编译MIPS ELF,还需要安装一些依赖项,具体命令如下所示:

apt-get install linux-libc-dev-mips-cross libc6-mips-cross libc6-dev-mips-cross binutils-mips-linux-gnu gcc-mips-linux-gnu g++-mips-linux-gnu

接下来,我们将使用一些C示例代码来输出当前的PID和PPID,以及WHILE循环当前迭代到哪一步。同时,这个示例还会执行FORK命令,并再次输出当前进程的PID和PPID。读者将会注意到,在执行FORK命令后会出现两个输出:一个来自父进程,另一个来自子进程。 在MIPS中,调试FORK命令是非常重要的技巧,因为调试器在处理它们时比较费时(当然,CLONE也是如此)。

下面是待编译的示例代码。需要注意的是,在将其保存到文件中的时候,一定要使用诸如mips-test.c之类的文件名。

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(){

    printf("PID: %d\n",getpid());

    printf("PPID: %d\n",getppid());

    int i = 0;

    while(i &lt; 4){

        sleep(1);

        printf("ITERATION #%d\n",i);

        i = i + 1;

    }

    fork();

    for (int w = 0; w &lt;= 10; w=w+1){

        printf("%d: %d\t%d\n",getpid(),getppid());

        sleep(1);

    }

    printf("EXITING: %d\n",getpid());

    return 0;

}

利用下面的命令,我们就可以将上面的源代码交叉编译为MIPS-Little Endian代码:

    mipsel-linux-gnu-gcc -xc -static -o mips-test.elf mips-test.c

这样,我们会得到一个名为mips-test.elf的文件。为了运行该文件,需要执行CHMOD + X mips-test.elf命令。但是,如果试图运行该文件的话,系统会报错,并指出计算机不支持该体系结构。在下一节中,我们将介绍如何在x86机器上运行MIPS ELF程序。

在x86系统上运行MIPS ELF程序

首先,让我们再次安装一些依赖项:

    apt-get install qemu

    apt-get install bridge-utils

QEMU是一个仿真器,在它的帮助下,我们就可以在主机中运行不同体系结构的程序了。稍后,我们还会介绍如何使用bridge-utils来启用网络功能。

QEMU有两种模式:用户模式和完全系统仿真模式。

QEMU的用户模式

在用户模式下,我们可以通过命令行执行下列命令,以便在本地运行二进制文件(如我们刚刚编译的那个):

    qemu-mipsel [-strace] [-g 12345] mips-test.elf

要想按as-is方式来运行二进制代码的话,则需要忽略命令行中的-strace和-g 12345选项,只需使用qemu-mipsel mips-test.elf即可。

其中,选项-strace的作用是将二进制文件的系统调用输出到终端。这样做的好处是,可以帮助了解二进制代码正在从事哪些活动。

-g 12345选项的作用是让QEMU通过一个正在侦听12345端口的GDB Stub Loader来打开ELF。这样的话,系统会让它执行到入口点的时候暂停该程序,并等待GDB建立会话。 我们将在文章后面讨论如何附加到这个GDB Stub loader上。

QEMU的系统仿真模式

现在,请读者为接下来史诗般的冒险做好心理准备。首先,我们需要下载几个文件:

https://people.debian.org/~aurel32/qemu/

特别是对于本例,您将面临Debian的MIPS Little Endian风格和相应的内核映像的两项新挑战。

https://people.debian.org/~aurel32/qemu/mipsel/debian_wheezy_mipsel_standard.qcow2

https://people.debian.org/~aurel32/qemu/mipsel/vmlinux-3.2.0-4-4kc-malta

下载好这两个文件后,请在相同文件夹中创建一个shell脚本,并将其命名诸如start.sh之类的名称。该脚本的代码如下所示(请根据自己的情况修改代码底部的路径变量):

#!/bin/bash

qemu=qemu-system-mipsel

path="$HOME/Documents/Debian-MipsEL"

hda="$path/debian_wheezy_mipsel_standard.qcow2"

kernel="$path/vmlinux-3.2.0-4-4kc-malta"

iface=tap0

echo "Stopping eth0, starting tap0"

/etc/qemu-ifup tap0 || quit 1 "Failed to start tap0"

echo "Starting Debian MIPS"

$qemu -net nic -net tap,ifname=$iface,script=no,downscript=no \

-M malta -kernel $kernel -hda $hda -append "root=/dev/sda1 console=tty0" -nographic

QEMU会自动创建文件/etc/qemu-ifup,正常情况下它应该会为模拟的MIPS系统启用网络连接,但是,我在使用过程中遇到了很多问题,因此,我们将使用修改后的版本。之后,需要备份当前的qemu-ifup文件,因为我们将用下面的代码替换该文件的内容(警告:一定要执行IFCONFIG,并为脚本顶部的前3个变量指定合适的值,这些变量为:ETH0IPADDR、GATEWAY和BROADCAST):

#!/bin/bash

ETH0IPADDR=192.168.147.142

GATEWAY=192.168.147.1

BROADCAST=192.168.147.255

USER=root

# First take eth0 down, then bring it up with IP address 0.0.0.0

/sbin/ifconfig eth0 down

/sbin/ifconfig eth0 0.0.0.0 promisc up

# Bring up the tap device (name specified as first argument, by QEMU)

/usr/sbin/openvpn --mktun --dev $1 --user $USER

/sbin/ifconfig $1 0.0.0.0 promisc up

# Create the bridge between eth0 and the tap device

/sbin/brctl addbr br0

/sbin/brctl addif br0 eth0

/sbin/brctl addif br0 $1

# Only a single bridge so loops are not possible, turn off spanning tree protocol

/sbin/brctl stp br0 off

# Bring up the bridge with ETH0IPADDR and add the default route

/sbin/ifconfig br0 $ETH0IPADDR netmask 255.255.255.0 broadcast $BROADCAST

/sbin/route add default gw $GATEWAY

接下来,我们需要设法禁用这个桥接网络,为此,可以编辑/etc/qemu-ifdown文件,并用下面的代码替换原来内容:

#! /bin/sh

# Bring down eth0 and br0

/sbin/ifconfig eth0 down

/sbin/ifconfig br0 down

# Delete the bridge

/sbin/brctl delbr br0

# Bring up eth0 in "normal" mode

/sbin/ifconfig eth0 -promisc

/sbin/ifconfig eth0 up

# Delete the tap device

/usr/sbin/openvpn --rmtun --dev $1

现在,为了启动仿真的MIPS系统,只需运行start.sh脚本(不要忘记CHMOD + X)就行了。当然,启动过程需要一点时间,但如果一切正常,就会看到MIPS Debian仿真系统的登录提示,默认的用户名/密码为root/root:

实际上,我们的仿真系统的功能是非常齐全的,包括联网功能。如若不信的话,可以通过ping一下Google的DNS ping 8.8.8.8来进行验证。当然,我们希望能收到相应的TTL。

现在,要想从新创建的Debian MIPS实例中获取文件的话,python应该是最简单的方法。在Kali宿主虚拟机上(而不是在Debian MIPS机器上)切换到编译好的mips-test.elf文件所在目录。 然后,在终端中输入以下命令:

python -m SimpleHTTPServer

该命令将启动mips-test.elf所在文件夹中的HTTP服务器。现在,回到Debian MIPS机器,并输入以下命令:

wget http://[YOUR KALI ETH0 IP HERE]:8000/mips-test.elf

这样的话,就会把mips-test.elf文件下载到Debian MIPS系统中。只要借助LS命令,就可以找到这个文件了。然后,通过CHMOD + x mips-test.elf运行它。很好,这样一来我们只需传输相应的文件,就可以在仿真的Debian MIPS Little Endian系统上(而非在Windows/Linux/Mac机器上虚拟化的Kali Linux操作系统中)运行交叉编译的MIPS Little Endian二进制文件了。

为了恢复Kali机器的网络功能,请运行以下命令:

/etc/qemu-ifdown tap0

在x86机器上调试MIPS的ELF程序

现在,我们开始介绍如何进行调试MIPS的ELF程序。当前,我们的系统已经可以编译MIPS二进制文件,并使用QEMU用户模式来独立运行它们了,或者使用QEMU系统仿真模式在本地模拟的Debian MIPS系统中运行它们了。

在我们继续学习下文之前,不妨先来熟悉一下radare和gdb-multiarch这两个工具。其中,Gdb-Multiarch是一个支持多种体系结构的GDB调试器,而Radare则可对多体系结构的文件进行反汇编和调试。接下来,我们使用以下命令从GitHub中安装它们的最新版本(虽然Kali已经提供了Radare软件,但提供的版本通常较旧):

apt-get install gdb-multiarch

git clone https://github.com/radare/radare2

cd radare2/sys

chmod +x install.sh

./install.sh

安装Radare2和GDB-Multiarch后,就可以开始我们的调试之旅了。不过,在选择调试攻击组合时,我们有多种方案可选。当然,每种组合都有自己的优点和缺点,这就要求我们多多尝试,以便找到最适合自己的那种。接下来,将为读者演示启动各个组合所需的命令以及如何附加到所需调试器。

1.) QEMU 用户模式、GDB STUB与 GDB-MULTIARCH

为了启动这些工具,请从Kali终端中输入下列命令:

qemu-mipsel -g 12345 mips-test.elf

gdb-multiarch

target remote 127.0.0.1:12345

2.) QEMU用户模式、GDB STUB和RADARE2 GDB模式

为了启动这些工具,请从Kali终端中输入下列命令:

        qemu-mipsel -g 12345 mips-test.elf

        radare2 -a mips -b 32 -D gdb -d gdb://127.0.0.1:12345

        e dbg.follow.child=true

        e dbg.forks=true

3.) QEMU用户模式和RADARE2本地调试器

为了启动这些工具,请从Kali终端中输入下列命令:

        radare2 -a mips -b 32 -d dbg:///mips-test.elf

        e dbg.follow.child=true

        e dbg.forks=true

        aa

        s sym.main

        af

        VV

4.) QEMU用户模式和IDAPRO远程GDB

为了启动这些工具,请从Kali终端中输入下列命令:

        qemu-mipsel -g 12345 mips-test.elf

在运行IDAPRO的机器上运行:

        select the remoteGDB debugger from the drop down debugger menu

        specify the IP of your Kali machine and Port 12345

 

5.)QEMU系统仿真模式与GDB-MULTIARCH本地编译

为了启动这些工具,请从Kali终端中输入下列命令:

        ./start.sh

为了启动这些工具,请从Debian MIPS终端中输入下列命令:

        apt-get install gdb-multiarch

        gdb-multiarch mips-test.elf

6.)QEMU系统仿真与RADARE2本地编译

为了启动这些工具,请从Kali终端中输入下列命令:

        ./start.sh

从Debian MIPS终端中输入下列命令:

        wget https://radare.mikelloc.com/get/2.4.0/radare2_2.4.0_mipsel.deb

        dpkg -i radare2_2.4.0_mipsel.deb

        radare2 -a mips -b 32 -d dbg:///mips-test.elf

            e dbg.follow.child=true

            e dbg.forks=true

            aa

            s sym.main

            af

            VV

7.) QEMU系统仿真、GDBSERVER与远程GDB-MULTIARCH

为了启动这些工具,请从Kali终端中输入下列命令:

      ./start.sh

从Debian MIPS终端中输入下列命令:

        gdbserver 127.0.0.1:12345 mips-test.elf

从Kali终端中输入下列命令:

        gdb-multiarch

            target remote [Debian MIPS IP]:12345

8.) QEMU系统仿真、GDBSERVER与远程RADARE2 GDB模式

从Kali终端中输入下列命令:

        ./start.sh

从Debian MIPS终端中输入下列命令:

        gdbserver 127.0.0.1:12345 mips-test.elf

从Kali终端中输入下列命令:

        radare2 -a mips -b 32 -D gdb -d gdb:///[Debian MIPS IP]:12345

            e dbg.follow.child=true

            e dbg.forks=true

            aa

            s sym.main

            af

           VV

9.) QEMU系统仿真、GDBSERVER与IDAPRO远程附加GDB

从Kali终端中输入下列命令:

        ./start.sh

从Debian MIPS终端中输入下列命令:

        gdbserver 127.0.0.1:12345 mips-test.elf

在运行IDAPRO的机器上运行:

        select the remoteGDB debugger from the drop down debugger menu

        specify the IP of your Debian MIPS machine and Port 12345

技巧与陷阱

读者可以从上面的9个组合中找到适合自己的调试方式。下面我来讲一下自己在使用上述组合过程中的一些经验,以及发现的一些难点。

当使用QEMU用户模式运行MIPS二进制文件时,实际的进程空间是QEMU。如果直接附加到PID的话,将会进入QEMU进程空间,但是调试器却将其识别为x86指令集,而非MIPS。此外,在进程的内存空间中定位MIPS入口点以及在有效指令中暂停都是非常困难的:

  • 当涉及调试FORKS、CLONES和单步执行指令时,Radare2在处理MIPS架构方面并不给力。如果发出“e dbg.forks = true”命令,调用程序可以在调用FORK时正确暂停。但是,一旦您附加到子进程并单步执行指令时,就会失去对该程序的控制权。但是,它还会继续执行,就像收到了你让它继续运行的命令一样。我发现,要想暂停程序,必须在FORK后面手动放置一个断点。之后,才可以使用F7、ds等方法来遍历代码。
  • 为了让Radare2在Debian MIPS系统上取得最佳效果,打开文件的时候不要直接进入调试,而是先执行“radare2 -a mips -b 32 mips-test.elf”命令,然后进入命令行后再发出“doo”命令。因为这样的话,似乎比打开文件的同时直接进入调试模式能够更好地完成符号加载和代码分析。
  • IDAPRO远程GDB调试方法的局限性在于,无法指定GDB命令行选项,例如“set follow-fork-mode child”,因此也就无法调试子进程或线程。
  • 在仿真的Debian MIPS系统中本地使用radare2或gdb-multiarch通常是最有效的调试方法,因为不必处理在x86 QEMU用户模式进程中模拟的进程。然而,这种方法会非常慢,因为处理器的模拟主频被设为200MHz,并且只能打开一个终端会话(除非您设置了SSH)。

小结

祝贺您“顽强地”读完本文。虽然网上已经有了许多介绍MIPS仿真、调试和分析的文章,但是我们发现其中大部分都局限于某个方面。例如,某篇文章可能会展示如何编译MIPS,但却没有介绍如何运行它。或者有的文章虽然讲解了如何模拟MIPS,却没有介绍如何正确设置网络。所以,我们打算博采众长,将各个方面汇集在一起,以便于读者全面学习相关知识,最后,祝读者阅读愉快!

相关视频:https://www.youtube.com/watch?v=kQxH3OMIvd8。

原文链接:https://www.ringzerolabs.com/2018/03/the-wonderful-world-of-mips.html

 

 

 

Spread the word. Share this post!

Meet The Author

Leave Comment