Linux-0.11 文件系统exec.c详解
Linux-0.11 文件系统exec.c详解
模块简介
该模块实现了二进制可执行文件和shell脚本文件的加载和执行。
函数详解
create_tables
static unsigned long * create_tables(char * p,int argc,int envc)
该函数的作用是建立参数和环境变量指针表。
create_table的作用就是建立指针表去指向copy_string拷贝的字符串。
unsigned long *argv,*envp;unsigned long * sp;sp = (unsigned long *) (0xfffffffc & (unsigned long) p);sp -= envc+1;envp = sp;sp -= argc+1;argv = sp;put_fs_long((unsigned long)envp,--sp);put_fs_long((unsigned long)argv,--sp);put_fs_long((unsigned long)argc,--sp);while (argc-->0) {put_fs_long((unsigned long) p,argv++);while (get_fs_byte(p++)) /* nothing */ ;}put_fs_long(0,argv);while (envc-->0) {put_fs_long((unsigned long) p,envp++);while (get_fs_byte(p++)) /* nothing */ ;}put_fs_long(0,envp);return sp;
count
static int count(char ** argv)
该函数用于计算参数的个数。
argv数组的最后一项是NULL,以此作为循环终止的条件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRmQGiRv-1685200650093)(null)]
int i=0;char ** tmp;if ((tmp = argv))while (get_fs_long((unsigned long *) (tmp++)))i++;return i;
copy_strings
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,unsigned long p, int from_kmem)
该函数的作用是从用户拷贝命令行参数和环境字符串拷贝到内核空间。
在do_execve函数中定义了一个page数组unsigned long page[MAX_ARG_PAGES]
,该数组定义在内核空间中,用于存储用户态传递过来的环境参数和命令行参数。初始时,程序定义了一个指向该空间末端(128k-4)处的空间偏移量p, 当随着环境参数和命令行参数的拷贝,p指针将向前进行移动, 如下图所示:
在函数的内部,考虑了参数不处于用户空间的场景,使用from_kmen,为了便于理解,可以默认from_kmen=0。
* from_kmem argv * argv *** 0 user space user space* 1 kernel space user space* 2 kernel space kernel space
该函数在开始处,定义了一些参数,并获取处ds寄存器和fs寄存器的值。
char *tmp, *pag=NULL;int len, offset = 0;unsigned long old_fs, new_fs;if (!p)return 0; /* bullet-proofing */new_fs = get_ds();old_fs = get_fs();if (from_kmem==2)set_fs(new_fs);
接下来,因此传入的参数的数量argc个,因此对其进行遍历,依次将其拷贝到page数组中。
while (argc-- > 0) {if (from_kmem == 1)set_fs(new_fs);if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))panic("argc is wrong");if (from_kmem == 1)set_fs(old_fs);len=0; /* remember zero-padding */do {len++;} while (get_fs_byte(tmp++));//将tmp指向字符串的末端if (p-len < 0) { /* this shouldn't happen - 128kB */set_fs(old_fs);return 0;}while (len) {//逐字节拷贝--p; --tmp; --len;if (--offset < 0) {offset = p % PAGE_SIZE;if (from_kmem==2)set_fs(old_fs);if (!(pag = (char *) page[p/PAGE_SIZE]) &&!(pag = (char *) (page[p/PAGE_SIZE] =get_free_page()))) return 0;if (from_kmem==2)set_fs(new_fs);}*(pag + offset) = get_fs_byte(tmp);}}if (from_kmem==2)set_fs(old_fs);return p;
change_ldt
static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
该函数的作用是修改LDT中段基址和段限长度, 同时将参数和环境空间放置在数据段的末端。
unsigned long code_limit,data_limit,code_base,data_base;int i;code_limit = text_size+PAGE_SIZE -1;code_limit &= 0xFFFFF000;data_limit = 0x4000000;code_base = get_base(current->ldt[1]);data_base = code_base;set_base(current->ldt[1],code_base);set_limit(current->ldt[1],code_limit);set_base(current->ldt[2],data_base);set_limit(current->ldt[2],data_limit);
/* make sure fs points to the NEW data segment */__asm__("pushl $0x17\n\tpop %%fs"::);data_base += data_limit;for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {data_base -= PAGE_SIZE;if (page[i])put_page(page[i],data_base);}return data_limit;
do_execve
int do_execve(unsigned long * eip,long tmp,char * filename,char ** argv, char ** envp)
该函数的作用是用于加载并执行其他的程序。通常会跟着fork之后调用。
第一部分是变量的定义,没啥好说的。
struct m_inode * inode;struct buffer_head * bh;struct exec ex;unsigned long page[MAX_ARG_PAGES];int i,argc,envc;int e_uid, e_gid;int retval;int sh_bang = 0;unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
下面这里是对变量的check。eip[1]
实际上就是源程序的cs寄存器值。对于用户态的程序, TI字段为1,RPL=3,代码段的序号为1,因此cs寄存器的值为0x000f,因此如果eip[1] & 0xffff
的值不是0x000f,也就意味着execve是从内核进程调用的,但是内核进程是不能被替换掉的,因此这里进行检查。
| 段描述符索引 |TI | RPL |
0x0017 = |0 0 0 0 0 0 0 0 0 0 0 1 0| 1 | 1 1 |
0x000f = |0 0 0 0 0 0 0 0 0 0 0 0 1| 1 | 1 1 |
if ((0xffff & eip[1]) != 0x000f)panic("execve called from supervisor mode");
接下来根据文件的路径获取i节点。
if (!(inode=namei(filename))) /* get executables inode */return -ENOENT;
然后统计参数和环境变量的个数。
argc = count(argv);
envc = count(envp);
如果inode不是常规文件,则返回错误。
restart_interp:if (!S_ISREG(inode->i_mode)) { /* must be regular file */retval = -EACCES;goto exec_error2;}
下面的代码用于判断进程是否有权限执行。
i = inode->i_mode;e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;if (current->euid == inode->i_uid)i >>= 6;else if (current->egid == inode->i_gid)i >>= 3;if (!(i & 1) &&!((inode->i_mode & 0111) && suser())) {retval = -ENOEXEC;goto exec_error2;}
执行到这里说明进程有权限运行程序,接着便是去磁盘中读取程序的第一个数据块。
if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {retval = -EACCES;goto exec_error2;
}
ex = *((struct exec *) bh->b_data); /* read exec-header */
下面这里便是判断文件是否是一个脚本文件。
if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {
此时bh块中的数据内容已经被拷贝到了ex结构体中,因此可以释放bh块。
接下来因为Linux-0.11只支持ZMAGIC格式可执行文件,因此如果格式不等于ZMAGIC将直接返回错误。
此外,如果可执行文件太大或者文件缺失不全,那么也不能运行。首先如果程序的代码段+数据段+bss段的长度超过了50M,则返回错误。 其次如果执行文件的长度小于(代码段+数据段+符号表长度+执行头)的长度,也会返回错误。
brelse(bh);
if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {retval = -ENOEXEC;goto exec_error2;
}
如果执行文件代码开始处没有位于1024字节边界处,则也不能执行。
if (N_TXTOFF(ex) != BLOCK_SIZE) {printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);retval = -ENOEXEC;goto exec_error2;
}
如果sh_bang标志没有设置,则复制指定个数的命令行参数和环境字符串到参数和环境空间。如果sh_bang为true,代表是运行的脚本程序,此时命令行参数和环境字符串已经进行了复制。
if (!sh_bang) {p = copy_strings(envc,envp,page,p,0);//拷贝环境字符串p = copy_strings(argc,argv,page,p,0);//拷贝命令行参数if (!p) {retval = -ENOMEM;goto exec_error2;}}
这里放回进程原来所需要执行的程序的i节点,并让进程executable指向新执行文件的i节点。然后复位原进程的所有信号处理句柄。再根据执行时关闭文件句柄close_on_exec位图标志,关闭指定的打开文件,并复位该标志。
if (current->executable)iput(current->executable);
current->executable = inode;
for (i=0 ; i<32 ; i++)current->sigaction[i].sa_handler = NULL;
for (i=0 ; i<NR_OPEN ; i++)if ((current->close_on_exec>>i)&1)sys_close(i);
current->close_on_exec = 0;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
if (last_task_used_math == current)last_task_used_math = NULL;
current->used_math = 0;
p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
p = (unsigned long) create_tables((char *)p,argc,envc);
接下来重新设置进程个字段的值。例如brk=a_text+a_data+a_bss
current->brk = ex.a_bss +(current->end_data = ex.a_data +(current->end_code = ex.a_text));
current->start_stack = p & 0xfffff000;
current->euid = e_uid;
current->egid = e_gid;
如果执行文件的代码段加上数据段长度不在页面的边界上,则将剩余部分置为0。
i = ex.a_text+ex.a_data;
while (i&0xfff)put_fs_byte(0,(char *) (i++));
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
return 0;
Q & A
相关文章:

Linux-0.11 文件系统exec.c详解
Linux-0.11 文件系统exec.c详解 模块简介 该模块实现了二进制可执行文件和shell脚本文件的加载和执行。 函数详解 create_tables static unsigned long * create_tables(char * p,int argc,int envc)该函数的作用是建立参数和环境变量指针表。 create_table的作用就是建立…...

NET框架程序设计-第1章.NET框架开发平台体系架构
1.1 .NET 框架基本组成 .NET 框架的核心便是通用语言运行时(Commomn Language Runtime,简称 CLR),CLR 是一个可被各种不同的编程语言所使用的运行时。 托管模块(mangaed module): 一个需要 CLR 才能执行的标准 Window…...

(哈希表 ) 349. 两个数组的交集 ——【Leetcode每日一题】
❓349. 两个数组的交集 难度:简单 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1: 输入:nums1 [1,2,2,1], nums2 [2,2] 输出:[…...
JavaScript基本语法(二)
JavaScript基本语法 1、变量1.1、简介1.2、变量命名规则1.3、JS的关键字和保留字1.4、声明提升 2、JavaScript数据类型2.1、基本类型2.2、引用类型2.3、两种类型的区别2.4、字符串常用方法 3、数据类型转换 1、变量 1.1、简介 在 JavaScript 中声明一个新变量的方法是使用关键…...

ChatGPT3.5-4资源汇总,直连无梯子
什么是ChatGPT? ChatGPT,全称:聊天生成预训练转换器(英语:Chat Generative Pre-trained Transformer),是OpenAI开发的人工智能聊天机器人程序,于2022年11月推出。该程序使用基于GPT-3.5、GPT-4…...

【Netty】使用 SSL/TLS 加密 Netty 程序(二十)
文章目录 前言一、SSL/TLS概述二、Sslhandler类 前言 回顾Netty系列文章: Netty 概述(一)Netty 架构设计(二)Netty Channel 概述(三)Netty ChannelHandler(四)ChannelP…...

runway gen2
来自Runway文生成视频ai大模型Gen-2_哔哩哔哩_bilibili来自Runway文生成视频ai大模型Gen-2,距离视频制作自由又近了一步。, 视频播放量 1651、弹幕量 0、点赞数 21、投硬币枚数 2、收藏人数 42、转发人数 22, 视频作者 旭升说, 作者简介 一起聊下互联网的那些事&…...

Day2:Windows网络编程-TCP
今天开始进入Windows网络编程的学习,在学习的时候总是陷入Windows复杂的参数,纠结于这些。从老师的讲解中,这些内容属于是定式,主要学习写的逻辑。给自己提个醒,要把精力放在正确的位置,不要无端耗费精力。…...
leetcode1985. 找出数组中的第 K 大整数
给你一个字符串数组 nums 和一个整数 k 。nums 中的每个字符串都表示一个不含前导零的整数。 返回 nums 中表示第 k 大整数的字符串。 注意:重复的数字在统计时会视为不同元素考虑。例如,如果 nums 是 ["1","2","2"]&am…...

基于深度学习的高精度野生动物检测识别系统(PyTorch+Pyside6+YOLOv5模型)
摘要:基于深度学习的高精度野生动物检测(水牛、犀牛、斑马和大象)识别系统可用于日常生活中或野外来检测与定位野生动物目标,利用深度学习算法可实现图片、视频、摄像头等方式的野生动物目标检测识别,另外支持结果可视…...

从零开始 Spring Boot 35:Lombok
从零开始 Spring Boot 35:Lombok 图源:简书 (jianshu.com) Lombok是一个java项目,旨在帮助开发者减少一些“模板代码”。其具体方式是在Java代码生成字节码(class文件)时,根据你添加的相关Lombok注解或类来…...
深入解析Spring源码系列:Day 6 - Spring MVC原理
深入解析Spring源码系列:Day 6 - Spring MVC原理 欢迎来到本系列的第六篇博客。在前几篇博客中,我们探索了Spring框架的核心概念,包括Bean的生命周期、作用域、AOP原理和事务管理。今天,我们将深入研究Spring框架中的MVC…...
Cmake中message函数 如何优雅地输出
message函数说明 在CMake中,message()函数用于向终端输出信息。 message([<mode>] "message text" ...)函数的<mode>参数可以是以下之一: (none): 等同于STATUS,但不推荐使用。STATUS: 输出的信息会被发送到CMake的…...
人工智能基础部分20-生成对抗网络(GAN)的实现应用
大家好,我是微学AI,今天给大家介绍一下人工智能基础部分20-生成对抗网络(GAN)的实现应用。生成对抗网络是一种由深度学习模型构成的神经网络系统,由一个生成器和一个判别器相互博弈来提升模型的能力。本文将从以下几个方面进行阐述࿱…...
JavaScript表单事件(上篇)
目录 一、input: 当表单元素的值发生改变时触发,适用于大多数表单元素。 二、change: 当表单元素的值发生改变且失去焦点时触发,适用于输入框、下拉列表等。 三、submit: 当表单提交时触发,适用于 form 元素。 四、reset: 当表单重置时触…...

vb6 Webview2微软Edge Chromium内核执行JS取网页数据测速
微软Edge Chromium内核执行JS获取网页数据测试 ExcuteScript eval(document.body.innerHTML) from : https://www.163.com 采集的网页HTM字符串占用字节空间1.2MB ExcuteScript回调事件中取得JS执行结果,用时 54 毫秒 其中JSON转字符13.5209毫秒 jSON数据长度: 增…...

编码,Part 1:ASCII、汉字及 Unicode 标准
个人博客 编码的历史由来就懒得介绍了,只需要知道人类处理文本信息是以字符为基本单位,而计算机在最底层只认识 0/1,所以当计算机要为人类存储/呈现字符时,就需要有一个规则,在字符和 0/1 序列之间建立映射关系&#…...
C++ Eigen库矩阵操作
C Eigen库 序号功能例子1赋值Eigen::MatrixXf mat (12,1); \\% mat << 1, 2, 3, 4,5,6,7,8,9,10,11,12;2Inplace操作 \\% resizemat.resize(4, 3); \\% 1 5 9 \\% 2 6 10 \\% 3 7 11 \\% 4 8 123转置 \\% transposeInPlacemat.transposeInPlace(); \\% 1 2 3 4 \\% 5…...

Linux-0.11 boot目录bootsect.s详解
Linux-0.11 boot目录bootsect.s详解 模块简介 bootsect.s是磁盘启动的引导程序,其概括起来就是代码的搬运工,将代码搬到合适的位置。下图是对搬运过程的概括,可以有个印象,后面将详细讲解。 bootsect.s主要做了如下的三件事: 搬…...

django组件552
前言:相信看到这篇文章的小伙伴都或多或少有一些编程基础,懂得一些linux的基本命令了吧,本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python:一种编程语言&…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...

leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...