实验步骤
- 创建两个用户,分别为userh和userl,两个用户具有不同的安全级,其中userh用户的安全级高于userl的安全级,并分别创建安全级不同的用户目录,之后分别设置用户的密码。
useradd –m –h SYS_SECURITY –v SYS_SECURITY userh
useradd –m –h SYS_PUBLIC –v SYS_PUBLIC userl
# 创建两个用户,分别设置其安全级
setlevel SYS_SECURITY /home/ userh
setlevel SYS_ PUBLIC /home/ userl
# 设置两用户的主目录为相应的安全级
passwd userh
passwd userl
# 为两个用户设置密码
2.创建接收端和发送端的代码,由于在安胜操作系统下创建代码比较繁琐,因此我们尝试在windows下创建代码,之后使用mount命令,通过挂载U盘完成代码在两个系统之间的传输。
3.其中在发送端(高权限用户)的代码中,我们编写了1个函数,即sendChar该函数完成了字符发送工作。
//字符发送函数。根据 ch 的 ASCII 码值,fork 同等数量的进程
void sendChar(char ch)
4.而在接收端(低权限用户)的代码中,我们主要写了2个函数,分别是writeToFile以及receiveChar,他们两个完成了以下的工作。
//将ch写入接收文件
void writeToFile(char ch);
// receiveChar () 函数用于接接收一个字符,用此刻的和2秒前的pid差值判断此次传送的字符 ASCII 码值
char receiveChar();
5.我们分别在低权限用户userl和高权限用户userh的文件目录中对于两个代码进行编译,从而获得用于发送和接受的可执行程序,并且再高权限用户userh的文件目录中,编写需要发送的文件(sender_text.txt)。
6.之后我们分别运行两个程序,我们可以发现sender_text.txt中的字符都通过sender程序创建了多个进程,并且receiver程序通过对进程的阅读从而获取了传输的字符,并且成功的写入了文件中,下图运行中的两个程序,而通过我们对于接收文件(receiver_text.txt)的阅读可以发现和我们待传输的文件是相同的。
实验分析
1.该实验是在具有强制访问控制的安胜操作系统的环境下进行操作,从而获得进程标识符通道传输的隐蔽通道方案。
for (i = 0; i < count; i++) { //创建 count 个子进程
/* fork() 函数非常特殊,一次调用会有两个返回值。
*父进程 Father 调用fork()函数后,会新建一个子进程 Son。
*Son 是 Father 的镜像,也就是说,Son 的程序段、数据段、PC值都和 Father 一模一样,唯一的区别是,Son 的 fpid = 0,而 Father 的 fpid = Son的进程id
*/
fpid = fork();
if (fpid < 0) {
printf("fork error.\n");
} else if (fpid == 0) { // 子进程的 fpid = 0
printf("Sending [%c], last_pid = %d.\n", ch, getpid());
exit(0); //子进程要主动 exit,把CPU让给父进程使用
}
}
2.其中主要是通过子进程的创建从而进行传输操作,其中父进程通过调用fork()函数从而获得了子进程,子进程的个数则是由所需要传输的字符来决定的。当父进程调用fork函数的时候,子进程复制父进程的所有内存空间并占用CPU,当再次fork的时候子进程将会终止并且exit(0),从而将CPU的控制权再次交给父进程使用,既能保证进程ID的变化,也能保障程序继续执行。
sleep(2); //父进程挂起2秒
fpid2 = fork(); //两秒过后,父进程再创建一个子进程,目的是获得当前的 pid
//执行 fork() 之后,子进程的 fpid2=0,而父进程的 fpid=子进程的id
if (fpid2 < 0) {
printf("fork error.\n");
} else if (fpid2 == 0) {
exit(0); //子进程直接exit退出
}
iDiff = fpid2 - fpid1 - 1; //由于第一次fork占用了一个pid,因此做差时需要减1
ch = (char) iDiff;
if (iDiff < 8) {
//如果发现2秒内进程 ID 变化在8以内,就认为是噪声,自动忽略。
printf("Receiving round ends. pid_diff = [%d], data is [%c]\n", iDiff, ch);
return (char) 0;
}
3.而在接收过程中,我们要确保发送方和接收方有足够的时间对于一个字符进行传输,即当发送方发送结束一个字符之后要有足够时间让接收方进行获得,因此将时间设定为2s,根据本组同学的实验发现,2s远远大于fork128(ASCII码最大值)个子进程的所用时间,因此选择了2s。同时为防止噪声的存在,我们选择了8个子进程的噪声缓冲即两个pid之间差小于8则认为是噪声并将其舍弃,从而保障即能舍弃所有噪声(主要是最开始的时候)也能够阅读所有的字符(例如空格)。
实验结果
我们成功实现了在强制访问控制下通过进程描述符从而进行不同权限用户之间的信息传递即隐蔽通道,并获得了成功。
同时我们也在实验中遇到由于噪声的存在,导致传输的失败,其中错误的将字符3传输为字符),说明该信道在传输中还是很不稳定的,当我们通过加入噪声的方案可以将该隐蔽通道的危害性降为最低。
实验代码
- 发送端代码:
#include
#include
#include
void sendChar(char ch);
int main()
{
char ch;
FILE *fp;
int i;
if ((fp = fopen("./sender_text.txt", "r")) == NULL) { //读取文件,发送内容预写在 txt 文件中
printf("fopen error.\n");
exit(0);
}
while ((ch = fgetc(fp)) != EOF) {
sendChar(ch); //发送一个字符
sleep(2); //每隔2秒发送一次
}
fclose(fp);
return 0;
}
/**
字符发送函数。
根据 ch 的 ASCII 码值,fork 同等数量的进程
*/
void sendChar(char ch) {
pid_t fpid; // 用fpid记录fork函数返回的值
int i;
int count = (int) ch; //char 转型成 int
//打印Log
printf("***************************************************\n");
printf("Sending [%c], new pid count = [%d].\n", ch, count);
for (i = 0; i < count; i++) { //创建 count 个子进程
fpid = fork();
if (fpid < 0) {
printf("fork error.\n");
} else if (fpid == 0) { // 子进程的 fpid = 0
printf("Sending [%c], last_pid = %d.\n", ch, getpid());
exit(0); //子进程要主动 exit,把CPU让给父进程使用
}
}
}
- 接收端代码:
#include
#include
#include
int bStart = 0; //表示传输是否开始,bStart=1 表示传输已经开始
void writeToFile(char ch);
char receiveChar();
int main()
{
char ch;
int i;
remove("./receiver_text.txt"); //删除旧的接收文件
while (((ch = receiveChar()) != (char) 0) || bStart == 0) { //如果bStart==1 且 最近2秒没有收到任何数据,就认为传输已经结束
if (bStart == 0) {
printf("Not start.\n"); //bStart为0表示发送端还没开始发送
}
else {
printf("Write [%c] to file.\n", ch); //接收到发送端发送字符ch
writeToFile(ch); //将ch写入接收文件
}
}
printf("End.\n");
return 0;
}
void writeToFile(char ch) { //将ch写入接收文件
FILE *fp;
if ((fp = fopen("./receiver_text.txt", "a")) == NULL) {
printf("fopen error.\n");
exit(0);
}
fputc(ch, fp);
fclose(fp);
}
/**
* receiveChar () 函数用于接接收一个字符
* 用此刻的和2秒前的pid差值判断此次传送的字符 ASCII 码值
*/
char receiveChar() {
pid_t fpid1, fpid2; //fpid1, fpid2表示fork函数返回的值
int iDiff; //差值
char ch;
printf("***************************************************\n");
printf("Receiving round begins.\n");
fpid1 = fork();
if (fpid1 < 0){
printf("fork error.\n");
} else if (fpid1 == 0) { //fpid1==0,说明是子进程
exit(0); //子进程需要立即exit将CPU让给父进程
}
sleep(2); //父进程挂起2秒
fpid2 = fork(); //两秒过后,父进程再创建一个子进程,目的是获得当前的 pid
if (fpid2 < 0) {
printf("fork error.\n");
} else if (fpid2 == 0) {
exit(0); //子进程直接exit退出
}
iDiff = fpid2 - fpid1 - 1; //由于第一次fork占用了一个pid,因此做差时需要减1
ch = (char) iDiff;
if (iDiff < 8) {
//如果发现2秒内进程 ID 变化在8以内,就认为是噪声,自动忽略。
printf("Receiving round ends. pid_diff = [%d], data is [%c]\n", iDiff, ch);
return (char) 0;
} else {
if (bStart == 0) { //第一次接收时,由于发送端的祖先进程本身也是一个进程,也占用了一个pid,因此需要减1
iDiff--;
ch--;
}
printf("Receiving round ends. pid_diff = [%d], data is [%c]\n", iDiff, ch);
bStart = 1; //更新标识,表示以后就不是第一次接收了
return ch;
}
}
PS:首行缩进可以使用两个& emsp ;(去掉空格)