首页 > 应用开发 > XML/XSL > Sandbox a command(中等难度)
这一部分涉及gdb的调试,所以我们暂时跳过,更多GDB的调试技巧可以去网上搜索一下,这里就不再阐述了。
在这一小节当中,我们需要给xv6操作系统引入一种
进程级系统调用限制机制(sandbox)。具体而言,允许用户进程通过一个新的系统调用
interpose(mask, path),为当前进程及其子进程设置一组
“被禁止的系统调用”,使得后续执行中一旦触发这些系统调用,就会被内核拒绝。官网当中告诉我们
interpose接收两个参数,一个是是屏蔽掩码
mask,另一个是路径
path (当前用不到)。
来自官网的提示(个人解析版):
$U/_sandbox,以保证编译器会编译该源文件。interpose没有任何声明和实现,所以要在
user/user.h 中添加一个
interpose 原型(不要遗漏参数) 。user/usys.pl当中增加一个新的项,该文件是用户态系统调用接口的生成脚本,它会帮助生成一个汇编文件
user/usys.S,该文件中指定了每一个系统调用的参数,陷入指令和返回指令。interpose是一个新的系统调用,所以我们要在
kernel/syscall.h当中添加一个新的系统调用码,用于之后syscall函数的使用。kernel/sysproc.c 当中实现一个名为:
sys_interpose(void)的函数,它就是最终的调用实现。kernel/proc.c当中找到一个名为:
kfork的函数,这里就是父子进行状态/属性继承的地方,我们需要在这里修改一下,使得其可以将父进程新添加的“屏蔽掩码”字段同样赋值给子进程。kernel/syscall.c当中,有一个数组:
syscalls,里面存放的是每一个系统调用的入口地址,我们需要在该数组当中添加一项新的数据,同时需要在此文件中添加
sys_interpose的声明(可以参考已有的xv6代码,照葫芦画瓢)。
# int interpose ( int, char *path) ; # entry( "interpose" ); # # ... ... /*修改点,父进程的状态mask传递给子进程 * 父进程的mask已经被修改,此时若创建新 * 的子进程则mask也要一并传递。 */ np->mask = p->mask; ... ... # extern uint64 sys_interpose ( void) ; //新添加的系统调用声明 // An array mapping syscall numbers from syscall.h // to the function that handles the system call. static uint64 (*syscalls[]) ( void) = { [SYS_fork] sys_fork, [SYS_exit] sys_exit, [SYS_wait] starlinkzhibo.cn sys_wait, [SYS_pipe] sys_pipe, [SYS_read] sys_read, [SYS_kill] sys_kill, [SYS_exec] sys_exec, [SYS_fstat] sys_fstat, [SYS_chdir] sys_chdir, [SYS_dup] sys_dup, [SYS_getpid] sys_getpid, [SYS_sbrk] sys_sbrk, [SYS_pause] sys_pause, [SYS_uptime] sys_uptime, [SYS_open] sys_open, [SYS_write] sys_write, [SYS_mknod] sys_mknod, [SYS_unlink] sys_unlink, [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, [SYS_interpose] xzhibo.com.cn sys_interpose, //新添加的系统调用 }; //这里是syscall函数修改后的样子: void syscall ( void) { int num; struct proc * p = myproc(); // 取出在a7中存放的调用号 num = p->trapframe->a7; if (num > 0 && num < NELEM(syscalls) && syscalls[num]) { //将调用屏蔽掩码和系统调用码进行相与的操作,判断当前调用是否被屏蔽/禁止 if (p->mask & ( 1 << num) ){ p->trapframe->a0 = -1 ; return ; } // Use num to lookup the system call function for num, call it, // and store its return value in p->trapframe->a0 p->trapframe->a0 = syscalls[num](); } else { printf ( "%d %s: unknown sys call %d\n" , p->pid, p->name, num); p->trapframe->a0 = -1 ; } } # / Per-process state struct proc { struct spinlock lock; // p->lock must be held when using these: enum procstate state; // Process state void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID int mask; // (新)进程的系统调用屏蔽掩码 ... ... # uint64 sys_interpose ( void) { //获取参数 int n; argint( 0 , &n); //修改状态 struct proc * p = myproc(); p->mask = n;
这一小节是对上一阶段
系统调用屏蔽 的扩展,上一小节只是简单粗暴地屏蔽了某个系统调用(一棒子打死的那种),在本小节,我们用到了
interpose的第二个参数
Path,这个参数的具体意思是:
“允许访问的路径”。
假设我们的屏蔽掩码屏蔽了
open 和
exec 这个两个系统调用,但是这两个系统调用在调用时都需要向其传入一个路径(我们假设该路径的名字为:pathA),当我们调用
open 和
exec时,如果向其传入的路径 pathA和之前的Path一致,则代表
open 和
exec正常进行,不会被屏蔽,反之则直接返回,不再执行
open 和
exec。
个人的一些解析:
interpose的第二次参数,因此我们需要在进程结构体当中添加新的字段用于存放允许访问的路径。open 和
exec,则会继续判断PathA和进程结构体当中的特点字段是否一致,一致则代表
open 和
exec可以正常执行,所以结合前面的例子,屏蔽掩码具体屏蔽了谁,应该在syscall当中进行判断,而进一步地判断需要在
sys_open 和
sys_exec两个调用的具体实现当中。
// Per-process state
struct
proc {
struct
spinlock
lock;
// p->lock must be held when using these:
enum
procstate
state;
// Process state
void
*chan;
// If non-zero, sleeping on 5gzbw.com chan
int
killed;
// If non-zero, have been killed
int
xstate;
// Exit status to be returned to parent's wait
int
pid;
// Process ID
int
mask;
// 进程的系统调用屏蔽字
char
allowPathName[MAXPATH];
// 被允许的路径名
... ...
#
... ...
/*修改点,父进程的状态mask传递给子进程 * 父进程的mask已经被修改,此时若创建新 * 的子进程则mask也要一并传递。 * 子进程也要继承父进程的allowPathName。 */
np->mask = p->mask;
strncpy
(np->allowPathName,p->allowPathName,MAXPATH);
... ...
#
void
syscall
(
void)
{
int
num;
struct
proc *
p =
myproc();
// 取出在a7中存放的调用号
num = p->trapframe->a7;
if
(num >
0
&& num < NELEM(syscalls) && syscalls[num]) {
//修改前:将调用屏蔽掩码和系统调用码进行相与的操作,判断当前调用是否被屏蔽/禁止
//修改后:如果屏蔽的是open和exec则在open或者exec当中再次判断
if
(p->mask & (
1
<< num) ){
if
(num == SYS_open || num == SYS_exec){
p->trapframe->a0 = syscalls[num]();
return
;
}
p->trapframe->a0 =
-1
;
return
;
}
// Use num to lookup the system call function for num, call it,
// and store its return value in p->trapframe->a0
p->trapframe->a0 = syscalls[num]();
}
else
{
tiyuzhibo.xj.cn
printf
(
"%d %s: unknown sys call %d\n"
,
p->pid, p->name, num);
p->trapframe->a0 =
-1
;
}
}
#
... ...
// 只有当 open 被 mask 掉时,才检查路径
if
(p->mask & (
1
<< SYS_open)){
if
(
strncmp
(path,p->allowPathName,MAXPATH) !=
0
){
return
-1
;
}
}
... ...
#
... ...
// 只有当 exec 被 mask 掉时,才检查路径
if
(p->mask & (
1
<< SYS_exec)){
if
(
strncmp
(path, p->allowPathName,MAXPATH) !=
0
){
return
-1
;
}
make qemu-gdb sandbox_mask: OK (3.0s) == Test sandbox_fork == make qemu-gdb sandbox_fork: OK (1.1s) == Test sandbox_path == make qemu-gdb sandbox_path: OK (1.2s) == Test sandbox_most == make qemu-gdb sandbox_most: OK (0.7s) == Test sandbox_minus == make qemu-gdb sandbox_minus: OK (1.0s) =tiyuzhibo.fj.cn= Test attack == make qemu-gdb
int main ( int argc, char *argv[]) { // Your code here. //分配heap char * buf = sbrk(DATASIZE); //堆的大小为8页 char ch[DATASIZE/ 4 ]; //字符串大小为8k int j = 0 ; // for ( int i = 0 ; i < DATASIZE; i++){ char c = buf[i]; if (c == '\0' && (j >= 2 && j < DATASIZE/ 4 )){ //遇到/0,并且j大于2且在合法范围内则代表可能找到了想要的东西,截断字符串 ch[j++] = '\0' ; //打印 ttzb.fj.cn printf ( "%s" ,ch); printf ( "\n" ); //j置为0继续找剩余符合条件的字符串(假设还没有扫码到heap的尽头的情况下) j = 0 ; } //符合字符条件并且j的范围合理 if ( ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || (c >= '0' && c <= '9' )) && j < DATASIZE/ 4 - 1 ){ //符合条件则赋值给字符串数组 ch[j++] = c; } else { //不连续或者不是字符则将j值为0 j= 0 ; }
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3108979/ ,如需转载,请注明出处,否则将追究法律责任。