chroot系统调用及示例

chroot函数详解

  1. 函数介绍

chroot函数是Linux系统中用于改变进程根目录的系统调用函数,它的名字来源于”change root”。可以把chroot想象成一个”虚拟监狱管理员”,它能够为进程创建一个隔离的文件系统环境,让进程认为某个指定的目录就是文件系统的根目录(/)。

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

chroot通过改变进程的根目录视图,创建了一个受限的执行环境。在这个环境中,进程无法访问指定根目录之外的文件系统,从而提供了一定程度的安全隔离。这就像给进程戴上了一副”有色眼镜”,让它只能看到特定范围内的文件系统。

重要说明: chroot本身不是安全边界,经验丰富的攻击者可能通过各种方式”跳出”chroot环境。

使用场景:

  • 系统维护和修复

  • 软件构建环境隔离

  • 测试环境搭建

  • 简单的沙箱环境

  • 系统恢复和救援

  • 旧版容器技术的基础

  1. 函数原型
1
2
3
4
#include <unistd.h>

int chroot(const char *path);

  1. 功能

chroot函数的主要功能是改变调用进程及其子进程的根目录。调用成功后,指定的目录将成为新的文件系统根目录(/),所有相对路径和绝对路径的解析都会基于这个新的根目录。

  1. 参数

path: 新的根目录路径

  • 类型:const char*

  • 含义:指向新根目录的路径字符串

  • 该路径必须是一个已存在的目录

  1. 返回值
  • 成功: 返回0

失败: 返回-1,并设置errno错误码

  • EACCES:权限不足(需要CAP_SYS_CHROOT能力)

  • EBUSY:当前目录是文件系统的根目录且忙

  • EFAULT:path指向无效内存

  • EIO:I/O错误

  • ELOOP:符号链接循环

  • ENAMETOOLONG:路径名过长

  • ENOENT:目录不存在

  • ENOTDIR:path不是目录

  • EPERM:操作不被允许

  1. 相似函数或关联函数
  • chdir(): 改变当前工作目录

  • pivot_root(): 更现代的根目录切换函数

  • mount(): 挂载文件系统

  • unshare(): 创建新的命名空间

  • clone(): 创建进程时指定命名空间

  • setuid()/setgid(): 改变用户/组ID

  • capset(): 设置进程能力

  1. 示例代码

示例1:基础chroot使用 - 简单环境切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>

// 创建chroot环境
int create_chroot_environment(const char* chroot_path) {
printf("创建chroot环境: %s\n", chroot_path);

// 创建根目录
if (mkdir(chroot_path, 0755) == -1 && errno != EEXIST) {
perror("创建根目录失败");
return -1;
}

// 创建基本目录结构
const char* dirs&#91;] = {
"bin", "lib", "lib64", "usr", "etc", "dev", "tmp", "proc"
};

for (int i = 0; i < 8; i++) {
char full_path&#91;256];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
perror("创建目录失败");
return -1;
}
}

// 创建基本设备文件(简化版本)
char dev_path&#91;256];
snprintf(dev_path, sizeof(dev_path), "%s/dev", chroot_path);

// 创建null设备节点
char null_path&#91;256];
snprintf(null_path, sizeof(null_path), "%s/dev/null", chroot_path);
if (mknod(null_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/null失败: %s\n", strerror(errno));
}

// 创建zero设备节点
char zero_path&#91;256];
snprintf(zero_path, sizeof(zero_path), "%s/dev/zero", chroot_path);
if (mknod(zero_path, S_IFCHR | 0666, makedev(1, 5)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/zero失败: %s\n", strerror(errno));
}

printf("chroot环境创建完成\n");
return 0;
}

// 显示当前目录结构
void show_directory_tree(const char* path, int depth) {
DIR* dir = opendir(path);
if (dir == NULL) {
printf("无法打开目录: %s\n", path);
return;
}

// 显示缩进
for (int i = 0; i < depth; i++) {
printf(" ");
}
printf("%s/\n", path);

struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

// 显示文件/目录
for (int i = 0; i < depth + 1; i++) {
printf(" ");
}
printf("%s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");
}

closedir(dir);
}

// 显示文件系统信息
void show_filesystem_info() {
char cwd&#91;1024];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("当前工作目录: %s\n", cwd);
}

