介绍Linux内核函数Start z

本文详细介绍了Linux Kernel启动过程中的关键步骤,从打印版本信息到完成start_kernel()函数的执行,涉及体系结构设置、内存初始化、中断处理等多项核心任务。

Start_kernel()中调用了一系列初始化函数,以完成kernel本身的设置。这些动作有的是公共的,有的则是需要配置的才会执行的。

在start_kernel()函数中,

输出Linux版本信息(printk(linux_banner))

设置与体系结构相关的环境(setup_arch())

页表结构初始化(paging_init())

使用"arch/alpha/kernel/entry.S"中的入口点设置系统自陷入口(trap_init())

使用alpha_mv结构和entry.S入口初始化系统IRQ(init_IRQ())

核心进程调度器初始化(包括初始化几个缺省的Bottom-half,sched_init())

时间、定时器初始化(包括读取CMOS时钟、估测主频、初始化定时器中断等,time_init())

提取并分析核心启动参数(从环境变量中读取参数,设置相应标志位等待处理,(parse_options())

控制台初始化(为输出信息而先于PCI初始化,console_init())

剖析器数据结构初始化(prof_buffer和prof_len变量)

核心Cache初始化(描述Cache信息的Cache,kmem_cache_init())

延迟校准(获得时钟jiffies与CPU主频ticks的延迟,calibrate_delay())

内存初始化(设置内存上下界和页表项初始值,mem_init())

创建和设置内部及通用cache("slab_cache",kmem_cache_sizes_init())

创建uid taskcount SLAB cache("uid_cache",uidcache_init())

创建文件cache("files_cache",filescache_init())

创建目录cache("dentry_cache",dcache_init())

创建与虚存相关的cache("vm_area_struct","mm_struct",vma_init())

块设备读写缓冲区初始化(同时创建"buffer_head"cache用户加速访问,buffer_init())

创建页cache(内存页hash表初始化,page_cache_init())

创建信号队列cache("signal_queue",signals_init())

初始化内存inode表(inode_init())

创建内存文件描述符表("filp_cache",file_table_init())

检查体系结构漏洞(对于alpha,此函数为空,check_bugs())

SMP机器其余CPU(除当前引导CPU)初始化(对于没有配置SMP的内核,此函数为空,smp_init())

启动init过程(创建第一个核心线程,调用init()函数,原执行序列调用cpu_idle() 等待调度,init())

至此start_kernel()结束,基本的核心环境已经建立起来了。


<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<think>我们正在讨论Linux环境下程序执行与glibc的关系,以及如何劫持_start函数入口。根据引用内容,我们知道_start函数来自glibc,它负责调用__libc_start_main,进而初始化程序并调用main函数。用户的问题有两个部分:1.程序是否必须依赖glibc?2.如何劫持_start函数入口?首先,关于程序是否必须依赖glibc: -并不是所有Linux程序都必须依赖glibc。程序可以直接使用系统调用而不通过glibc,或者使用其他C库(如musl)。但是,大多数动态链接的程序都会依赖glibc,因为它是Linux上最常用的C库。 其次,关于劫持_start函数入口:-由于_start是程序的入口点(由ELF头中的e_entry指定),我们可以通过修改这个入口点来劫持控制流。但是,通常_start是由glibc提供的,所以劫持方法取决于我们如何链接程序。下面我将详细展开回答。注意:在回答中,我会使用LaTeX格式表示变量和代码片段,例如:`$e_entry$`。同时,我会在引用内容后添加引用标记[^1][^2][^3][^4]。</think>### Linux程序执行与glibc依赖分析 #### 🧩 Linux程序是否必须依赖glibc? **并非必须**,但**动态链接程序默认依赖**: ```mermaid graph TD A[Linux程序] --> B[静态链接程序] A --> C[动态链接程序] B -->|不依赖| D[独立二进制] C -->|依赖| E[glibc动态库] ``` 1. **静态链接程序** - 编译时添加 `-static` 选项:`gcc -static -o program program.c` - 所有库函数(包括`_start`)被直接嵌入可执行文件 - 完全独立运行,无需外部glibc支持 - 文件大小较大(包含完整库代码) 2. **动态链接程序** - 默认编译方式:`gcc -o program program.c` - 依赖系统中安装的glibc动态库(通常位于`/lib/x86_64-linux-gnu/libc.so.6`) - 启动流程: ```mermaid sequenceDiagram 内核->>+ld.so(动态链接器): 加载程序 ld.so->>+glibc _start: 跳转到入口点 glibc _start->>+__libc_start_main: 准备参数 __libc_start_main->>+用户main: 调用主函数 ``` 3. **替代方案** - **musl libc**:轻量级C库,可替代glibc - **直接系统调用**:完全绕过C库(需汇编实现) - **自定义运行时**:如Go语言程序的独立运行时环境 > **关键结论**:Linux程序**可以不依赖glibc**,但动态链接的C/C++程序默认依赖glibc提供的`_start`入口和运行时支持[^1][^2][^4]。 --- ### 🔐 劫持_start函数入口的技术 #### ⚙️ 劫持原理 利用ELF(可执行与可链接格式)的入口点机制: ```c // ELF头结构(部分) typedef struct { unsigned char e_ident[16]; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; // 程序入口点地址 ← 劫持目标 // ... } Elf64_Ehdr; ``` #### 🛠️ 劫持方法 1. **二进制修补(直接修改ELF)** ```bash # 1. 获取当前入口地址 readelf -h program | grep Entry # 2. 编写劫持函数(hook.S) .global _start _start: mov $0x1, %rax # sys_write mov $1, %rdi # stdout lea msg(%rip), %rsi mov $len, %rdx syscall jmp *orig_entry(%rip) # 跳转回原入口 orig_entry: .quad 0x401000 # 原始入口地址 msg: .asciz "Hooked _start!\n" len = . - msg # 3. 编译劫持代码 as hook.S -o hook.o # 4. 修改程序入口点 objcopy --add-section .hook=hook.o \ --set-section-flags .hook=alloc,code \ --change-section-address .hook=0x200000 \ program program_hooked # 5. 更新ELF头 objcopy --set-section-flags .hook=alloc,code,load \ --change-entry 0x200000 \ program_hooked ``` 2. **LD_PRELOAD劫持(仅动态链接程序)** ```c // hook.c #include <stdio.h> #include <dlfcn.h> void _start() { printf("Hooked by LD_PRELOAD!\n"); // 获取原始_start地址 void (*orig_start)() = dlsym(RTLD_NEXT, "_start"); // 调用原始_start orig_start(); } ``` 编译与使用: ```bash gcc -shared -fPIC hook.c -o hook.so -ldl LD_PRELOAD=./hook.so ./program ``` 3. **动态链接器劫持(高级)** - 修改`/etc/ld.so.preload`全局预加载 - 替换`ld-linux.so`动态链接器 - 内核模块拦截`execve`系统调用 #### 🛡️ 防护措施 1. **静态编译保护** ```bash gcc -static -Wl,-z,now -Wl,-z,relro -o program program.c ``` 2. **ELF完整性校验** ```bash # 检查入口点是否被修改 readelf -h program | grep 'Entry point address' # 验证数字签名 openssl dgst -sha256 -verify pubkey.pem -signature program.sig program ``` 3. **安全启动机制** ```bash # 启用内核安全特性 echo 1 > /proc/sys/kernel/kptr_restrict echo 1 > /proc/sys/kernel/dmesg_restrict ``` --- ### 💡 技术总结 | 特性 | 静态链接程序 | 动态链接程序 | |---------------------|-------------------------------|-------------------------------| | **glibc依赖** | ❌ 不依赖 | ✅ 依赖 | | **_start劫持难度** | ⭐⭐⭐⭐ (需二进制修补) | ⭐⭐ (LD_PRELOAD即可) | | **入口点保护** | ⭐⭐ (可修改ELF头) | ⭐⭐⭐ (多重防护机制) | | **典型应用场景** | 容器基础镜像/安全敏感程序 | 通用Linux应用程序 | > **关键结论**:劫持`_start`入口在技术上可行,但静态编译程序具有更强的抗劫持能力。动态链接程序可通过LD_PRELOAD实现轻量级劫持,但内核级防护可有效阻止恶意劫持[^1][^2][^3]。 --- ### 相关问题 1. 如何检测程序是否已被劫持`_start`入口点? 2. musl libc与glibc在程序启动流程上有何本质区别? 3. 除了`_start`,还有哪些关键函数可能在程序启动阶段被劫持? 4. 在容器环境中如何有效防止libc入口点被篡改? 5. 直接使用系统调用实现程序启动需要哪些关键步骤?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值