Problems...

... and solutions by <creative.sysadms@chalmers.se>

Maintained by <appro@fy.chalmers.se>


P. MathLM, Mathematica License Manager, is vulnerable to trivial denial-of-service attack...
S. The LD_PRELOAD module below agressively timeouts transactions as well as fixes several other problems with mathlm, see commentary section in the source for further details.

[an error occurred while processing this directive] [an error occurred while processing this directive]
mathlm.sentry.c
/* * Mathematica license manager sentry. * * Mathematica license manager appears to be a single-threaded * application performing a single transaction at a time. This means * that clients that failed to complete transaction for some reason, * e.g. loss of network connectivity or even for a malicious purpose * (see http://www.securityfocus.com/archive/1/200462), shall prevent * other clients from obtaining the license. This module aggressively * times out transactions thus effectively avoids the denial of service. * * Then I enforce SO_REUSEADDR option so that the mathlm process can be * restarted instantly without having to wait till all TIME_WAIT-ed * connection disappear from netstat output. * * Then I mimic BSD signal semantic as apparently that's what Wolfram * programmers expect... Problem here is that Wolfram doesn't realize * that unlike BSD Solaris resets the disposition of signal to some * predefined default (can be coredump, exit or ignore depending on * signal) upon signal delivery and if one intends to keep handling the * signal, then one has to either reset handler in handler itself or * make sure SA_RESETHAND is not set. * * Then I map NULL pointer read-only so that printf("\"%s License * returned\"",NULL); doesn't cause segmentation violation. I don't * endorse this programming practice, just trying to survive... * * To compile (on Solaris): * * cc -O -D_REENTRANT -Kpic -G -z text -o mathlm.acl.so mathlm.acl.c * * To engage (on Solaris): * * env LD_PRELOAD=/some/place/mathlm.acl.so mathlm * * Other platforms on demand. * <appro@fy.chalmers.se> */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <dlfcn.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <inet/common.h> #include <poll.h> #include <signal.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> #define TIMEOUT 500 /* 1/2 second */ #pragma init(setmeup) static void setmeup() { int fd; if ((fd=open("/dev/zero",O_RDONLY)) >= 0) mmap(NULL,sizeof(void*),PROT_READ,MAP_PRIVATE|MAP_FIXED,fd,0), close(fd); setsid(); } int _accept (int s, struct sockaddr *addr, void *addrlen); ssize_t _read (int fd, void *buf, size_t nbyte); ssize_t _write (int fd, const void *buf, size_t nbyte); int _close (int fd); int _setsockopt(int socket, int level, int option_name, const void *option_value, unsigned int option_len); static fd_set sockets_set; void (*signal (int sig,void (*handler)()))() { struct sigaction sa; void (*ret) (); sigaction (sig,NULL,&sa); sa.sa_flags &= ~SA_RESETHAND; sa.sa_flags |= SA_RESTART; ret = sa.sa_handler; sa.sa_handler = handler; sigaction (sig,&sa,NULL); return ret; } /* * Why on whole earth does mathlm set SO_REUSEADDR to 0? Why do we have * to wait till every closed connection in TIME_WAIT state times out? */ int setsockopt (int socket, int level, int option_name, const void *option_value, unsigned int option_len) { int on=1; if (level==SOL_SOCKET && option_name==SO_REUSEADDR) return _setsockopt (socket,level,option_name,(void *)&on,sizeof(on)); else return _setsockopt (socket,level,option_name,option_value,option_len); } /* * Compliment read and write on sockets returned by accept with timeout. */ ssize_t read(int fd, void *buf, size_t nbyte) { ssize_t ret=1; struct pollfd fds; if (fd<0 || !FD_ISSET (fd,&sockets_set)) return _read (fd,buf,nbyte); fds.fd = fd; fds.events = POLLIN; ret = poll (&fds,1,TIMEOUT); if (ret>0 && !(fds.revents&(POLLERR|POLLHUP))) ret = _read (fd,buf,nbyte); else { errno = EPIPE; shutdown (fd,0); } return ret; } ssize_t write(int fd, const void *buf, size_t nbyte) { ssize_t ret; struct pollfd fds; if (fd<0 || !FD_ISSET (fd,&sockets_set)) return _write (fd,buf,nbyte); fds.fd = fd; fds.events = POLLOUT; ret = poll (&fds,1,TIMEOUT); if (ret>0 && !(fds.revents&(POLLERR|POLLHUP))) ret = _write (fd,buf,nbyte); else { errno = EPIPE; shutdown (fd,0); } return ret; } /* * ... mark the socket in sockets_set */ int accept (int s, struct sockaddr *addr, void *addrlen) { int ret; struct sockaddr_in *_addr; if ((ret = _accept (s,addr,addrlen)) >= 0) FD_SET (ret,&sockets_set); return ret; } int close (int fd) { if (fd>=0) FD_CLR(fd,&sockets_set); return _close(fd); }


P. ICA Client for Unix, version 5.x and below disregards umask and ignorantly chmods all the files it touches to rwxrwxrwx...
S. Shortest module ever:-) Compile with gcc -shared and LD_PRELOAD to wfica.

[an error occurred while processing this directive] [an error occurred while processing this directive]
wfica_chmod.c
int chmod () { return 0; }


P. Solaris lpq exhausts privileged ports...
S. We run SAMBA and export a bunch of printers, lpq is called rather often and privileged ports tend to get exhausted now and then. So I intercept calls to rresvport and set SO_LINGER option on allocated socket.

[an error occurred while processing this directive] [an error occurred while processing this directive]
lpq_letitgo.c
#if 0 Compile with: cc -O -D_REENTRANT -Kpic -G -z text -xstrconst -o lpq_letitgo.so lpq_letitgo.c Usage is a bit challenging as you can't LD_PRELOAD modules to set-uid apps. Following set-root-uid perl script covers up for it. #!/usr/bin/perl -T foreach $i (keys %ENV) { if ($i !~ /^(TZ|USER|LOGNAME|HOME|TMPDIR|PRINTER|LPDEST)$/) { delete $ENV{$i}; } } $ENV{LD_PRELOAD}="/usr/local/lib/lpq_letitgo.so"; $ENV{_DROP_TO_RUID}="$<"; $<=$>; exec ("/usr/ucb/lpq",@ARGV); #endif #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <dlfcn.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <inet/common.h> #include <poll.h> #include <signal.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> static int (*__lib_rresvport_af)(int *,int); int _setsockopt(int socket, int level, int option_name, const void *option_value, unsigned int option_len); int rresvport_af (int *alport, int af) { int ret; struct linger l={1,0}; ret=(*__lib_rresvport_af)(alport,af); if (ret>=0) _setsockopt (ret,SOL_SOCKET,SO_LINGER,(void *)&l,sizeof(l)); return ret; } #pragma init(setmeup) static void setmeup() { int fd; char *env; uid_t uid; void *libh; if (getuid()==0 && (env=getenv("_DROP_TO_RUID"))) { if (uid = strtoul (env,NULL,10)) setreuid (uid,-1); } if (!(libh = dlopen ("libsocket.so.1",RTLD_LAZY))) { fprintf (stderr,"can't dlopen(\"%s\").","libsocket.so.1"); exit(1); } if (!(__lib_rresvport_af = (int (*)(int *,int))dlsym (libh,"rresvport_af"))) { fprintf (stderr, "can't link to \"%s\" from %s", "rresvport_af","libsocket.so.1"); exit (1); } } #if 0 int setsockopt (int socket, int level, int option_name, const void *option_value, unsigned int option_len) { struct linger l={1,0}; if (level==SOL_SOCKET && option_name==SO_LINGER) return _setsockopt (socket,level,option_name,(void *)&l,sizeof(l)); else _setsockopt (socket,SOL_SOCKET,SO_LINGER,(void *)&l,sizeof(l)); return _setsockopt (socket,level,option_name,option_value,option_len); } #endif


P. VMware 2.x didn't support mounting of CD-ROM images, you had to burn a CD to access the data...
S. The module below intercepts open and ioctl system calls and emulates minimum set of CD-ROM ioctls on a plain file. See the source for details on usage.

