============================================================================== -------------[ BFi numero 9, anno 3 - 03/11/2000 - file 18 di 21 ]------------ ============================================================================== -[ REVERSiNG ]---------------------------------------------------------------- ---[ KEYGEN REVERSiNG - STUDIAMOCi L'ALGO -----[ syscalo Tool dasm (lo trovate su http://racl.immagika.org sezione tool e risorse) Allegato keygen.tgz Descrizione Keygen e' un piccolo programmino che ho trovato nella mia distibuzione linux (/usr/bin/keygen appartenente al package rpm xf86-3.3.5-29) Il programma genera una chiave pseudo-casuale; a noi interessa vedere l'algoritmo che hanno utilizzato i programmatori e per fare questo procediamo disassemblando l'eseguibile. Iniziamo Disassembliamo il programma: rev@rVm:~/keygenRev > dasm keygen keygen.dasm (tralascio l'output di dasm) Innanzitutto rechiamoci all'entry point del programma; lo trovate scritto in chiaro nel disassemblato: start address 0x080484b0 Ok, ora vediamo un po' di codice: i miei commenti sono indicati tra /* */ o preceduti da # Disassembly of section .text: /* Come potete ben vedere siamo giunti al disassemblato della sezione text che come ben noto contiene il codice eseguibile del programma */ /* Tutte queste istruzioni vengono aggiunte dal compilatore per "lanciare" il codice da noi scritto */ 0x080484b0 xorl %ebp,%ebp 0x080484b2 popl %esi 0x080484b3 movl %esp,%ecx 0x080484b5 andl $0xfffffff8,%esp 0x080484b8 pushl %eax 0x080484b9 pushl %esp 0x080484ba pushl %edx 0x080484bb pushl $0x8048688 0x080484c0 pushl $0x80483ac 0x080484c5 pushl %ecx 0x080484c6 pushl %esi 0x080484c7 pushl $0x8048520 Reference to function : __libc_start_main 0x080484cc call 0x08048438 /* E da qui viene "lanciato" il codice che abbiamo scritto noi */ 0x080484d1 hlt Ma come facciamo a sapere dove inizia il programma vero e proprio? Semplice, basta guardare l'ultimo valore pushato a __libc_start_main: 0x080484c7 pushl $0x8048520 Il valore 0x8048520 e' l'indizzo della prima istruzione del programma. Ricerchiamo questo valore nel disassemblato e andiamo a leggere le istruzioni assembly; ho inserito anche la descrizione delle funzioni chiamate (estratte dalle pagine di man) per capire meglio cosa fa il programma: ############################################## ## ## Procedura generazione "numeri" casuali ## Estratta dal disassemblato di keygen ## /usr/bin/keygen ## ############################################## # allocazione stack frame 0x08048520 pushl %ebp 0x08048521 movl %esp,%ebp # allocazione spazio per var locali 0x08048523 subl $0x50,%esp #50h=80 -> 20 word # bkp registri a cura della funzione chiamata # infatti questa parte di programma e' a tutti gli effetti una funzione e # quindi deve preoccuparsi di salvare i registri 0x08048526 pushl %edi 0x08048527 pushl %esi 0x08048528 pushl %ebx # INIZIO CHIAMATE A FUNZIONI PER OTTENERE VALORI DA UTILIZZARE COME SEME PER # LA FUNZIONE RANDOM # Reference to function : gethostid # # #include # # long int gethostid(void); # # # Get a unique 32-bit identifier for the current # machine. # 0x08048529 call 0x08048488 0x0804852e movl %eax,%esi #%esi=(#1) # Reference to function : getuid # # #include # #include # # uid_t getuid(void); # # # getuid returns the real user ID of the current process. # 0x08048530 call 0x08048458 0x08048535 movl %eax,%edi #%edi=(#2) # Reference to function : getgid # # #include # #include # # gid_t getgid(void); # # getgid returns the real group ID of the current process. # 0x08048537 call 0x08048468 0x0804853c movl %eax,0xffffffb4(%ebp) #(%ebp-4Ch)=(#3) # Reference to function : getpid # # #include # # pid_t getpid(void); # # # getpid returns the process ID of the current process. # (This is often used by routines that generate unique tem- # porary file names.) # 0x0804853f call 0x080483e8 0x08048544 movl %eax,%ebx #%ebx=(#4) # Reference to function : getppid # # #include # # pid_t getppid(void); # # getppid returns the process ID of the parent of the cur- # rent process. # 0x08048546 call 0x08048428 # NON SALVA IL VALORE DI RITORNO - chiamata inutile # Reference to function : getpgrp # # #include # # pid_t getpgrp(void); # # getpgid returns the process group ID of the process speci- # fied by pid. If pid is zero, the process ID of the cur- # rent process is used. # # getpgrp is equivalent to getpgid(0). # 0x0804854b call 0x080483f8 0x08048550 movl %eax,0xffffffb0(%ebp) #(%ebp-50h)=(#6) 0x08048553 pushl $0x0 #parametro2 per gettimeofday 0x08048555 leal 0xffffffb8(%ebp),%eax #copia ind %ebp-48h in %eax 0x08048558 pushl %eax #parametro1 per gettimeofday # Reference to function : gettimeofday # # #include # #include # # int gettimeofday(struct timeval *tv, struct timezone *tz); # # gettimeofday and settimeofday can set the time as well as # a timezone. tv is a timeval struct, as specified in # /usr/include/sys/time.h: # # struct timeval { # long tv_sec; /* seconds */ # long tv_usec; /* microseconds */ # }; # # # and tz is a timezone : # # struct timezone { # int tz_minuteswest; /* minutes W of Greenwich */ # int tz_dsttime; /* type of dst correction */ # }; # 0x08048559 call 0x08048478 0x0804855e leal 0xffffffc0(%ebp),%eax #copia ind %ebp-40h in %eax 0x08048561 pushl %eax #parametro2 per statfs # Possible reference to string: # "." 0x08048562 pushl $0x80486ac #parametro1 per statfs # Reference to function : statfs # # #include # # int statfs(const char *path, struct statfs *buf); # # # statfs returns information about a mounted file system. # path is the path name of any file within the mounted # filesystem. buf is a pointer to a statfs structure # defined as follows: # # struct statfs { # long f_type; /* type of filesystem (see below) */ # long f_bsize; /* optimal transfer block size */ # long f_blocks; /* total data blocks in file system */ # long f_bfree; /* free blocks in fs */ # long f_bavail; /* free blocks avail to non-superuser */ # long f_files; /* total file nodes in file system */ # long f_ffree; /* free file nodes in fs */ # fsid_t f_fsid; /* file system id */ # long f_namelen; /* maximum length of filenames */ # long f_spare[6]; /* spare for later */ # }; # 0x08048567 call 0x08048408 # a questo punto e' terminato il "recupero" dei valori da usare come seme per # la funzione random. # ora il programma prende tutti i valori ottenuti e li passa uno alla volta # alla funzione da me chiamata Call_much (per non dover sempre trascrivere # l'indirizzo) 0x0804856c pushl %esi #passa (#1) # chiama Call_much 0x0804856d call 0x08048610 0x08048572 pushl %edi #passa (#2) # chiama Call_much 0x08048573 call 0x08048610 0x08048578 movl 0xffffffb4(%ebp),%edx #copia ind %ebp-4Ch in %edx (#3) 0x0804857b pushl %edx #passa (#3) # chiama Call_much 0x0804857c call 0x08048610 0x08048581 pushl %ebx #passa (#4) # chiama Call_much 0x08048582 call 0x08048610 0x08048587 addl $0x20,%esp #libera lo stack da 8 word 0x0804858a pushl %ebx #passa (#4) -- seconda volta (al posto di #5) # chiama Call_much 0x0804858b call 0x08048610 0x08048590 movl 0xffffffb0(%ebp),%edx #copia %ebp-50h in %edx (#6) 0x08048593 pushl %edx #passa (#6) # chiama Call_much 0x08048594 call 0x08048610 0x08048599 movl 0xffffffb8(%ebp),%eax #copia %ebp-48h in %eax (#7) 0x0804859c pushl %eax #passa (#7) # chiama Call_much 0x0804859d call 0x08048610 0x080485a2 movl 0xffffffbc(%ebp),%eax #copia %ebp-44h in %eax (#7+1) 0x080485a5 pushl %eax #passa (#7+1) # chiama Call_much 0x080485a6 call 0x08048610 0x080485ab movl 0xffffffc8(%ebp),%eax #copia %ebp-38h in %eax (#8+2) 0x080485ae pushl %eax #passa (#8+2) # chiama Call_much 0x080485af call 0x08048610 0x080485b4 movl 0xffffffcc(%ebp),%eax #%ebp-34h (#8+3) 0x080485b7 pushl %eax # chiama Call_much 0x080485b8 call 0x08048610 0x080485bd movl 0xffffffd0(%ebp),%eax #%ebp-30h (#8+4) 0x080485c0 pushl %eax # chiama Call_much 0x080485c1 call 0x08048610 0x080485c6 movl 0xffffffd4(%ebp),%eax #%ebp-2Ch (#8+5) 0x080485c9 pushl %eax # chiama Call_much 0x080485ca call 0x08048610 0x080485cf addl $0x20,%esp #libera lo stack di 8 word 0x080485d2 movl 0xffffffd8(%ebp),%eax #%ebp-28h (#8+6) 0x080485d5 pushl %eax # chiama Call_much 0x080485d6 call 0x08048610 # terminata l'eleborazione procede a stampare la key ottenuta; i valori # vengono stampati come esadecimali # passa gli indirizzi dei 4 campi dati per la printf 0x080485db movl 0x80497c4,%eax #ultimo 0x080485e0 pushl %eax 0x080485e1 movl 0x80497c0,%eax #sono nelle locazioni da 0x80497b8 0x080485e6 pushl %eax 0x080485e7 movl 0x80497bc,%eax #a 0x80497c4 0x080485ec pushl %eax 0x080485ed movl 0x80497b8,%eax #primo 0x080485f2 pushl %eax # stringa di formattazione per la printf # Possible reference to string: # "%08lx%08lx%08lx%08lx" 0x080485f3 pushl $0x80486ae #passa il formato della stringa # Reference to function : printf 0x080485f8 call 0x08048448 #chiama la printf # ripristino registri e disallocazione stack frame 0x080485fd leal 0xffffffa4(%ebp),%esp 0x08048600 popl %ebx 0x08048601 popl %esi 0x08048602 popl %edi 0x08048603 movl %ebp,%esp 0x08048605 popl %ebp 0x08048606 ret #ritorna dalla chiamata ############################################################### # Spiegazione della funzione Call_much # # La funzione Call_much usa il valore passato come seme per la funzione # random. # Poi effettua uno xor dei valori della chiave da ottenere con i valori # restituiti da random. # ############################################################### # Referenced from call at 0804856d ; 08048573 ; 0804857c ; 08048582 ; 0804858b ; # 08048594 ; 0804859d ; 080485a6 ; 080485af ; 080485b8 ; 080485c1 ; 080485ca ; # 080485d6 ; # Call_much: 0x08048610 pushl %ebp 0x08048611 movl %esp,%ebp 0x08048613 pushl %esi 0x08048614 pushl %ebx # fine allocazione stack frame e salvataggio registri 0x08048615 movl 0x8(%ebp),%eax #copia il parametro passato in %eax 0x08048618 pushl %eax #passa %eax alla funzione srandom # Reference to function : srandom # # #include # # void srandom(unsigned int seed); # # # The srandom() function sets its argument as the seed for a # new sequence of pseudo-random integers to be returned by # random(). These sequences are repeatable by calling sran- # dom() with the same seed value. If no seed value is pro- # vided, the random() function is automatically seeded with # a value of 1. # 0x08048619 call 0x08048498 #chiama srandom 0x0804861e addl $0x4,%esp #libera lo stack # %ebx, %esi indirizzi inizio e fine numero da elaborare 0x08048621 movl $0x80497b8,%ebx #copia l'indirizzo in %ebx # 0x80497b8 locazione dove INIZIA il numero da elaborare 0x08048626 movl $0x80497c4,%esi #copia l'indirizzo in %esi # 0x80497c4 locazione dove FINISCE il numero da elaborare 0x0804862b nop # Reference to function : random # Referenced from jump at 08048638 ; # # #include # # long int random(void); # # The random() function uses a non-linear additive feedback # random number generator employing a default table of size # 31 long integers to return successive pseudo-random num- # bers in the range from 0 to RAND_MAX. # # call_m1: #etichetta aggiunta da me per avere un riferimento per il salto 0x0804862c call 0x08048418 #chiama la funzione random # xora il valore ritornato da random con quello puntato da %ebx 0x08048631 xorl %eax,(%ebx) 0x08048633 addl $0x4,%ebx #incrementa %ebx di 4; passa alla word successiva 0x08048636 cmpl %esi,%ebx #confronta %esi con %ebx # salta a call_m1 0x08048638 jle 0x0804862c #esegue l'elaborazione fino a quando %ebx<=%esi # ripristina registri e disalloca stack frame per il ritorno dalla chiamata 0x0804863a leal 0xfffffff8(%ebp),%esp 0x0804863d popl %ebx 0x0804863e popl %esi 0x0804863f movl %ebp,%esp 0x08048641 popl %ebp 0x08048642 ret Ecco spiegato l'algoritmo utilizzato dal programma. A mio avviso e' un buon metodo per generare chiavi "casuali" per un uso giornaliero 8-) Riproduciamo l'algoritmo in C: /*file syskeygen.c*/ #include #include #include #include #include #include void call_much(long int); /*array per contenere la key*/ static long int key[4]; void call_much(long int num) { int i; srandom(num); for(i=0; i<4; i++) key[i]^=random(); } main(void) { long int hostid, uid, gid, pid, pgrp; /*parametri per gettimeofday*/ struct timeval *tv; struct timezone *tz=0; /*parametri per statfs*/ char path[]="."; struct statfs *buf; /*allocazione delle strutture*/ tv=(struct timeval *) malloc(sizeof(struct timeval)); buf=(struct statfs *) malloc(sizeof(struct statfs)); hostid=gethostid(); uid=getuid(); gid=getgid(); pid=getpid(); pgrp=getpgrp(); gettimeofday(tv, tz); statfs(path, buf); /* inizio chiamate a call_much */ call_much(hostid); call_much(uid); call_much(gid); call_much(pid); call_much(pid); /* ripetuta due volte con il valore pid, come nel disassemblato*/ call_much(pgrp); call_much(tv->tv_sec); call_much(tv->tv_usec); call_much(buf->f_blocks); call_much(buf->f_bfree); call_much(buf->f_bavail); call_much(buf->f_files); call_much(buf->f_ffree); printf("\n\033[10G\033[1m\033[31m\033[44m syscalo key generator \033[m\n"); printf("\nkey: %08lx%08lx%08lx%08lx\n", key[0], key[1], key[2], key[3]); exit(0); } Credo non sia il caso di commentare il codice vista la semplicita'. Note finali Spero abbiate trovato interessante questo tutorial, nonostante sia abbastanza semplice; per chi inizia credo possa essere un buon punto di partenza. Ora potreste sfruttare questo algoritmo per migliorarlo, ottimizzarlo, o semplicemente prendere spunto per crearne uno vostro. -x bye to all x- syscalo ============================================================================== ---------------------------------[ EOF 18/21 ]-------------------------------- ==============================================================================