电子科技大学卓中卓二轮——分析笔记

1. 子系统的关键工作原理

在Linux子系统(Subsystem for Linux, 简称WSL)中,API(应用程序编程接口)的转换和映射是一个关键过程,目的是让Windows应用程序能够与Linux环境中的系统调用无缝交互。WSL使用了名为User-Mode Linux (UML)的技术,以及 Wine(一个开源兼容Windows API的运行库)来实现在Windows上运行Linux应用程序。

1.1 API映射与转换

由于所有用户进程总的虚拟地址空间比可用的物理内存大很多,因此只有最常用的部分才与物理页帧关联。

1.1.1 API映射

首先用户进程在调用mmap系统调用之后,系统会为其在当前进程的虚拟地址空间中寻址一段连续的空闲地址(遍历vm_area_struct链表),并为其创建一个vm_area_struct结构,完成之后,进程就有了一个专门用于mmap映射的虚拟内存区。

由于这个区域的线性地址没有对应的物理页框,所以系统会调用内核空间的系统调用函数mmap,运用它对vm_area_struct结构中的虚拟地址建立其相应的页表项(将虚拟地址转换成物理地址)。

(mmap实现的源码,来自“映射-程序驱动源码.txt”)

在驱动程序中,mmap函数中会包含一系列的检查,以便对异常情况进行判断。

检查1:映射大小是否在预定义的MAPLEN(之前就定义的宏,用来表示在内核中申请的物理页框的大小)之内,如果超过,那就自然而然返回错误(毕竟太大了)

检查2:检查是否为共享映射(因为mmap只支持共享映射,如果是写权限,那么返回错误)。

检查3:如果区域是可写入的,设置VM_LOCKONFAULT标志,这样映射的区域在发生缺页异常时会锁定在物理内存中,防止被交换到磁盘。

检查4:检查偏移量是否为0,表明映射请求从文件或者设备的起始位置开始,有一套特定的函数用于处理对该映射区域的特殊操作。但是当偏移量不是0的时候,就会返回错误。也就是说,这个mmap映射方法仅允许从文件或设备的起始位置映射,不允许偏移映射。

1.2 二进制翻译与仿真

对于那些不能直接通过API映射运行的应用,模拟器或兼容层可能需要执行二进制代码的翻译,将源操作系统的机器指令转换为目标操作系统可理解的形式。

QEMU 软件虚拟化采用的思路是二进制指令翻译技术,这里 Target 表示我们要运行目标架构代码,而 Host 表示我们拥有的真实 CPU 架构,通常是 x86。QEMU 通过 TCG,提取 Target,将其翻译成 TCG 中间代码,最后再将中间代码翻译成 Host(搭载 QEMU 的真实物理机平台)架构指令。( Target指令 ----> TCG ----> Host指令)

1.2.1 QEMU Tcg 工作原理

整体流程

target instruction -> micro-op -> tcg-> host instruction

1.执行主函数(cpu_exec):主要负责中断异常处理、找到代码翻译块。

2.TCG在翻译过程中,会将翻译好的代码缓存起来。因此在翻译TB(翻译块)的过程中,首先会判断pc对应的TB是否存在缓存中。如果存在,则直接取出。如果不存在,则调用tb_find_slow()函数,进行翻译工作。

3.在调用tb_find_slow()函数的时候,如果发现没有可供翻译的代码,就使用tb_gen_code()完成代码的翻译工作。

使用 tb_alloc() 分配一个新的翻译块 tb,用于记录将要翻译的 pc 等信息。如果翻译块太多或者生成的代码太多,则清空翻译缓存 TBs。

使用 cpu_gen_code() 翻译 tb,并将翻译好的代码存在一个缓冲区中(gen_code_buf)。

在对一个翻译块进行二进制翻译时,TCG 需要维护一些信息完成动态翻译,即 TCGContext,TCG 上下文包含三类信息:内存池、标号和变量。

内存池:用于二进制转换期间的内存管理。TCG变量:临时、局部和全局变量。

将目标指令翻译成TCG中间码