// 显示根目录内容
printf("根目录内容:\n");
DIR* root_dir = opendir("/");
if (root_dir) {
struct dirent* entry;
int count = 0;
while ((entry = readdir(root_dir)) != NULL && count < 10) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" %s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");
count++;
}
}
if (count >= 10) {
printf(" ... (更多文件)\n");
}
closedir(root_dir);
}
}

int main() {
printf("=== 基础chroot使用示例 ===\n");

const char* chroot_dir = "/tmp/my_chroot";

// 检查是否具有root权限
if (geteuid() != 0) {
printf("警告: chroot需要root权限运行\n");
printf("请使用sudo运行此程序\n");
exit(EXIT_FAILURE);
}

// 创建chroot环境
if (create_chroot_environment(chroot_dir) == -1) {
exit(EXIT_FAILURE);
}

printf("\n1. chroot前的文件系统状态:\n");
show_filesystem_info();
show_directory_tree(chroot_dir, 0);

// 获取当前工作目录
char original_cwd&#91;1024];
if (getcwd(original_cwd, sizeof(original_cwd)) == NULL) {
perror("获取当前目录失败");
exit(EXIT_FAILURE);
}
printf("原始工作目录: %s\n", original_cwd);

// 执行chroot
printf("\n2. 执行chroot操作:\n");
printf("切换根目录到: %s\n", chroot_dir);

if (chroot(chroot_dir) == -1) {
perror("chroot失败");
exit(EXIT_FAILURE);
}

printf("✓ chroot操作成功\n");

// 改变工作目录到新的根目录
if (chdir("/") == -1) {
perror("改变工作目录失败");
exit(EXIT_FAILURE);
}

printf("\n3. chroot后的文件系统状态:\n");
show_filesystem_info();

// 创建一些测试文件
printf("\n4. 在chroot环境中创建文件:\n");
int fd = open("/test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, "这是chroot环境中的测试文件\n", 26);
close(fd);
printf("创建文件: /test_file.txt\n");
}

// 创建目录
if (mkdir("/mydir", 0755) == 0) {
printf("创建目录: /mydir\n");
}

// 显示chroot环境内容
show_directory_tree("/", 0);

// 尝试访问原始系统文件(应该失败)
printf("\n5. 尝试访问原始系统文件:\n");
if (access("/etc/passwd", F_OK) == -1) {
printf("✓ 无法访问原始系统文件 /etc/passwd (预期行为)\n");
} else {
printf("✗ 仍然可以访问原始系统文件\n");
}

// 清理测试文件
unlink("/test_file.txt");
rmdir("/mydir");

printf("\n=== 基础chroot演示完成 ===\n");
printf("注意: 此程序在chroot环境中结束\n");

return 0;
}

示例2:安全chroot实现 - 防止逃逸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <pwd.h>

// 安全chroot函数
int secure_chroot(const char* new_root) {
struct stat root_stat, cwd_stat;
char cwd&#91;4096];

printf("执行安全chroot到: %s\n", new_root);

// 1. 验证新根目录存在且是目录
if (stat(new_root, &root_stat) == -1) {
perror("无法访问根目录");
return -1;
}

if (!S_ISDIR(root_stat.st_mode)) {
fprintf(stderr, "指定路径不是目录\n");
return -1;
}

// 2. 验证新根目录权限
if (access(new_root, R_OK | X_OK) == -1) {
perror("根目录权限不足");
return -1;
}

// 3. 改变当前工作目录到根目录
if (chdir(new_root) == -1) {
perror("改变到根目录失败");
return -1;
}

// 4. 获取当前目录的inode信息
if (getcwd(cwd, sizeof(cwd)) == NULL) {
perror("获取当前目录失败");
return -1;
}

if (stat(".", &cwd_stat) == -1) {
perror("获取当前目录状态失败");
return -1;
}

// 5. 执行chroot
if (chroot(".") == -1) {
perror("chroot失败");
return -1;
}

// 6. 再次改变到根目录(防止某些逃逸技术)
if (chdir("/") == -1) {
perror("最终改变目录失败");
return -1;
}

printf("✓ 安全chroot完成\n");
return 0;
}

// 在chroot环境中运行的函数
void run_in_chroot() {
printf("\n=== 在chroot环境中运行 ===\n");

// 显示环境信息
printf("进程ID: %d\n", getpid());
printf("父进程ID: %d\n", getppid());

char cwd&#91;4096];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("当前工作目录: %s\n", cwd);
}

