linux系统的文件透明加解密的驱动程序(一)

linux系统的文件透明加解密的驱动程序(一)

  (2013-09-10 23:30:44)
这是一个在系统调用层劫持系统调用,实现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
·在Fedora 17,Linux 3.3内核中试验成功

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值