Linux如何启动一个进程(linux如何启动一个进程快捷键)
gudong366 2025-07-10 16:50 10 浏览
在本文中,您将了解当一个进程调用execve()时,Linux内核内部发生了什么,内核如何准备栈,并且控制权如何传递给用户空间进程进行执行。
概述
- 内核接收用户空间程序的SYS_execve()调用。
- 内核将可执行文件(特定部分)读入特定的内存位置。
- 内核准备栈、堆、信号等。
- 内核将执行权传递给用户空间程序。
检查二进制文件
我们从一个简单的Linux C程序开始:
int main(int argc, char *argv[0]) {
return 0;
}
使用gcc -static -o none none.c编译它,并找出一些细节:
$ readelf -h none
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4014f0
Start of program headers: 64 (bytes into file)
Start of section headers: 760112 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 10
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 29
第一条指令从0x4014f0的“入口点”开始。这些指令是由编译器(gcc、go等)生成的。它们根据编译器的不同而有所不同。
让我们将二进制文件加载到gdb中,并对0x4014f0处的指令进行反汇编。这些指令执行一些清理工作,但最终会调用main()函数(或Go语言的等效函数)。
让我们在入口点(0x4014f0)设置一个断点,并使用两个命令行选项(firstarg和secondarg)运行该应用程序:
gdb ./none
pwndbg> disass 0x4014f0
pwndbg> br *0x4014f0
pwndbg> r firstarg secondarg
0x4014f0 <_start> xor ebp, ebp
0x4014f2 <_start+2> mov r9, rdx
0x4014f5 <_start+5> pop rsi
[...]
──────────────────────[ STACK ]──────────────────────
00:0000│ rsp 0x7ffca4229540 — 0x3
01:0008│ 0x7ffca4229548 — 0x7ffca422a4b3 — '/sec/root/none'
02:0010│ 0x7ffca4229550 — 0x7ffca422a4c2 — 'firstarg'
03:0018│ 0x7ffca4229558 — 0x7ffca422a4cb — 'secondarg'
04:0020│ 0x7ffca4229560 — 0x0
05:0028│ 0x7ffca4229568 — 0x7ffca422a4d5 — 'BASH_ENV=/etc/shellrc'
[...]
(如果您使用的是没有pwngdb的gdb,则可能需要使用x/64a $rsp来列出堆栈的前64个条目。)
堆栈指针rsp位于0x7ffd4f48bd10。让我们使用grep -F '[stack]' /proc/$(pidof none)/maps找出堆栈的结束位置:
7ffd4f46c000-7ffd4f48d000 rw-p 00000000 00:00 0 [stack]
内核已将堆栈内存分配从0x7ffd4f46c000到0x7ffd4f48d000,总共132 KB。它将动态增长到8MB(ulimit -s kilobytes)。我们的程序(目前为止;参见rsp)只使用从rsp地址(0x7ffd4f48bd10)向下到堆栈的相同结束位置(0x7ffd4f48d000)的堆栈,共计4,848字节(echo $((0x7ffd4f48d000 - 0x7ffd4f48bd10)) == 4848)。
这是执行的“诞生”:内核将控制权交给了我们的程序。我们的程序即将执行它的第一条指令——迈出第一步(可以这么说)。
堆栈上现在包含了程序从内核获取的所有运行信息。它包含了参数列表、环境变量和许多其他有趣的信息。
让我们dump堆栈:
pwndbg> dump binary memory stack.dat $rsp 0x7ffd4f48d000
然后将其加载到hd <stack.dat或者xxd <stack.dat,并进行一次小小的预览...
03 00 00 00 00 00 00 00 b3 a4 22 a4 fc 7f 00 00 |..........".....|
c2 a4 22 a4 fc 7f 00 00 cb a4 22 a4 fc 7f 00 00 |..".......".....|
00 00 00 00 00 00 00 00 d5 a4 22 a4 fc 7f 00 00 |..........".....|
eb a4 22 a4 fc 7f 00 00 11 a5 22 a4 fc 7f 00 00 |..".......".....|
25 a5 22 a4 fc 7f 00 00 30 a5 22 a4 fc 7f 00 00 |%.".....0.".....|
[...]
b4 5c 18 e0 ed f9 fb 0d 30 78 38 36 5f 36 34 00 |.\......0x86_64.|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00 00 00 2f 73 65 63 2f 72 6f 6f 74 2f 6e 6f 6e |.../sec/root/non|
65 00 66 69 72 73 74 61 72 67 00 73 65 63 6f 6e |e.firstarg.secon|
64 61 72 67 00 42 41 53 48 5f 45 4e 56 3d 2f 65 |darg.BASH_ENV=/e|
74 63 2f 73 68 65 6c 6c 72 63 00 43 48 45 41 54 |tc/shellrc.CHEAT|
5f 43 4f 4e 46 49 47 5f 50 41 54 48 3d 2f 65 74 |_CONFIG_PATH=/et|
[...]
55 4d 4e 53 3d 31 31 38 00 2f 73 65 63 2f 72 6f |UMNS=118./sec/ro|
6f 74 2f 6e 6f 6e 65 00 00 00 00 00 00 00 00 00 |ot/none.........|
很多指针。很多字符串。很多未知的内容。
让我们跟踪从execve()到程序的入口点的调用。
execve()通过系统调用调用内核,然后调用do_execve()函数:
最终,这将进入do_execveat_common()函数。bprm结构被创建,并将关于程序的各种信息分配给它(参见binfmts.h)。
对于我们来说,程序的文件名、环境变量和选项(argv)从内核内存复制到进程的堆栈中。堆栈向较低的地址增长:堆栈上放置的第一项(bprm->filename)因此位于堆栈的最大地址(底部),在它的上方(较小的地址)是envp,然后是argv。
我们继续跟踪到bprm_execve()函数,在调用exec_binprm()之前完成一些检查。然后进入search_binary_handler()函数,内核检查二进制文件是否为ELF格式、是否为shebang(#!)或是否是通过binfmt-misc模块注册的其他类型。内核然后调用适当的函数来加载二进制文件。
在我们的情况下,它是一个ELF二进制文件,因此调用load_elf_binary()函数。内核创建内存,然后将二进制文件的节映射到内存中。它调用begin_new_exec()函数为新进程设置所有的凭据和权限。
然后,内核检查ELF二进制文件是否应该由解释器(ld.so)加载:
或者,对于像我们的静态二进制文件这样的情况,直接加载而无需解释器:
最后,调用create_elf_tables()函数。这是我们感兴趣的堆栈魔术发生的地方。
首先,函数arch_align_stack()向堆栈中添加随机数量的零(例如堆栈随机化),以使(某些)缓冲区溢出攻击的可靠性稍微降低。然后,它将堆栈对齐到16字节(例如,将堆栈指针设置为下一个较低的地址,该地址与16字节对齐,使用& ~0xf)。
然后,内核将"x86_64\0"放入堆栈中,然后在其上方添加16字节的随机数据(libc将其用作其伪随机数生成器的种子):
然后,内核创建ELF辅助表:这是一个包含(id,value)对的集合,描述正在运行的程序以及它所运行的环境的有用信息,从内核传递到用户空间。
列表以零标识符和零值(例如16字节的0x00)结束。列表中大约有20个条目(320字节)。
表以ARCH_DLINFO(它展开为AT_SYSINFO_EHDR + AT_MINSIGSTKSZ)开始。
这些“标识符”在auxvec.h中定义:
在gdb中,我们程序的堆栈上的ELF辅助表如下所示(注意:上面的“标识符”值是十进制的,但gdb显示为十六进制):
[... above is argc ...]
[... above is argvp ...]
[... above here is envp ...]
0x7ffca42296a8: 0x21 0x7ffca4351000 <-- AT_SYSINFO_EDHR
0x7ffca42296b8: 0x33 0xd30 <-- AT_MINSIGSTKSZ
0x7ffca42296c8: 0x10 0x178bfbff <-- AT_HWCAP
0x7ffca42296d8: 0x6 0x1000 <-- AT_PAGESZ
0x7ffca42296e8: 0x11 0x64
0x7ffca42296f8: 0x3 0x400040
0x7ffca4229708: 0x4 0x38
0x7ffca4229718: 0x5 0xa
0x7ffca4229728: 0x7 0x0
0x7ffca4229738: 0x8 0x0
0x7ffca4229748: 0x9 0x4014f0 <-- Our entry point
0x7ffca4229758: 0xb 0x0
0x7ffca4229768: 0xc 0x0
0x7ffca4229778: 0xd 0x0
0x7ffca4229788: 0xe 0x0
0x7ffca4229798: 0x17 0x0
0x7ffca42297a8: 0x19 0x7ffca42297f9 <-- Ptr to Random
0x7ffca42297b8: 0x1a 0x2
0x7ffca42297c8: 0x1f 0x7ffca422afe9 <-- Ptr to filename
0x7ffca42297d8: 0xf 0x7ffca4229809 <-- Ptr to x86_64
0x7ffca42297e8: 0x0 0x0 <-- NULL + NULL
[... ELF table stops here ...]
0x7ffca42297f8: 0xe8e8de3a49831f00 0xdfbf9ede0185cb4 <-- RND16
0x7ffca4229808: 0x34365f363878af 0x0 <-- "x86_64" + '\0'
[... below is empty space from stack randomization ...]
[... below are argv strings ...]
[... below are env strings ...]
[... last is the filename (/root/none) ...]
然后,内核分配堆栈内存来存储elf-aux表、argv和env指针以及argc值(+1),并将堆栈顶部对齐到16字节。(它尚未将elf-aux表复制到堆栈上。这将在稍后进行):
...然后将argc、argv指针和env指针放入堆栈中:
...然后将elf-aux表(即上述的elf_info)复制到堆栈上(对齐后,在env指针的下方)。
(聪明的读者可能会注意到,'RND16'并不以对齐的地址开始 - 0x7ffca42297f9:这是因为在调用STACK_ROUND()函数将elf-info表和env/argv指针放入堆栈之前,RND16已经被放入了堆栈中。)
现在回到load_elf_binary()函数中,内核设置寄存器,清除一些内容,最后 (!) 调用START_THREAD()函数来启动程序。
事后想法:有人指出
https://lwn.net/Articles/631631/。他们的ASCII艺术比我的好。它展示了在执行之前堆栈的布局(但是他们把它画反了;从顶部开始以最大地址,最底部是最小地址):
相关推荐
- U盘文件被删怎么简单恢复(u盘里的文件被误删了怎么找回)
-
现在这个社会不是靠关系靠路子,主要还是靠实力。刘强在机关工作,人长得帅气,工作能力又强。唯独一样不好,脾气太大,动不动就发火,因为小事常和同事发生口角。一次他火大的差点把办公桌给掀翻了,领导见他野蛮的...
- 不小心删除了一些文件?9 个最佳免费硬盘恢复软件
-
恢复您曾经无意或意外删除的所有文件和数据。您是否曾经错误地删除了一个对您的工作至关重要并导致您丢失所有进度的文件?我们为您提供了一些最好的免费硬盘恢复软件,以帮助您恢复意外删除的文件,以解决您的文件删...
- Studio 中文版:数据救援神器,误删 / 分区损坏 / RAID 恢复一键找回
-
Studio中文版:数据救援神器,误删/分区损坏/RAID恢复一键找回当文件意外删除、分区损坏,或RAID阵列崩溃时,一款可靠的数据恢复工具往往能挽回关键损失。R-Studio中文版...
- 你值得拥有的11款Linux数据恢复工具
-
如果你使用的是Linux操作系统,那么你一定想知道一旦硬盘崩溃的话又该如何保存和恢复数据。其实,现在有很多Linux数据恢复工具可以让我们摆脱数据安全的困扰。小编已经为各位准备好了一些最好的Linux...
- 误删文件内容怎么恢复(误删文件内容怎么恢复回来)
-
在日常使用电脑的过程中,误删文件的情况时有发生。无论是由于操作失误还是病毒攻击,误删文件都会给我们带来不小的困扰。幸运的是,随着技术的发展,误删文件恢复已不再是难题。本文将介绍几款国内外知名的误删...
- u盘如何恢复删除的文件?推荐5款u盘数据恢复软件!
-
在日常生活与工作中,U盘作为便捷的数据存储载体,频繁用于传输和保存各类重要文件。然而,误删文件的情况却时有发生,无论是珍贵的照片、重要的工作文档,还是精心制作的视频,一旦删除,都可能带来不小的麻烦。...
- 怎么恢复删除的数据?5种有效的数据恢复方法汇总!
-
在数字化办公与生活的时代,电脑里的每一份数据都承载着重要信息。然而,一个误操作就可能导致数据被删除,无论是尚未保存的重要文档,还是珍藏多年的照片,都可能瞬间“消失”。但其实,数据删除并不意味着永久丢...
- u盘删除文件怎么找回?5个数据恢复工具汇总,助你巧妙恢复数据!
-
在日常使用U盘的过程中,误删文件的情况时有发生,重要的工作文档、珍贵的照片视频一旦消失,难免让人焦急万分。别担心,只要选对数据恢复工具,被删除的数据仍有找回的可能。下面就为你汇总5款实用的数据...
- Linux下恢复误删文件:思路+实践(linux删除如何恢复)
-
周五篮球群里有人问误删文件了怎么恢复,得知是ext4文件系统之后我推荐了ext4magic这个工具,然后又有人提到了xfs的话怎么办,正好前几天看到DaveChinner在邮件列表里提到了这个问题,...
- 苹果放大招!不用虚拟机了,Mac直接跑Linux容器,开发者效率翻倍
-
苹果这次真给开发者送福利了!今天凌晨(6月10日),苹果在官宣的Containerization框架直接炸了技术圈——Mac现在能原生运行Linux容器镜像了!这可不是虚拟机那种“套娃”方案,而是基...
- 7 款老牌经典软件,值得收藏(经典老歌软件)
-
Calibrehttps://calibre-ebook.com/Calibre是一个电脑电子书管理软件。肯定有人说了,电子书还要管理?那当然了。它的功能更强大的让你想象不到,首先它可以导入PDF,...
- 神仙级的免费开源电子书阅读器,还支持听书功能
-
神仙级的免费开源电子书阅读器,还支持听书功能,极空间部署『KoodoReader』哈喽小伙伴们好,我是Stark-C~前段时间不是给大家分享的电子书管理工具『TaleBook』嘛~,然后就有粉丝私信...
- 如何在Ubuntu系统中重置root密码(ubuntu忘记密码重置root密码命令)
-
很多人有个问题,就是喜欢把密码设置得很长很复杂,结果谁也没防住,却成功防住了自己ヽ(.ˇдˇ;)ノ对于现代人,特别是年轻人,都有过忘记密码的经历吧。在这篇文章中,我们来了解如何在Ubuntu1...
- 5款功能强大的PDF阅读器,让PDF阅读更轻松
-
分享5款功能强大的PDF阅读器,拥有丰富的PDF阅读工具,支持PDF文档划线、笔记、标记等操作,让PDF阅读更轻松!1.嗨动PDF编辑器一款实用的PDF处理软件,不仅可以阅读PDF文档,还能直接编辑、...
- 上班摸鱼利器! 免费好用的电子书阅读器,NAS轻松部署Koodo Reader
-
哈喽,大家好我是生活爱好者。笔者也是一名小说爱好者,平时用手机用某信读书,会员也开了,在家看体验也不错,但是上班的时候,在工作快速完成之后,想摸个鱼用手机就不太方便啦,作为爱折腾的人,必须要工作认真,...
- 一周热门
- 最近发表
- 标签列表
-
- linux一键安装 (31)
- linux运行java (33)
- ln linux (27)
- linux 磁盘管理 (31)
- linux 内核升级 (30)
- linux 运行python (28)
- linux 备份文件 (30)
- linux 网络测试 (30)
- linux 网关配置 (31)
- linux jre (32)
- linux 杀毒软件 (32)
- linux语法 (33)
- linux博客 (33)
- linux 压缩目录 (37)
- linux 查看任务 (32)
- 制作linux启动u盘 (35)
- linux 查看存储 (29)
- linux乌班图 (31)
- linux挂载镜像 (31)
- linux 软件源 (28)
- linux题目 (30)
- linux 定时脚本 (30)
- linux 网站搭建 (28)
- linux 远程控制 (34)
- linux bind (31)