// 显示用户信息
printf("用户ID: %d\n", getuid());
printf("有效用户ID: %d\n", geteuid());
printf("组ID: %d\n", getgid());

// 显示根目录内容
printf("根目录内容:\n");
DIR* dir = opendir("/");
if (dir) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" %s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");
}
}
closedir(dir);
}

// 创建测试文件
int fd = open("/chroot_test.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* test_content = "Chroot环境测试文件\n创建时间: ";
write(fd, test_content, strlen(test_content));

// 添加时间戳
time_t now = time(NULL);
char time_str&#91;64];
snprintf(time_str, sizeof(time_str), "%s", ctime(&now));
// 移除换行符
char* newline = strchr(time_str, '\n');
if (newline) *newline = '\0';
write(fd, time_str, strlen(time_str));
write(fd, "\n", 1);
close(fd);
printf("创建测试文件: /chroot_test.txt\n");
}

// 显示测试文件内容
fd = open("/chroot_test.txt", O_RDONLY);
if (fd != -1) {
char buffer&#91;256];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf("测试文件内容:\n%s", buffer);
}
close(fd);
}

// 演示环境隔离
printf("\n环境隔离测试:\n");

// 尝试访问原始系统文件
const char* system_files&#91;] = {
"/etc/passwd",
"/etc/shadow",
"/proc/1/cmdline",
"/sys/kernel",
"/dev/sda"
};

for (int i = 0; i < 5; i++) {
if (access(system_files&#91;i], F_OK) == 0) {
printf(" 能够访问: %s\n", system_files&#91;i]);
} else {
printf(" 无法访问: %s (%s)\n", system_files&#91;i], strerror(errno));
}
}

// 清理测试文件
unlink("/chroot_test.txt");
}

// 演示chroot逃逸防护
void demonstrate_escape_protection() {
printf("\n=== chroot逃逸防护演示 ===\n");

// 这些是常见的chroot逃逸尝试
printf("尝试常见的逃逸方法:\n");

// 1. 尝试通过..访问上级目录
if (chdir("..") == 0) {
char cwd&#91;4096];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf(" cd .. 后的目录: %s\n", cwd);
}
// 回到根目录
chdir("/");
} else {
printf(" cd .. 失败 (预期行为)\n");
}

// 2. 尝试通过绝对路径访问
if (access("/etc/passwd", F_OK) == 0) {
printf(" 能够访问 /etc/passwd (可能存在问题)\n");
} else {
printf(" 无法访问 /etc/passwd (正常隔离)\n");
}

// 3. 尝试创建符号链接到外部
if (symlink("/etc/passwd", "/passwd_link") == 0) {
printf(" 创建符号链接成功\n");
// 测试符号链接是否有效
if (access("/passwd_link", F_OK) == 0) {
printf(" 符号链接指向有效文件\n");
} else {
printf(" 符号链接无效或被隔离\n");
}
unlink("/passwd_link");
} else {
printf(" 无法创建符号链接 (正常)\n");
}
}

int main() {
printf("=== 安全chroot实现示例 ===\n");

// 检查权限
if (geteuid() != 0) {
printf("错误: 此程序需要root权限运行\n");
exit(EXIT_FAILURE);
}

const char* chroot_path = "/tmp/secure_chroot";

// 创建安全的chroot环境
printf("1. 创建安全chroot环境:\n");

// 创建基本目录结构
const char* dirs&#91;] = {"bin", "etc", "dev", "tmp", "usr", "lib", "lib64"};
if (mkdir(chroot_path, 0755) == -1 && errno != EEXIST) {
perror("创建根目录失败");
exit(EXIT_FAILURE);
}

for (int i = 0; i < 7; i++) {
char full_path&#91;256];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
perror("创建目录失败");
exit(EXIT_FAILURE);
}
}

// 创建基本设备文件
char null_path&#91;256];
snprintf(null_path, sizeof(null_path), "%s/dev/null", chroot_path);
if (mknod(null_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/null失败\n");
}

printf("chroot环境创建完成: %s\n", chroot_path);

// 执行安全chroot
printf("\n2. 执行安全chroot:\n");
if (secure_chroot(chroot_path) == -1) {
exit(EXIT_FAILURE);
}

// 在chroot环境中运行
run_in_chroot();

// 演示逃逸防护
demonstrate_escape_protection();

// 显示最终状态
printf("\n=== chroot环境演示完成 ===\n");
printf("当前仍在chroot环境中\n");

return 0;
}

示例3:chroot环境构建与程序执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/wait.h>

// 复制文件到chroot环境
int copy_file_to_chroot(const char* src, const char* dst_chroot, const char* dst_path) {
char full_dst_path&#91;512];
snprintf(full_dst_path, sizeof(full_dst_path), "%s%s", dst_chroot, dst_path);

// 确保目标目录存在
char* last_slash = strrchr(full_dst_path, '/');
if (last_slash) {
*last_slash = '\0';
// 创建目录(简化实现)
mkdir(full_dst_path, 0755);
*last_slash = '/';
}

// 打开源文件
int src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
printf("警告: 无法打开源文件 %s: %s\n", src, strerror(errno));
return -1;
}

// 创建目标文件
int dst_fd = open(full_dst_path, O_CREAT | O_WRONLY | O_TRUNC, 0755);
if (dst_fd == -1) {
printf("警告: 无法创建目标文件 %s: %s\n", full_dst_path, strerror(errno));
close(src_fd);
return -1;
}

// 复制文件内容
char buffer&#91;8192];
ssize_t bytes_read, bytes_written;

while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
perror("写入目标文件失败");
close(src_fd);
close(dst_fd);
return -1;
}
}

