这是一个在系统调用层劫持系统调用,实现linux系统的文件透明加解密的驱动程序(一)。最后编写
Makefile文件(请看下一讲),生成.so文件,动态加载到内核中即可。
#include
······
#include
#define Encryption blowfish
#define Decryption blowfish
#define KERNEL_BUF_SIZE 1024*8
static char *key = "world";
static char *filename = "ttt.c";
//系统调用表sys_call_table存储了所有系统调用对应的服务例程的函数地址
static unsigned long *sys_call_table=NULL;//存放系统调用表的起始地址
//内核模块通过module_param来传递命令行参数
//module_param(name, type, perm);变量名、类型、权限掩码(用来作一个辅助的sysfs入口)
//charp一个字符指针值,内存为用户提供的字串分配
//S_IRUGO可以被所有人读取
module_param(key, charp, S_IRUGO);
module_param(filename, charp, S_IRUGO);
//__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节
数进行对齐,是GCC特有的语法。
//__attribute__关键字主要是用来在函数或数据声明中设置其属性,给函数赋给属性的主要目的在于让
编译器进行优化
//packed可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位
对齐。
//中断向量表表项结构体
struct {
unsigned short limit;
unsigned int base;
}__attribute__((packed)) idtr;
//中断向量描述符的结构体
struct _idt
{
unsigned short offset_low;
unsigned short segment_sel;
unsigned char reserved, flags;
unsigned short offset_high;
}__attribute__((packed));
//函数定义前加宏asmlinkage,表示这些函数通过堆栈而不是通过寄存器传递参数。
//告诉编译器仅从堆栈中获取该函数的参数
//定义一个函数指针,用于保存原系统调用read和write在系统调用表中的内容。
asmlinkage ssize_t (*orig_read)(int fd,void *buf, size_t count);
asmlinkage ssize_t (*orig_write)(int fd,void *buf, size_t count);
long enFile_fd;
unsigned int orig_cr0;//用于保存初始的CR0寄存器的值
static char *kernel_buf;
struct file *info_file = NULL;
unsigned int clear_and_ret_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret = 0;
//asm表示后面的代码为内嵌汇编,是__asm__的别名
//volatile表示编译器不要优化代码,后面的指令保持原样,是__volatile__的别名
//执行int 0x80后,系统调用的参数保存在寄存器中,eax传递的是系统调用号。
//汇编代码,用于取出CR0寄存器的值
//AT&T汇编代码使用小写字母,寄存器需要加前缀%
//AT&T语法第一个为源操作数,第二个为目的操作数,方向从左到右
//"movl %1, %0":"=r"(result):"m"(input) "=r"是表达式的说明和限制,(result)是每个操作数对应的C
表达式
//result前面的限制字符串是=r,=表示result是输出操作数
//冒号后第一项对应的是%0,第二项对应的是%1
//输入部分为空,也就是我们可以直接从CR0寄存器中取数;输出部分位cr0变量,a表示将cr0和eax相关
联,执行完后,cr0寄存器中的值就赋给了变量cr0.
asm volatile("movl %%cr0, %�x":"=a"(cr0));
ret = cr0;
//CR0的第16位是写保护位,0表示禁用写保护,1表示开启
cr0 &= 0xfffeffff;
//汇编代码,将修改后的CR0值写入CR0寄存器
//输出部分为空,我们直接将结果输出到cr0寄存器中;输入部分为变量cr0,它和eax寄存器相关联,执
行完后,变量cr0的值就赋给了寄存器cr0.
asm volatile("movl %�x, %%cr0"::"a"(cr0));
return ret;//返回初始CR0值
}
//改回原CR0寄存器的值
void setback_cr0(unsigned int val)
{
asm volatile("movl %�x, %%cr0"::"a"(val));
}
//查找系统调用表到sys_call_table的偏移量
char* findoffset(char *start)
{
char *ptr = start;
int i = 0;
for(; i < 100; i++){
if(*(ptr+i) == '\xff' && *(ptr+ i + 1) == '\x14' && *(ptr+ i + 2) == '\x85'){
printk("find offset %d\n",i);
return ptr + i;
}
}
return NULL;
}
//加解密程序
void blowfish(const char * bfKey,char * pData, long size)
{
int i;
for(i = 0; i < size; ++i){
pData[i]=pData[i]^bfKey[0];
//printk("en : %c\n",pData[i]);
}
}
//找到系统调用表并替换内容
//通过中断向量表,找到系统调用的中断向量
unsigned long *getscTable(void)
{
//中断向量表IDT的入口地址是通过IDTR寄存器来确定的
struct _idt *idt;
unsigned long system_call = 0, sct = 0;
unsigned short offset_low,offset_high;
char *p = NULL;
orig_cr0 = clear_and_ret_cr0(); //注意在这里设置一下cr0
// 从idtr中获得中断描述符(相当于中断号号表)的首地址
//idtr寄存器的内容可以通过汇编指令sidt取出
__asm__("sidt %0" : "=m" (idtr));
setback_cr0(orig_cr0);
//由于每个中端描述符为8个字节,而软中断为int 0x80,据此获取系统调用中断即0x80的中断描述符的
首地址
//idt是获取到系统调用中断向量地址(即每一向量是中断服务程序的入口地址),即0x80地址
idt = (void *)(idtr.base + 8 * 0x80);
offset_low = idt->offset_low;
offset_high = idt->offset_high;
//获取系统调用中断发生时的中断处理例程的地址
system_call = (offset_high<<16)|offset_low;
//找到系统调用表(即中断向量表,系统调用表的入口地址是system_call)中,系统调用项的首地址,即
中断服务程序的入口地址
p = findoffset((char*)system_call);
if(NULL != p) {
//3表示3个指令码
sct = *(unsigned long*)(p+3);
}
return (unsigned long*)sct;
}
//具有解密功能的读
//判断是否是需要解密的文件,如果是执行替换后的read函数将数据拷贝到内核空间解密数据,否则调用
原来的read函数输出数据。
asmlinkage ssize_t hacked_read(unsigned int fd,char * buf,size_t count)
{
int err = 0;
struct file *file;
ssize_t ret = -EBADF;
//根据文件描述符获得文件的file结构,从而获得其文件名
file = fget(fd);
//调用原来的read系统调用将数据输入
ret=orig_read(fd,buf,count);
//判断当前进行read系统调用的文件是否为加载模块时指定的文件
if(!strcmp(file->f_dentry->d_name.name,filename)){
//判断要操作的字符数目是否是操作预先的分配值
if(count > KERNEL_BUF_SIZE ){
kfree(kernel_buf);
kernel_buf=kmalloc(count+10, GFP_KERNEL);
memset(kernel_buf, 0,count+10 );
}
//将数据拷贝到内核空间中
err = copy_from_user(kernel_buf,buf,count);
if(err){
printk("copy_from_user error!\n");
}
//解密数据
Decryption(key,kernel_buf,count);
//将数据拷回到用户空间
err = copy_to_user(buf,kernel_buf,count);
if(err){
printk("copy_to_user error!\n");
}
}
return ret;
}
//具有加密功能的写
//判断是否是需要加密的文件,如果是执行替换后write函数将数据拷贝到内核空间加密数据,否则调用
原来的write函数输入数据。
asmlinkage ssize_t hacked_write(unsigned int fd,char * buf,size_t count)
{
int err = 0;
struct file *file;
ssize_t ret = -EBADF;
//根据文件描述符获得文件的file结构,从而获得其文件名
file = fget(fd);
//判断当前进行write系统调用的文件是否为加载模块时指定的文件
if(!strcmp(file->f_dentry->d_name.name,filename)){
//判断要操作的字符数目是否时操作预先的分配值
if(count > KERNEL_BUF_SIZE ){
kfree(kernel_buf);
kernel_buf=kmalloc(count+10, GFP_KERNEL);
memset(kernel_buf, 0,count+10 );
}
//将数据拷贝到内核空间中
err = copy_from_user(kernel_buf,buf,count);
if(err){
printk("copy_from_user error!\n");
}
//加密数据
Encryption(key,kernel_buf,count);
//将数据拷回到用户空间
err = copy_to_user(buf,kernel_buf,count);
if(err){
printk("copy_to_user error\n");
}
}
//调用原来的write系统调用将数据输入
ret = orig_write(fd,buf,count);
return ret;
}
static int hack_init(void)
{
sys_call_table = getscTable();
if(NULL != sys_call_table){
orig_read=(asmlinkage ssize_t (*)(int ,void *, size_t))sys_call_table[__NR_read];
orig_write=(asmlinkage ssize_t (*)(int ,void *, size_t))sys_call_table[__NR_write];
orig_cr0 = clear_and_ret_cr0();
sys_call_table[__NR_read]=(unsigned long)hacked_read;
sys_call_table[__NR_write]=(unsigned long)hacked_write;
setback_cr0(orig_cr0);
kernel_buf=kmalloc(KERNEL_BUF_SIZE, GFP_KERNEL);
memset(kernel_buf, 0,KERNEL_BUF_SIZE );
}
return 0;
}
void hack_cleanup(void)
{
orig_cr0 = clear_and_ret_cr0();
printk("UnHacked 3!");
//sys_call_table = getscTable();
if(sys_call_table){
sys_call_table[__NR_read]=(unsigned long)orig_read;
sys_call_table[__NR_write]=(unsigned long)orig_write;
}
setback_cr0(orig_cr0);
kfree(kernel_buf);
return;
}
MODULE_LICENSE("GPL");
module_init(hack_init);
module_exit(hack_cleanup);
·生成加解密模块:make
·装载模块,文件名为test.c 密码为zz: insmod encryption.ko key = "zz" filename = "test.c"
·对test.c进行编辑: vi test.c
·卸载模块查看效果 : rmmod encryption
·查看加密后的文件: vi test.c
·加载模块,查看原文件: insmod encryption.ko key = "zz" filename = "test.c"
·查看源文件: vi test.c
·在Fedora 17,Linux 3.3内核中试验成功
关于上一讲中的Makefile的书写 (2013-09-10 23:59:52) 转载▼
标签: pwd make moudles it 分类: 个人日记
obj-m := encryption.o
##m:编译成模块,但不会编译进内核
KDIR := /lib/modules/`uname -r`(反逗点)/build
##指定内核路径
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
linux系统的文件透明加解密的驱动程序(二) (2013-09-11 00:05:14) 转载▼
标签: vfs 加解密 动态加载 file结构体 传递性 分类: 个人日记
这是一个在VFS层劫持系统调用,实现Linux系统的文件透明加解密的驱动程序(二)。最后编写Makefile
(请看上一讲),生成.so文件,动态加载到内核中即可。
#include
······
#include
#define Encryption blowfish
#define Decryption blowfish
#define FOP file->f_dentry->d_inode->i_fop
static char *key = "zz";
static char *filename = "ttt.c";
module_param(key, charp, S_IRUGO);
module_param(filename, charp, S_IRUGO);
char *root_fs = "/test.c";
unsigned int orig_cr0;
typedef ssize_t (*read_t)(struct file *, char __user *, size_t, loff_t *);
typedef ssize_t (*write_t)(struct file *, const char __user *, size_t, loff_t *);
read_t orig_read = NULL;
write_t orig_write = NULL;
unsigned int clear_and_ret_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret = 0;
asm volatile("movl %%cr0, %�x":"=a"(cr0));
ret = cr0;
cr0 &= 0xfffeffff;
asm volatile("movl %�x, %%cr0"::"a"(cr0));
return ret;
}
void setback_cr0(unsigned int val)
{
asm volatile("movl %�x, %%cr0"::"a"(val));
}
void blowfish(const char *bfkey, char *pData, size_t size)
{
int i = 0;
for(; i < size; ++i){
pData[i] = pData[i]^bfkey[0];
}
}
ssize_t hacked_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
ssize_t ret = -EBADF;
ret = orig_read(file, buf, count, offset);
if(!strcmp(file->f_dentry->d_name.name, filename))
Decryption(key, buf, count);
return ret;
}
ssize_t hacked_write(struct file *file, const char __user *buf, size_t count, loff_t
*offset)
{
ssize_t ret = -EBADF;
if(!strcmp(file->f_dentry->d_name.name, filename))
Encryption(key, (char __user *)buf, count);
ret = orig_write(file, buf, count, offset);
return ret;
}
int patch_vfs(const char *p, read_t *orig_read, write_t *orig_write, read_t new_read,
write_t new_write)
{
struct file *file;
file = filp_open(p, O_RDONLY, 0);
if(IS_ERR(file))
return -1;
orig_cr0 = clear_and_ret_cr0();
if(orig_read)
*orig_read = FOP->read;
if(orig_write)
*orig_write = FOP->write;
((struct file_operations *)(FOP))->read = new_read;
((struct file_operations *)(FOP))->write = new_write;
setback_cr0(orig_cr0);
filp_close(file, 0);
return 0;
}
int unpatch_vfs(const char *p, read_t orig_read, write_t orig_write)
{
struct file *file;
file = filp_open(p, O_RDONLY, 0);
if(IS_ERR(file))
return -1;
orig_cr0 = clear_and_ret_cr0();
((struct file_operations *)(FOP))->write = orig_write;
((struct file_operations *)(FOP))->read = orig_read;
setback_cr0(orig_cr0);
filp_close(file, 0);
return 0;
}
static __init int patch_init(void)
{
patch_vfs(root_fs, &orig_read, &orig_write, hacked_read, hacked_write);
return 0;
}
static __exit void patch_cleanup(void)
{
unpatch_vfs(root_fs, orig_read, orig_write);
}
MODULE_LICENSE("GPL");
module_init(patch_init);
module_exit(patch_cleanup);
·本驱动程序的缺点是必须在/目录下有test.c文件,才能实现加解密功能。
·生成加解密模块:make
·装载模块,文件名为test.c 密码为zz: insmod encryption.ko key = "zz" filename = "test.c"
·对test.c进行编辑: vim test.c
·卸载模块查看效果 : rmmod encryption
·查看加密后的文件: vim test.c
·加载模块,查看原文件: insmod encryption.ko key = "zz" filename = "test.c"
·查看源文件: vim test.c
Makefile文件(请看下一讲),生成.so文件,动态加载到内核中即可。
#include
······
#include
#define Encryption blowfish
#define Decryption blowfish
#define KERNEL_BUF_SIZE 1024*8
static char *key = "world";
static char *filename = "ttt.c";
//系统调用表sys_call_table存储了所有系统调用对应的服务例程的函数地址
static unsigned long *sys_call_table=NULL;//存放系统调用表的起始地址
//内核模块通过module_param来传递命令行参数
//module_param(name, type, perm);变量名、类型、权限掩码(用来作一个辅助的sysfs入口)
//charp一个字符指针值,内存为用户提供的字串分配
//S_IRUGO可以被所有人读取
module_param(key, charp, S_IRUGO);
module_param(filename, charp, S_IRUGO);
//__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节
数进行对齐,是GCC特有的语法。
//__attribute__关键字主要是用来在函数或数据声明中设置其属性,给函数赋给属性的主要目的在于让
编译器进行优化
//packed可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位
对齐。
//中断向量表表项结构体
struct {
unsigned short limit;
unsigned int base;
}__attribute__((packed)) idtr;
//中断向量描述符的结构体
struct _idt
{
unsigned short offset_low;
unsigned short segment_sel;
unsigned char reserved, flags;
unsigned short offset_high;
}__attribute__((packed));
//函数定义前加宏asmlinkage,表示这些函数通过堆栈而不是通过寄存器传递参数。
//告诉编译器仅从堆栈中获取该函数的参数
//定义一个函数指针,用于保存原系统调用read和write在系统调用表中的内容。
asmlinkage ssize_t (*orig_read)(int fd,void *buf, size_t count);
asmlinkage ssize_t (*orig_write)(int fd,void *buf, size_t count);
long enFile_fd;
unsigned int orig_cr0;//用于保存初始的CR0寄存器的值
static char *kernel_buf;
struct file *info_file = NULL;
unsigned int clear_and_ret_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret = 0;
//asm表示后面的代码为内嵌汇编,是__asm__的别名
//volatile表示编译器不要优化代码,后面的指令保持原样,是__volatile__的别名
//执行int 0x80后,系统调用的参数保存在寄存器中,eax传递的是系统调用号。
//汇编代码,用于取出CR0寄存器的值
//AT&T汇编代码使用小写字母,寄存器需要加前缀%
//AT&T语法第一个为源操作数,第二个为目的操作数,方向从左到右
//"movl %1, %0":"=r"(result):"m"(input) "=r"是表达式的说明和限制,(result)是每个操作数对应的C
表达式
//result前面的限制字符串是=r,=表示result是输出操作数
//冒号后第一项对应的是%0,第二项对应的是%1
//输入部分为空,也就是我们可以直接从CR0寄存器中取数;输出部分位cr0变量,a表示将cr0和eax相关
联,执行完后,cr0寄存器中的值就赋给了变量cr0.
asm volatile("movl %%cr0, %�x":"=a"(cr0));
ret = cr0;
//CR0的第16位是写保护位,0表示禁用写保护,1表示开启
cr0 &= 0xfffeffff;
//汇编代码,将修改后的CR0值写入CR0寄存器
//输出部分为空,我们直接将结果输出到cr0寄存器中;输入部分为变量cr0,它和eax寄存器相关联,执
行完后,变量cr0的值就赋给了寄存器cr0.
asm volatile("movl %�x, %%cr0"::"a"(cr0));
return ret;//返回初始CR0值
}
//改回原CR0寄存器的值
void setback_cr0(unsigned int val)
{
asm volatile("movl %�x, %%cr0"::"a"(val));
}
//查找系统调用表到sys_call_table的偏移量
char* findoffset(char *start)
{
char *ptr = start;
int i = 0;
for(; i < 100; i++){
if(*(ptr+i) == '\xff' && *(ptr+ i + 1) == '\x14' && *(ptr+ i + 2) == '\x85'){
printk("find offset %d\n",i);
return ptr + i;
}
}
return NULL;
}
//加解密程序
void blowfish(const char * bfKey,char * pData, long size)
{
int i;
for(i = 0; i < size; ++i){
pData[i]=pData[i]^bfKey[0];
//printk("en : %c\n",pData[i]);
}
}
//找到系统调用表并替换内容
//通过中断向量表,找到系统调用的中断向量
unsigned long *getscTable(void)
{
//中断向量表IDT的入口地址是通过IDTR寄存器来确定的
struct _idt *idt;
unsigned long system_call = 0, sct = 0;
unsigned short offset_low,offset_high;
char *p = NULL;
orig_cr0 = clear_and_ret_cr0(); //注意在这里设置一下cr0
// 从idtr中获得中断描述符(相当于中断号号表)的首地址
//idtr寄存器的内容可以通过汇编指令sidt取出
__asm__("sidt %0" : "=m" (idtr));
setback_cr0(orig_cr0);
//由于每个中端描述符为8个字节,而软中断为int 0x80,据此获取系统调用中断即0x80的中断描述符的
首地址
//idt是获取到系统调用中断向量地址(即每一向量是中断服务程序的入口地址),即0x80地址
idt = (void *)(idtr.base + 8 * 0x80);
offset_low = idt->offset_low;
offset_high = idt->offset_high;
//获取系统调用中断发生时的中断处理例程的地址
system_call = (offset_high<<16)|offset_low;
//找到系统调用表(即中断向量表,系统调用表的入口地址是system_call)中,系统调用项的首地址,即
中断服务程序的入口地址
p = findoffset((char*)system_call);
if(NULL != p) {
//3表示3个指令码
sct = *(unsigned long*)(p+3);
}
return (unsigned long*)sct;
}
//具有解密功能的读
//判断是否是需要解密的文件,如果是执行替换后的read函数将数据拷贝到内核空间解密数据,否则调用
原来的read函数输出数据。
asmlinkage ssize_t hacked_read(unsigned int fd,char * buf,size_t count)
{
int err = 0;
struct file *file;
ssize_t ret = -EBADF;
//根据文件描述符获得文件的file结构,从而获得其文件名
file = fget(fd);
//调用原来的read系统调用将数据输入
ret=orig_read(fd,buf,count);
//判断当前进行read系统调用的文件是否为加载模块时指定的文件
if(!strcmp(file->f_dentry->d_name.name,filename)){
//判断要操作的字符数目是否是操作预先的分配值
if(count > KERNEL_BUF_SIZE ){
kfree(kernel_buf);
kernel_buf=kmalloc(count+10, GFP_KERNEL);
memset(kernel_buf, 0,count+10 );
}
//将数据拷贝到内核空间中
err = copy_from_user(kernel_buf,buf,count);
if(err){
printk("copy_from_user error!\n");
}
//解密数据
Decryption(key,kernel_buf,count);
//将数据拷回到用户空间
err = copy_to_user(buf,kernel_buf,count);
if(err){
printk("copy_to_user error!\n");
}
}
return ret;
}
//具有加密功能的写
//判断是否是需要加密的文件,如果是执行替换后write函数将数据拷贝到内核空间加密数据,否则调用
原来的write函数输入数据。
asmlinkage ssize_t hacked_write(unsigned int fd,char * buf,size_t count)
{
int err = 0;
struct file *file;
ssize_t ret = -EBADF;
//根据文件描述符获得文件的file结构,从而获得其文件名
file = fget(fd);
//判断当前进行write系统调用的文件是否为加载模块时指定的文件
if(!strcmp(file->f_dentry->d_name.name,filename)){
//判断要操作的字符数目是否时操作预先的分配值
if(count > KERNEL_BUF_SIZE ){
kfree(kernel_buf);
kernel_buf=kmalloc(count+10, GFP_KERNEL);
memset(kernel_buf, 0,count+10 );
}
//将数据拷贝到内核空间中
err = copy_from_user(kernel_buf,buf,count);
if(err){
printk("copy_from_user error!\n");
}
//加密数据
Encryption(key,kernel_buf,count);
//将数据拷回到用户空间
err = copy_to_user(buf,kernel_buf,count);
if(err){
printk("copy_to_user error\n");
}
}
//调用原来的write系统调用将数据输入
ret = orig_write(fd,buf,count);
return ret;
}
static int hack_init(void)
{
sys_call_table = getscTable();
if(NULL != sys_call_table){
orig_read=(asmlinkage ssize_t (*)(int ,void *, size_t))sys_call_table[__NR_read];
orig_write=(asmlinkage ssize_t (*)(int ,void *, size_t))sys_call_table[__NR_write];
orig_cr0 = clear_and_ret_cr0();
sys_call_table[__NR_read]=(unsigned long)hacked_read;
sys_call_table[__NR_write]=(unsigned long)hacked_write;
setback_cr0(orig_cr0);
kernel_buf=kmalloc(KERNEL_BUF_SIZE, GFP_KERNEL);
memset(kernel_buf, 0,KERNEL_BUF_SIZE );
}
return 0;
}
void hack_cleanup(void)
{
orig_cr0 = clear_and_ret_cr0();
printk("UnHacked 3!");
//sys_call_table = getscTable();
if(sys_call_table){
sys_call_table[__NR_read]=(unsigned long)orig_read;
sys_call_table[__NR_write]=(unsigned long)orig_write;
}
setback_cr0(orig_cr0);
kfree(kernel_buf);
return;
}
MODULE_LICENSE("GPL");
module_init(hack_init);
module_exit(hack_cleanup);
·生成加解密模块:make
·装载模块,文件名为test.c 密码为zz: insmod encryption.ko key = "zz" filename = "test.c"
·对test.c进行编辑: vi test.c
·卸载模块查看效果 : rmmod encryption
·查看加密后的文件: vi test.c
·加载模块,查看原文件: insmod encryption.ko key = "zz" filename = "test.c"
·查看源文件: vi test.c
·在Fedora 17,Linux 3.3内核中试验成功
关于上一讲中的Makefile的书写 (2013-09-10 23:59:52) 转载▼
标签: pwd make moudles it 分类: 个人日记
obj-m := encryption.o
##m:编译成模块,但不会编译进内核
KDIR := /lib/modules/`uname -r`(反逗点)/build
##指定内核路径
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
linux系统的文件透明加解密的驱动程序(二) (2013-09-11 00:05:14) 转载▼
标签: vfs 加解密 动态加载 file结构体 传递性 分类: 个人日记
这是一个在VFS层劫持系统调用,实现Linux系统的文件透明加解密的驱动程序(二)。最后编写Makefile
(请看上一讲),生成.so文件,动态加载到内核中即可。
#include
······
#include
#define Encryption blowfish
#define Decryption blowfish
#define FOP file->f_dentry->d_inode->i_fop
static char *key = "zz";
static char *filename = "ttt.c";
module_param(key, charp, S_IRUGO);
module_param(filename, charp, S_IRUGO);
char *root_fs = "/test.c";
unsigned int orig_cr0;
typedef ssize_t (*read_t)(struct file *, char __user *, size_t, loff_t *);
typedef ssize_t (*write_t)(struct file *, const char __user *, size_t, loff_t *);
read_t orig_read = NULL;
write_t orig_write = NULL;
unsigned int clear_and_ret_cr0(void)
{
unsigned int cr0 = 0;
unsigned int ret = 0;
asm volatile("movl %%cr0, %�x":"=a"(cr0));
ret = cr0;
cr0 &= 0xfffeffff;
asm volatile("movl %�x, %%cr0"::"a"(cr0));
return ret;
}
void setback_cr0(unsigned int val)
{
asm volatile("movl %�x, %%cr0"::"a"(val));
}
void blowfish(const char *bfkey, char *pData, size_t size)
{
int i = 0;
for(; i < size; ++i){
pData[i] = pData[i]^bfkey[0];
}
}
ssize_t hacked_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
ssize_t ret = -EBADF;
ret = orig_read(file, buf, count, offset);
if(!strcmp(file->f_dentry->d_name.name, filename))
Decryption(key, buf, count);
return ret;
}
ssize_t hacked_write(struct file *file, const char __user *buf, size_t count, loff_t
*offset)
{
ssize_t ret = -EBADF;
if(!strcmp(file->f_dentry->d_name.name, filename))
Encryption(key, (char __user *)buf, count);
ret = orig_write(file, buf, count, offset);
return ret;
}
int patch_vfs(const char *p, read_t *orig_read, write_t *orig_write, read_t new_read,
write_t new_write)
{
struct file *file;
file = filp_open(p, O_RDONLY, 0);
if(IS_ERR(file))
return -1;
orig_cr0 = clear_and_ret_cr0();
if(orig_read)
*orig_read = FOP->read;
if(orig_write)
*orig_write = FOP->write;
((struct file_operations *)(FOP))->read = new_read;
((struct file_operations *)(FOP))->write = new_write;
setback_cr0(orig_cr0);
filp_close(file, 0);
return 0;
}
int unpatch_vfs(const char *p, read_t orig_read, write_t orig_write)
{
struct file *file;
file = filp_open(p, O_RDONLY, 0);
if(IS_ERR(file))
return -1;
orig_cr0 = clear_and_ret_cr0();
((struct file_operations *)(FOP))->write = orig_write;
((struct file_operations *)(FOP))->read = orig_read;
setback_cr0(orig_cr0);
filp_close(file, 0);
return 0;
}
static __init int patch_init(void)
{
patch_vfs(root_fs, &orig_read, &orig_write, hacked_read, hacked_write);
return 0;
}
static __exit void patch_cleanup(void)
{
unpatch_vfs(root_fs, orig_read, orig_write);
}
MODULE_LICENSE("GPL");
module_init(patch_init);
module_exit(patch_cleanup);
·本驱动程序的缺点是必须在/目录下有test.c文件,才能实现加解密功能。
·生成加解密模块:make
·装载模块,文件名为test.c 密码为zz: insmod encryption.ko key = "zz" filename = "test.c"
·对test.c进行编辑: vim test.c
·卸载模块查看效果 : rmmod encryption
·查看加密后的文件: vim test.c
·加载模块,查看原文件: insmod encryption.ko key = "zz" filename = "test.c"
·查看源文件: vim test.c
·在Fedora 17,Linux 3.3内核中试验成功
http://www.jm8848.com 有实验产品,大家可以交流