gen_intermediate_code() 函数定义在不同架构的 target-xxx/translate.c 文件中。该函数将 target 指令翻译成中间码。操作码和操作数分开存储。操作码存放在 gen_opc_buf变量,操作数存放在 gen_opparam_buf变量。翻译过程就是不断向上述两个缓冲区填充操作码和操作数。

首先翻译步骤都在函数gen_intermediate_code_internal()中,将基本块(basic block, tb) 翻译成 TCG 中间代码。

然后就可以开始翻译

TCG中间码翻译成宿主机指令

tcg_gen_code() 函数定义在 tcg.c 文件中。该函数将中间代码翻译成 host code

将 TCG 中间码翻译成 host 机器码的函数是 tcg_gen_code_common 。

主要过程如下

  • 从缓存中找到 TCG 操作码和相应参数;
  • 为输入参数分配 host 平台的寄存器;
  • 为输出参数分配 host 平台的寄存器;
  • 输出翻译好的二进制指令到翻译缓存中。

1.3 兼容层与沙盒机制

兼容层:是一种软件技术,一个操作系统上能够运行原本为另一个操作系统设计的应用程序或系统调用,而不需要对这些程序进行重大修改。

为了确保应用的安全性,操作系统的兼容层会创建一个沙盒环境。其本质是为了实现不同应用程序之间的相互隔离,使其各自运行在自己的虚拟机进程中。

好处:

  • 进程隔离:沙盒机制会创建独立的进程来运行应用程序,每一个进程都有自己独立的内存空间和资源,这样就不会影响到其他进程。
  • 权限控制:沙盒机制通过权限控制来限制应用程序对系统资源的访问权限,这样可以防止数据破坏。
  • 资源限制:沙盒机制可以对其应用程序的某些资源进行限制,比如CPU利用率、内存使用情况等,这样可以让不同的应用程序都得到类似的资源,防止出现某个应用程序运行缓慢的情况。
  • 数据隔离:这也是保持其安全性最重要的一部分,将不同应用程序的数据存储在独立的文件系统中,与其他应用程序的数据隔离开来。

案例分析:windows内核级虚拟技术沙盒

1.文件重定向:

文件重定向是windows64位系统中存在的一种文件访问机制。在windows64位系统中,C盘下存在system32和syswow64两个文件夹,其中system32文件夹里面存放的是64位文件,而syswow64文件夹下存放的是32位文件。

因此,当32位的程序尝试访问system32文件夹的时候,重定向机制就上线了,将那个程序重定向导stswow64文件夹下。

2.Minifilter:

Minifilter是Windows操作系统中一种轻量级(仅关注关键的文件操作)的文件系统过滤驱动程序,用于监控、拦截和修改文件系统。

3.

2.Linux和Windows(R额actOS项目)在文件系统上的差异

2.1 API接口

  • Linux:Linux系统中,文件操作主要通过标准的POSIX API完成,包括open(), read(), write(), close()等系统调用,遵循UNIX的设计理念,强调简洁、模块化和通用性。POSIX API倾向于使用小而专注的函数,每个函数完成一个特定任务,如open()用于打开文件。这种风格鼓励组合基本操作以实现复杂功能。

上述功能函数就是由一个个小函数组装而成,且看起来很简洁,模块化。

  • Windows API(ReactOS):更侧重于面向对象的设计,单个函数往往提供更多的功能集成。例如,CreatFile()不仅用于创建或打开文件,还能够设置文件的访问模式、共享模式、安全属性等。这种设计便于开发直观、易于理解和维护的高级应用程序。

就比如ReatOS中的加载目录子项函数,源代码中涉及行数将近200行,上述只截取了部分内容。不具有POSIX API的简洁性和模块化。

2.2 目录结构

  • Linux(兼容性广):Linux内核通过VFS抽象层(虚拟文件系统),为上层应用提供统一的接口,使得应用无需关心底层文件系统的具体实现。VFS允许不同的文件系统共存,并通过一个通用的接口进行操作,提高了系统的灵活性和兼容性。

在(include/linux/fs.h)中会定义超级块(super_block)和索引节点(inode)。它们包含了数据的一些基本信息以及权限等。由于每个文件系统都有一个超级块实例,VFS就可以通过超级块来管理和追踪文件系统状态且所有文件操作都间接或直接通过inode进行。