close(src_fd);
close(dst_fd);

printf("复制文件: %s -> %s\n", src, full_dst_path);
return 0;
}

// 构建基本的chroot环境
int build_basic_chroot(const char* chroot_path) {
printf("构建基本chroot环境: %s\n", chroot_path);

// 创建目录结构
const char* dirs&#91;] = {
"", "bin", "sbin", "etc", "dev", "usr", "usr/bin",
"usr/sbin", "lib", "lib64", "tmp", "var", "var/tmp"
};

for (int i = 0; i < 13; i++) {
char full_path&#91;256];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
if (errno != EEXIST) {
printf("警告: 创建目录失败 %s: %s\n", full_path, strerror(errno));
}
}
}

// 创建基本设备文件
char dev_path&#91;256];
snprintf(dev_path, sizeof(dev_path), "%s/dev/null", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/null失败\n");
}

snprintf(dev_path, sizeof(dev_path), "%s/dev/zero", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 5)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/zero失败\n");
}

snprintf(dev_path, sizeof(dev_path), "%s/dev/random", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 8)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/random失败\n");
}

snprintf(dev_path, sizeof(dev_path), "%s/dev/urandom", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 9)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/urandom失败\n");
}

// 复制基本命令(根据系统实际情况调整)
printf("复制基本命令...\n");

// 复制shell
copy_file_to_chroot("/bin/sh", chroot_path, "/bin/sh");

// 复制基本命令
const char* basic_commands&#91;] = {
"/bin/ls", "/bin/cat", "/bin/echo", "/bin/pwd",
"/usr/bin/id", "/bin/ps"
};

for (int i = 0; i < 6; i++) {
copy_file_to_chroot(basic_commands&#91;i], chroot_path, basic_commands&#91;i]);
}

// 创建基本配置文件
char etc_passwd&#91;256];
snprintf(etc_passwd, sizeof(etc_passwd), "%s/etc/passwd", chroot_path);
int fd = open(etc_passwd, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* passwd_content =
"root:x:0:0:root:/root:/bin/sh\n"
"nobody:x:65534:65534:nobody:/:/bin/sh\n";
write(fd, passwd_content, strlen(passwd_content));
close(fd);
printf("创建 /etc/passwd\n");
}

char etc_group&#91;256];
snprintf(etc_group, sizeof(etc_group), "%s/etc/group", chroot_path);
fd = open(etc_group, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* group_content =
"root:x:0:\n"
"nobody:x:65534:\n";
write(fd, group_content, strlen(group_content));
close(fd);
printf("创建 /etc/group\n");
}

printf("基本chroot环境构建完成\n");
return 0;
}