[an error occurred while processing this directive] [an error occurred while processing this directive]
vmcdimage.c
#if 0 # # http://fy.chalmers.se/~appro/linux/vmcdimage.c # # Copyright (c) 2001 by <appro@fy.chalmers.se> # # Permissions to copy and use are granted as long as the copyright # notice is retained. All warranties are however disclaimed. # # This module emulates CD-ROM ioctls on plain files and is ment to # be LD_PRELOAD-ed to VMware. Idea is to trick VMware to mount # CD-ROM images without burning a CD. Once preloaded (e.g. with # Ženv LD_PRELOAD=./vmcdimage.so vmwareŽ) the module intercepts a # number of system calls to /dev/cdrom and emulates responses # whenever appropriate. Note that /dev/cdrom is normally a symbolic # link and you can adjust it at your will on-the-fly! No VMware # restart is required! If you find the idea of using /dev/cdrom # disgusting, set _DEV_CDROM environmnet variable and adjust your # VMware guest configuration accordingly. Keep in mind that vmware # is a set-root-uid program, so that you have to be root in order to # LD_PRELOAD this module. # # Cheers, Andy:-) # /bin/sh << EOS MODNAME=\`basename "$0" .c\` set -x gcc -O -fbuiltin -fomit-frame-pointer -fPIC -shared -o \$MODNAME.so "$0" -ldl EOS exit # # Revision history: # # 20010822 Support for bootable CD images added. # 20010823 Support for mortal users(*) added. # # (*) as LD_PRELOAD works for root only, mortals has to pass a # set-root-uid wrapper of the following design (feel motivated # to rewrite it in C): # #!/usr/bin/perl -T foreach $i (%ENV) { if ($i !~ /^(DISPLAY|XAUTHORITY|TZ|USER|LOGNAME|HOME|_DEV_CDROM)$/) { delete $ENV{$i}; } } $ENV{LD_PRELOAD} ="/usr/lib/vmware/vmcdimage.so"; $ENV{_DROP_TO_RUID}="$<"; # instruct the .so to drop privileges $<=$>; exec ("/usr/bin/vmware",@ARGV); # #endif #ifndef __linux # error "Linux only" #endif #define _LIBC #define _LIBC_REENTRANT #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <dlfcn.h> #include <linux/cdrom.h> #ifdef __GNU_LIBRARY__ # if __GNU_LIBRARY__==6 # define LIBC_SO "libc.so.6" # else # define LIBC_SO "libc.so.5" # endif #else # error "what's your libc?" #endif static int (*__libc_open)(const char *path,int flags, ...); static int (*__libc_close)(int f); static int (*__libc_ioctl)(int f, int request, ...); static char _dev_cdrom_path[1024]="/dev/cdrom"; static fd_set _dev_cdrom_fds; static int _dev_cdrom_fd=-1; static struct stat _dev_cdrom_stat; asm (" .section \".init\" call _dev_cdrom_init "); static _dev_cdrom_init() { char *env; uid_t uid; void *libh; if (getuid()==0 && (env=getenv("_DROP_TO_RUID"))) { if (uid = strtoul (env,NULL,10)) setresuid (uid,-1,-1); } if (!(libh = dlopen (LIBC_SO,RTLD_LAZY))) { fprintf (stderr,"can't dlopen(\"%s\").",LIBC_SO); exit (1); } if (!(__libc_close = (int (*)(int))dlsym (libh,"close"))) { fprintf (stderr,"can't link to \"close\" from %s",LIBC_SO); exit (1); } if (!(__libc_open = (int (*)(const char *,int,...))dlsym (libh,"open"))) { fprintf (stderr,"can't link to \"open\" from %s",LIBC_SO); exit (1); } if (!(__libc_ioctl = (int (*)(int,int,...))dlsym (libh,"ioctl"))) { fprintf (stderr,"can't link to \"ioctl\" from %s",LIBC_SO); exit (1); } if (env=getenv ("_DEV_CDROM")) strncpy (_dev_cdrom_path,env,sizeof(_dev_cdrom_path)-1); } static int safer_open (const char *path,int flags) { uid_t uid; int fd; setresuid (-1,0,-1); fd = (*__libc_open) (path,flags); if (uid=getuid()) /* mortal user running */ { setresuid (-1,uid,-1); if (fd>=0) { /* make sure it's a cdrom device! */ if ((*__libc_ioctl) (fd,CDROM_DRIVE_STATUS,INT_MAX) == -1) { close (fd); fd = (*__libc_open) (path,flags); } } else /* might happen in NFS environment */ { fd = (*__libc_open) (path,flags); } } return fd; } static int my_open (const void *ra,const char *path,int flags,mode_t mode) { int fd; if (strcmp (path,_dev_cdrom_path)) return -ENOSYS; if (_dev_cdrom_fd==-1) { flags &= ~O_CREAT; if ((fd=safer_open (path,flags))>=0) { FD_SET(fd,&_dev_cdrom_fds); fstat (_dev_cdrom_fd=fd,&_dev_cdrom_stat); } return fd; } else { if ((fd=dup(_dev_cdrom_fd))>=0) { FD_SET(fd,&_dev_cdrom_fds); } return fd; } return -ENOSYS; } static int my_close (const void *ra,int fd) { FD_CLR(fd,&_dev_cdrom_fds); if (fd == _dev_cdrom_fd) _dev_cdrom_fd=-1; return -ENOSYS; } static int my_ioctl (const void *ra,int fd,int request,int arg) { static int cdrom_options=0xd,media_changed=1; struct stat sb; int newfd,i,ret; off_t size; if (((request>>8)&0xff) == 'S') { if (fd >= 0 && FD_ISSET(fd,&_dev_cdrom_fds)) { fprintf (stderr,"CDROM ioctl(%d,0x%x,0x%08x): ",fd,request,arg); switch (request) { case CDROMPAUSE: case CDROMRESUME: case CDROMPLAYMSF: case CDROMPLAYTRKIND: case CDROMVOLREAD: case CDROMSUBCHNL: fprintf (stderr,"discarding audio ioctls\n"); __set_errno(ENOSYS); return -1; case CDROM_MEDIA_CHANGED: if (media_changed) { fprintf (stderr,"\"media\" changed\n"); cdrom_options = 0xd; media_changed = 0; return 1; } /* no break; */ case CDROM_DRIVE_STATUS: if (stat (_dev_cdrom_path,&sb) != 0) { fprintf (stderr,"\"tray\" is open\n"); return CDS_TRAY_OPEN; } if (sb.st_dev != _dev_cdrom_stat.st_dev || sb.st_ino != _dev_cdrom_stat.st_ino ) { newfd = safer_open (_dev_cdrom_path,O_RDONLY|O_NONBLOCK); if (newfd < 0) { fprintf (stderr,"close the \"tray\"\n"); return CDS_TRAY_OPEN; } dup2 (newfd,_dev_cdrom_fd); (*__libc_close) (newfd); fprintf (stderr,"changing \"media\", "); memcpy (&_dev_cdrom_stat,&sb,sizeof(struct stat)); media_changed = 1; } else if (sb.st_size != _dev_cdrom_stat.st_size) { /* use with extreme caution */ fprintf (stderr,"\"media\" is changing, "); memcpy (&_dev_cdrom_stat,&sb,sizeof(struct stat)); media_changed = 1; } /* no break; */ default: if (!S_ISREG(_dev_cdrom_stat.st_mode)) { fprintf (stderr,"assuming hardware %s\n",_dev_cdrom_path); return -ENOSYS; } } switch (request) /* minimum ioctl set to mount CD-ROM image under VMware 2.0.4 */ { case CDROMMULTISESSION: fprintf (stderr,"emulating single session\n"); i=((struct cdrom_multisession *)arg)->addr_format; memset ((void *)arg,0,sizeof(struct cdrom_multisession)); ((struct cdrom_multisession *)arg)->addr_format=i; if (i==CDROM_MSF) ((struct cdrom_multisession *)arg)->addr.msf.second=2; else if (i==CDROM_LBA) ((struct cdrom_multisession *)arg)->addr.lba=0; else { __set_errno(EINVAL); return -1; } return 0; case CDROMREADTOCHDR: fprintf (stderr,"emulating single track\n"); ((struct cdrom_tochdr *)arg)->cdth_trk0=1; ((struct cdrom_tochdr *)arg)->cdth_trk1=1; return 0; case CDROMREADTOCENTRY: { struct cdrom_tocentry *te=(struct cdrom_tocentry *)arg; /* I'm not sure about these three, suggestions appreciated! */ te->cdte_adr = 1; te->cdte_ctrl = 4; te->cdte_datamode = 0; switch (te->cdte_track) { case 1: /* the track at LBA #0 */ if (te->cdte_format == CDROM_MSF) te->cdte_addr.msf.frame = 0, te->cdte_addr.msf.second = 2, te->cdte_addr.msf.minute = 0; else if (te->cdte_format == CDROM_LBA) te->cdte_addr.lba = 0; else { __set_errno(EINVAL); return -1; } fprintf (stderr,"track #0 at 0\n"); break; case 0xaa: /* the lead-out at st_size */ if (te->cdte_format == CDROM_MSF) size = _dev_cdrom_stat.st_size + 150, te->cdte_addr.msf.frame = size%75, te->cdte_addr.msf.second = (size/=75)%60, te->cdte_addr.msf.minute = (size)/60; else if (te->cdte_format == CDROM_LBA) te->cdte_addr.lba = _dev_cdrom_stat.st_size; else { __set_errno(EINVAL); return -1; } fprintf (stderr,"track #aa at %d\n",_dev_cdrom_stat.st_size); break; default: fprintf (stderr,"bad track #%x\n",te->cdte_track); __set_errno(EINVAL); return -1; } return 0; } case CDROM_DRIVE_STATUS: fprintf (stderr,"\"disk\" is always in\n"); return CDS_DISC_OK; case CDROM_MEDIA_CHANGED: fprintf (stderr,"\"media\" not changed\n"); return 0; case CDROM_DISC_STATUS: fprintf (stderr,"emulating generic data cd\n"); return CDS_DATA_1; case CDROM_LOCKDOOR: fprintf (stderr,"locking, doing nothing...\n"); return 0; case CDROMEJECT: fprintf (stderr,"ejecting, doing nothing...\n"); media_changed = 1; return 0; case CDROM_SET_OPTIONS: cdrom_options |= arg; fprintf (stderr,"emulated uopt is 0x%x\n",cdrom_options); return cdrom_options; case CDROM_CLEAR_OPTIONS: cdrom_options &= ~arg; fprintf (stderr,"emulated uopt is 0x%x\n",cdrom_options); return cdrom_options; default: fprintf (stderr,"unimplemented"); __set_errno(ENOSYS); return -1; } } } return -ENOSYS; } asm (" .text .type __open,@function .global __open .type open,@function .global open .align 4 __open: open: call my_open cmpl $-38,%eax jne 1f jmp *__libc_open 1: ret .size __open,.-__open .size open,.-open .text .type __close,@function .global __close .type close,@function .global close .align 4 __close: close: call my_close cmpl $-38,%eax jne 1f jmp *__libc_close 1: ret .size __close,.-__close .size close,.-close .type ioctl,@function .global ioctl .align 4 ioctl: call my_ioctl cmpl $-38,%eax jne 1f jmp *__libc_ioctl 1: ret .size ioctl,.-ioctl ");


P. VMware 3.x doesn't support mounting of DVD-ROM images. Well, you can mount it, but it fails to read beyond 2GB...
S. I've started by extending vmcdimage.c above with minimum support for CDROM_SEND_PACKET ioctls, to make it possible to mount the image as "raw" CD-ROM... But it turned out that following is more than sufficient to cross the 2GB limit.

[an error occurred while processing this directive] [an error occurred while processing this directive]
open64.c
#if 0 /bin/sh << EOS MODNAME=\`basename "$0" .c\` set -x gcc -O -fPIC -shared -o \$MODNAME.so "$0" EOS exit #endif asm(".text\n" ".type __open,@function\n" ".global __open\n" ".type open,@function\n" ".global open\n" ".align 4\n" "__open:\n" "open:\n" " orl $0100000,8(%esp) #O_LAGREFILE\n" " jmp __open64\n" ".size __open,.-__open\n" ".size open,.-open\n" );


P. It's impossible to get VMware 4.x working with ESD, Enlightenment Sound Daemon...
S. VMware recommends to get rid of it, but I see no reason why should I do that:-) But there is a catch! esddsp is the very same kind of fiddler as myself. It plays on LD_PRELOAD-ing modules intercepting calls to open(2) and couple of others. Setting LD_PRELOAD variable prior esddsp vmware invocation wouldn't work, as it will be [ignorantly] overridden by the esddsp script. Therefore, LD_PRELOAD variable has to be set according to the example in attached code. Order is important! In case you wonder what's the heck with VMware 4.x. As the code implies the problem is that VMware 4.x calls open64(2) while libesddps.so re-implements open(2). So the snippet simply forwards calls to open64(2) to open(2) enforcing in O_LARGEFILE flag, so that the result is equivalent.

[an error occurred while processing this directive] [an error occurred while processing this directive]
64open.c
#if 0 /bin/sh << EOS MODNAME=\`basename "$0" .c\` set -x gcc -O -fPIC -shared -o \$MODNAME.so "$0" env LD_PRELOAD_PATH="/usr/lib" \ LD_PRELOAD="libesddsp.so libesd.so `pwd`/\$MODNAME.so" \ ESDDSP_NAME="vmware" vmware EOS exit #endif asm(".text\n" ".type open64,@function\n" ".global open64\n" ".align 4\n" "open64:\n" " orl $0100000,8(%esp) #O_LAGREFILE\n" " jmp open\n" ".size open64,.-open64\n" );


P. SCSI analyzers cost thousands of dollars...
S. If you have VMware, you can easily spy on SCSI "traffic" by exporting "Generic" SCSI devices to the guest OS and pre-loading this module to vmware binary. You most likely want to customize it for your own particular needs.

[an error occurred while processing this directive] [an error occurred while processing this directive]
sgiosniffer.c
#if 0 # /bin/sh << EOS MODNAME=\`basename "$0" .c\` set -x gcc -O -fbuiltin -fomit-frame-pointer -fPIC -shared -o \$MODNAME.so "$0" -ldl EOS exit # #endif #ifndef __linux # error "Linux only" #endif #define _LIBC #define _LIBC_REENTRANT #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <dlfcn.h> #include <scsi/sg.h> #include <linux/cdrom.h> #ifdef __GNU_LIBRARY__ # if __GNU_LIBRARY__==6 # define LIBC_SO "libc.so.6" # else # define LIBC_SO "libc.so.5" # endif #else # error "what's your libc?" #endif static int (*__libc_ioctl)(int f, int request, ...); asm (" .section \".init\" call _dev_cdrom_init "); static _dev_cdrom_init() { char *env; uid_t uid; void *libh; if (!(libh = dlopen (LIBC_SO,RTLD_LAZY))) { fprintf (stderr,"can't dlopen(\"%s\").",LIBC_SO); exit (1); } if (!(__libc_ioctl = (int (*)(int,int,...))dlsym (libh,"ioctl"))) { fprintf (stderr,"can't link to \"ioctl\" from %s",LIBC_SO); exit (1); } } static int _lntox(unsigned char *p,int n,char *s) { static char conv[]="0123456789ABCDEF"; int i; for (i=0;i<n;i++,p++,s+=3) s[0]=conv[(p[0]>>4)&0xF], s[1]=conv[p[0]&0xF], s[2]=' '; return n*3; } static int my_ioctl (const void *ra,int fd,int request,void *p) { int ret,len; unsigned char c; static char out[128]; #if 1 if (request == SG_IO) { struct sg_io_hdr *arg=(struct sg_io_hdr *)p; ret=(*__libc_ioctl)(fd,request,arg); c=arg->cmdp[0]; if (c!=0x28 /* READ(10) */ && c!=0x2A /* WRITE(10) */ && c!=0x00 /* TEST UNIT READY */ && c!=0x4A /* GET NOTIFICATION */ && c!=0x1E /* PREVENT REMOVAL */ && c!=0x5C /* READ BUFFER CAPACITY */ ) { len=_lntox(arg->cmdp,10,out); if (arg->dxfer_direction != SG_DXFER_NONE) { out[len-1]=arg->dxfer_direction&1?'<':'>'; len+=_lntox(arg->dxferp,arg->dxfer_len>16?16:arg->dxfer_len,out+len); out[len++]='\n'; out[len]='\0'; } else { out[len++]='\n';out[len]='\0'; } write(2,out,len); } return ret; } else if (request == CDROM_SEND_PACKET) { struct cdrom_generic_command *arg=(struct cdrom_generic_command *)p; ret=(*__libc_ioctl)(fd,request,arg); c=arg->cmd[0]; if (c!=0x28 /* READ(10) */ && c!=0x2A /* WRITE(10) */ && c!=0x00 /* TEST UNIT READY */ && c!=0x4A /* GET NOTIFICATION */ && c!=0x1E /* PREVENT REMOVAL */ && c!=0x5C /* READ BUFFER CAPACITY */ ) { len=_lntox(arg->cmd,10,out); if (arg->data_direction != CGC_DATA_NONE) { out[len-1]=arg->data_direction&1?'>':'<'; len+=_lntox(arg->buffer,arg->buflen>16?16:arg->buflen,out+len); out[len++]='\n'; out[len]='\0'; } else { out[len++]='\n';out[len]='\0'; } write(2,out,len); } return ret; } #endif return -38; } asm (" .text .type ioctl,@function .global ioctl .align 4 ioctl: call my_ioctl cmpl $-38,%eax jne 1f jmp *__libc_ioctl 1: ret .size ioctl,.-ioctl ");


P. Solaris doesn't recognize HOSTALIASES environment variable:-(
S. Compile and LD_PRELOAD this libhost.c module of Johan Rudholm's design. See the source for further details. Posted with explicit Johan's permission.

[an error occurred while processing this directive] [an error occurred while processing this directive]
libhost.c
/* Hostaliases for SunOS 5.6, 5.7, 5.8 and 5.9 by Johan Rudholm, 2003-04 == To compile: % cc -O -D_REENTRANT -Kpic -G -z text -ldl -lsocket -lnsl -o libhost.so \ libhost.c % gcc -O -D_REENTRANT -fpic -shared -z text -ldl -lsocket -lnsl -o libhost.so \ libhost.c == To use: % env LD_PRELOAD=/path/to/libhost.so command or you can setenv LD_PRELOAD but this will make setuid and setgid programs complain about libhost.so not being in a secure directory (see ld.so.1(1)). == Some information: I've located four functions that seem to be most oftenly used to look up names, these are: gethostbyname gethostbyname_r getipnodebyname getaddrinfo The two last functions doesn't seem to exist on 5.6 and 5.7 but this library still implements them, however since they will never be called this won't be a problem.. I had to implement my own lookup function for hostaliases since res_hostalias(3RESOLV) is horribly slow (reads one byte at a time) and only seems to exist on 5.8 and 5.9. Thanks to Andy Polyakov for help with the asm() and more :-) */ #define getaddrinfo mask_getaddrinfo_declaration #include <netdb.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdio.h> #include <sys/socket.h> #include <arpa/nameser.h> #include <stdlib.h> #include <dlfcn.h> #include <link.h> #include <netinet/in.h> #include <sys/utsname.h> #include <ctype.h> #undef getaddrinfo /* * Get the real symbols from their libs */ struct hostent *(*__gethostbyname) (const char *name); struct hostent *(*__gethostbyname_r) (const char *, struct hostent *, char *, int, int *); static int (*__getaddrinfo) (const char *, const char *, const void *, void **); static struct hostent *(*__getipnodebyname) (const char *, int, int, int *); static const char *hostalias(const char *name, char *buf, int bufsize); static char *gethostbyname_name = NULL; #if defined(__SUNPRO_C) # pragma init(setmeup) /* SPARCCompiler specific #pragma */ #elif defined(__GNUC__) # if defined(__i386__) && (defined(sun) || defined(__sun)) /* Solaris x86 assembler doesn't accept quotes around .init:-( */ asm (".section .init call setmeup"); # elif defined(__i386__) || defined(__sparc__) asm (".section \".init\" call setmeup nop"); # elif defined(__alpha__) && defined(__linux) asm (".section \".init\" jsr setmeup"); # endif #endif static void setmeup (void) { void *handle, *handle2; struct utsname uts; if (uname (&uts) < 0) { fprintf (stderr, "uname failed\n"); exit (1); } handle2 = dlopen ("libnsl.so.1", RTLD_LAZY); if (handle2 == NULL) { fprintf (stderr, "Cannot dlopen libnsl.so.1\n"); exit (1); } __gethostbyname = (struct hostent * (*) (const char *)) dlsym (handle2, "gethostbyname"); if (__gethostbyname == NULL) { fprintf (stderr, "Cannot dlsym gethostbyname\n"); exit (1); } __gethostbyname_r = (struct hostent * (*) (const char *, struct hostent *, char *, int, int *)) dlsym (handle2, "gethostbyname_r"); if (__gethostbyname_r == NULL) { fprintf (stderr, "Cannot dlsym gethostbyname_r\n"); exit (1); } if (!strcmp (uts.sysname, "SunOS") && strcmp (uts.release, "5.8") >= 0) { handle = dlopen ("libsocket.so.1", RTLD_LAZY); if (handle == NULL) { fprintf (stderr, "Cannot dlopen libsocket.so.1\n"); exit (1); } __getaddrinfo = (int (*)(const char *, const char *, const void *, void **)) dlsym (handle, "getaddrinfo"); if (__getaddrinfo == NULL) { fprintf (stderr, "Cannot dlsym getaddrinfo\n"); exit (1); } __getipnodebyname = (struct hostent * (*) (const char *, int, int, int *)) dlsym (handle2, "getipnodebyname"); if (__getipnodebyname == NULL) { fprintf (stderr, "Cannot dlsym getipnodebyname\n"); exit (1); } } } /* * gethostbyname * * It's possible that gethostbyname calls gethostbyname_r to do it's * deeds but we probably can't rely on this everywhere. */ struct hostent *gethostbyname(const char *name) { const char *newname; char tmp[1024]; struct hostent *ret; if ((newname = hostalias (name, tmp, 1024))) ret = (*__gethostbyname) ((gethostbyname_name = (char *)newname)); else ret = (*__gethostbyname) ((gethostbyname_name = (char *)name)); gethostbyname_name = NULL; return (ret); } /* * gethostbyname_r */ struct hostent *gethostbyname_r(const char *name, struct hostent *result, char *buffer, int buflen, int *h_errnop) { const char *newname; char tmp[1024]; if ((gethostbyname_name != name) && (newname = hostalias (name, tmp, 1024))) return ((*__gethostbyname_r) (newname, result, buffer, buflen, h_errnop)); else return ((*__gethostbyname_r) (name, result, buffer, buflen, h_errnop)); } /* * getaddrinfo */ int getaddrinfo(const char *nodename, const char *servname, const void *hints, void **res) { const char *newname; char tmp[1024]; if ((newname = hostalias (nodename, tmp, 1024))) return ((*__getaddrinfo) (newname, servname, hints, res)); else return ((*__getaddrinfo) (nodename, servname, hints, res)); } /* * getipnodebyname */ struct hostent *getipnodebyname(const char *name, int af, int flags, int *error_num) { const char *newname; char tmp[1024]; if ((newname = hostalias (name, tmp, 1024))) return ((*__getipnodebyname) (newname, af, flags, error_num)); else return ((*__getipnodebyname) (name, af, flags, error_num)); } /* * hostalias, inspired by FreeBSD: /usr/src/lib/libc/net/res_query.c */ static const char *hostalias(const char *name, char *buf, int bufsize) { char *file; const char *cp; FILE *fp; /* * We won't look up names with dots in them */ for (cp = name; *cp; cp++) if (*cp == '.') return (NULL); file = getenv ("HOSTALIASES"); if (file == NULL || (fp = fopen (file, "r")) == NULL) return (NULL); while (fgets (buf, bufsize, fp)) { if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; if (!strncmp (buf, name, strlen (name)) && isspace ((int)buf[strlen (name)])) { cp = buf+strlen(name); while (*cp && isspace ((int)*cp)) cp++; if (*cp) { fclose (fp); return (cp); } else continue; } } fclose (fp); return (NULL); }


P. Adobe PDF Writer requires LPT1: to be present, while nowadays you can run into a PC without parallel port (laptops and slim-line servers)
S. Yes, this is a Windows thing! The code targets Windows 2000/XP/.NET and requires DLL_PRELOAD module loaded. The idea is to create a symbolic link from LPT1 to \Devices\Null before Print Spooler service starts. The module is therefore can be pre-loaded to SERVICES.EXE, parent to all services.

[an error occurred while processing this directive] [an error occurred while processing this directive]
lptone.c
/* * Copyright (c) 2003 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -Zl -MD -LD lptone.c user32.lib kernel32.lib ntdll.lib * * Pre-load as: * * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DLL_PRELOAD] * "SERVICES.EXE"="lptone.dll" * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _DLL #error "_DLL is not defined." #endif #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,erw") #pragma comment(linker,"/merge:.rdata=.text") #pragma comment(linker,"/merge:.data=.text") #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #include <tchar.h> #include <windows.h> #include <winbase.h> #if defined(UNICODE) || defined(_UNICODE) #define _snprintf _snwprintf #endif #define PORT _T("LPT1") #define DEVICE _T("\\Device\\Null") BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { TCHAR out[256]; MSGBOXPARAMS m; LPVOID lpMsgBuf; while (reason==DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(h); if (QueryDosDevice(PORT,out,sizeof(out)/sizeof(TCHAR))) break; else if (GetLastError() != ERROR_FILE_NOT_FOUND) break; if (DefineDosDevice(DDD_RAW_TARGET_PATH,PORT,DEVICE) == 0) { FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, // Default language (LPTSTR) &lpMsgBuf, 0, NULL); _snprintf (out,sizeof(out)/sizeof(TCHAR)-1, _T("DefineDosDevice(\"%s\",\"%s\") failed: %s"), PORT,DEVICE,lpMsgBuf); out[sizeof(out)/sizeof(TCHAR)-1]=_T('\0'); LocalFree(lpMsgBuf); m.cbSize = sizeof(m); m.hwndOwner = NULL; m.lpszCaption = _T("LPTONE Failure Notice"); m.dwStyle = MB_OK; m.hInstance = NULL; m.lpszIcon = IDI_ERROR; m.dwContextHelpId = 0; m.lpfnMsgBoxCallback= NULL; m.dwLanguageId = MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US); m.lpszText = out; MessageBoxIndirect(&m); } break; } return TRUE; }


P. Sometimes it takes 2 minutes to logout Windows 2000/XP (yes, Windows again)
S. If you're observant enough, you'll also notice that when it happens, your [roaming] profile will not be updated and the recent changes will consequently be lost. The problem is recognized by Microsoft, and several attempts to work it around were actually made, but none of them was successful. The problem is that Win32 Subsystem server, CSRSS.EXE, (well, rather one of DLLs dynamically loaded to its address space) leaks handle[s] to your personal registry hive, which makes it improssible to unload the latter. So we do it the hard way:-) In order to catch the moment of logout, I intercept call to UnloadUserProfile in WINLOGON.EXE (yes, this means that module in question is to be pre-loaded to WINLOGON.EXE). Upon call to this function, I list processes to identify my Win32 Subsystem server, "attach" to it, then "steal" the handles to my registry hive and just close them. Needless to mention that it requires DLL_PRELOAD module loaded.

[an error occurred while processing this directive] [an error occurred while processing this directive]
WINLOGOUT.c
/* * Copyright (c) 2003 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -Zl -MD -LD WINLOGOUT.c advapi32.lib kernel32.lib ntdll.lib * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _DLL #error "_DLL is not defined." #endif #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,erw") #pragma comment(linker,"/merge:.rdata=.text") #pragma comment(linker,"/merge:.data=.text") #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #include <stdio.h> #include <tchar.h> #include <windows.h> #include <winbase.h> /* M$ headers are *complete* mess!!! */ #ifndef STATUS_INFO_LENGTH_MISMATCH #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) #endif #define NtQuerySystemInformation _NtQuerySystemInformation #include <winternl.h> #undef NtQuerySystemInformation #define SystemHandleInformation 16 typedef LONG KPRIORITY; typedef struct { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; LARGE_INTEGER SpareLi2; LARGE_INTEGER SpareLi3; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; ULONG SpareUl3; SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; ULONG PeakWorkingSetSize; ULONG WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER ReadOperationCount; LARGE_INTEGER WriteOperationCount; LARGE_INTEGER OtherOperationCount; LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; } MORE_SYSTEM_PROCESS_INFORMATION; typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { USHORT UniqueProcessId; USHORT CreatorBackTraceIndex; UCHAR ObjectTypeIndex; UCHAR HandleAttributes; USHORT HandleValue; PVOID Object; ULONG GrantedAccess; } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG NumberOfHandles; SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[ 1 ]; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; NTSTATUS NTAPI NtQuerySystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); typedef struct _OBJECT_TYPE_INFORMATION { UNICODE_STRING TypeName; ULONG TotalNumberOfObjects; ULONG TotalNumberOfHandles; ULONG TotalPagedPoolUsage; ULONG TotalNonPagedPoolUsage; ULONG TotalNamePoolUsage; ULONG TotalHandleTableUsage; ULONG HighWaterNumberOfObjects; ULONG HighWaterNumberOfHandles; ULONG HighWaterPagedPoolUsage; ULONG HighWaterNonPagedPoolUsage; ULONG HighWaterNamePoolUsage; ULONG HighWaterHandleTableUsage; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccessMask; BOOLEAN SecurityRequired; BOOLEAN MaintainHandleCount; ULONG PoolType; ULONG DefaultPagedPoolCharge; ULONG DefaultNonPagedPoolCharge; } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; typedef struct _OBJECT_ALL_TYPES_INFORMATION { ULONG NumberOfTypes; OBJECT_TYPE_INFORMATION TypeInformation[1]; } OBJECT_ALL_TYPES_INFORMATION, *POBJECT_ALL_TYPES_INFORMATION; typedef enum _OBJECT_INFORMATION_CLASS { ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectAllTypesInformation, ObjectHandleInformation } OBJECT_INFORMATION_CLASS; NTSTATUS NTAPI NtQueryObject ( IN HANDLE ObjectHandle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG ObjectInformationLength, OUT PULONG ReturnLength OPTIONAL ); static DWORD find_csrpid() { SIZE_T RequiredLength; NTSTATUS s; static DWORD csrpid=0; if (csrpid) return csrpid; csrpid=(DWORD)-1; /* Dump processes and find this session's CSRSS.EXE */ { MORE_SYSTEM_PROCESS_INFORMATION *ProcTable,*Proc; DWORD sess; RequiredLength=64*1024; /* a bit large for ws, but ok for ts */ ProcTable = VirtualAlloc(NULL,RequiredLength,MEM_COMMIT,PAGE_READWRITE); if (ProcTable == NULL) return -1; while ((s=NtQuerySystemInformation(SystemProcessInformation, ProcTable, (ULONG)RequiredLength,NULL)) == STATUS_INFO_LENGTH_MISMATCH) { RequiredLength += 16*1024; VirtualFree(ProcTable,0,MEM_RELEASE); ProcTable = VirtualAlloc (NULL,RequiredLength, MEM_COMMIT,PAGE_READWRITE); if (ProcTable == NULL) return -1; } if (s) return s; ProcessIdToSessionId (GetCurrentProcessId(),&sess); Proc=ProcTable; do { if (Proc->SessionId == sess && Proc->ImageName.Length==18 && !_wcsnicmp(Proc->ImageName.Buffer,L"CSRSS.EXE",9) ) { csrpid=(DWORD)Proc->UniqueProcessId; break; } Proc=(MORE_SYSTEM_PROCESS_INFORMATION *) ((char *)Proc+Proc->NextEntryOffset); } while (Proc->NextEntryOffset); VirtualFree (ProcTable,0,MEM_RELEASE); } return csrpid; } static NTSTATUS list_keys(DWORD csrpid,void (*cb)(DWORD,void *),void *cbdata) { SIZE_T RequiredLength; UCHAR KeyType=0; SYSTEM_HANDLE_INFORMATION *HandleTable=NULL; NTSTATUS s; int i; /* Retrieve Object Type table and find Key's index */ { OBJECT_ALL_TYPES_INFORMATION *Types; OBJECT_TYPE_INFORMATION *t; s=NtQueryObject(NULL,ObjectAllTypesInformation,NULL,0,&RequiredLength); if (s==STATUS_INFO_LENGTH_MISMATCH) { Types = VirtualAlloc(NULL,RequiredLength,MEM_COMMIT,PAGE_READWRITE); if (Types == NULL) return -1; if (s=NtQueryObject(NULL,ObjectAllTypesInformation, Types,RequiredLength,&RequiredLength)) { VirtualFree (Types,0,MEM_RELEASE); return s; } for (t=Types->TypeInformation,i=0;i<Types->NumberOfTypes;i++) { if (t->TypeName.Length==6 && !_wcsnicmp(t->TypeName.Buffer,L"Key",3)) { KeyType = i+1; break; } t=(OBJECT_TYPE_INFORMATION *)( (char *)t->TypeName.Buffer + ((t->TypeName.MaximumLength+3)&~3) ); } } else return s; VirtualFree (Types,0,MEM_RELEASE); } if (KeyType==0) return -1; /* Retrieve Handle Table */ { SYSTEM_HANDLE_INFORMATION Handle; s=NtQuerySystemInformation(SystemHandleInformation, &Handle,sizeof(Handle),(ULONG *)&RequiredLength); while (s==STATUS_INFO_LENGTH_MISMATCH) { RequiredLength += 4*1024; if (HandleTable) VirtualFree (HandleTable,0,MEM_RELEASE); HandleTable = VirtualAlloc(NULL,RequiredLength,MEM_COMMIT,PAGE_READWRITE); if (HandleTable == NULL) return -1; s=NtQuerySystemInformation(SystemHandleInformation, HandleTable,(ULONG)RequiredLength,(ULONG *)&RequiredLength); } } if (s) { VirtualFree (HandleTable,0,MEM_RELEASE); return s; } for (i=0;i<HandleTable->NumberOfHandles;i++) { if (HandleTable->Handles[i].UniqueProcessId == csrpid) { if (HandleTable->Handles[i].ObjectTypeIndex==KeyType) (*cb)(HandleTable->Handles[i].HandleValue,cbdata); } } VirtualFree (HandleTable,0,MEM_RELEASE); return 0; } typedef struct { HANDLE csr,log; } CB_DATA; static void closeremotehandle (DWORD handle,CB_DATA *d) { HANDLE hcopy=NULL,me=GetCurrentProcess(); NTSTATUS s; struct { UNICODE_STRING Name; wchar_t Buffer[96]; } NameQuery; while (d->csr) { if (DuplicateHandle (d->csr,(HANDLE)handle,me,&hcopy,0,0,DUPLICATE_SAME_ACCESS) == 0) break; s=NtQueryObject(hcopy,ObjectNameInformation,&NameQuery,sizeof(NameQuery),NULL); CloseHandle(hcopy); if (s && s!=STATUS_INFO_LENGTH_MISMATCH) break; if (NameQuery.Name.Length>=34 && !_wcsnicmp(NameQuery.Name.Buffer,L"\\REGISTRY\\User\\S-",17)) { DuplicateHandle (d->csr,(HANDLE)handle,GetCurrentProcess(),&hcopy,0,0, DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS); CloseHandle(hcopy); #if 0 { TCHAR str[256],*arr[]={str}; _snprintf(str,sizeof(str)/sizeof(str[0])-1, "WINLOGOUT.dll: Closing %x, %*S", handle,NameQuery.Name.Length,NameQuery.Name.Buffer); str[sizeof(str)/sizeof(str[0])-1]=0; ReportEvent(d->log,EVENTLOG_INFORMATION_TYPE,0,1000,NULL,1,0,arr,NULL); } #endif } break; } } typedef BOOL (WINAPI *UnloadUserProfile_T) ( IN HANDLE Token, IN HANDLE Profile ); static UnloadUserProfile_T _UnloadUserProfile=NULL,*__UnloadUserProfile=NULL; static BOOL WINAPI UnloadUserProfile_ ( IN HANDLE Token, IN HANDLE Profile ) { static int once=1; if (once) { HANDLE csr,applog; CB_DATA d; once=0; applog=OpenEventLog(NULL,_T("Application")); if (csr=OpenProcess(PROCESS_DUP_HANDLE,0,find_csrpid())) { d.csr=csr; d.log=applog; list_keys(find_csrpid(),closeremotehandle,&d); CloseHandle(csr); } else { TCHAR str[64],*arr[]={str}; _snprintf(str,sizeof(str)/sizeof(str[0])-1, "WINLOGOUT.dll: Unable to OpenProcess(%d)",find_csrpid()); str[sizeof(str)/sizeof(str[0])-1]=0; ReportEvent(applog,EVENTLOG_ERROR_TYPE,0,1000,NULL,1,0,arr,NULL); } CloseEventLog (applog); } return (*_UnloadUserProfile)( Token, Profile); } static int _lstricmp(const char *s1, const char *s2) { char c1,c2; int ret; while (c1=*s1, c2=*s2, c1&&c2) { c1|=0x20, c2|=0x20; /* lower the case */ if (ret=c1-c2) return ret; s1++, s2++; } return c1-c2; } BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { DWORD acc; HMODULE hmod; IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_headers; IMAGE_DATA_DIRECTORY *dir; IMAGE_IMPORT_DESCRIPTOR *idesc; IMAGE_THUNK_DATA *thunk; switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(h); if (!(hmod=GetModuleHandle(_T("USERENV.DLL")))) { OutputDebugString(_T("USERENV.DLL not found?")); return FALSE; } _UnloadUserProfile=(UnloadUserProfile_T)GetProcAddress(hmod,"UnloadUserProfile"); if (!(hmod=GetModuleHandle(NULL))) { OutputDebugString(_T("NULL not found?")); return FALSE; } dos_header = (IMAGE_DOS_HEADER *)hmod; nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew); dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress); while (idesc->Name) { if (!_lstricmp((char *)hmod+idesc->Name,"USERENV.DLL")) break; idesc++; } if (!idesc->Name) { OutputDebugString(_T("Can't locate USERENV.DLL import descriptor")); return FALSE; } for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)_UnloadUserProfile; thunk++) ; __UnloadUserProfile=(UnloadUserProfile_T *)thunk; if (!VirtualProtect (__UnloadUserProfile,sizeof(void*),PAGE_EXECUTE_READWRITE,&acc)) { OutputDebugString(_T("Unable to unlock Thunk Table")); return FALSE; } *__UnloadUserProfile=UnloadUserProfile_; VirtualProtect (__UnloadUserProfile,sizeof(void*),acc,&acc); break; case DLL_PROCESS_DETACH: VirtualProtect (__UnloadUserProfile,sizeof(void*),PAGE_EXECUTE_READWRITE,&acc); *__UnloadUserProfile=_UnloadUserProfile; VirtualProtect (__UnloadUserProfile,sizeof(void*),acc,&acc); break; } return TRUE; }


