3 利用proc实现内核和应用层交互
Linux中Proc被称为虚拟文件系统,位于/proc中,其实就是内存的内容。可以利用Proc实现内核和应用程序的数据交互。
对内核,要使用特殊的函数创建、删除proc文件, 参考下面代码:
3.1内核myProc_test.c代码
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <linux/proc_fs.h>
#include <net/ip.h>
static struct proc_dir_entry* proc_mtd;
static char szProcBuffer[5000];
static char szTemp1[1000], szTemp2[1000];
extern int iAckCount;
static int iWritingLength;
//相当于每执行#cat /proc/myProc_test 一次,就执行profile_read一次, iAckCount就递增一次
static int procfile_read(char* buffer, char** buffer_location, off_t offset, int buffer_legnth, int zero)
{
int iLen, iReturnLength;
if( offset > 0 ) return 0;
iWritingLength = 0;
sprintk(szTemp1, "iAckCount = %d/n", iAckCount++);
if( (iWritingLength + strlen(szTemp1)) < 5000)
{
strcat(szProcBuffer, szTemp1);
iWritingLength = strlen(szProcBuffer);
}
else
{
szProcBuffer[0] = '/0';
iWritingLength= 0;
}
*buffer_location = szProcBuffer;
iReturnLength = iWritingLength;
iWritingLength = 0;
return iReturnLength;
}
static int procfile_write(struct file* file, const char* buffer, unsigned long count)
{
iWritingLength = count;
if(iWritingLength > 5000 ) iWritingLength = 5000;
if( copy_from_user(szProcBuffer, buffer, iWritingLength))
{
return -EFAULT;
}
return iWritingLength;
}
int init_hook(void)
{
//create dir /proc/myProc_test
if((proc_mtd = create_proc_entry("myProc_test", 0, 0)))
{
proc_mtd->read_proc = procfile_read;
proc_mtd->write_proc = procfile_write;
printk("myProc: myProc installed/n");
}
return 0;
}
void remove_hook(void)
{
iAckCount = 0;
if( proc_mtd) remove_proc_entry("myProc_test", 0);
printk("myProc: myProc removed/n");
}
MODULE_LICENSE("GPL");
module_init(init_hook);
module_exit(remove_hook);
编译后,insmod加载后,执行
#cat /proc/myProc_test
iAckCount = 0;
#cat /proc/myProc_test
iAckCount = 1;
可以看到,内核的变量在增长
3.2应用层test.c
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <curses.h>
#include <stdlib.h>
int main( int argc, char* argv[])
{
FILE* pf;
if(( pf = fopen("/proc/myProc_test", "wb")) == 0)
{
return -1;
}
char buf[] = ("test --> /proc/myProc_test/n");
int w = fprintf(pf, "%s/n",buf);
fclose(pf);
printf("write: %d/n", w);
return 0;
}
编译后
#./test
#cat /myProc_test
test --> /proc/myProc_test
iAckCount = 2;
可以看到,应用程序的字符串内核中可以调用读取了
3.3内核写入/proc中
将myHook_TCPSYN修改为,写入/proc中,不用重编译内核,直接修改Module代码即可
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/ip.h>
#include <linux/proc_fs.h>
#include <net/ip.h>
//big-endian和little-endian转换,big用在网络中,而linux中是little
#define u32toValue(x) ((__u32)( /
(((__u32)(x) & (__u32)0x000000ffUL) << 24) | /
(((__u32)(x) & (__u32)0x0000ff00UL) << 8) | /
(((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | /
(((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
extern int myHook_TCPSYN_count;
extern int (*myHook_TCPSYN)(struct sk_buff*);
//转为可打印地址
static void IpAddressToString(unsigned long i, char* s)
{
unsinged long j1, j2,j3,j4;
j1 = (i & 0x000000FF);
j2 = (i & 0x0000FF00)>>8;
j3 = (i & 0x00FF0000)>>16;
j4 = (i & 0xFF000000)>>24;
sprintf(s, "%u.%u.%u.%u", j4,j3,j2,j1); //注意顺序
}
static struct proc_dir_entry* proc_mtd;
static char szProcBuffer[5000];
static char szTmp[1000];
//相当于每执行#cat /proc/myProc_test 一次,就执行profile_read一次, iAckCount就递增一次
static int profile_read(char* buffer, char** buffer_location, off_t offset, int buffer_legnth, int zero)
{
int iLen, iReturnLength;
if( offset > 0 ) return 0;
*buffer_location = szProcBuffer; //相当于接口参数从全局变量传入值
iReturnLength = iWritingLength;
iWritingLength = 0;
return iReturnLength;
}
//hook函数的实现
int myHook_TCPSYN_impl(struct sk_buff* skb)
{
strcut iphdr* iph;
strcut tcphdr* th;
char szSourceIP[50], szTargetIP[50];
iph = skb->nh.iph;
if( iph->protocol == IPPROTO_TCP) //判断是否是TCP
{
th = skb->h.th;
if( th->syn) //判断是否是SYN包
{
IpAddressToString(u32toValue(skb->nh.iph->saddr), szSourceIP);
IpAddressToString(u32toValue(skb->nh.iph->daddr), szTargetIP);
if( strcmp(szSourceIP, "192.168.0.1") == 0 && //判断源地址是否是自己, 根据实践地址修改
strcmp(szTargetIP, "192.168.0.2") == 0) //判断目的地址, 根据实践地址修改
{
myHook_TCPSYN_count++;
if( myHook_TCPSYN_count > 0x0fffff) myHook_TCPSYN_count = 0;
//原来是printk显示到/var/log/message中
printk(%s --> %s ===> SYNcount=%d/n", szSourceIP, szTargetIP, myHook_TCPSYN_count);
sprintf(szTmp,"%s --> %s ===> SYNcount=%d/n", szSourceIP, szTargetIP, myHook_TCPSYN_count);
//不需要调用函数,只需要将字符串写入全局变量就可以,每次#cat时读取
memcpy(&szProcBuffer[iWritingLength], szTmp, strlen(szTmp));
iWritingLength += strlen(szTmp);
}
}
}
return 0;
}
static int init_hook( void )
{
myHook_TCPSYN = myHook_TCPSYN_impl; //后门函数指向了真正的实现函数
printk("myHook_TCPSYN installed"); //表示加载成功
return 0;
}
static int exit_hook(void)
{
myHook_TCPSYN= 0; //后门函数清0
printk("myHook_TCPSYN removed/n"); //表示卸载成功
return 0;
}
MODULE_LICENSE("GPL"); //表示采用的开放协议
module_init(init_hook); //为当加载Module时,自动调用init_hook
module_exit(exit_hook); //当卸载Module时,自动调用exit_hook;
编译后,
#ping 192.168.0.1
#cat /proc/myProc_test
192.168........................ //显示值,表示内核写入了/proc中,应用程序也完全可以正常读取