// 在chroot环境中执行命令
int execute_in_chroot(const char* chroot_path, const char* command) {
pid_t pid = fork();

if (pid == -1) {
perror("fork失败");
return -1;
}

if (pid == 0) {
// 子进程
// 执行chroot
if (chroot(chroot_path) == -1) {
perror("chroot失败");
exit(EXIT_FAILURE);
}

// 改变到根目录
if (chdir("/") == -1) {
perror("chdir失败");
exit(EXIT_FAILURE);
}

// 执行命令
execl("/bin/sh", "sh", "-c", command, (char*)NULL);
perror("执行命令失败");
exit(EXIT_FAILURE);
} else {
// 父进程等待子进程结束
int status;
waitpid(pid, &status, 0);

if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("命令执行完成,退出码: %d\n", exit_code);
return exit_code;
} else if (WIFSIGNALED(status)) {
int signal = WTERMSIG(status);
printf("命令被信号终止: %d\n", signal);
return -1;
}
}

return 0;
}

// 显示chroot环境内容
void show_chroot_contents(const char* chroot_path) {
printf("\nchroot环境内容:\n");

DIR* dir = opendir(chroot_path);
if (dir) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" /%s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");

// 显示子目录内容(仅一层)
if (entry->d_type == DT_DIR) {
char sub_path&#91;512];
snprintf(sub_path, sizeof(sub_path), "%s/%s", chroot_path, entry->d_name);
DIR* sub_dir = opendir(sub_path);
if (sub_dir) {
struct dirent* sub_entry;
int count = 0;
while ((sub_entry = readdir(sub_dir)) != NULL && count < 5) {
if (strcmp(sub_entry->d_name, ".") != 0 &&
strcmp(sub_entry->d_name, "..") != 0) {
printf(" %s%s\n", sub_entry->d_name,
sub_entry->d_type == DT_DIR ? "/" : "");
count++;
}
}
if (count >= 5) {
printf(" ...\n");
}
closedir(sub_dir);
}
}
}
}
closedir(dir);
}
}

int main() {
printf("=== chroot环境构建与程序执行示例 ===\n");

if (geteuid() != 0) {
printf("错误: 此程序需要root权限运行\n");
exit(EXIT_FAILURE);
}

const char* chroot_path = "/tmp/full_chroot";

// 构建chroot环境
printf("1. 构建完整的chroot环境:\n");
if (build_basic_chroot(chroot_path) == -1) {
exit(EXIT_FAILURE);
}

show_chroot_contents(chroot_path);

// 在chroot环境中执行命令
printf("\n2. 在chroot环境中执行命令:\n");

// 执行基本命令
const char* commands&#91;] = {
"echo 'Hello from chroot!'",
"ls -la /",
"pwd",
"id",
"cat /etc/passwd"
};

for (int i = 0; i < 5; i++) {
printf("\n执行命令: %s\n", commands&#91;i]);
printf("--- 输出开始 ---\n");
execute_in_chroot(chroot_path, commands&#91;i]);
printf("--- 输出结束 ---\n");
}

// 创建和运行简单脚本
printf("\n3. 创建和运行脚本:\n");

// 创建脚本文件
char script_path&#91;256];
snprintf(script_path, sizeof(script_path), "%s/test_script.sh", chroot_path);
int fd = open(script_path, O_CREAT | O_WRONLY | O_TRUNC, 0755);
if (fd != -1) {
const char* script_content =
"#!/bin/sh\n"
"echo '=== 测试脚本开始 ==='\n"
"echo '当前时间:' $(date)\n"
"echo '当前用户:' $(id)\n"
"echo '当前目录:' $(pwd)\n"
"ls -la /\n"
"echo '=== 测试脚本结束 ==='\n";
write(fd, script_content, strlen(script_content));
close(fd);
printf("创建测试脚本: /test_script.sh\n");
}

// 执行脚本
printf("执行测试脚本:\n");
printf("--- 脚本输出开始 ---\n");
execute_in_chroot(chroot_path, "/test_script.sh");
printf("--- 脚本输出结束 ---\n");

// 演示安全性
printf("\n4. 安全性演示:\n");

// 尝试访问宿主系统文件
printf("尝试访问宿主系统文件:\n");
const char* dangerous_commands&#91;] = {
"ls -la /etc",
"cat /etc/shadow 2>/dev/null || echo '无法访问/etc/shadow'",
"ls -la /root 2>/dev/null || echo '无法访问/root'",
"find /proc -maxdepth 2 2>/dev/null | head -5"
};

for (int i = 0; i < 4; i++) {
printf("\n执行安全测试命令: %s\n", dangerous_commands&#91;i]);
printf("--- 输出开始 ---\n");
execute_in_chroot(chroot_path, dangerous_commands&#91;i]);
printf("--- 输出结束 ---\n");
}

// 清理测试脚本
unlink(script_path);

printf("\n=== chroot环境演示完成 ===\n");
printf("环境路径: %s\n", chroot_path);
printf("注意: 环境文件仍保留在系统中\n");

return 0;
}

示例4:chroot高级应用 - 系统维护工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <mntent.h>

// chroot环境管理器
typedef struct {
char path&#91;512];
int is_active;
pid_t original_pid;
time_t create_time;
} chroot_manager_t;

static chroot_manager_t manager = {0};

// 创建完整的系统恢复环境
int create_recovery_environment(const char* chroot_path) {
printf("创建系统恢复环境: %s\n", chroot_path);

// 创建完整的目录结构
const char* essential_dirs&#91;] = {
"", "bin", "sbin", "etc", "dev", "proc", "sys", "tmp",
"var", "var/log", "var/run", "usr", "usr/bin", "usr/sbin",
"usr/lib", "lib", "lib64", "mnt", "media", "root", "home"
};

for (int i = 0; i < 21; i++) {
char full_path&#91;512];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, essential_dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
printf("警告: 创建目录失败 %s: %s\n", full_path, strerror(errno));
}
}

// 创建设备文件
printf("创建基本设备文件...\n");
struct {
const char* path;
int major, minor;
mode_t mode;
} devices&#91;] = {
{"/dev/null", 1, 3, S_IFCHR | 0666},
{"/dev/zero", 1, 5, S_IFCHR | 0666},
{"/dev/full", 1, 7, S_IFCHR | 0666},
{"/dev/random", 1, 8, S_IFCHR | 0666},
{"/dev/urandom", 1, 9, S_IFCHR | 0666},
{"/dev/tty", 5, 0, S_IFCHR | 0666}
};

