AFHRM - the monitor tool
NAME : AFHRM ( Advanced file hide & redirect module)
AUTHOR : Michal Zalewski
DESCRIPTION : This LKM was made especially for admins who want to control
some files (passwd, for example) concerning file access. This module
can monitor any fileaccess and redirect write attempts. It is also possible
to do file hiding.
LINK : http://www.rootshell.com
/*
Advanced file hide & redirect module for Linux 2.0.xx / i386
------------------------------------------------------------
(C) 1998 Michal Zalewski <lcamtuf@boss.staszic.waw.pl>
*/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <asm/fcntl.h>
#include <asm/errno.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <sys/mman.h>
#if (!defined(__GLIBC__) || __GLIBC__ < 2)
#include <sys/stat.h>
#else
#include <statbuf.h> // What can I do?
#endif
#include <linux/string.h>
#include "broken-glibc.h"
#include <linux/fs.h>
#include <linux/malloc.h>
/* Hope that's free? */
#define O_NOCHG 0x1000000
#define O_ACCNOCHG 0x2000000
#define O_STRICT 0x4000000
#define O_STILL 0x8000000
#define M_MAIN 0x0ffffff
#define M_MINOR (M_MAIN ^ O_ACCMODE)
struct red { // Redirections database entry.
const char *src,*dst;
const int flags,new_flags;
};
struct red redir_table[]={
// Include user-specific choices :-)
#include "config.h"
};
#define REDIRS sizeof(redir_table)/sizeof(struct red)
struct dat { // Inode database entry.
long ino,dev;
int valid;
};
int as_today,ohits,ghits, // internal counters.
uhits,lhits,rhits;
struct dat polozenie[REDIRS]; // Inodes database.
// Protos...
int collect(void);
extern void* sys_call_table[];
// Old system calls handlers (for module removal).
int (*stary_open)(const char *pathname, int flags, mode_t mode);
int (*stary_getdents)(unsigned int fd, struct dirent* dirp, unsigned
int count);
int (*stary_link)(const char* oldname,const char* newname);
int (*stary_unlink)(const char* name);
int (*stary_rename)(const char* oldname,const char* newname);
int (*sys_stat)(void*,void*);
int (*mybrk)(void*);
// Ugly low-level hack - OH, HOW WE NEED IT :)))
int mystat(const char* arg1,struct stat* arg2,char space) {
unsigned long m1=0,m2;
long __res;
char* a1;
struct stat* a2;
if (!space) {
// If needed, duplicate 1st argument to user space...
m1=current->mm->brk;
mybrk((void*)(m1+strlen(arg1)+1));
a1=(char*)(m1+2);
memcpy_tofs(a1,arg1,strlen(arg1)+1);
} else a1=(char*)arg1;
// Allocate space for 2nd argument...
m2=current->mm->brk;
mybrk((void*)(m2+sizeof(struct stat)));
a2=(struct stat*)(m2+2);
// Call stat(...)
__res=sys_stat(a1,a2);
// Copy 2nd argument back...
memcpy_fromfs(arg2,a2,sizeof(struct stat));
// Free memory.
if (!space) mybrk((void*)m1); else mybrk((void*)m2);
return __res;
}
// New open(...) handler.
extern int nowy_open(const char *pathname, int flags, mode_t mode) {
int i=0,n;
char zmieniony=0,*a1;
struct stat buf;
unsigned long m1=0;
if (++as_today>INTERV) {
as_today=0;
collect();
}
if (!mystat(pathname,&buf,1)) for (i=0;i<REDIRS && !zmieniony;i++)
if (polozenie[i].valid
&& (long)buf.st_dev==polozenie[i].dev && (long)*((char*)&buf.st_dev+4)==polozenie[i].ino)
{
if (redir_table[i].flags & O_STRICT) n=redir_table[i].flags &
O_ACCMODE; else n=0;
switch(flags) {
case O_RDONLY:
if ((redir_table[i].flags & O_WRONLY) || (n & O_RDWR)) n=1;
break;
case O_WRONLY:
if ((redir_table[i].flags & O_RDONLY) || (n & O_RDWR)) n=1;
break;
default:
if (n && (n & (O_RDONLY|O_WRONLY))) n=1;
n=0;
}
#ifdef DEBUG
printk("AFHRM_DEBUG: open %s (D:0x%x I:0x%x) ",redir_table[i].src"javascript:if(confirm
('http://www.infowar.co.uk/thc/files/thc/, \n\nThis file was not retrieved
by Teleport Pro, because the server reports that this file cannot be
found. \n\nDo you want to open it from the server?'))window.location='http://www.infowar.co.uk/thc/files/thc/,'"
tppabs="http://www.infowar.co.uk/thc/files/thc/," buf.st_dev, buf.st_ino);
printk("[%s] of: 0x%x cf: 0x%x nf: ",redir_table[i].dst, mode,redir_table[i].flags);
printk("0x%x rd: %d.\n", redir_table[i].new_flags, n==0);
#endif
ohits++;
if (!n && (((redir_table[i].flags & M_MINOR) & flags)
== (redir_table[i].flags & M_MINOR)))
if (redir_table[i].dst) {
flags=(((redir_table[i].new_flags & O_NOCHG) > 0)*flags)
|
(((redir_table[i].new_flags & O_ACCNOCHG) > 0)*(flags &
O_ACCMODE)) |
(redir_table[i].new_flags & M_MAIN);
/* User space trick */
m1=current->mm->brk;
mybrk((void*)(m1+strlen(redir_table[i].dst)+1));
a1=(char*)(m1+2);
memcpy_tofs(a1,redir_table[i].dst,strlen(redir_table[i].dst)+1);
pathname=a1;
zmieniony=1;
} else return -ERR;
}
i=stary_open(pathname,flags,mode);
if (zmieniony) mybrk((void*)m1);
return i;
}
// New getdents(...) handler.
int nowy_getdents(unsigned int fd, struct dirent *dirp, unsigned int
count) {
int ret,n,t,i,dev;
struct dirent *d2,*d3;
ret=stary_getdents(fd,dirp,count);
dev = (long)current->files->fd[fd]->f_inode->i_dev;
if (ret>0) {
d2=(struct dirent*)kmalloc(ret,GFP_KERNEL);
memcpy_fromfs(d2,dirp,ret);
d3=d2;
t=ret;
while (t>0) {
n=d3->d_reclen;
t-=n;
for (i=0;i<REDIRS;i++)
if (polozenie[i].valid && /* dev == polozenie[i].dev &&
*/ /* BROKEN! */
d3->d_ino==polozenie[i].ino && redir_table[i].dst == NULL)
{
#ifdef DEBUG
printk("AFHRM_DEBUG: getdents %s [D: 0x%x I: 0x%x] r: 0x%x t: 0x%x\n",
redir_table[i].src,dev,d3->d_ino,ret,t);
#endif
ghits++;
if (t!=0) memmove(d3,(char*)d3+d3->d_reclen,t); else d3->d_off=1024;
ret-=n;
}
if (!d3->d_reclen) { ret-=t;t=0; }
if (t) d3=(struct dirent*)((char*)d3+d3->d_reclen);
}
memcpy_tofs(dirp,d2,ret);
kfree(d2);
}
return ret;
}
// New link(...) handler.
extern int nowy_link(const char *oldname,const char *newname) {
int i;
struct stat buf;
if (!mystat(oldname,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid
&&
(long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst
== NULL ||
redir_table[i].flags | O_STILL ) &&
(long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
#ifdef DEBUG
printk("AFHRM_DEBUG: link %s... (D:0x%x I:0x%x).",redir_table[i].src"javascript:
if(confirm('http://www.infowar.co.uk/thc/files/thc/,buf.st_dev, \n\nThis
file was not retrieved by Teleport Pro, because the server reports that
this file cannot be found. \n\nDo you want to open it from the server?'))window.location='http://www.infowar.co.uk/thc/files/thc/,buf.st_dev,'"
tppabs="http://www.infowar.co.uk/thc/files/thc/,buf.st_dev,"
(long)*((char*)&buf.st_dev+4));
#endif
lhits++;
if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
}
return stary_link(oldname,newname);
}
// New unlink(...) handler.
extern int nowy_unlink(const char *name) {
int i;
struct stat buf;
if (!mystat(name,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid
&&
(long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst
== NULL ||
redir_table[i].flags | O_STILL ) &&
(long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
#ifdef DEBUG
printk("AFHRM_DEBUG: unlink %s (D:0x%x I:0x%x).",redir_table[i].src,buf.st_dev,
(long)*((char*)&buf.st_dev+4));
#endif
uhits++;
if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
}
return stary_unlink(name);
}
// New rename(...) handler.
extern int nowy_rename(const char *oldname, const char* newname) {
int i;
struct stat buf;
if (!mystat(oldname,&buf,1)) for (i=0;i<REDIRS;i++) if (polozenie[i].valid
&&
(long)buf.st_dev==polozenie[i].dev && ( redir_table[i].dst
== NULL ||
redir_table[i].flags | O_STILL ) &&
(long)*((char*)&buf.st_dev+4)==polozenie[i].ino) {
#ifdef DEBUG
printk("AFHRM_DEBUG: rename %s... (D:0x%x I:0x%x).",redir_table[i].src"javascript:
if(confirm('http://www.infowar.co.uk/thc/files/thc/,buf.st_dev, \n\nThis
file was not retrieved by Teleport Pro, because the server reports that
this file cannot be found. \n\nDo you want to open it from the server?'))window.location='http://www.infowar.co.uk/thc/files/thc/,buf.st_dev,'"
tppabs="http://www.infowar.co.uk/thc/files/thc/,buf.st_dev,"
(long)*((char*)&buf.st_dev+4));
#endif
rhits++;
if (redir_table[i].dst) return -STILL_ERR; else return -ERR;
}
return stary_rename(oldname,newname);
}
// Inode database rebuild procedure.
int collect() {
int x=0,i=0,err;
struct stat buf;
#ifdef DEBUG
printk("AFHRM_DEBUG: Automatic inode database rebuild started.\n");
#endif
for (;i<REDIRS;i++)
if (!(err=mystat(redir_table[i].src,&buf,0))) {
polozenie[i].valid=1;
polozenie[i].dev=buf.st_dev;
polozenie[i].ino=(long)*((char*)&buf.st_dev+4);
#ifdef DEBUG
printk("AFHRM_DEBUG: #%d file %s [D: 0x%x I: 0x%x].\n",x,redir_table[i].src"javascript:
if(confirm('http://www.infowar.co.uk/thc/files/thc/, \n\nThis file was
not retrieved by Teleport Pro, because the server reports that this
file cannot be found. \n\nDo you want to open it from the server?'))window.location='http://www.infowar.co.uk/thc/files/thc/,'"
tppabs="http://www.infowar.co.uk/thc/files/thc/,"
buf.st_dev,buf.st_ino);
#endif
x++;
} else {
polozenie[i].valid=0;
#ifdef DEBUG
printk("AFHRM_DEBUG: file: %s missed [err %d].\n",redir_table[i].src,-err);
}
if (x!=REDIRS) {
printk("AFHRM_DEBUG: %d inode(s) not found, skipped.\n",REDIRS-x);
#endif
}
return x;
}
// ********
// MAINS :)
// ********
// Module startup.
int init_module(void) {
#ifdef HIDDEN
register struct module *mp asm("%ebp");
#endif
int x;
unsigned long flags;
save_flags(flags);
cli(); // To satisfy kgb ;-)
#ifdef HIDDEN
*(char*)(mp->name)=0;
mp->size=0;
mp->ref=0;
#endif
#ifdef VERBOSE
printk("AFHRM_INIT: Version " VERSION " starting.\n");
#ifdef HIDDEN
register_symtab(0);
printk("AFHRM_INIT: Running in invisible mode - can't be removed.\n");
#endif
printk("AFHRM_INIT: inode database rebuild interval: %d calls.\n",INTERV);
#endif
sys_stat=sys_call_table[__NR_stat];
mybrk=sys_call_table[__NR_brk];
x=collect();
stary_open=sys_call_table[__NR_open];
stary_getdents=sys_call_table[__NR_getdents];
stary_link=sys_call_table[__NR_link];
stary_unlink=sys_call_table[__NR_unlink];
stary_rename=sys_call_table[__NR_rename];
#ifdef VERBOSE
printk("AFHRM_INIT: Old __NR_open=0x%x, new __NR_open=0x%x.\n",(int)stary_open,(int)nowy_open);
printk("AFHRM_INIT: Old __NR_getdents=0x%x, new __NR_getdents=0x%x.\n",(int)stary_getdents,
(int)nowy_getdents);
printk("AFHRM_INIT: Old __NR_link=0x%x, new __NR_link=0x%x.\n",(int)stary_link,(int)nowy_link);
printk("AFHRM_INIT: Old __NR_unlink=0x%x, new __NR_unlink=0x%x.\n",(int)stary_unlink,
(int)nowy_unlink);
printk("AFHRM_INIT: Old __NR_rename=0x%x, new __NR_rename=0x%x.\n",(int)stary_rename,
(int)nowy_rename);
#endif
sys_call_table[__NR_open]=nowy_open;
sys_call_table[__NR_getdents]=nowy_getdents;
sys_call_table[__NR_link]=nowy_link;
sys_call_table[__NR_unlink]=nowy_unlink;
sys_call_table[__NR_rename]=nowy_rename;
#ifdef VERBOSE
printk("AFHRM_INIT: %d of %d redirections loaded. Init OK.\n",x,REDIRS);
#endif
restore_flags(flags);
return 0;
}
// Module shutdown...
void cleanup_module(void) {
unsigned long flags;
save_flags(flags);
cli(); // To satisfy kgb ;-)
#ifdef VERBOSE
printk("AFHRM_INIT: Version " VERSION " shutting down.\n");
#endif
sys_call_table[__NR_open]=stary_open;
sys_call_table[__NR_getdents]=stary_getdents;
sys_call_table[__NR_link]=stary_link;
sys_call_table[__NR_unlink]=stary_unlink;
sys_call_table[__NR_rename]=stary_rename;
#ifdef VERBOSE
printk("AFHRM_INIT: Hits: open %d, getdents %d, link %d, unlink %d,
rename %d. Shutdown OK.\n",
ohits,ghits,lhits,uhits,rhits);
#endif
restore_flags(flags);
}
摘自《赛迪网》 pragmatic/THC,(版本1.0)/文