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

admin 2024年4月12日12:32:34评论4 views字数 8648阅读28分49秒阅读模式
作者:wh0am1i@知道创宇404实验室
时间:2024年4月11日
前面的文章中,我们已经成功编译并启动了 VxWorks,本文将重点介绍 VxWorks 的启动流程,并使用GDB进行调试以更深入地研究启动过程。

1 编译可调式的 VxWorks

参考资料

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

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

图 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 所示:

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

图 1-2 配置 VSB debug

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

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

图 1-3 新建 VIP

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

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

图 1-4 配置 Debug agent 图

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

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

图 1-5 配置 shell 图

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

2  使用 qemu 启动 VxWorks

参考资料

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

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 所示:

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

图 2-1 查看 qemu 版本

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

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

图 2-2 查找 VxWorks

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

qemu-img create file.img 512M

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

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

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

使用以下命令启动 VxWorks

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 所示:

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

图 2-4 启动 VxWorks

3  调试 VxWorks

参考资料

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

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 所示:

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

图 3-1 GDB 链接 qemu

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

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

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

图 3-2 sysInit

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

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

图 3-3 usrInit 函数

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

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 所示:

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

图 3-4 usrRoot 函数

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

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 所示:

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

图 3-5 VxWorks 启动流程图

4  内核应用程序

参考资料

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

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

  • 由 object module loader 下载并动态链接到操作系统。

  • 静态链接到操作系统,使其成为 kernel image 的一部分。

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

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

图 4-1 usrAppInit 函数

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

#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 所示:

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

图 4-2 helloWorld 函数运行成功

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

void test () {
char buf[8];
gets(buf);
}

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

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

图 4-3 溢出测试

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

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

图 4-4 GDB 查看溢出情况

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

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

图 4-5 溢出时栈中数据

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

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

图 4-6 未溢出的情况

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

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

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

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

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

图 4-8 VxWorks 保护机制

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

5 与 Linux 内存布局进行对比

参考资料

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

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

图 5-1 内存映射关系

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

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

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

图 5-2 Linux 虚拟内存布局

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

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

图 5-3 adrSpaceShow 命令使用

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

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

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

图 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 所示:

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

图 5-5 配置 MMU

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

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

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

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

  • vmContextShow 需要添加 INCLUDE_VM_SHOW 和INCLUDE_VM_SHOW_SHELL_CMD

  • rtpMemShow 需要添加 INCLUDE_MEM_EDR_RTP_SHOW 和 INCLUDE_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

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

作者名片

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

原创 Paper | VxWorks 启动流程及溢出测试分析
往 期 热 门
(点击图片跳转)

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

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

“阅读原文”更多精彩内容!

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月12日12:32:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 Paper | VxWorks 启动流程及溢出测试分析http://cn-sec.com/archives/2650618.html

发表评论

匿名网友 填写信息