前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原创 Paper | VxWorks 启动流程及溢出测试分析

原创 Paper | VxWorks 启动流程及溢出测试分析

作者头像
Seebug漏洞平台
发布2024-04-23 16:28:03
2040
发布2024-04-23 16:28:03
举报
文章被收录于专栏:Seebug漏洞平台Seebug漏洞平台

时间:2024年4月11日

前面的文章中,我们已经成功编译并启动了 VxWorks,本文将重点介绍 VxWorks 的启动流程,并使用GDB进行调试以更深入地研究启动过程。

1 编译可调式的 VxWorks

参考资料

首先新建 VSB 项目,配置如图 1-1 所示:

图 1-1 新建 BSP

下一步为 BSP 项目添加 DEBUG 配置选项,操作步骤如下:

  1. 在 Project Explorer 视图中展开 BSP 项目
  2. 右键单击Source Build Configuration ,然后选择 Edit Source Build Configuration
  3. 搜索 debug 关键字,选择 Global Debug Flagvalue 改为 y

最终效果如图1-2 所示:

图 1-2 配置 VSB debug

最后构建 VSB。构建完 VSB 之后创建 VIP,如图 1-3 所示:

图 1-3 新建 VIP

在 VIP 中需要配置 INCLUDE_DEBUG_AGENTINCLUDE_DEBUG_AGENT_START,可以搜索 DEBUG_AGENT 进行配置,配置完如图 1-4 所示:

图 1-4 配置 Debug agent 图

还需要添加 INCLUDE_SHELL INCLUDE_USB_INIT INCLUDE_USB_XHCI_HCD_INIT INCLUDE_USB_GEN2_STORAGE_INIT,部分配置项如图 1-5 所示:

图 1-5 配置 shell 图

确保以上配置处于加粗的状态,配置完成后构建 VxWorks 镜像。

2 使用 qemu 启动 VxWorks

参考资料

本次使用 qemu 6.0.1 进行启动,使用 qemu 源码编译安装,步骤如下:

代码语言:javascript
复制
wget https://download.qemu.org/qemu-6.0.1.tar.xz
tar -xvf qemu-6.0.1.tar.xz
cd qemu-6.0.1/
./configure
make 
make install

编译完成之后查看 qemu 版本,如图 2-1 所示:

图 2-1 查看 qemu 版本

进入 VIP/default 目录并找到编译完成的 VxWorks,如图 2-2 所示:

图 2-2 查找 VxWorks

接着使用 qemu-img 创建模拟存储设备,命令如下:

代码语言:javascript
复制
qemu-img create file.img 512M

将 VxWorks 与 file.img 放入同一文件夹内,如图 2-3 所示:

图 2-3 VxWorks 与 file.img 同一文件夹内

使用以下命令启动 VxWorks

代码语言:javascript
复制
qemu-system-x86_64 -machine q35 -m 2048 -smp 8 -serial stdio -kernel vxWorks -nographic -monitor none -device nec-usb-xhci,id=usb0,msi=off,msix=off -drive if=none,id=stick,file=file.img -device usb-storage,bus=usb0.0,drive=stick

启动成功如图 2-4 所示:

图 2-4 启动 VxWorks

3 调试 VxWorks

参考资料

接着使用 qemu 对 VxWorks 进行调试,启动命令如下:

代码语言:javascript
复制
qemu-system-x86_64 -machine q35 -m 2048 -smp 8 -serial stdio -kernel vxWorks -nographic -s -S -monitor none -device nec-usb-xhci,id=usb0,msi=off,msix=off -drive if=none,id=stick,file=file.img -device usb-storage,bus=usb0.0,drive=stick

使用 GDB 对 qemu 进行链接,如图 3-1 所示:

图 3-1 GDB 链接 qemu

首先,GDB在地址0x000000000000fff0处暂停,对应的源代码位置在<vsb_project>/krnl/configlette/dataSegPad.c文件中。dataSegPad的作用是确保内存管理单元(MMU)页大小边界对齐,在连接VxWorks时,它被明确指定为加载行上的第一个模块。这样做是为了确保数据段中的数据结构是数据段中的第一个项目,以避免数据段与由文本段占用的页面重叠。

一旦MMU初始化完成,VxWorks便开始其启动流程。第一个执行的函数是sysInit。

图 3-2 sysInit

sysInit函数是VxWorks的启动入口。它的主要功能包括禁用中断、设置堆栈,并调用usrInit()函数。初始堆栈被设置为从sysInit()的地址向下增长。这个堆栈仅被usrInit()函数使用,在此之后不再被使用。随后,程序进入usrInit函数,如图 3-3 所示:

图 3-3 usrInit 函数

通过进一步的调试,在 usrInit 函数中一共调用了以下函数:

代码语言:javascript
复制
sysStart (startType);               //清除 BSS 并设置向量表基地址。
cacheLibInit (USER_I_CACHE_MODE, USER_D_CACHE_MODE); // 初始化缓存。
gpDtbInit = dt_blob_start;          // 初始化DTB
usrFdtInit ((void*)DTB_RELOC_ADDR, (int)DTB_MAX_LEN); // 初始化 flat device tree 库
usrBoardLibInit();                  // 初始化板级子系统,提供 BSP 访问 API
usrAimCpuInit ();                   // 初始化 cpu
excVecInit ();                      // 初始化exception向量
vxCpuLibInit ();                    // 初始化 CPU 识别函数
usrCacheEnable ();                  // 缓存使能
objOwnershipInit ();                // 初始化 objOwnerLib 库,其中包含对象所有权函数。
objInfoInit ();                     // 初始化查找的对象功能
objLibInit ((OBJ_ALLOC_FUNC)FUNCPTR_OBJ_MEMALLOC_RTN, (OBJ_FREE_FUNC)FUNCPTR_OBJ_MEMFREE_RTN, OBJ_MEM_POOL_ID, OBJ_LIBRARY_OPTIONS); // 初始化 objLib 库,该库提供了 VxWorks 用户对象管理工具的接口。
vxMemProbeInit ();                  // 初始化 vxMemProbe() 异常处理
classListLibInit ();                // 初始化对象列表
semLibInit ();                      // 初始化信号量                                
condVarLibInit ();                  // 初始化condition variables库
classLibInit ();                    // 初始化 class 库
kernelBaseInit ();                  // 初始化内核对象
taskCreateHookInit ();              // 初始化 task hook 相关
sysDebugModeInit ();                // 设置 debug flag 让系统处于调试模式
umaskLibInit(UMASK_DEFAULT);        // 提供对内核环境中 POSIX 文件模式创建掩码的支持(支持 unmask())
usrKernelInit (VX_GLOBAL_NO_STACK_FILL); // 初始化内核

特别注意 sysInit 函数中的最后一个函数 usrKernelInit ,usrKernelInit 初始化并启动系统的第一个任务,随后进入 usrRoot ,如图 3-4 所示:

图 3-4 usrRoot 函数

usrRoot 函数是系统第一个任务的入口地址,主要是负责 post-kernel 的初始化,该函数存在大量的初始化函数,具体函数如下:

代码语言:javascript
复制
usrKernelCoreInit ();               // 初始化 Event 信号,消息队列,看门狗,hook dbg
poolLibInit ();                     // 初始化内存池,池中的块大小在池创建时指定的并每块大小一致
memInit (pMemPoolStart, memPoolSize, MEM_PART_DEFAULT_OPTIONS); // 初始化 memLib 库,该库主要是用于提供 RTP 堆分配内存块的 API
memPartLibInit (pMemPoolStart, memPoolSize); // 初始化 core 内存块
kProxHeapInit (pMemPoolStart, memPoolSize); // 初始化 kernel proximity heap,主要是核心附件的堆分配
pgPoolLibInit();                    // 初始化 Page Pool
pgPoolVirtLibInit();                // 初始化 Page Pool 虚拟空间
pgPoolPhysLibInit();                // 初始化 Page Pool 物理空间 
usrMmuInit ();                      // 根据 BSP 的 sysPhysMemDesc 表初始化全局的 MMU 映射
pmapInit();                         // 提供物理地址映射到 kernel/RTP 的功能
kCommonHeapInit (KERNEL_COMMON_HEAP_INIT_SIZE, KERNEL_COMMON_HEAP_INCR_SIZE); // 初始化内核堆,用于内核和内核应用程序的动态内存分配,使用 ANSI 标准的 malloc 、free 进行管理
usrKernelCreateInit ();             // 初始化 Object creation,比如:消息队列,看门狗,信号
usrNetApplUtilInit ();              // 初始化 application/stack logging
envLibInit (ENV_VAR_USE_HOOKS);     // 初始化 envLib,为了兼容 UNIX 环境变量,可以用过 putenv 创建修改环境变量
edrStubInit ();                     // 在 BOOT记录中记录 ED&R
usrSecHashInit ();                  // 初始化 secHash,如:MD5,SHA1,SHA256
usrDebugAgentBannerInit ();         // debug agnet banner
usrShellBannerInit ();              // shell baner
vxbDmaLibInit();                    // 初始化 VxBus DMA 子系统
vxbIsrHandlerInit (VXB_MAX_INTR_VEC, VXB_MAX_INTR_CHAIN); // 初始化 VxBus ISR handler 方法
vxbIntLibInit (VXB_MAX_INTR_DEFER); // 初始化 VxBus 中断
vxDyncIntLibInit();                 // 初始化支持消息中断的 VxBus 动态中断控制器
vxIpiLibInit ();                    // 初始化对称多处理 (SMP) 和非对称多处理 (AMP) 中断。
miiBusFdtLibInit();                 // 初始化 MII bus FDT 子系统
miiBusLibInit();                    // 初始化 MII bus 系统
vxbPciInit ();                      // 初始化 VxBus PCI 子系统库,该子系统库提供 PCI 主机控制器驱动程序
vxbPciMsiInit ();                   // 处理 PCI设备的 MSI 和 MSI-X 中断
vxbParamLibInit ();                 // 初始化driver parameter机制,driver parameter 的默认值可以被 BSP(DST) 覆盖
usrIaPciUtilsInit();                // 初始化 intel PCI
sysHwInit1 ();                      // 附加系统的初始化,如 PIC, IPI 向量
boardInit();                        // 板级初始化
kernelIdleTaskActivate();           // 添加对 Idle Tasks 的支持 (SMP Only)
usrIosCoreInit ();                  // 内核 I/O
usrNetworkInit0 ();                 // 初始化网络
vxbLibInit ();                      // 初始化VxBus子系统
intStartupUnlock ();                                // 打开中断
sysIntEnableFlagSet();                          // 标记中断使能
usrSerialInit ();                   // 设置标准输入、输出设备
usrClkInit ();                      // 初始化时钟
cpcInit ();                         // CPUs Cross-Processor Call (SMP Only)
vxdbgCpuLibInit ();                 // 初始化 VxDBG 对 CPU 的控制
pgMgrBaseLibInit();                 // 初始化 Basic Page Manager
usrKernelExtraInit ();              // 初始化内核其它机制,如:Signal、POSIX
usrIosExtraInit ();                 // 初始化IO系统其它机制,如:系统日志,标准 IO 库
usrHostnameSetup (TARGET_HOSTNAME_DEFAULT); // 设置 hostname 为 TARGET_HOSTNAME_DEFAULT,一般情况下为 target
sockLibInit ();                     // Socket 接口
selTaskDeleteHookAdd ();            // select机制的初始化
cpuPwrLightMgrInit ();cpuPwrMgrEnable (TRUE); // 空闲时 CPU 电源管理
cplusCtorsLink ();                  // 确保在内核启动时调用编译器生成的初始化函数,包括 C++ 静态对象的初始化函数。
usrSmpInit ();                      // 多处理器支持
miiBusMonitorTaskInit();            // MII 总线监控任务。
usrNetworkInit ();                  // 完成网络系统初始化
usrBanner ();                       // 启动时显示 Wind River banner
usrToolsInit ();                    // 软件开发工具,例如 target loader、符号表、debug库、kernel shell等
usrAppInit ();                      // 系统启动后调用项目文件中应用程序的初始化函数 usrAppInit(),用户程序入口