在(include/linux/dcache.h)中定义目录项(dentry),用于加速路径名解析和缓存目录项信息,与inode相关联。

同时Linux也定义了一些列接口函数vfs_open()和vfs_read()等。

所以,当应用程序调用系统调用如open()read()时,VFS会根据调用参数找到相应的inode和dentry,然后调用该文件系统类型的特定操作函数。这样在不同的文件系统中,它们其实看到的都是相同的接口和行为。

  • ReactOS(针对性兼容):ReactOS作为Windows操作系统的开源替代,其重点在于实现与Windows的兼容性,因此它主要支持Windows系统中最常见的文件系统,包括FAT16、FAT32和NTFS。这些文件系统的支持确保了ReactOS能够运行大多数为Windows设计的应用程序和驱动程序,但这也意味着ReactOS在文件系统支持的广度上不及Linux。

2.3 权限管理

  • Linux:Linux使用传统的UNIX权限模型,包括用户ID、组ID以及读、写、执行权限位,同时也支持ACL(访问控制列表)和SELinux等更精细的权限控制机制,为系统安全提供了强大而灵活的基础。

在(fs/namei.c)和(include/linux/fs.h)等文件中定义了如何根据文件的i_mode字段(包含读、写、执行权限位)以及进程的有效用户ID和组ID来判断访问权限。

而Linux的ACL支持主要在(fs/acl.c)和(include/linux/fs_acl.h)等文件中实现。这些代码处理了如何解析和应用ACL规则,以及与基本权限模型的集成。

核心部分位于内核源码的(security/selinux)目录下的SELinux则通过安全上下文给文件、进程等对象添加额外的安全标签,并根据策略决定访问权限。

  • ReactOS:ReactOS遵循Windows的访问控制模型,使用ACL来管理文件和对象的访问权限,这与Windows的Security Descriptors和用户权限体系相一致,包括用户账户、组和权限分配,但可能缺乏Linux中一些高级安全功能如SELinux。

ReactOS采用Windows式的ACL模型,实现位于(reactos/subsys/win32/security)目录,特别是(acl.c)、(security.c)等文件。这些文件中包含了处理ACL的创建、解析、应用逻辑,以及与文件和对象权限检查相关的代码。ReactOS中的Security Descriptors用于描述对象的安全属性。

所以它们主要使用的模型存在差异:Linux基于经典的UNIX权限和扩展的ACL、SELinux,而ReactOS则采用了Windows的ACL和Security Descriptors模型。Linux更偏向于简洁和模块化的权限管理,而ReactOS更注重与Windows应用的兼容性和权限的细粒度控制。

2.4 生态环境

POSIX API广泛应用于类Unix系统,包括Linux、macOS等,促进了跨平台软件的开发。

Windows API则是Windows平台特有的,拥有庞大的开发者社区和丰富的库支持,特别适合开发与Windows桌面环境深度整合的应用。

3.机制及特点

上述代码用于模拟Window API中的CreatFile函数行为,但在Linux环境下工作。其目的是接收一个看起来像Window路径的字符串,并在Linux系统上以类似的方式打开文件。

1.包括必要的头文件,包括标准输入输出、文件控制、错误号和字符串操作。

2.定义两个常量,INVALID_HANDLE_VALUE 对应于Windows中无效的句柄值,通常为-1;

FILE_ACCESS_FLAGS宏用于根据读写需求设置Linux下的文件打开标志。

3.CreateFileCompt函数接收三个参数:一个是Windows路径的字符串winPath,一个是表示所需访问权限的desiredAccess,一个是未使用,但后续可能有作用的shareMode。

4.在函数内部,首先定义一个与winPath长度相同的字符数组linuxPath,用于存放转换后的Linux路径。

5.使用strcpy函数将输入的windows风格路径复制到新数组中。

6.通过遍历linuxPath中的每个字符,如果发现反斜杠 \ ,就将其替换为正斜杠 / ,这是Linux中的路径分隔符。

7.调用自定义的FILE_ACCESS_FLAGS宏,根据desiredAccess参数决定Linux的文件打开模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值