for (int i = 0; i < 6; i++) {
char full_path&#91;512];
snprintf(full_path, sizeof(full_path), "%s%s", chroot_path, devices&#91;i].path);
if (mknod(full_path, devices&#91;i].mode, makedev(devices&#91;i].major, devices&#91;i].minor)) == -1 && errno != EEXIST) {
printf("警告: 创建设备文件失败 %s: %s\n", full_path, strerror(errno));
}
}

// 复制系统管理工具
printf("复制系统管理工具...\n");
const char* sysadmin_tools&#91;] = {
"/bin/sh", "/bin/bash", "/bin/ls", "/bin/cat", "/bin/cp",
"/bin/mv", "/bin/rm", "/bin/mkdir", "/bin/rmdir", "/bin/ln",
"/bin/find", "/bin/grep", "/bin/ps", "/bin/kill", "/sbin/ifconfig",
"/sbin/ip", "/sbin/fsck", "/sbin/mkfs", "/bin/mount", "/bin/umount",
"/usr/bin/vi", "/usr/bin/nano", "/bin/tar", "/usr/bin/gzip",
"/usr/bin/bzip2", "/bin/df", "/bin/du", "/usr/bin/top"
};

int copied_count = 0;
for (int i = 0; i < 28; i++) {
if (access(sysadmin_tools&#91;i], F_OK) == 0) {
if (copy_file_to_chroot(sysadmin_tools&#91;i], chroot_path, sysadmin_tools&#91;i]) == 0) {
copied_count++;
}
}
}
printf("成功复制 %d 个系统工具\n", copied_count);

// 创建配置文件
printf("创建基本配置文件...\n");

// /etc/passwd
char passwd_path&#91;512];
snprintf(passwd_path, sizeof(passwd_path), "%s/etc/passwd", chroot_path);
int fd = open(passwd_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* passwd_content =
"root:x:0:0:root:/root:/bin/bash\n"
"admin:x:1000:1000:Admin User:/home/admin:/bin/bash\n";
write(fd, passwd_content, strlen(passwd_content));
close(fd);
}

// /etc/group
char group_path&#91;512];
snprintf(group_path, sizeof(group_path), "%s/etc/group", chroot_path);
fd = open(group_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* group_content =
"root:x:0:\n"
"admin:x:1000:\n";
write(fd, group_content, strlen(group_content));
close(fd);
}

// /etc/hosts
char hosts_path&#91;512];
snprintf(hosts_path, sizeof(hosts_path), "%s/etc/hosts", chroot_path);
fd = open(hosts_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* hosts_content =
"127.0.0.1\tlocalhost\n"
"::1\tlocalhost ip6-localhost ip6-loopback\n";
write(fd, hosts_content, strlen(hosts_content));
close(fd);
}

printf("系统恢复环境创建完成\n");
return 0;
}

// 在chroot中挂载特殊文件系统
int mount_special_filesystems(const char* chroot_path) {
printf("挂载特殊文件系统...\n");

char proc_path&#91;512], sys_path&#91;512], dev_path&#91;512];
snprintf(proc_path, sizeof(proc_path), "%s/proc", chroot_path);
snprintf(sys_path, sizeof(sys_path), "%s/sys", chroot_path);
snprintf(dev_path, sizeof(dev_path), "%s/dev", chroot_path);

// 挂载/proc
if (mount("proc", proc_path, "proc", 0, NULL) == -1) {
printf("警告: 挂载/proc失败: %s\n", strerror(errno));
} else {
printf("挂载 /proc 到 %s\n", proc_path);
}

// 挂载/sys
if (mount("sysfs", sys_path, "sysfs", 0, NULL) == -1) {
printf("警告: 挂载/sys失败: %s\n", strerror(errno));
} else {
printf("挂载 /sys 到 %s\n", sys_path);
}

// 创建并挂载tmpfs到/tmp
char tmp_path&#91;512];
snprintf(tmp_path, sizeof(tmp_path), "%s/tmp", chroot_path);
if (mount("tmpfs", tmp_path, "tmpfs", 0, "size=100M") == -1) {
printf("警告: 挂载/tmp失败: %s\n", strerror(errno));
} else {
printf("挂载 tmpfs 到 %s\n", tmp_path);
}

return 0;
}

// 卸载特殊文件系统
int unmount_special_filesystems(const char* chroot_path) {
printf("卸载特殊文件系统...\n");

char mounts&#91;]&#91;512] = {
"%s/tmp",
"%s/sys",
"%s/proc"
};

for (int i = 0; i < 3; i++) {
char mount_point&#91;512];
snprintf(mount_point, sizeof(mount_point), mounts&#91;i], chroot_path);
if (umount(mount_point) == -1) {
if (errno != EINVAL) { // 忽略未挂载的错误
printf("警告: 卸载 %s 失败: %s\n", mount_point, strerror(errno));
}
} else {
printf("卸载 %s\n", mount_point);
}
}

return 0;
}

// 初始化chroot管理器
int init_chroot_manager(const char* chroot_path) {
strncpy(manager.path, chroot_path, sizeof(manager.path) - 1);
manager.is_active = 0;
manager.original_pid = getpid();
manager.create_time = time(NULL);

printf("初始化chroot管理器\n");
printf(" 环境路径: %s\n", manager.path);
printf(" 管理器PID: %d\n", manager.original_pid);

return 0;
}

// 激活chroot环境
int activate_chroot_environment() {
if (manager.is_active) {
printf("chroot环境已激活\n");
return 0;
}

printf("激活chroot环境: %s\n", manager.path);

// 挂载特殊文件系统
mount_special_filesystems(manager.path);

// 执行chroot
if (chroot(manager.path) == -1) {
perror("chroot失败");
return -1;
}

// 改变到根目录
if (chdir("/") == -1) {
perror("chdir失败");
return -1;
}

manager.is_active = 1;
printf("✓ chroot环境已激活\n");

return 0;
}

// 交互式shell
int start_interactive_shell() {
printf("\n=== 启动交互式shell ===\n");
printf("提示: 输入 'exit' 退出shell\n");
printf("当前环境: chroot @ %s\n", manager.path);
printf("========================\n");

// 启动shell
execl("/bin/bash", "bash", "--norc", "--noprofile", (char*)NULL);

// 如果execl失败
perror("启动shell失败");
return -1;
}

// 执行系统维护任务
int perform_system_maintenance() {
printf("=== 系统维护任务 ===\n");

// 检查文件系统
printf("1. 检查文件系统:\n");
system("df -h");

// 检查磁盘使用情况
printf("\n2. 磁盘使用情况:\n");
system("du -sh /* 2>/dev/null | head -10");

// 检查进程
printf("\n3. 当前进程:\n");
system("ps aux --forest | head -15");

// 检查网络
printf("\n4. 网络状态:\n");
system("ip link show | head -10");

// 检查系统日志
printf("\n5. 系统日志检查:\n");
system("dmesg | tail -10");

return 0;
}

int main(int argc, char* argv&#91;]) {
printf("=== chroot高级应用 - 系统维护工具 ===\n");

if (geteuid() != 0) {
printf("错误: 此工具需要root权限运行\n");
exit(EXIT_FAILURE);
}

const char* chroot_path = "/tmp/recovery_chroot";

// 初始化管理器
init_chroot_manager(chroot_path);

// 检查命令行参数
if (argc > 1) {
if (strcmp(argv&#91;1], "create") == 0) {
// 创建恢复环境
printf("创建恢复环境...\n");
if (create_recovery_environment(chroot_path) == -1) {
exit(EXIT_FAILURE);
}
printf("恢复环境创建完成: %s\n", chroot_path);
return 0;
} else if (strcmp(argv&#91;1], "shell") == 0) {
// 激活并启动shell
printf("启动恢复shell...\n");
if (activate_chroot_environment() == -1) {
exit(EXIT_FAILURE);
}
start_interactive_shell();
return 0;
} else if (strcmp(argv&#91;1], "maintain") == 0) {
// 执行维护任务
if (activate_chroot_environment() == -1) {
exit(EXIT_FAILURE);
}
perform_system_maintenance();
return 0;
} else {
printf("用法: %s &#91;create|shell|maintain]\n", argv&#91;0]);
printf(" create - 创建恢复环境\n");
printf(" shell - 启动交互式shell\n");
printf(" maintain - 执行系统维护任务\n");
return 1;
}
}

// 交互式菜单
printf("\n系统维护工具菜单:\n");
printf("1. 创建恢复环境\n");
printf("2. 启动恢复shell\n");
printf("3. 执行系统维护\n");
printf("4. 退出\n");

int choice;
printf("请选择操作 (1-4): ");
if (scanf("%d", &choice) != 1) {
printf("输入错误\n");
return 1;
}

switch (choice) {
case 1:
printf("创建恢复环境...\n");
create_recovery_environment(chroot_path);
break;
case 2:
printf("启动恢复shell...\n");
activate_chroot_environment();
start_interactive_shell();
break;
case 3:
printf("执行系统维护...\n");
activate_chroot_environment();
perform_system_maintenance();
break;
case 4:
printf("退出工具\n");
break;
default:
printf("无效选择\n");
return 1;
}

printf("\n=== 系统维护工具结束 ===\n");

return 0;
}

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 编译示例1
sudo gcc -o chroot_example1 chroot_example1.c
sudo ./chroot_example1

# 编译示例2
sudo gcc -o chroot_example2 chroot_example2.c
sudo ./chroot_example2

# 编译示例3
sudo gcc -o chroot_example3 chroot_example3.c
sudo ./chroot_example3

# 编译示例4
sudo gcc -o chroot_example4 chroot_example4.c
sudo ./chroot_example4 create
sudo ./chroot_example4 shell

重要注意事项

权限要求: chroot需要CAP_SYS_CHROOT能力,通常需要root权限

安全性限制: chroot不是安全边界,不能完全防止逃逸

目录验证: 必须确保新根目录的安全性和完整性

文件描述符: chroot不影响已打开的文件描述符

符号链接: 注意处理符号链接可能带来的安全问题

设备文件: 需要正确创建必要的设备文件

库依赖: 确保所需的共享库在chroot环境中可用

最佳实践

权限最小化: 在chroot后尽快降低权限

环境清理: 清理不必要的环境变量和文件描述符

目录验证: 验证新根目录的完整性和安全性

设备文件: 只创建必要的设备文件

库依赖: 确保所有依赖库都在chroot环境中

监控审计: 监控chroot环境中的活动

定期更新: 定期更新chroot环境中的软件包

通过这些示例,你可以理解chroot在系统管理和安全隔离方面的应用,虽然现代容器技术已经提供了更好的解决方案,但chroot仍然是一个重要的系统管理工具。

chroot系统调用详解, chroot函数使用示例, linux chroot命令说明, chroot函数作用与用法, 如何使用chroot改变根目录, chroot系统调用原理, linux中chroot函数详解, chroot命令教程, chroot函数参数解释, chroot实现隔离环境方法

data-ad-format="auto" data-full-width-responsive="true">