在 usrAppInit 函数中主要是用户自定义的程序,不做深入讨论。最后用图总结一下 VxWorks 的启动流程,如图 3-5 所示:

图 3-5 VxWorks 启动流程图

4 内核应用程序

参考资料

usrAppInit 函数会 VxWorks 启动后启动内核应用程序,那么怎么把程序添加到自启动函数中去呢?在此之前初步认识一下内核应用程序。

在 VxWorks 中内核应用程序在内核空间执行,这一点与 Unix Linux 不同,内核应用程序可以是:

  • 由 object module loader 下载并动态链接到操作系统。
  • 静态链接到操作系统,使其成为 kernel image 的一部分。

首先找到 usrAppInit.c 文件,在 c 文件中到 usrAppInit 函数,其函数内容如图 4-1 所示:

图 4-1 usrAppInit 函数

编写一个函数并使用 taskSpawn 启动,代码如下:

代码语言:javascript
复制
#include <taskLib.h>
#include <stdio.h>
#include <string.h>

void helloWorld () {
    printf("hello vxworks!");
}

void usrAppInit (void)
{
#ifdef  USER_APPL_INIT
    USER_APPL_INIT;     /* for backwards compatibility */
#endif
    /* TODO: add application specific code here */
    taskSpawn("hello", 100, 0, 8192, (FUNCPTR)helloWorld, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

编译镜像并启动 VxWorks,如图 4-2 所示:

图 4-2 helloWorld 函数运行成功

接着修改代码,代码如下所示:

代码语言:javascript
复制
void test () {
    char buf[8];
    gets(buf);
}

这是一个经典的栈溢出,通过 GDB 来看一下 VxWorks 中各个寄存器的情况,同样编译后启动 VxWorks。在 test 函数下断点,首先进行测试,如图 4-3 所示:

图 4-3 溢出测试

首先是溢出情况,如图 4-4 所示:

图 4-4 GDB 查看溢出情况

在这里可以看到返回地址已经被指向了未知的地址。再查看栈中的情况,如图 4-5 所示:

图 4-5 溢出时栈中数据

而在未溢出的情况下,会跳转到 shellInternalFunctionCall 函数,如图 4-6 所示:

图 4-6 未溢出的情况

未溢出时栈中数据,如图 4-7 所示:

图 4-7 未溢出时栈中数据

再来看看 VxWorks 的保护机制,如图 4-8 所示:

图 4-8 VxWorks 保护机制

VxWorks 并没有什么保护机制,因此在利用漏洞比较方便,可以直接执行 shellcode,同时由于 VxWorks 的特性在程序崩溃时就会重启,因此在利用时需要保证程序不会崩溃退出。

5 与 Linux 内存布局进行对比

参考资料

在 Linux 中操作系统将不同进程的虚拟地址和不同内存的物理地址映射起来,进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存。如图 5-1 所示:

图 5-1 内存映射关系

虚拟地址与物理地址的映射有分段、分页以及结合使用三种方式,在 Linux 中内存分页把虚拟空间和物理空间分成大小固定的页。

虚拟内存分为内核空间和用户空间,根据位数的不同,地址空间的范围也不同,32 位和 64 位范围如图 5-2 所示:

图 5-2 Linux 虚拟内存布局

在 VxWorks 中同样存在虚拟内存,同样使用 MMU 进行管理,但是在 VxWorks 中存在多个分区,可以使用 adrSpaceShow 命令展示当前内存使用情况,如图 5-3 所示:

图 5-3 adrSpaceShow 命令使用

对于32位与64位CPU,VxWorks7 所提供的内存管理机制是相同的,虚拟内存被分区管理,每个分区具有专门的用处和相应的分配机制。

VxWorks 虚拟内存大致结构如下,如图5-4 所示:

图 5-4 VxWorks 虚拟内存布局

  • Shared User Virtual Memory:共享用户虚拟内存区用于为共享映射分配虚拟内存,如共享数据区、共享库、使用mmap()MAP_SHARED选项进行内存映射
  • RTP Private Virtual Memory RTP私有虚拟内存区用于创建RTP的私有映射:代码与数据段、RTP堆空间以及使用mmap()MAP_PRIVATE选项进行内存映射,在系统中的所有RTP都可以访问整个RTP私有内存区。所以,RTP使用重叠地址空间管理
  • Kernel System Virtual Memory 内核系统虚拟内存区包含了内核系统内存。从中可以定位到内核镜像(text、data、bss)、内核临接堆(kernel proximity heap)
  • Kernel Virtual Memory Pool 内核虚拟内存池用于在内核中实现内存的动态管理。该区域用于按需分配虚拟内存,如创建和扩展内核应用程序、内存映射设备、DMA内存、用户保留内存和一致性内存等需求。

在此基础上还有一个 Global RAM Pool 用于动态分配RAM空间的内部分配,该内存池用于创建或扩充:内核通用堆(kernel common heap)、RTP私有内存与共享内存。全局RAM内存池也为如下对象提供内存:VxWorks内核镜像、用户保留内存、持久内存、DMA32堆空间等。

需要注意的是:VxWorks 字节顺序为 little-endian ,在网络程序中必须使用 htons() 将端口转换为网络字节顺序。

在 VxWorks 中可以针对处理器的MMU配置架构独立的接口,以提供虚拟内存支持。在 BSP 中搜索 MMU 相关的内容,如图 5-5 所示:

图 5-5 配置 MMU

可以通过 VM_PAGE_SIZE 虚拟内存默认分页大小,默认值是 0x1000 即 4KB,如图 5-6 所示:

图 5-6 配置虚拟内存分页大小

VxWorks 中可以通过 vmContextShow()rtpMemShow() 函数排查,对应的需要在 BSP 中添加

  • vmContextShow 需要添加 INCLUDE_VM_SHOWINCLUDE_VM_SHOW_SHELL_CMD
  • rtpMemShow 需要添加 INCLUDE_MEM_EDR_RTP_SHOWINCLUDE_MEM_EDR_RTP_SHOW_SHELL_CMD

6 总结

参考资料

本次主要是通过调试的方式来熟悉了一下 VxWorks 的启动流程,之所以这么做是为了加深印象,在 VIP 项目启动流程以源码的形式给出了,主要文件在 <VIP_Project>/orhConfig.csysLib.csysAlib.s 文件中。

在编译中的过程并不需要开启调试,VxWorks 的调试模式主要还是针对 WorkBench,本次实验用的版本为 2018 版的 VxWorks,对应的 WorkBranch 对 GDB调试的支持并不好。

VxWorks 作为业界领先的实时操作系统,还有许多内容值得我们学习。

另外一个注意点:WorkBench 在新版中对于GDB 的支持更完善了,并不需要使用这种方式进行调试。

7 参考链接

参考资学完了前面三个程序后,可以说已经入门了单片机开发,能进行以下几种基础操作:控制端口输出,编写中断函数,通过uart口输出调试信息。

[1]https://www.vxworks.net/app/907-vxworks-7-programmer-guide-memory-management

[2] https://mp.weixin.qq.com/s/SUhkdP9i7ie-ZESsCVRWmA

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Seebug漏洞平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档