P. Sometimes I/O buffers are required to be aligned in a specific manner (most notably when accessing /dev/raw/raw* in Linux)
S. This module maintains single common intermediate buffer aligned at page boundary and makes sure all I/O requests are aligned. This is very quick-n-dirty hack! But it does the job if all you want to do is to fool around with such programs as tar, dd, cpio, ... and /dev/raw/raw*. See the code for an example...

[an error occurred while processing this directive] [an error occurred while processing this directive]
aligned_io.c
#if 0 # # This LD_PRELOAD-able module keeps buffers [passed to /dev/raw/...] # aligned according to 'man raw'. Maintaining appropriate blocking # factor is still up to the application this module will be # LD_PRELOAD-ed to. Most common example would most likely be: # # env LD_PRELOAD=`pwd`/aligned_io.so tar cBbf 64 /dev/raw/raw1 ... # /bin/sh << EOS MODNAME=\`basename "$0" .c\` (set -x; gcc -O -fomit-frame-pointer -fPIC -shared -o \$MODNAME.so "$0") [ $# -gt 0 ] && (set -x; env LD_PRELOAD=`pwd`/\$MODNAME.so $*) EOS exit # #endif #ifndef __linux # error "Linux only, at least for the moment..." #endif #include <stdio.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> #define ALIGN 2048 #define ASIZE (32*1024) #ifndef DEBUG #define DEBUG 0 #endif static void *aligned_buffer; typedef ssize_t (*io_op) (int fd,void *buf,size_t count); extern ssize_t __write(int fd, void *buf, size_t count); extern ssize_t __read(int fd, void *buf, size_t count); static ssize_t do_io (io_op op,int fd,void *buf,size_t count) { int ret; #if DEBUG fprintf (stderr,"-%d:%p\n",fd,buf); #endif if ((size_t)buf&(ALIGN-1)) { if (aligned_buffer == NULL) aligned_buffer = mmap (NULL,ASIZE,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0); #if DEBUG fprintf (stderr,"+%d:%p\n",fd,aligned_buffer); #endif /* I count on caller to notice that the return value is smaller */ if (count>ASIZE) count=ASIZE; if (op==__write) memcpy (aligned_buffer,buf,count); ret=(*op) (fd,aligned_buffer,count); if (op==__read && ret>0) memcpy (buf,aligned_buffer,ret); return ret; } return (*op) (fd,buf,count); } ssize_t write(int fd, const void *buf, size_t count) { return do_io (__write,fd,(void *)buf,count); } ssize_t read(int fd, void *buf, size_t count) { return do_io (__read,fd,buf,count); }


P. Tru64 clu_upgrade(8) floods root file system when performing preinstall procedure for rolling TruCluster upgrade...
S. Tru64 tar exhibits [annoying] quality to follow symbolic links at autofs(8) mount point(s) even if instructed not to. Apparently it's intentional (they probably want to maintain an illusion of loopback mount point), but in this particluar case is totally inappropriate. Ideally clu_upgrade should explicitly backup files without crossing the device boundary. Meanwhile it's possible to work around the problem by tricking tar to cache first device being returned by lstat(2) and return failure upon cache miss...

[an error occurred while processing this directive] [an error occurred while processing this directive]
tardeviceonly.c
#if 0 # # If executed this file compiles an .so module and prints the suggested # usage line... # /bin/sh << EOS MODNAME=\`basename "$0" .c\` (set -x; cc -O -shared -o \$MODNAME.so "$0" \ -init my_init -hidden_symbol my_init \ -expect_unresolved '__Arg?' ) 1>&2 && echo env _RLD_LIST=`pwd`/\$MODNAME.so:DEFAULT $SHELL EOS exit #endif #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> static int screwing_tar; static dev_t first_device; void my_init () { extern char **__Argv[]; extern int __Argc; if (__Argc>1 && strstr ((*__Argv)[0],"tar") && strchr ((*__Argv)[1],'c') ) screwing_tar = 1; } int lstat (const char *path,struct stat *buf) { int ret=__lstat (path,buf); if (ret==0 && screwing_tar) { if (first_device==0) first_device = buf->st_dev; else if (buf->st_dev != first_device) { errno = ENOENT; return -1; } } return ret; }


P. Sometimes you have to maintain virtual identity...
S. Imagine you have a virtual interface or cluster alias set up. Most network server programs allow you to bind their incoming sockets to the interface of interest [e.g. "bind interfaces only" in Samba]. But there come occasions when you have to maintain virtual identity even on outgoing sockets, maybe without having the luxuary or not willing to modify the source code to suit your needs. TruCluster for example provides for this on per target port basis [out_alias parameter in /etc/clua_services], but this affect all applications and applies to default cluster alias only, which is not necessarily what you had in mind. Therefore attached module:-) If pre-loaded into target application's address space, this module maintains virtual identity on all sockets opened by that particular process and its direct siblings.

[an error occurred while processing this directive] [an error occurred while processing this directive]
bind_to.c
#if 0 # # See http://fy.chalmers.se/~appro/LD_*-gallery/ for details. # # Following is Tru64 compiler line. Pre-load by setting _RLD_LIST # environment variable to "/path/to/bind_to.so:DEFAULT" and BIND_TO # environment variable - to interface name of interest, e.g.: # # env _RLD_LIST=`pwd`/bind_to.so:DEFAULT BIND_TO=virtual daemon # /bin/sh << EOS MODNAME=\`basename "$0" .c\` case `uname -s` in OSF1) (set -x; cc -O -shared -o \$MODNAME.so "$0" -B symbolic -init my_init -hidden_symbol my_init );; SunOS) (set -x; cc -O -Kpic -G -z text -o \$MODNAME.so "$0" -lsocket -lnsl );; esac EOS exit #endif #define gethostname __gethostname #define gethostbyaddr __gethostbyaddr #define sysinfo __sysinfo #define socket __socket #define bind __bind #define connect __connect #define sendto __sendto #define listen __listen #define uname __uname #ifdef __sun #define __gethostname _gethostname #define __gethostbyaddr _gethostbyaddr #define __sysinfo _sysinfo #define __socket _socket #define __bind _bind #define __connect _connect #define __sendto _sendto #define __listen _listen #define __uname _uname #endif #include <sys/socket.h> #include <unistd.h> #include <sys/utsname.h> #include <sys/systeminfo.h> #include <netdb.h> #undef uname #undef listen #undef sendto #undef connect #undef bind #undef socket #undef sysinfo #undef gethostbyaddr #undef gethostname #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #include <ctype.h> #include <dlfcn.h> static fd_set socket_set; static struct sockaddr_in myself; static char *hostname; #ifdef __sun #undef __gethostbyaddr #define __gethostbyaddr gethostbyaddr_libnsl struct hostent *(*__gethostbyaddr) (const char *name, int len, int type); #endif #if defined(__SUNPRO_C) #pragma init(my_init) /* SPARCCompiler specific #pragma */ static #endif void my_init () { char *env=getenv("BIND_TO"); struct hostent *ret; #ifdef __sun void *libnsl; libnsl = dlopen ("libnsl.so.1",RTLD_LAZY); if (libnsl == NULL) fprintf (stderr,"Cannot dlopen(\"libnsl.so.1\")\n"), exit (255); __gethostbyaddr = (struct hostent *(*)(const char *,int,int)) dlsym (libnsl, "gethostbyaddr"); if (__gethostbyaddr == NULL) fprintf (stderr,"Cannot dlsym(\"gethostbyname\")\n"), exit (255); #endif if (env==NULL) fprintf (stderr,"${BIND_TO} is undefined\n"), exit(255); if (!(ret=gethostbyname (env))) fprintf (stderr,"unable to gethostbyname(\"%s\")\n",env), exit(255); memcpy (&myself.sin_addr,ret->h_addr,sizeof(myself.sin_addr)); myself.sin_family = AF_INET; if (isdigit(env[0])) { ret = __gethostbyaddr ((char *)&myself.sin_addr,sizeof(myself.sin_addr), myself.sin_family); if (ret != NULL) hostname = strdup (ret->h_name); } else hostname = strdup(env); } struct hostent *gethostbyaddr(const char *addr, int len, int type) { struct hostent *ret=__gethostbyaddr(addr,len,type); static struct hostent hostcopy; if (ret!=NULL && hostname && type==myself.sin_family && !memcmp (addr,&myself.sin_addr.s_addr,sizeof(myself.sin_addr))) { hostcopy = *ret, ret = &hostcopy, ret->h_name = hostname; } return ret; } long sysinfo(int command, char *buf, long count) { long ret=__sysinfo(command, buf, count); if (command==SI_HOSTNAME && ret>=0 && hostname) { if (count>0) strncpy (buf,hostname,count-1), buf[count-1]='\0'; return strlen (hostname) + 1; } return ret; } int gethostname(char *name, int len) { long ret=__sysinfo(SI_HOSTNAME, name, len); if (ret>=0 && hostname) strncpy (name,hostname,len); return ret==-1?-1:0; } int uname (struct utsname *name) { int ret=__uname (name); if (ret>=0 && hostname) strncpy (name->nodename,hostname,SYS_NMLN); return ret; } int socket (int domain, int type, int protocol) { int s = __socket (domain,type,protocol); if (s>=0 && s<FD_SETSIZE && domain && domain==myself.sin_family) FD_SET (s,&socket_set); return s; } int listen (int s, int log) { if (s>=0 && s<FD_SETSIZE && FD_ISSET(s,&socket_set)) FD_CLR (s,&socket_set); return __listen (s,log); } int bind (int s, const struct sockaddr *addr, int len) { struct sockaddr_in copy; if (s>=0 && s<FD_SETSIZE && FD_ISSET(s,&socket_set)) { FD_CLR (s,&socket_set); copy = *(struct sockaddr_in *)addr; if (copy.sin_family == myself.sin_family && copy.sin_addr.s_addr == INADDR_ANY) { copy.sin_addr = myself.sin_addr; return __bind (s,(struct sockaddr *)&copy,sizeof(copy)); } } return __bind (s,addr,len); } int connect (int s, const struct sockaddr *addr, int len) { if (s>=0 && s<FD_SETSIZE && FD_ISSET(s,&socket_set)) { FD_CLR (s,&socket_set); __bind (s,(struct sockaddr *)&myself,sizeof(myself)); } return __connect (s,addr,len); } ssize_t sendto (int s, const void *msg, size_t mlen, int f, const struct sockaddr *addr, int alen) { if (s>=0 && s<FD_SETSIZE && FD_ISSET(s,&socket_set)) { FD_CLR (s,&socket_set); __bind (s,(struct sockaddr *)&myself,sizeof(myself)); } return __sendto (s,msg,mlen,f,addr,alen); }


P. Why not per-executable environment variables?
S. Yet another DLL_PRELOAD Windows module. Idea is to set environment variables on per-executable basis. E.g. _CLUSTER_NETWORK_NAME_ set to %USERNAME% in ICA Client context on Terminal Server can become very handy;-) But why not a wrapper script [such as we used to do in Unix] you ask? Well, then you might have to re-associate application CLSID, which is nothing but boring... And if you use simpler interpretator (such as CMD or Perl), you might end up with an ugly black window instantly popping up and disappe.a..r...

[an error occurred while processing this directive] [an error occurred while processing this directive]
environ.c
/* * Copyright (c) 2004 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -Zl -MD -LD environ.c advapi32.lib kernel32.lib ntdll.lib * * Pre-load as: * * [HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\DLL_PRELOAD\WFICA32.EXE] * @="environ.dll" * * [HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\DLL_PRELOAD\WFICA32.EXE\environ.dll] * "_CLUSTER_NETWORK_NAME_"=hex(2):25,00,55,00,53,00,45,00,52,00,4e,00,41,00,4d,\ * 00,45,00,25,00,00,00 * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _DLL #error "_DLL is not defined." #endif #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,er") #pragma comment(linker,"/merge:.rdata=.text") #define UNICODE #define _UNICODE #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #include <tchar.h> #include <windows.h> #include <winbase.h> #include <malloc.h> #define SIZEOF_A(a) (sizeof(a)/sizeof(a[0])) /* * Implement some trivia in order to be excused from linking with MSVCRT. */ static void _memmove (void *dst, void *src, size_t n) { unsigned char *d=dst,*s=src; while (n--) *d++ = *s++; return; } static size_t _lstrlen (TCHAR *str) { int len=0; while (*str) { str++, len++; } return len; } static TCHAR *_lstrrchr (TCHAR *str,TCHAR c) { TCHAR *p = NULL; while (*str) { if (*str == c) p = str; str++; } return p; } static TCHAR *_lstrchr (TCHAR *str,TCHAR c) { TCHAR *p = NULL; while (*str) { if (*str == c) { p = str; break; } str++; } return p; } static TCHAR *_lstrncpy (TCHAR *dst,TCHAR *src,size_t n) { TCHAR *ret=dst; while(--n && *src) { *dst++ = *src++; } *dst=_T('\0'); return ret; } static void _setenv_by_key (HKEY hkey) { DWORD nv,maxnamelen,namelen,maxvaluelen,valuelen,type,ret; TCHAR *name,*value,expanded[1024]; if (RegQueryInfoKey (hkey,NULL,NULL,NULL,NULL,NULL,NULL, &nv,&maxnamelen,&maxvaluelen, NULL,NULL) != ERROR_SUCCESS || nv==0) return; name = _alloca ((maxnamelen+1)*sizeof(TCHAR)); if (name==NULL) return; value = _alloca (maxvaluelen); if (value==NULL) return; while (nv--) { namelen = maxnamelen+1; valuelen = maxvaluelen; if (RegEnumValue (hkey,nv,name,&namelen,NULL,&type,(void *)value,&valuelen) != ERROR_SUCCESS) break; if (type!=REG_SZ && type!=REG_EXPAND_SZ) continue; if (type==REG_EXPAND_SZ) { ret = ExpandEnvironmentStrings (value,expanded,SIZEOF_A(expanded)); if (ret==0 || ret>SIZEOF_A(expanded)) continue; } SetEnvironmentVariable (name,type==REG_EXPAND_SZ?expanded:value); } } #define MY_REGKEY \ _T("Software\\Microsoft\\Windows NT\\CurrentVersion\\DLL_PRELOAD") BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { TCHAR str[256],kname[256],*s; size_t l; HKEY hkey; while (reason==DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(h); _lstrncpy (kname,MY_REGKEY,SIZEOF_A(kname)-1), l=_lstrlen (kname), kname[l++]=_T('\\'); /* append executable name... */ if (!GetModuleFileName (NULL,str,SIZEOF_A(str))) break; (s = _lstrrchr (str,_T('\\'))) ? s++ : (s=str); _lstrncpy (kname+l,s,SIZEOF_A(kname)-l-1), l=_lstrlen (kname), kname[l++]=_T('\\'); /* append this module name... */ if (!GetModuleFileName (h,str,SIZEOF_A(str))) break; (s = _lstrrchr (str,_T('\\'))) ? s++ : (s=str); _lstrncpy (kname+l,s,SIZEOF_A(kname)-l); if (RegOpenKeyEx (HKEY_CURRENT_USER,kname,0,KEY_QUERY_VALUE,&hkey) == ERROR_SUCCESS) _setenv_by_key (hkey), RegCloseKey (hkey); if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,kname,0,KEY_QUERY_VALUE,&hkey) == ERROR_SUCCESS) _setenv_by_key (hkey), RegCloseKey (hkey); break; } return TRUE; }


P. Ever tried to access files with zapped ACL on NTFS volume? Doesn't work even for privileged user, does it?
S. Here is yet another DLL_PRELOAD Windows module which allows commodity programs executed by privileged user to bypass access control mechanisms [therefore the name: BYPass ACcess Control]. With this module pre-loaded you [being a privileged user!] can traverse directories, open files and manipulate ACLs on NTFS objects without regard to their effective permissions. This is achieved by "unlocking" the Backup privilege for current process and patching a system call to open all files "for Backup." This is essentially what NTBACKUP.EXE does... A reminder that DLL_PRELOAD works only with programs linked with USER32.DLL is due here. Because [X]CACLS.EXE are not linked with it and don't work:-( So don't ask... Explorer works fine, but it's tricky to pre-load the module on temporary need-for-a-moment basis. I personally recommend [and stick to] following procedure:
  • open Folder Options control panel -> View pane and make sure you "Launch folder windows in a separate process,"
  • close all folder views and double-check that you have only one explorer.exe process left running,
  • open Command Prompt window and issue "set DLL_PRELOAD=bypacc.dll" command followed by "explorer,"
  • at this point you should have unconstrained Explorer window...
  • proceed to step 2 when done!

