【毕业设计】:Linux主机文件完整性校验工具的设计与实现 指导老师:曹晓梅
摘要
自Linux首次出现,其强大的功能和众多的优点便受到了许多人的关注。相比于Windows系统,Linux更容易受到服务商们的青睐。然而,正因为如此,Linux系统也面临更多安全相关的问题,其中文件系统的完整性对Linux系统安全至关重要。
本文主要介绍了如何利用一种新型的HMAC算法对Linux的系统文件进行完整性校检。在Linux中常见的文件类型有普通文件、目录文件、链接文件与管道文件,除此之外还有比较少见的设备文件、套接字文件以及其他类型文件。该软件的核心功能就是能够识别这些文件的类型并使用新型算法将从这些文件中提取出的信息摘要进行加密生成认证码用于核验,并创建一个GLADE用户界面,再把核验后的数据结果通过该界面展现出来。这种新型的HMAC算法是一种更加简便且高效的算法。通过计算并保存系统文件的认证码至数据库,再次使用认证码时通过和当前文件计算出的认证码作比较,从而识别它们的变化来对系统文件的完整性进行保护。本次设计实现了对选定的文件夹中指定类型的系统文件进行认证码的计算,并能够进行二次核验,以此来确定系统文件是否被篡改。
关键词:Linux;系统文件;完整性; HMAC
第一章 绪论
1.1 研究背景
Linux系统是被全球广泛使用的一种操作系统,由于其开源,稳定,灵活,对硬件的需求较低,不用经常进行升级,许多企业都用它来搭建服务器,但也正是因为这些特性,针对Linux系统的安全攻击时常发生。Linux中的一切内容几乎都是以文件的形式存储的[1],一旦在Linux系统文件中植入病毒或者后门,不法分子将可能提权从而控制住系统主机,或通过篡改原有文件内容的方式来实现违法目的,这将会对Linux系统数据造成严重威胁。例如yam2-minerd挖矿程序以及watch-smartd挖矿牧马,这些挖矿程序会在成功入侵系统后写入一个文件,然后会修改文件配置进行进程隐藏,需要时还可以通过ssh远程登录遥控,对服务器造成了巨大的危害且难以被察觉。由此可见保护Linux文件完整性的重要性以及建立一个完整性保护机制的必要性。
为了保护Linux系统文件数据的安全,如何验证Linux系统文件的完整性便是其中一个重要的课题[2]。通过研究该课题,可以实现一种新型的且高效的Linux系统文件完整性校验工具,它可以被广泛运用于日常的服务器安全运维工作中,从而减少运维人员的工作量,提高运维人员的工作效率。尽管Linux系统本身拥有校验文件的函数md5sum,但是这种函数的加密时间长,一次只能加密一个文件且并不支持所有的文件类型,再加上操作繁琐且不是可视化界面,不利于使用者的操作。所以设计一个拥有可视化的界面,能够方便地操作文件完整性验证,甚至可以主动统计操作结果,用一种直观的方式呈现给使用者便成为了一个研究课题。
1.2研究目的
Linux文件系统的完整性保护至关重要,但是现有的完整性保护机制还仍然不够完善与实用,使用的便捷性也还不尽人意。因此,设计开发一个用于验证Linux系统文件完整性的软件便成为了一个十分重要的课题。这个软件不仅具有较高的安全性,能够验证Linux系统的系统文件完整性,而且还要有较高的工作效率以及良好的交互界面,能够方便使用者快速且直观地了解到Linux系统文件的完整性是否有问题以及问题出现在哪个文件上,是什么样的问题, 从而减少运维人员的工作量,提高运维人员的工作效率[3] 。
当用户选定好需要核验的系统文件夹后,只需要点击计算或者是核验按钮,就可以快速且直观地了解到文件夹中哪些文件的完整性不变,哪些文件被篡改或者是凭空出现、消失,并能够迅速找到这些文件的位置进行进一步的查看,从而保证了Linux系统的安全性。
除此之外,考虑到Linux系统的系统文件夹之多以及文件类型之广,软件还应该拥有一定的筛选功能,能够满足使用者在限定范围以及限定条件下的验证需求。为了方便使用者从宏观角度了解到所有文件夹中所有类型文件的完整性状况,软件还应该要有统计功能,这样使用者将能更加直观地从宏观角度了解到Linux系统的情况。
1.3研究内容
本课题主要针对Guest OS为Linux的环境,围绕使用HMAC算法,用C语言编程语言实现对任意指定的Linux系统文件夹中的指定类型文件进行认证码的生成与核验。针对HMAC算法要通过对比说明为什么要选用基于哈希函数的MAC算法并且要和传统HMAC算法做性能上的比对。另外还需创建一个Glade工程来实现用户的可视化界面,界面需要有多个窗口负责展示各种操作后的数据结果,最后还应该有合理布局的挂件用于实现交互。软件通过比较篡改前后生成的系统文件认证码,可以判断该文件篡改前后生成的认证码不同,从而判断该文件已经被修改;若是没有找到可比较的认证码,或者是没有能够进行比较的认证码,则可以判断该文件原本不存在与该文件夹目录下或者该文件夹目录下的文件出现缺失。第一次生成的认证码会被存放在数据库中,并赋予一个状态码用于后续操作的核验。等到核验的时候,便可以通过状态码统计一次核验成功和失败的文件数量,并和详细的错误文件路径和错误原因一同提交给可视化界面方便使用者进行查验。如果数据库出现错误,例如表缺失或者是数据错误,软件能够查询识别到并及时地进行处理从而防止软件程序的崩溃。
1.4章节组织
本文的组织结构如下:
第一章:绪论,这部分阐述了课题的研究背景以及研究目的和内容,最后论述了论文结构。
第二章:预备知识,简略介绍了Linux系统以及什么是文件完整性校检,并对哈希函数以及MD5进行了简略的介绍。核心内容是介绍新型HMAC算法以及使用方法。
第三章:系统分析与设计,这是论文的核心部分,在进行需求分析后,详细介绍了软件的设计思路以及实现流程,根据功能将软件分成了五个模块进行逐一的介绍。
第四章:系统实现,详细介绍了软件的各个关键功能的设计实现原理及过程。
第五章:运行与测试,主要介绍了软件的运行环境以及软件的运行核验成功结果以及运行核验失败结果。
第二章 预备知识
略,主要引用了文献以及前提知识
第三章 系统设计
略,主要是对系统的逻辑概念实现论述,没有涉及到设计
第四章 系统实现
4.1 认证码加密功能实现
使用的核心算法是一种新型的HMAC算法,该算法的效率以及安全性相较于传统的HMAC算法有所提高,具体的使用方法如下:
(1) 从文件中提取必要信息组成信息摘要。
(2) 将信息摘要送至HMAC算法加密生成认证码。
(3) 存储认证码以及文件绝对路径。
(4) 核验时重新加密生成认证码。
(5) 根据文件路径重新取出文件的原认证码。
(6) 新认证码和原认证码比对,判断完整性是否被破坏。
(7) 如果完整性被破坏则判断是哪种错误原因。
算法的具体实现方法如下文描述:
4.1.1 MD5加密
在获得信息摘要的部分信息后,首先要组装信息摘要,然后加密生成一个MD5散列值,核心代码如下:
void md5(char *temp1,char *temp2,char* the_md5){ int i; unsigned char decrypt[16]; char buf[16][2]; char *encrypt; encrypt = calloc(strlen(temp1)+strlen(temp2)+2,sizeof(char)); strcpy(encrypt,""); strcat(encrypt,temp1); strcat(encrypt," "); strcat(encrypt,temp2); MD5_CTX md5;//MD5格式设置 MD5Init(&md5); //初始化 MD5Update(&md5,encrypt,strlen(encrypt));//加密步骤 MD5Final(&md5,decrypt); //最终步骤 for(i=0;i<16;i++) { sprintf(buf[i], "%02x", decrypt[i]); strcat(the_md5,buf[i]); } strcat(the_md5,"\0"); free(encrypt); }
4.1.2 转换
转换分为两步,第一步是十六进制转换成二进制,核心代码如下:
void c2c(char *a,char *char_bin){//16进制字符串转二进制字符串 int i,b[32]; for(i=0;i){ if(a[i]>47&&a[i]<58) b[i] = a[i] - 48; else b[i] = a[i] - 87; strcat(char_bin,temp[b[i]]); } }
第二步是二进制字符串转数组,核心代码如下:
void c2i(char *char_bin,int *int_bin){ //二进制字符串转数组 int i; for(i=0;i){ int_bin[i] = char_bin[i] - 48; } }
4.1.3 异或
对每一个二进制数和其前一个数进行异或,最后一个数不用,核心代码如下。
void XOR(int *int_bin){//异或 int a = int_bin[127],b ,i;//最后一个不用 for(i=0;i<128;i++){ b = int_bin[i]; int_bin[i] = int_bin[i] ^ a; a = b; } }
4.1.4 左循环
左循环的目的是制造雪崩效应,核心代码如下:
void reverse(int R[], int from, int to ) { int i ,temp ; for(i=0; i<(to-from+1)/2; i++) { temp = R[from+i]; R[from+i] = R[to-i]; R[to-i] = temp; } } void ROL(int *xor_bin){//左循环 int n = 128,p = n % 7; reverse(xor_bin, 0, p-1); reverse(xor_bin, p, n-1); reverse(xor_bin, 0, n-1); }
4.1.5转成字符
在完成了所有的操作后,需要将二进制转换回字符,核心代码如下:
void i2c(int *rol_bin,char *c_bin){//整型转字符 int i,sum; strcpy(c_bin,""); for(i=0;i<128;i+=4){ sum = rol_bin[i]*8 + rol_bin[i+1]*4 + rol_bin[i+2]*2 + rol_bin[i+3]*1; c_bin[i/4] = hex[sum] ; } }
4.2 文件完整性核验统计功能的实现
4.2.1 核验功能
在认证码生成功能的基础上,添加了单独的核验功能用于进行核验,并与后续的统计功能相连接。在每次生成了认证码后都会调用该文件在数据库中的原认证码进行核验,并把结果传递给统计功能模块。其实现核心代码如下:
cmd = calloc(strlen(tablename) + strlen(road) + 500,sizeof(char));//第一次 strcpy(cmd,"");//组装cmd命令 strcat(cmd,"select aucode from "); strcat(cmd,tablename); strcat(cmd," where road = \""); strcat(cmd,road); strcat(cmd,"\";"); count++; mysql_query(&mysql, cmd); res = *mysql_store_result(&mysql); free(cmd);//第一次 if((row = mysql_fetch_row(&res))!=NULL){ if(strcmp(row[0],the_md5) == 0){//验证成功 cmd = calloc(strlen(tablename) + strlen(road) + 500,sizeof(char));//第二次 *suc_count = *suc_count + 1; strcpy(cmd,""); strcat(cmd,"update "); strcat(cmd,tablename); strcat(cmd," set flag = 1 where road = \""); strcat(cmd,road); strcat(cmd,"\";"); mysql_query(&mysql, cmd); free(cmd);//第二次 } else{//被修改过了 cmd = calloc(strlen(tablename) + strlen(road) + 500,sizeof(char));//第三次 *fai_count = *fai_count + 1; strcpy(cmd,""); strcat(cmd,"update "); strcat(cmd,tablename); strcat(cmd," set flag = 1 where road = \""); strcat(cmd,road); strcat(cmd,"\";"); mysql_query(&mysql, cmd); free(cmd); }
4.2.2 统计功能
数据统计模块被分割成了两个部分放在两个函数中,分别负责单一文件的类型分类以及总数的统计。为了方便将所有的数据传递回给可视化界面,特意设计了数据结构res用于传递。数据结构如下:
typedef struct{ int suc_count; //用于收集成功的文件数量 int fai_count; //用于收集失败的文件数量 int lost_count; //用于收集缺失的文件数量 }res;
通过这个数据结构,就可以将所有的数据传递给可视化界面的窗口。数据统计的核心代码如下:
while((row = mysql_fetch_row(&res))!=NULL){ t = calloc(length(sum_num),sizeof(char)); cmd = calloc(strlen(t) + strlen(row[0]) + 500,sizeof(char));//第二次分配空间 *lost_count = *lost_count + 1; printf("缺失文件:%s\n",row[0]); if((row = mysql_fetch_row(&res))!=NULL){ if(strcmp(row[0],the_md5) == 0){//验证成功 cmd = calloc(strlen(tablename) + strlen(road) + 500,sizeof(char));//第二次 *suc_count = *suc_count + 1; else{//被修改过了 cmd = calloc(strlen(tablename) + strlen(road) + 500,sizeof(char));//第三次 *fai_count = *fai_count + 1; strcpy(cmd,""); strcat(cmd,"update "); strcat(cmd,tablename); strcat(cmd," set flag = 1 where road = \""); strcat(cmd,road); strcat(cmd,"\";"); mysql_query(&mysql, cmd); free(cmd); else{//没有录入的文件 *fai_count = *fai_count + 1; printf("not_exist_road:%s\n",road); t = calloc(length(sum_num) + 500,sizeof(char)); cmd = calloc(strlen(t) + strlen(road) + 500,sizeof(char));//第五次 itoa(sum_num,t);
4.3 可视化界面功能实现
4.3.1进度条功能
在介绍进度条之前,我需要简略介绍贯穿着整个可视化界面的数据结构gtk,gtk数据结构负责引入和初始化所有和界面有关的函数,并且是通过gtk来传递所有的可视化界面挂机参数,其结构如下:
t
ypedef struct{ GtkBuilder *builder;//总结构 GtkWindow *window;//窗口 GtkWidget *progress;//进度条 GtkWidget *treeview[2];//树状图 GtkTextView *textview[10]; //显示图 GtkWidget *button[6];//按钮 GtkWidget *label[3];//标签 GtkSwitch *_switch[16];//按钮 char filename[10];//文件名 }gtk;
进度条挂件功能就是通过这个数据结构进行初始化的。
进度条功能的实现目的有两个,第一个是为了美化界面;第二个是为了让使用者了解到软件还在正常运作中,由于进度条的展示时间是和标签以及计算或者核验线程所关联的,因此需要使用锁的结构。当完成了一个计算后,程序会对该线程进行上锁,对进度条进行解锁,将正在执行的进度条百分比直接调整到百分百后停止运行,代表着执行结束,使用者可以进行下一步的操作。核心代码如下:
int set_progress(gtk *p){//进度条 int i; val = val + 0.05; if(val>1) val = 0; if(st == 1){ //锁被拨到1,代表处于计算模式 gtk_label_set_text( GTK_LABEL(p->label[0]), files[flag]); gtk_label_set_text( GTK_LABEL(p->label[1]), "文件夹"); gtk_label_set_text( GTK_LABEL(p->label[2]), "计算中"); } if(st == 2){ //锁被拨到2,代表处于核验模式中 gtk_label_set_text( GTK_LABEL(p->label[0]), files[flag]); gtk_label_set_text( GTK_LABEL(p->label[1]), "文件夹"); gtk_label_set_text( GTK_LABEL(p->label[2]), "核验中"); } if(flag == -1){ //锁被拨到-1,代表功能执行结束 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(p->progress),1); gtk_label_set_text( GTK_LABEL(p->label[0]),"所有"); gtk_label_set_text( GTK_LABEL(p->label[1]), "文件夹"); gtk_label_set_text( GTK_LABEL(p->label[2]), "执行完毕"); if(check_database()) for(i=0;i<6;i++) gtk_widget_set_sensitive (p->button[i],TRUE); flag = 0; return 0; } gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(p->progress),val); return 1; }
根据文件夹的变换,进度条下方的标签也会随着一起改变,直到所要求的操作全部完成。
4.3.2文件路径实时显示功能
在设计之初,考虑使用的是只使用一个textview文本视图进行文件进度的实时显示,但是文本视图只支持一次性输入多行文件,不支持多次输入,因此我设置了10个文本视图用于显示文件进度,并设计了方案用于判断每个文件路径应该显示在哪个文本视图中。核心代码如下:
for(i=0;i<10;i++){ itoa(i+1,t); strcat(c,t); GtkTextView *textview = GTK_TEXT_VIEW(gtk_builder_get_object (p.builder, c)); p.textview[i] = textview; strcpy(c,"textview"); strcpy(t,""); } int show(gtk *p){//具体状态显示屏 char t1[10]; int i; MYSQL mysql; MYSQL_RES res ; MYSQL_ROW row ; gchar *t2; GtkTextBuffer *buffer; GtkTextIter start_find, end_find; mysql_init(&mysql); mysql_real_connect(&mysql,"localhost","root","root","AuCode",3306,NULL,0); char cmd[100]; itoa(num,t1); strcpy(cmd,"");//组装cmd命令 strcat(cmd,"select road from "); strcat(cmd,p->filename); strcat(cmd," where id = "); strcat(cmd,t1); strcat(cmd,";"); mysql_query(&mysql, cmd); res = *mysql_store_result(&mysql); if((row = mysql_fetch_row(&res))==NULL){//是空的 printf("over2\n"); num = 1; return 0; } if(num<10){ //还没有填满视图前,递归向下显示文本视图 buffer = gtk_text_view_get_buffer(p->textview[num]); //申请内存 gtk_text_buffer_set_text (buffer,row[0],-1); //设置文本内容 } else{//当所有的文本视图都被填满后,每次都会先将各个视图的内容向上移动一个再在最后显示新的文本内容。 for(i=1;i<10;i++){ buffer = gtk_text_view_get_buffer(p->textview[i]); //申请内存 gtk_text_buffer_get_start_iter(buffer, &start_find); //确定起始地址 gtk_text_buffer_get_end_iter(buffer, &end_find); //确定终止地址 t2 = (char *)gtk_text_buffer_get_text(buffer, &start_find, &end_find,FALSE); //获取内容 buffer = gtk_text_view_get_buffer(p->textview[i-1]); //重新申请 gtk_text_buffer_set_text (buffer,(char *)t2,-1); //放置到上层 } buffer = gtk_text_view_get_buffer(p->textview[9]); //新的文本内容内存申请 gtk_text_buffer_set_text (buffer,row[0],-1); //放置 } num++; mysql_close(&mysql); return 1; }
4.4 非核心功能的实现
4.4.1 数据库交互功能
数据库的交互主要涉及到了初始化、执行命令和取出数据三个功能,所有的执行函数都被封包在了函数中,只需要进行调用便可以实现和数据库的交互。在程序中由于在多个不同的地方都需要进行数据库的交互,因此数据库的交互功能零碎地分散在各处,其核心代码大体如下:
初始化:
void init_mysql(MYSQL mysql,char *tablename){//初始化数据库 char *cmd; cmd = calloc(strlen(tablename)+1000,sizeof(char)); mysql_init(&mysql); if(mysql_real_connect(&mysql,"localhost","root","root","AuCode",3306,NULL,0))//链接 printf("Database into successfully.\n"); else { mysql_real_connect(&mysql,"localhost","root","root",NULL,3306,NULL,0); if (!mysql_query(&mysql, "create database AuCode;")) { mysql_query(&mysql, "use AuCode;"); printf("creat database successfully!\n"); } else printf("ERROR: create database failed\n"); } strcpy(cmd,"");//组装cmd命令 strcat(cmd,"create table "); strcat(cmd,tablename); strcat(cmd," (id INTEGER NOT null primary key,road varchar(1000) BINARY NOT null,aucode char(33) NOT null,flag INTEGER NOT null);"); check_table(mysql,cmd,tablename); strcpy(cmd,""); strcat(cmd,"create table wrong (id INTEGER NOT null ,road varchar(1000) BINARY NOT null primary key,flag INTEGER NOT null);"); check_table(mysql,cmd,"wrong"); mysql_close(&mysql);//关闭 free(cmd); }
指令执行(部分):
strcpy(cmd,"");//组装cmd命令 strcat(cmd,"insert into "); strcat(cmd,tablename); strcat(cmd," (id,road,aucode,flag) values("); strcat(cmd,num); strcat(cmd,",\""); strcat(cmd,temp2); strcat(cmd,"\",'"); strcat(cmd,the_md5); strcat(cmd,"',0);"); id++; if (mysql_query(&mysql, cmd) != 0) { printf("%s\n",cmd); printf("ERROR: execute \n"); exit(1); }
取出数据(部分):
mysql_query(&mysql, cmd); res = *mysql_store_result(&mysql); while((row = mysql_fetch_row(&res))!=NULL){//部分
4.4.2文件夹选择功能
文件夹选择模块能够让使用者挑选需要用于计算或者核验的文件夹,从而减少操作的时间。核心代码如下:
char files[7][10] = {"bin","boot","dev","etc","lib","root","sbin"};//用于存放所有的系统文件夹 void set_switch(gtk *p){ int i; for(i=0;i<16;i++){ if(gtk_switch_get_active (p->_switch[i])) //如果开关被激活,则说明这个文件夹需要被操作,否则置零 type[i] = 1; else type[i] = 0; } for(i=0;i<16;i++) printf("type[%d]:%d\n",i,type[i]); printf("\n"); }
4.4.3 文件类型选择功能
和文件夹选择模块类似,文件类型选择的目的也是为了让使用者能够更加高效率地使用软件。由于使用的开关类型相同,都是switch类型,因此文件夹选择和文件类型选择的判断统一在了type数组中集中判断。但和文件夹选择模块不同的是,文件夹选择是根据数组下标进行遍历;而文件类型选择是根据传递的参数进行判断是否进行下一步的操作。核心代码如下:
int transform(int d_type,int *type){ switch(d_type){ case 0: return type[7]; //未知类型 case 1: return type[0]; //管道 case 2: return type[1]; //字符设备 case 6: return type[2]; //块设备 case 8: return type[3]; //常规文件 case 10: return type[4]; //符号链接 case 12: return type[5]; //套接字 case 14: return type[6]; //链接 } }
Case后跟随的数字是各种文件类型在d_type中所赋予的文件数字标识,将这些数字标识作为参数传入transform函数中后悔返回这种文件类型的判断,如果返回0则不需要对该文件类型进行操作;如果返回1则需要对该文件类型进行操作。
第五章 运行与测试
5.1 运行环境
软件所在的运行虚拟机系统为ubuntu 16.04 LTS,操作系统类型为64位,内存3.8GiB,磁盘为29.5GB;
软件所在的实机运行系统为Windows10教育版,操作系统类型为64位,内存8GB。
虚拟机为VMware Workstation,分配内存4GB,分配处理器为4核,硬盘分配30GB。
Mysql版本 14.14
GTK版本 3.0
5.2 计算运行结果
当执行计算功能时,程序会根据所选择的文件夹和文件类型进行认证码的计算,结果如图5.1所示:
5.3 核验运行结果
5.3.1 核验成功结果
当没有出现错误文件的时候,运行的结果如图 5.2 所示:
5.3.2 核验失败结果
当出现了错误文件的时候,运行的结果如图5.3所显示:
通过手动删除bin文件夹的相关数据,造成了错误的出现。
5.4 程序后续改进方案
1、让文件选择范围更加详细灵活,原本软件最初是打算使用类似于选择栏以及文件选择器的方式进行勾选,但是没有在网络上找到详细的相关3.0教程,2.0可实现函数已经弃用,故使用了开关替代。选择栏类的挂件占用界面控件更小,更加美观,并且更加灵活,并且可以选择文件夹的下属文件夹进行范围选择。
2、错误文件着色系统,针对文件出现的错误类型,对文件路径所在行进行着色。这个功能在Glade2.0是可以实现的,但3.0目前没有列装,故暂时没有上线功能。根据错误类型,文件路径可以实时变换颜色,可以富含更多的信息量。
3、数据库检索预警,为了防止出现文件夹所属数据表的缺失导致软件崩溃,一个在程序运行前对数据库的检索预警系统是不可缺失的。但考虑到实现方式复杂,难以插入该功能,在文件夹有限的情况下选择了不列装。
4、更高效率的文件遍历机制,目前软件使用的是递归方式遍历,应对大量文件时的耗时依然无法高效运行。通过使用类似于区块链的技术能够让软件的运行效率上升。
5、临时文件识别系统,在Linux系统中有许多的文件夹及其内容是临时存在的,对于存放这些内容的文件夹,该软件还不具有识别能力。这可能需要一个较大的规则库用于识别。需要进行大量的数据积累后才能进行进一步的编写。
6、变更编写语言,C语言由于没有垃圾回收机制导致后期运行效率缓慢,并且是面向过程语言。后续可以考虑使用C++编写,效率将会提高。
致 谢
能够顺利完成本次毕业设计,仅仅是依靠我一个人是难以做到的。我在本毕业设计的过程中得到了许多人的帮助。首先我要感谢的是曹晓梅老师,曹晓梅老师为我的毕业设计课题提供了重要的文献以及两本关于密码学和Linux系统编程的书籍,这两本书籍为我的程序设计提供了巨大的帮助。从选题、文献选择、开题报告撰写、论文翻译,再到定期检查、中期检查,以及最后的论文撰写,曹晓梅老师都给予了我细心的指导以及热心的帮助。其次我想感谢张佳雯同学,张佳雯同学为我解答了许多早期所不清楚的论文以及学术上的问题,并多次帮助我修改论文的格式以及图表问题,还用书签一一帮我标记了出来,让我能够快速了解并掌握学术论文的编写。接着我想感谢南京邮电大学,学校问我们开放了图书馆,可用于免费查询知网等学术网站的论文并下载下来参考。这些论文来自海内外,获取方式都十分困难,但学校依旧是做到了。除此之外,我还要感谢学长学姐,没有他们的论文内容作为参考,许多程序设计上要注意的地方或者是程序设计的亮点我都不会那么快发现并为己所用。最后我要感谢陪伴我大学四年的任课老师,辅导员以及舍友,老师们为我打下了坚实的学术基础,让我掌握了许多的专业知识。辅导员及时的发布论文相关的信息,让我掌握学校舍友们也为我的论文编写提出了宝贵的意见,并在我深夜编写论文和代码给予了宽容谅解。没有他们的陪伴我也很难走到今天。
最后,向所有关心和帮助过我的领导、老师、同学和朋友表示由衷的谢意!
衷心地感谢在百忙之中评阅我的设计和参加答辩的各位老师!
参考文献
[1] 熊伟,董金明.嵌入式Linux中根文件系统的实现[J].电子测量技术,2007(7):70-80
[2] 杨柳,李陶深.Linux安全性分析[C].2008年中国信息技术与应用学术论坛2008-04-01
[3] 李洁,探究数据中心自动化运维平台的建设管理[J].建筑工程技术与设计,2018(16):5541
[4] 朱娜.“互联网+”时代Linux课程教学模式创新研究[J].信息与电脑,2016(11):247-248
[5] 李颖.浅谈Linux操作系统安全[J].天津市经理学院学报,2007(4):63-64
[6] 徐钦桂,徐治根,黄培灿等.Linux编程[M].北京:清华大学出版社,2019.
[7] 孙玉霞,计算机工程与设计[J],计算机工程与设计,2006(3):382-383,432
[8] 刘翔.加密HASH函数及其应用研究[J].电脑知识与技术,2016(10):54-55
[9] 牛飞斐,张若箐,杨亚涛等.基于Hash函数的计算机日志完整性检测模型设计[J],计算机
工程与设计,2014,35(03):830-834
[10] 廖东.基于混沌的并行密码Hash函数的设计[D].成都:,西南交通大学,2013
[11] 李慧.混沌单向Hash函数性能分析及设计准则研究[D].长沙:长沙理工大学,2012
[12] 庞广乾,一种基于哈希函数链的组群通信密钥分发机制[J].电脑知识与技术,
2008(33):1353-1354,1357
[13] 郑光明,胡博.基于,MD5,的文件完整性检测软件设计[J].,湖南理工学院学报(自然科学
版),2007(3):35-38
[14] 谢琦,杨文娜.一种基于MD5硬件Key的票据号产生方法[J],微计算机信
息,2009(24):35-36,163
[15] 申甲.一种基于MD5算法的B/S通信加密系统[J].信息技术,2010(11):124-126
[16] 文件完整性校验专家——MD5[J].个人电脑,,2004(08):232-232.
[17] 罗俊.Linux操作系统的安全性增强与实现[D].成都:电子科技大学,2005
[18] 杨成辉.基于HMAC口令认证的在线考试系统[D].厦门:厦门大学,2009
[19] Enrique G. Abad , Dr. Ariel M. Sison. Enhanced key generation algorithm of hashing
message authentication code[C]// the 3rd International Conference. 2019.
[20] S. Saleem, O. Popov and R. Dahman, Evaluation of security methods for ensuring the
integrity of digital evidence[C]//2011 International Conference on Innovations in Information Technology, 2011: 220-22
附注: