基于QEMU的NVRAM仿真

本文翻译自 http://www.devttys0.com/2012/03/emulating-nvram-in-qemu/

能够在Qemu中模拟嵌入式应用程序非常有用,但并非没有陷阱。我遇到的最常见的问题可能是的二进制程序试图从NVRAM中读取配置数据。由于二进制文件在Qemu中运行,而不是在目标设备上运行,因此显然没有要读取的NVRAM。

嵌入式应用程序通常通过共享库与NVRAM交互。该库又与包含设备当前配置设置的MTD分区接口交互。如果没有NVRAM配置数据,许多程序将无法正常运行,需要我们拦截NVRAM库调用并返回有效数据,以便在Qemu中正确执行应用程序。

这是从固件更新映像中提取的Web服务器,该二进制程序拒绝在Qemu下启动:
在这里插入图片描述

看起来httpd无法启动,因为它不知道要绑定到哪个IP地址。IP无法通过命令行参数设置,因此必须从其他位置获取此数据。让我们启动IDA并开始破解吧!
快速浏览httpd的主要功能可以发现罪魁祸首。httpd服务器尝试通过调用nvram_get来获取IP地址和协议设置。如果这些调用失败,则会打印出我们在上面看到的错误消息:
在这里插入图片描述

nvram_get函数是从共享库中导入的,使用LD_PRELOAD可以很容易地进行拦截:
在这里插入图片描述
但是,在我们开始拦截函数调用之前,我们需要了解有关nvram_get的更多信息。它似乎只接受一个参数,该参数是一个字符串(特别是上面的“lan_ipaddr”和“lan_proto”),但是它返回什么类型的数据?
从前面的反汇编中可以看出,nvram_get(“ lan_proto”)的返回值保存在寄存器R5中。后来,使用strcmp将R5指向的数据与字符串“ static”和“ dhcp”进行比较:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c5OdDIBi-1573560324787)(evernotecid://E268668F-4FC8-4A13-AD7F-97652A235300/appyinxiangcom/14020715/ENNote/p313?hash=84815cdc7fd861413de118137a01681f)]
同样,nvram(“ lan_ipaddr”)的返回值保存在寄存器R6中,该值稍后作为第一个参数传递给inet_aton:
在这里插入图片描述
因此,nvram_get的参数是键值对中的键字符串并返回相应的值字符串。我们可以使用以下代码轻松模拟此功能以及一些虚拟配置数据:

#include <stdio.h>
#include <string.h>

char *nvram_get(char *key)
{
        char *value = NULL;

        if(strcmp(key, "lan_ipaddr") == 0)
        {
                value = strdup("127.0.0.1");
        }

        if(strcmp(key, "lan_proto") == 0)
        {
                value = strdup("static");
        }

        printf("nvram_get(%s) == %s\n", key, value);
        return value;
}

我们需要将此代码作为共享库进行交叉编译,并将其复制到squashfs-root目录中,这个目录是我们运行qemu的目录:

eve@eve:~$ arm-linux-gcc -shared nvram.c -o nvram.so
eve@eve:~$ cp nvram.so squashfs-root/nvram.so

现在,我们将尝试再次在Qemu中运行httpd,这一次在LD_PRELOAD环境变量中指定nvram.so文件的路径:

eve@eve:~/squashfs-root$ sudo chroot . ./qemu-arm -E LD_PRELOAD="/nvram.so" usr/sbin/httpd 
usr/sbin/httpd: relocation error: /nvram.so: symbol __register_frame_info, version GLIBC_2.0 not defined in file libgcc_s.so.1 with link time reference

看起来nvram.so文件期望引用__register_frame_info函数,该函数符号在目标系统的libgcc_s.so库中不存在。发生这种情况是因为我们用来构建nvram.so的工具链与供应商用来为目标系统构建固件的工具链不同。我们希望__register_frame_info存在,而他们却没有。

由于供应商未为其系统发布GPL代码,因此我们不能简单地使用其工具链来重新构建nvram.so。我们可以向公司发起GPL请求,但是有一种更简单(更快!)的方法。我们只需要在nvram.c中为__register_frame_info符号添加占位符定义(函数声明):

#include <stdio.h>
#include <string.h>

void __register_frame_info(void) { }
void __deregister_frame_info(void) { }
void __unregister_frame_info(void) { }

char *nvram_get(char *key)
{
        char *value = NULL;

        if(strcmp(key, "lan_ipaddr") == 0)
        {
                value = strdup("127.0.0.1");
        }

        if(strcmp(key, "lan_proto") == 0)
        {
                value = strdup("static");
        }

        printf("nvram_get(%s) == %s\n", key, value);
        return value;
}

然后重新构建nvram.so,再次尝试运行httpd

eve@eve:~/squashfs-root$ sudo chroot . ./qemu-arm -E LD_PRELOAD="/nvram.so" usr/sbin/httpd 
httpd server started at port 80 (delay 0 second) 
nvram_get(lan_ipaddr) == 127.0.0.1
nvram_get(lan_proto) == static

我们已成功拦截nvram_get调用,httpd错误消息已消失,服务器似乎正在运行。让我们测试一哈:
在这里插入图片描述
成功!httpd现在可以与IDA的调试器进行一对一的交互了。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值