[an error occurred while processing this directive] [an error occurred while processing this directive]
bypacc.c
/* * Copyright (c) 2004 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -Zl -MD -LD bypacc.c advapi32.lib kernel32.lib * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _M_IX86 #error "x86 is the only supported, at least for now..." #endif #ifndef _DLL #error "_DLL is not defined." #endif #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,erw") #pragma comment(linker,"/merge:.rdata=.text") #pragma comment(linker,"/merge:.data=.text") #define UNICODE #define _UNICODE #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #include <windows.h> #include <winbase.h> #include <winnt.h> #include <tchar.h> #ifdef _WIN64 /* October 2002 Platform SDK is screwed up */ #define _RUNTIME_FUNCTION _RUNTIME_FUNCTION_ #define RUNTIME_FUNCTION RUNTIME_FUNCTION_ #define PRUNTIME_FUNCTION PRUNTIME_FUNCTION_ #endif #include <winternl.h> #ifndef FILE_DIRECTORY_FILE /* Excerpt from <ddk>/inc/wdm.h */ #define FILE_DIRECTORY_FILE 0x00000001 #define FILE_WRITE_THROUGH 0x00000002 #define FILE_SEQUENTIAL_ONLY 0x00000004 #define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 #define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 #define FILE_NON_DIRECTORY_FILE 0x00000040 #define FILE_CREATE_TREE_CONNECTION 0x00000080 #define FILE_COMPLETE_IF_OPLOCKED 0x00000100 #define FILE_NO_EA_KNOWLEDGE 0x00000200 #define FILE_OPEN_FOR_RECOVERY 0x00000400 #define FILE_RANDOM_ACCESS 0x00000800 #define FILE_DELETE_ON_CLOSE 0x00001000 #define FILE_OPEN_BY_FILE_ID 0x00002000 #define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 #define FILE_NO_COMPRESSION 0x00008000 #endif static int NtCreateMagic, NtOpenMagic; static void *_NtCreateFile, *_NtOpenFile; #undef IN #define IN volatile static NTSTATUS NtCreateFile_ ( void *ra, OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength ) { #if 0 OutputDebugStringW(ObjectAttributes->ObjectName->Buffer); #endif if (CreateOptions & FILE_OPEN_FOR_BACKUP_INTENT) CreateOptions |= FILE_DIRECTORY_FILE; else CreateOptions |= FILE_OPEN_FOR_BACKUP_INTENT; return NtCreateMagic; } static NTSTATUS NtOpenFile_ ( void *ra, OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ) { #if 0 OutputDebugStringW(ObjectAttributes->ObjectName->Buffer); #endif if (OpenOptions & FILE_OPEN_FOR_BACKUP_INTENT) OpenOptions |= FILE_DIRECTORY_FILE; else OpenOptions |= FILE_OPEN_FOR_BACKUP_INTENT; return NtOpenMagic; } #define B8_SIZE 5 static unsigned int PatchSysCall(void *syscall,void *patch, unsigned int *magic) { unsigned char *entry=(unsigned char *)syscall; DWORD acc; #ifdef _M_IX86 if (entry[0] != 0xb8) /* mov eax,imm32 */ return 0; if (VirtualProtect(entry,B8_SIZE,PAGE_EXECUTE_WRITECOPY,&acc) == 0) { OutputDebugString(_T("PatchSysCall: Unable to modify the code segment")); return 0; } *magic=*((unsigned int *)(entry+1)); entry[0] = 0xe8; /* call relative */ *((unsigned int *)(entry+1)) = (unsigned char *)patch -(entry+B8_SIZE); VirtualProtect(entry,B8_SIZE,acc,&acc); FlushInstructionCache(GetCurrentProcess(),entry,B8_SIZE); #endif return *magic; } static void UnpatchSysCall(void *syscall,unsigned int *magic) { unsigned char *entry=(unsigned char *)syscall; DWORD acc; if (*magic==0) return; #ifdef _M_IX86 if (VirtualProtect(entry,B8_SIZE,PAGE_EXECUTE_WRITECOPY,&acc) == 0) { OutputDebugString(_T("UnpatchSysCall: Unable to modify the code segment")); return; } entry[0] = 0xb8; /* mov eax,imm32 */ *((unsigned int *)(entry+1)) = *magic; magic=0; VirtualProtect(entry,B8_SIZE,acc,&acc); FlushInstructionCache(GetCurrentProcess(),entry,B8_SIZE); #endif } BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { HMODULE hmod; TOKEN_PRIVILEGES pst[2], /* not really, but it does the trick... */ *priv=pst; HANDLE hProc; switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(h); if (!(hmod=GetModuleHandle(_T("NTDLL.DLL")))) { OutputDebugString(_T("NTDLL.DLL not found?")); return FALSE; } _NtCreateFile=GetProcAddress(hmod,"NtCreateFile"), _NtOpenFile=GetProcAddress(hmod,"NtOpenFile"); CloseHandle(hmod); if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_DEFAULT, &hProc)) { OutputDebugString(_T("Failed to OpenProcessToken")); return FALSE; } memset (pst,0,sizeof(pst)); if (!LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &priv->Privileges[0].Luid)) { OutputDebugString(_T("Failed to lookup SE_BACKUP_NAME")); CloseHandle(hProc); return FALSE; } if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[1].Luid)) { OutputDebugString(_T("Failed to lookup SE_RESTORE_NAME")); CloseHandle(hProc); return FALSE; } priv->PrivilegeCount = 2; priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; priv->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hProc, FALSE, priv, 0, NULL, 0) || GetLastError() != ERROR_SUCCESS) { OutputDebugString(_T("Failed to AdjustTokenPrivileges")); CloseHandle(hProc); return FALSE; } CloseHandle(hProc); PatchSysCall(_NtCreateFile,NtCreateFile_,&NtCreateMagic); PatchSysCall(_NtOpenFile,NtOpenFile_,&NtOpenMagic); break; case DLL_PROCESS_DETACH: UnpatchSysCall(_NtCreateFile,&NtCreateMagic); UnpatchSysCall(_NtOpenFile,&NtOpenMagic); break; } return TRUE; }


P. ActiveSync 4.1 does not work in Citrix Presentation Server [nor "vanilla" Terminal Server] environment.
S. Yes, Windows again. Gr-r-r-r... This module is designed to modify behaviour of ActiveSync components, wcescomm.exe, wcesmg.exe and rapimgr.exe, in following manner. First of all the module places itself between KERNEL32.DLL and NTDLL.DLL and strips Global\ prefix from mutex and semaphore objects created/opened by either component. This makes ActiveSync adhere to session-specific object name space. Secondly it places itself between WS2_32.DLL and currently registered WSP, Windows Socket Provider, and re-bias references to 127.0.0.1 to a session-specific address from 127. subnet. This effectively isolates concurrently executing instances of the application suite in question in every given session, which is the key to successful application deployment in Terminal Server environment. It should be noted that Citrix already arranges for the latter, per-session loopback communication interface, so one can wonder why did I do it? Well, because we've found that it's actually possible to make ActiveSync work [at least] with rdesktop COM-port redirection even without Citrix! Reliable and seamless deployment, in particular of USB-connected PDAs, requires some Linux kernel as well as rdesktop patching. We plan to publish these patches as we make progress.

[an error occurred while processing this directive] [an error occurred while processing this directive]
vsync.c
/* * Copyright (c) 2006 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -Zl -MD -LD vsync.c ws2_32.lib kernel32.lib ntdll.lib * * Pre-load as: * * [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DLL_PRELOAD] * "wcescomm.exe"="vsync.dll" * "wcesmgr.exe"="vsync.dll" * "rapimgr.exe"="vsync.dll" * [HKLM\SOFTWARE\Microsoft\Windows CE Services\Http server] * "HttpSysOverride"=dword:1 * [-HKLM\SOFTWARE\Citrix\CtxHook\AppInit_Dlls\VIPHook\wcescomm.exe] * [-HKLM\SOFTWARE\Citrix\CtxHook\AppInit_Dlls\VIPHook\wcesmgr.exe] * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _DLL #error "_DLL is not defined." #endif #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,erw") #pragma comment(linker,"/merge:.rdata=.text") #pragma comment(linker,"/merge:.data=.text") #define UNICODE #define _UNICODE #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #include <winsock2.h> #include <ws2spi.h> #include <ws2tcpip.h> #include <windows.h> #include <winbase.h> #include <winnt.h> #include <winternl.h> #include <tchar.h> #include <stdarg.h> #ifdef _DEBUG static VOID DebugOutputA (const char *fmt,...) { va_list argv; char buf[256]; va_start(argv,fmt); _vsnprintf (buf,sizeof(buf)/sizeof(buf[0])-1,fmt,argv); buf[sizeof(buf)/sizeof(buf[0])-1]='\0'; OutputDebugStringA (buf); va_end(argv); } #endif /* Native API wrappers */ __declspec(dllimport) NTSTATUS WINAPI ZwCreateMutant ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN InitialOwner ); static NTSTATUS WINAPI NtCreateMutant_ ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN BOOLEAN InitialOwner ) { OBJECT_ATTRIBUTES *p = ObjectAttributes,ob; UNICODE_STRING us; if (p && p->ObjectName && p->ObjectName->Length>14 && !_wcsnicmp(L"global\\",p->ObjectName->Buffer,7)) { us = *p->ObjectName; us.Buffer += 7, us.Length -= 14, us.MaximumLength =- 14; ob = *p; ob.ObjectName = &us; p = &ob; } return ZwCreateMutant( MutantHandle, DesiredAccess, p, InitialOwner); } __declspec(dllimport) NTSTATUS WINAPI ZwOpenMutant ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); static NTSTATUS WINAPI NtOpenMutant_ ( OUT PHANDLE MutantHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ) { OBJECT_ATTRIBUTES *p = ObjectAttributes,ob; UNICODE_STRING us; if (p && p->ObjectName && p->ObjectName->Length>14 && !_wcsnicmp(L"global\\",p->ObjectName->Buffer,7)) { us = *p->ObjectName; us.Buffer += 7, us.Length -= 14, us.MaximumLength =- 14; ob = *p; ob.ObjectName = &us; p = &ob; } return ZwOpenMutant( MutantHandle, DesiredAccess, p); } __declspec(dllimport) NTSTATUS WINAPI ZwCreateSemaphore ( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN LONG InitialCount, IN LONG MaxCount ); static NTSTATUS WINAPI NtCreateSemaphore_ ( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN LONG InitialCount, IN LONG MaxCount ) { OBJECT_ATTRIBUTES *p = ObjectAttributes,ob; UNICODE_STRING us; if (p && p->ObjectName && p->ObjectName->Length>14 && !_wcsnicmp(L"global\\",p->ObjectName->Buffer,7)) { us = *p->ObjectName; us.Buffer += 7, us.Length -= 14, us.MaximumLength =- 14; ob = *p; ob.ObjectName = &us; p = &ob; } return ZwCreateSemaphore( SemaphoreHandle, DesiredAccess, p, InitialCount, MaxCount); } __declspec(dllimport) NTSTATUS WINAPI ZwOpenSemaphore ( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); static NTSTATUS WINAPI NtOpenSemaphore_ ( OUT PHANDLE SemaphoreHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ) { OBJECT_ATTRIBUTES *p = ObjectAttributes,ob; UNICODE_STRING us; if (p && p->ObjectName && p->ObjectName->Length>14 && !_wcsnicmp(L"global\\",p->ObjectName->Buffer,7)) { us = *p->ObjectName; us.Buffer += 7, us.Length -= 14, us.MaximumLength =- 14; ob = *p; ob.ObjectName = &us; p = &ob; } return ZwOpenSemaphore( SemaphoreHandle, DesiredAccess, p); } /* WSP wrappers */ static LPWSPSTARTUP _WSPStartup; static WSPPROC_TABLE WSPDispatch_; static struct in_addr vloopback; static SOCKET WSPAPI WSPAccept_( SOCKET s, struct sockaddr FAR * addr, LPINT addrlen, LPCONDITIONPROC lpfnCondition, DWORD_PTR dwCallbackData, LPINT lpErrno ) { SOCKET ret=WSPDispatch_.lpWSPAccept (s,addr,addrlen,NULL,(DWORD_PTR)NULL,lpErrno); struct sockaddr_in *sin=(void *)addr; #ifdef _DEBUG DebugOutputA("accept %p %p %s",ret,addr,addr?inet_ntoa(sin->sin_addr):"(null)"); #endif if (ret!=INVALID_SOCKET && addr!=NULL && addr->sa_family==AF_INET) { if (sin->sin_addr.s_addr == vloopback.s_addr || sin->sin_addr.s_addr == (vloopback.s_addr|0x0000807F)) sin->sin_addr.s_addr = 0x0100007F; } return ret; } static int WSPAPI WSPBind_( SOCKET s, const struct sockaddr FAR * name, int namelen, LPINT lpErrno ) { struct sockaddr_in *sin=(void *)name; if (name->sa_family == AF_INET) { if (sin->sin_addr.s_addr == 0x0100007F) { sin = _alloca(namelen); memcpy (sin,name,namelen); sin->sin_addr = vloopback; } else if (sin->sin_addr.s_addr == 0 && sin->sin_port!=0) { sin = _alloca(namelen); memcpy (sin,name,namelen); sin->sin_addr.s_addr = vloopback.s_addr|0x0000807F; } #ifdef _DEBUG DebugOutputA("bind %s:%d",inet_ntoa(sin->sin_addr),ntohs(sin->sin_port)); #endif } return WSPDispatch_.lpWSPBind (s,(void *)sin,namelen,lpErrno); } static int WSPAPI WSPConnect_( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, LPINT lpErrno ) { struct sockaddr_in *sin=(void *)name; int ret; if (name->sa_family == AF_INET) { if (sin->sin_addr.s_addr == 0x0100007F) { sin = _alloca(namelen); memcpy (sin,name,namelen); sin->sin_addr = vloopback; } #ifdef _DEBUG DebugOutputA("connect %s:%d",inet_ntoa(sin->sin_addr),ntohs(sin->sin_port)); #endif } ret = WSPDispatch_.lpWSPConnect (s,(void *)sin,namelen, lpCallerData,lpCalleeData,lpSQOS,lpGQOS,lpErrno); if (ret!=0 && (void*)sin!=(void*)name && *lpErrno==WSAECONNREFUSED) { sin->sin_addr.s_addr |= 0x0000807F; #ifdef _DEBUG DebugOutputA("retry connect %s:%d",inet_ntoa(sin->sin_addr),ntohs(sin->sin_port)); #endif ret = WSPDispatch_.lpWSPConnect (s,(void *)sin,namelen, lpCallerData,lpCalleeData,lpSQOS,lpGQOS,lpErrno); } return ret; } static int WSPAPI WSPGetPeerName_( SOCKET s, struct sockaddr FAR * name, LPINT namelen, LPINT lpErrno ) { int ret=WSPDispatch_.lpWSPGetPeerName(s,name,namelen,lpErrno); struct sockaddr_in *sin=(void *)name; if (ret==0 && name->sa_family==AF_INET) { if (sin->sin_addr.s_addr == vloopback.s_addr || sin->sin_addr.s_addr == (vloopback.s_addr|0x0000807F)) sin->sin_addr.s_addr = 0x0100007F; } return ret; } static int WSPAPI WSPGetSockName_( SOCKET s, struct sockaddr FAR * name, LPINT namelen, LPINT lpErrno ) { int ret=WSPDispatch_.lpWSPGetSockName(s,name,namelen,lpErrno); struct sockaddr_in *sin=(void *)name; if (ret==0 && name->sa_family==AF_INET) { if (sin->sin_addr.s_addr == vloopback.s_addr || sin->sin_addr.s_addr == (vloopback.s_addr|0x0000807F)) sin->sin_addr.s_addr = 0x0100007F; } return ret; } static int WSPAPI WSPStartup_( IN WORD wVersionRequested, OUT LPWSPDATA lpWSPData, IN LPWSAPROTOCOL_INFOW lpProtocolInfo, IN WSPUPCALLTABLE UpcallTable, OUT LPWSPPROC_TABLE lpProcTable ) { int ret = (*_WSPStartup)( wVersionRequested, lpWSPData, lpProtocolInfo, UpcallTable, &WSPDispatch_); /* Copy whole table... */ *lpProcTable = WSPDispatch_; /* ... and subclass selected functions */ lpProcTable->lpWSPAccept = WSPAccept_; lpProcTable->lpWSPBind = WSPBind_; lpProcTable->lpWSPConnect = WSPConnect_; lpProcTable->lpWSPGetPeerName = WSPGetPeerName_; lpProcTable->lpWSPGetSockName = WSPGetSockName_; return ret; } static PVOID WINAPI GetProcAddress_ ( HMODULE hModule, LPCSTR lpProcName ) { PVOID ret = GetProcAddress(hModule,lpProcName); if (ret && !strcmp(lpProcName,"WSPStartup")) { /* Place myself between WS2_32 and WSP */ _WSPStartup = ret; ret = WSPStartup_; } return ret; } PVOID *__NtCreateMutant =NULL, *__NtOpenMutant =NULL, *__NtCreateSemaphore =NULL, *__NtOpenSemaphore =NULL, *__GetProcAddress =NULL; BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { DWORD acc0,acc1,sess; HMODULE hmod; IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_headers; IMAGE_DATA_DIRECTORY *dir; IMAGE_IMPORT_DESCRIPTOR *idesc; IMAGE_THUNK_DATA *thunk; static void *page0,*page1; static size_t plen0,plen1; switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(h); if (!ProcessIdToSessionId(GetCurrentProcessId(),&sess) || sess==0) return FALSE; /* 0x7F524450 is 127.'R'.'D'.'P' */ vloopback.s_addr = htonl(0x7F524450+sess); if (!(hmod=GetModuleHandle(_T("KERNEL32.DLL")))) { OutputDebugStringA("KERNEL32.DLL not found?"); return FALSE; } dos_header = (IMAGE_DOS_HEADER *)hmod; nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew); dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress); while (idesc->Name) { if (!_stricmp((char *)hmod+idesc->Name,"NTDLL.DLL")) break; idesc++; } if (!idesc->Name) { OutputDebugStringA("Can't locate NTDLL.DLL import descriptor"); return FALSE; } page0 = (char *)hmod+idesc->FirstThunk; for (thunk=(IMAGE_THUNK_DATA *)page0; thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwCreateMutant; thunk++) ; if (thunk->u1.Function) __NtCreateMutant=(PVOID *)thunk; for (thunk=(IMAGE_THUNK_DATA *)page0; thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwOpenMutant; thunk++) ; if (thunk->u1.Function) __NtOpenMutant=(PVOID *)thunk; for (thunk=(IMAGE_THUNK_DATA *)page0; thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwCreateSemaphore; thunk++) ; if (thunk->u1.Function) __NtCreateSemaphore=(PVOID *)thunk; for (thunk=(IMAGE_THUNK_DATA *)page0; thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwOpenSemaphore; thunk++) ; if (thunk->u1.Function) __NtOpenSemaphore=(PVOID *)thunk; for (thunk=(IMAGE_THUNK_DATA *)page0;thunk->u1.Function;thunk++) ; plen0 = (size_t)thunk-(size_t)page0; if (!(hmod=GetModuleHandle(_T("WS2_32.DLL")))) { OutputDebugStringA("WS2_32.DLL not found?"); return FALSE; } dos_header = (IMAGE_DOS_HEADER *)hmod; nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew); dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress); while (idesc->Name) { if (!_stricmp((char *)hmod+idesc->Name,"KERNEL32.DLL")) break; idesc++; } if (!idesc->Name) { OutputDebugStringA("Can't locate KERNEL32.DLL import descriptor"); return FALSE; } page1 = (char *)hmod+idesc->FirstThunk; for (thunk=(IMAGE_THUNK_DATA *)page1; thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)GetProcAddress; thunk++) ; if (thunk->u1.Function) __GetProcAddress=(PVOID *)thunk; for (thunk=(IMAGE_THUNK_DATA *)page1;thunk->u1.Function;thunk++) ; plen1 = (size_t)thunk-(size_t)page1; if (!VirtualProtect (page0,plen0,PAGE_EXECUTE_READWRITE,&acc0)) { OutputDebugStringA("Unable to unlock KERNEL32.DLL Thunk Table"); return FALSE; } if (!VirtualProtect (page1,plen1,PAGE_EXECUTE_READWRITE,&acc1)) { OutputDebugStringA("Unable to unlock WS2_32.DLL Thunk Table"); return FALSE; } if (__NtCreateMutant) *__NtCreateMutant =NtCreateMutant_; if (__NtOpenMutant) *__NtOpenMutant =NtOpenMutant_; if (__NtCreateSemaphore) *__NtCreateSemaphore=NtCreateSemaphore_; if (__NtOpenSemaphore) *__NtOpenSemaphore =NtOpenSemaphore_; if (__GetProcAddress) *__GetProcAddress =GetProcAddress_; VirtualProtect (page0,plen0,acc0,&acc0); VirtualProtect (page1,plen1,acc1,&acc1); break; case DLL_PROCESS_DETACH: VirtualProtect (page0,plen0,PAGE_EXECUTE_READWRITE,&acc0); VirtualProtect (page1,plen1,PAGE_EXECUTE_READWRITE,&acc1); if (__NtCreateMutant) *__NtCreateMutant =ZwCreateMutant; if (__NtOpenMutant) *__NtOpenMutant =ZwOpenMutant; if (__NtCreateSemaphore) *__NtCreateSemaphore=ZwCreateSemaphore; if (__NtOpenSemaphore) *__NtOpenSemaphore =ZwOpenSemaphore; if (__GetProcAddress) *__GetProcAddress =GetProcAddress; VirtualProtect (page0,plen0,acc0,&acc0); VirtualProtect (page1,plen1,acc1,&acc1); break; } return TRUE; }


P. Adobe Acrobat [Reader] 9 for Windows crashes upon startup...
S. Crash occurs if there is an unreadable yet traversable directory on your %APPDATA% path. I.e. such directory where you can not run 'dir' in, but can 'chdir' to a subdirectory. In our particular case %APPDATA% is redirected through a DFS root, where we forbid users to list referrals for security and primarily performance reasons. Crash takes places in MSVCRT and is worked around [naturally with DLL_PRELOAD's help] by pulling directory status information for '...\unreadable\subdir\.' instead of '...\unreadable\subdir'.

[an error occurred while processing this directive] [an error occurred while processing this directive]
statpatch.c
/* * Copyright (c) 2010 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -GS- -Zl -MD -LD statpatch.c kernel32.lib ntdll.lib * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _DLL #error "_DLL is not defined." #endif #ifdef _WIN64 #pragma comment(linker,"/entry:DllMain") #pragma comment(linker,"/merge:.rdata=.text") #else #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,erw") #pragma comment(linker,"/merge:.rdata=.text") #pragma comment(linker,"/merge:.data=.text") #endif #define UNICODE #define _UNICODE #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #include <windows.h> #include <winbase.h> #include <winnt.h> #ifdef _WIN64 /* October 2002 Platform SDK is screwed up */ #define _RUNTIME_FUNCTION _RUNTIME_FUNCTION_ #define RUNTIME_FUNCTION RUNTIME_FUNCTION_ #define PRUNTIME_FUNCTION PRUNTIME_FUNCTION_ #endif #include <winternl.h> #include <tchar.h> #ifndef STATUS_SUCCESS #define STATUS_SUCCESS (0x0) #define STATUS_ACCESS_DENIED (0xc0000022L) #endif #ifndef FILE_DIRECTORY_FILE /* Excerpt from <ddk>/inc/wdm.h */ #define FILE_DIRECTORY_FILE 0x00000001 #define FILE_WRITE_THROUGH 0x00000002 #define FILE_SEQUENTIAL_ONLY 0x00000004 #define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 #define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 #define FILE_NON_DIRECTORY_FILE 0x00000040 #define FILE_CREATE_TREE_CONNECTION 0x00000080 #define FILE_COMPLETE_IF_OPLOCKED 0x00000100 #define FILE_NO_EA_KNOWLEDGE 0x00000200 #define FILE_OPEN_FOR_RECOVERY 0x00000400 #define FILE_RANDOM_ACCESS 0x00000800 #define FILE_DELETE_ON_CLOSE 0x00001000 #define FILE_OPEN_BY_FILE_ID 0x00002000 #define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 #define FILE_NO_COMPRESSION 0x00008000 #endif /* from <ddk>/inc/ntdef.h */ #ifndef OBJ_CASE_INSENSITIVE #define OBJ_CASE_INSENSITIVE (0x00000040L) #endif #ifndef InitializeObjectAttributes #define InitializeObjectAttributes( p, n, a, r, s ) { \ (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ (p)->RootDirectory = r; \ (p)->Attributes = a; \ (p)->ObjectName = n; \ (p)->SecurityDescriptor = s; \ (p)->SecurityQualityOfService = NULL; \ } #endif /* from "Windows NT Native API" */ typedef struct _FILE_BOTH_DIR_INFORMATION { ULONG NextEntryOffset; ULONG FileIndex; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaSize; CCHAR ShortNameLength; WCHAR ShortName[12]; WCHAR FileName[1]; } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; #ifdef _DEBUG static VOID DebugOutputA (const char *fmt,...) { va_list argv; char buf[256]; va_start(argv,fmt); _vsnprintf (buf,sizeof(buf)/sizeof(buf[0])-1,fmt,argv); buf[sizeof(buf)/sizeof(buf[0])-1]='\0'; OutputDebugStringA (buf); va_end(argv); } #endif NTSYSAPI NTSTATUS NTAPI ZwClose( IN HANDLE FileHandle ); static IMAGE_THUNK_DATA *__NtCreateFile=NULL,*__NtCreateFile7=NULL; NTSYSAPI NTSTATUS NTAPI ZwCreateFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength ); #ifdef _DEBUG static NTSTATUS WINAPI NtCreateFile_ ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength ) { NTSTATUS ret; ret = ZwCreateFile( FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); DebugOutputA("create(\"%.*S\",%x,%x,%x)=%x", ObjectAttributes->ObjectName->Length/2, ObjectAttributes->ObjectName->Buffer, DesiredAccess,ShareAccess,CreateOptions,ret); return ret; } #endif static IMAGE_THUNK_DATA *__NtOpenFile=NULL,*__NtOpenFile7=NULL; NTSYSAPI NTSTATUS NTAPI ZwOpenFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ); #ifdef _DEBUG static NTSTATUS WINAPI NtOpenFile_ ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ) { NTSTATUS ret; ret = ZwOpenFile( FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions); DebugOutputA("open(\"%.*S\",%x,%x,%x)=%x", ObjectAttributes->ObjectName->Length/2, ObjectAttributes->ObjectName->Buffer, DesiredAccess,ShareAccess,OpenOptions,ret); return ret; } #endif static IMAGE_THUNK_DATA *__NtQueryDirectoryFile=NULL,*__NtQueryDirectoryFile7=NULL; NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG FileInformationLength, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan ); static NTSTATUS WINAPI NtQueryDirectoryFile_ ( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG FileInformationLength, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan ) { NTSTATUS ret; ret = ZwQueryDirectoryFile( FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, FileInformationLength, FileInformationClass, ReturnSingleEntry, FileName, RestartScan); if (ret==STATUS_ACCESS_DENIED && ReturnSingleEntry && FileInformationClass==3) { HANDLE h; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK io; #ifdef _DEBUG DebugOutputA(">re-open \"%.*S\"",FileName->Length/2,FileName->Buffer); #endif InitializeObjectAttributes(&oa,FileName,OBJ_CASE_INSENSITIVE,FileHandle,NULL); if (ZwOpenFile(&h,SYNCHRONIZE|FILE_READ_ATTRIBUTES|FILE_LIST_DIRECTORY, &oa,&io, FILE_SHARE_DELETE|FILE_SHARE_WRITE|FILE_SHARE_READ, FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE) == STATUS_SUCCESS) { /* Query . instead */ ret = ZwQueryDirectoryFile( h, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, FileInformationLength, FileInformationClass, ReturnSingleEntry, NULL, RestartScan); if (ret==STATUS_SUCCESS) { FILE_BOTH_DIR_INFORMATION *inf=(FILE_BOTH_DIR_INFORMATION *)FileInformation; ULONG len = FileName->Length; #ifdef _DEBUG DebugOutputA(">fixing FileInformation->FileName"); #endif if (len > (FileInformationLength-sizeof(*inf))) len=FileInformationLength-sizeof(*inf); inf->FileNameLength = len; memcpy(inf->FileName,FileName->Buffer,len); inf->ShortNameLength=0; } ZwClose(h); } } #ifdef _DEBUG DebugOutputA("query(\"%*S\",%d,%d)=%x", FileName ? FileName->Length/2 : 6, FileName ? FileName->Buffer : L"(null)", FileInformationLength,FileInformationClass,ret); #endif return ret; } static int _lstricmp(const char *s1, const char *s2) { char c1,c2; int ret; while (c1=*s1, c2=*s2, c1&&c2) { c1|=0x20, c2|=0x20; /* lower the case */ if (ret=c1-c2) return ret; s1++, s2++; } return c1-c2; } BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { DWORD acc; HMODULE hmod; IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_headers; IMAGE_DATA_DIRECTORY *dir; IMAGE_IMPORT_DESCRIPTOR *idesc; IMAGE_THUNK_DATA *thunk; static void *page=NULL,*page7=NULL; static size_t plen=0,plen7=0; switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(h); if (!(hmod=GetModuleHandle(_T("NTDLL.DLL")))) { OutputDebugString(_T("NTDLL.DLL not found?")); return FALSE; } if (!(hmod=GetModuleHandle(_T("KERNEL32.DLL")))) { OutputDebugString(_T("KERNEL32.DLL not found?")); return FALSE; } dos_header = (IMAGE_DOS_HEADER *)hmod; nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew); dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress); while (idesc->Name) { if (!_lstricmp((char *)hmod+idesc->Name,"NTDLL.DLL")) break; idesc++; } if (!idesc->Name) { OutputDebugString(_T("Can't locate NTDLL.DLL import descriptor in KERNEL32")); return FALSE; } for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwCreateFile; thunk++) ; __NtCreateFile=thunk; for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwOpenFile; thunk++) ; __NtOpenFile=thunk; for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwQueryDirectoryFile; thunk++) ; __NtQueryDirectoryFile=thunk; if ((void *)__NtOpenFile > (void *)__NtCreateFile) page=__NtCreateFile, plen=(size_t)__NtOpenFile-(size_t)page+sizeof(void(*)()); else page=__NtOpenFile, plen=(size_t)__NtCreateFile-(size_t)page+sizeof(void(*)()); if ((void *)__NtQueryDirectoryFile > (void *)((BYTE*)page+plen)) plen=(size_t)__NtQueryDirectoryFile-(size_t)page+sizeof(void(*)()); else if ((void *)__NtQueryDirectoryFile < page) page=__NtQueryDirectoryFile, plen+=(size_t)page-(size_t)__NtQueryDirectoryFile; if (!VirtualProtect (page,plen,PAGE_EXECUTE_READWRITE,&acc)) { OutputDebugString(_T("Unable to unlock KERNEL32 Thunk Table")); return FALSE; } #ifdef _DEBUG __NtOpenFile->u1.Function=(size_t)NtOpenFile_; __NtCreateFile->u1.Function=(size_t)NtCreateFile_; #endif __NtQueryDirectoryFile->u1.Function=(size_t)NtQueryDirectoryFile_; VirtualProtect (page,plen,acc,&acc); /* * On Windows 7 most native calls go through KernelBase.dll */ if (!(hmod=GetModuleHandle(_T("KernelBase.dll")))) { OutputDebugString(_T("KernelBase.dll not found...")); return TRUE; } dos_header = (IMAGE_DOS_HEADER *)hmod; nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew); dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress); while (idesc->Name) { if (!_lstricmp((char *)hmod+idesc->Name,"NTDLL.DLL")) break; idesc++; } if (!idesc->Name) { OutputDebugString(_T("Can't locate NTDLL.DLL import descriptor in KernelBase")); return TRUE; } for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwCreateFile; thunk++) ; __NtCreateFile7=thunk; for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwOpenFile; thunk++) ; __NtOpenFile7=thunk; for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk); thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)ZwQueryDirectoryFile; thunk++) ; __NtQueryDirectoryFile7=thunk; if ((void *)__NtOpenFile7 > (void *)__NtCreateFile7) page7=__NtCreateFile7, plen7=(size_t)__NtOpenFile7-(size_t)page7+sizeof(void(*)()); else page7=__NtOpenFile7, plen7=(size_t)__NtCreateFile7-(size_t)page7+sizeof(void(*)()); if ((void *)__NtQueryDirectoryFile7 > (void *)((BYTE*)page7+plen7)) plen7=(size_t)__NtQueryDirectoryFile7-(size_t)page7+sizeof(void(*)()); else if ((void *)__NtQueryDirectoryFile7 < page7) page7=__NtQueryDirectoryFile7, plen7+=(size_t)page7-(size_t)__NtQueryDirectoryFile7; if (!VirtualProtect (page7,plen7,PAGE_EXECUTE_READWRITE,&acc)) { OutputDebugString(_T("Unable to unlock KernelBase Thunk Table")); return FALSE; } #ifdef _DEBUG __NtOpenFile7->u1.Function=(size_t)NtOpenFile_; __NtCreateFile7->u1.Function=(size_t)NtCreateFile_; #endif __NtQueryDirectoryFile7->u1.Function=(size_t)NtQueryDirectoryFile_; VirtualProtect (page7,plen7,acc,&acc); break; case DLL_PROCESS_DETACH: VirtualProtect (page,plen,PAGE_EXECUTE_READWRITE,&acc); #ifdef _DEBUG __NtOpenFile->u1.Function=(size_t)ZwOpenFile; __NtCreateFile->u1.Function=(size_t)ZwCreateFile; #endif __NtQueryDirectoryFile->u1.Function=(size_t)ZwQueryDirectoryFile; VirtualProtect (page,plen,acc,&acc); if (page7==NULL) return TRUE; VirtualProtect (page7,plen7,PAGE_EXECUTE_READWRITE,&acc); #ifdef _DEBUG __NtOpenFile7->u1.Function=(size_t)ZwOpenFile; __NtCreateFile7->u1.Function=(size_t)ZwCreateFile; #endif __NtQueryDirectoryFile7->u1.Function=(size_t)ZwQueryDirectoryFile; VirtualProtect (page7,plen7,acc,&acc); break; } return TRUE; }


P. Low-traffic TCP connections tend to hang and ultimately break in NAT-ed environment...
S. For TCP to work through a NAT device, such as residential gateway, latter maintains table of active TCP connections. The table is periodically traversed and connections that are considered stale are removed from the table. This means that if there was no activity on a given TCP connection for certain period of time, chances are that it will be evicted from the NAT table, i.e. will be effectively broken. And given current state of busyness it takes inapproriately long time to detect that it's broken. This poses major irritation for remote interactive sessions, such as SSH or RDP. Periodic TCP keep-alive can come to rescue, but in addition to the fact that the default system-wide interval is surrealistically high, 2 hours, application has to explicitly set SO_KEEPALIVE socket option. In other words in addition to setting kernel parameter (net.ipv4.tcp_keepalive_time on Linux and Services\Tcpip\Parameters\KeepAliveTime on Windows) to lower value you might have to modify application code. Naturally you might find yourself in position of not being able to do the latter, especially on Windows. Therefore below Windows-specific DLL_PRELOAD module that sets up keep-alive on all sockets opened by chosen application. Example suggests to pre-load it to mstsc.exe, Windows RDP client...

[an error occurred while processing this directive] [an error occurred while processing this directive]
keepalive.c
/* * Copyright (c) 2006-2010 Andy Polyakov <appro@fy.chalmers.se> * * Build with: * * cl -Ox -GD -GF -Zl -MD -LD keepalive.c ws2_32.lib kernel32.lib ntdll.lib * * Pre-load as: * * [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DLL_PRELOAD] * "mstsc.exe"="keepalive.dll" * * See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details. * */ #ifndef _DLL #error "_DLL is not defined." #endif #ifdef _WIN64 #pragma comment(linker,"/entry:DllMain") #pragma comment(linker,"/merge:.rdata=.text") #else #pragma comment(linker,"/entry:DllMain@12") #pragma comment(linker,"/section:.text,erw") #pragma comment(linker,"/merge:.rdata=.text") #pragma comment(linker,"/merge:.data=.text") #endif #define UNICODE #define _UNICODE #if defined(WIN32) && !defined(_WIN32) #define _WIN32 #endif #define _WIN32_WINNT 0x0500 #define NTDDI_VERSION 0x05000100 //NTDDI_WIN2KSP1 #include <winsock2.h> #include <ws2tcpip.h> #include <ws2spi.h> #include <mstcpip.h> #include <windows.h> #include <winbase.h> #include <winnt.h> #include <winternl.h> #include <tchar.h> #include <stdarg.h> static VOID DebugOutputA (const char *fmt,...) { va_list argv; char buf[256]; va_start(argv,fmt); _vsnprintf (buf,sizeof(buf)/sizeof(buf[0])-1,fmt,argv); buf[sizeof(buf)/sizeof(buf[0])-1]='\0'; OutputDebugStringA (buf); va_end(argv); } /* WSP wrappers */ static LPWSPSTARTUP _WSPStartup; static WSPPROC_TABLE WSPDispatch_; static int WSPAPI WSPSetSockOpt_( SOCKET s, int level, int optname, const char *optval, int optlen, LPINT lpErrno ) { /* don't let application reset SO_KEEPALIVE */ int one=1; if (level==SOL_SOCKET && optname==SO_KEEPALIVE && !*optval) DebugOutputA("filtering SO_KEEPALIVE"), optval = (const char *)&one, optlen = sizeof(one); return WSPDispatch_.lpWSPSetSockOpt(s,level,optname,optval,optlen,lpErrno); } static int WSPAPI WSPConnect_( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, LPINT lpErrno ) { if (name->sa_family == AF_INET) { struct tcp_keepalive ka = { 1, 90*1000, 1000 }; DWORD len,err; int one=1; if (WSPDispatch_.lpWSPSetSockOpt(s,SOL_SOCKET,SO_KEEPALIVE, (const char *)&one,sizeof(one),&err)) DebugOutputA("failed to SO_KEEPALIVE %d",err); else DebugOutputA("SO_KEEPALIVE successful"); if (WSPDispatch_.lpWSPIoctl(s,SIO_KEEPALIVE_VALS, &ka,sizeof(ka),&ka,sizeof(ka),&len,NULL,NULL,NULL,&err)) DebugOutputA("failed to SIO_KEEPALIVE_VALS with %d",err); else DebugOutputA("SIO_KEEPALIVE_VALS successful"); } return WSPDispatch_.lpWSPConnect (s,(void *)name,namelen, lpCallerData,lpCalleeData,lpSQOS,lpGQOS,lpErrno); } static int WSPAPI WSPIoctl_( IN SOCKET s, IN DWORD dwIoControlCode, IN LPVOID lpvInBuffer, IN DWORD cbInBuffer, OUT LPVOID lpvOutBuffer, IN DWORD cbOutBuffer, OUT LPDWORD lpcbBytesReturned, IN LPWSAOVERLAPPED lpOverlapped, IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, IN LPWSATHREADID lpThreadId, OUT LPINT lpErrno ) { struct tcp_keepalive ka = { 1, 90*1000, 1000 }; if (dwIoControlCode == SIO_KEEPALIVE_VALS) { DebugOutputA("filtering SIO_KEEPALIVE_VALS"); lpvInBuffer = &ka; cbInBuffer = sizeof(ka); } return WSPDispatch_.lpWSPIoctl(s,dwIoControlCode,lpvInBuffer,cbInBuffer, lpvOutBuffer,cbOutBuffer,lpcbBytesReturned,lpOverlapped, lpCompletionRoutine,lpThreadId,lpErrno); } static int WSPAPI WSPStartup_( IN WORD wVersionRequested, OUT LPWSPDATA lpWSPData, IN LPWSAPROTOCOL_INFOW lpProtocolInfo, IN WSPUPCALLTABLE UpcallTable, OUT LPWSPPROC_TABLE lpProcTable ) { int ret = (*_WSPStartup)( wVersionRequested, lpWSPData, lpProtocolInfo, UpcallTable, &WSPDispatch_); /* Copy whole table... */ *lpProcTable = WSPDispatch_; /* ... and subclass selected functions */ lpProcTable->lpWSPConnect = WSPConnect_; lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt_; lpProcTable->lpWSPIoctl = WSPIoctl_; return ret; } static PVOID WINAPI GetProcAddress_ ( HMODULE hModule, LPCSTR lpProcName ) { PVOID ret = GetProcAddress(hModule,lpProcName); if (ret && !strcmp(lpProcName,"WSPStartup")) { /* Place myself between WS2_32 and WSP */ _WSPStartup = ret; ret = WSPStartup_; } return ret; } PVOID *__GetProcAddress =NULL; BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk) { DWORD acc0,acc1,sess; HMODULE hmod; IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_headers; IMAGE_DATA_DIRECTORY *dir; IMAGE_IMPORT_DESCRIPTOR *idesc; IMAGE_THUNK_DATA *thunk; static void *page0,*page1; static size_t plen0,plen1; switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(h); /* make sure WS32_32.DLL is linked */ plen0 = htonl(0); if (!(hmod=GetModuleHandle(_T("WS2_32.DLL")))) { OutputDebugStringA("WS2_32.DLL not found?"); return FALSE; } dos_header = (IMAGE_DOS_HEADER *)hmod; nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew); dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress); while (idesc->Name) { if (!_stricmp((char *)hmod+idesc->Name,"KERNEL32.DLL")) break; idesc++; } if (!idesc->Name) { OutputDebugStringA("Can't locate KERNEL32.DLL import descriptor"); return FALSE; } page1 = (char *)hmod+idesc->FirstThunk; for (thunk=(IMAGE_THUNK_DATA *)page1; thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)GetProcAddress; thunk++) ; if (thunk->u1.Function) __GetProcAddress=(PVOID *)thunk; for (thunk=(IMAGE_THUNK_DATA *)page1;thunk->u1.Function;thunk++) ; plen1 = (size_t)thunk-(size_t)page1; if (!VirtualProtect (page1,plen1,PAGE_EXECUTE_READWRITE,&acc1)) { OutputDebugStringA("Unable to unlock WS2_32.DLL Thunk Table"); return FALSE; } if (__GetProcAddress) *__GetProcAddress =GetProcAddress_; VirtualProtect (page1,plen1,acc1,&acc1); break; case DLL_PROCESS_DETACH: VirtualProtect (page1,plen1,PAGE_EXECUTE_READWRITE,&acc1); if (__GetProcAddress) *__GetProcAddress =GetProcAddress; VirtualProtect (page1,plen1,acc1,&acc1); break; } return TRUE; }


To be continued...