Contexte
Nous avons déjà un défi à propos de lancer SIGSEGV , alors pourquoi pas un défi à lancer SIGILL?
Qu'est-ce que SIGILL?
SIGILL est le signal d'une instruction illégale du processeur, ce qui arrive très rarement. L'action par défaut après réception de SIGILL est la fermeture du programme et l'écriture d'un vidage mémoire. Le signal ID de SIGILL est 4. Vous rencontrez très rarement SIGILL, et je ne sais absolument pas comment le générer dans votre code, sauf via sudo kill -s 4 <pid>
.
Règles
Vous aurez la racine dans vos programmes, mais si vous ne le souhaitez pas, vous pouvez également utiliser un utilisateur normal. Je suis sur un ordinateur Linux avec des paramètres régionaux allemands et je ne connais pas le texte anglais qui s’affiche après la capture de SIGILL, mais je pense que c’est quelque chose qui ressemble à «Instruction illégale». Le programme le plus court qui lance SIGILL gagne.
raise(SIGILL)
?Illegal instruction (core dumped)
.Réponses:
PDP-11 Assembler (UNIX Sixth Edition), 1 octet
L'instruction 9 n'est pas une instruction valide sur le PDP-11 (ce serait en octal, ce
000011
qui ne figure pas dans la liste d'instructions (PDF)). L'assembleur PDP-11 fourni avec UNIX Sixth Edition reproduit apparemment tout ce qu'il ne comprend pas directement dans le fichier; dans ce cas, 9 est un nombre, il génère donc une instruction littérale 9. Il a également la propriété impaire (inhabituelle dans les langages d'assemblage de nos jours) que les fichiers commencent à s'exécuter depuis le début, nous n'avons donc pas besoin de déclarations pour rendre le programme travail.Vous pouvez tester le programme à l'aide de cet émulateur , bien que vous deviez vous battre un peu pour le saisir.
Voici comment les choses se terminent une fois que vous avez compris comment utiliser le système de fichiers, l'éditeur, le terminal et des objets similaires que vous pensiez déjà savoir utiliser:
J'ai confirmé avec la documentation qu'il
SIGILL
s'agissait d' un signal authentique (et il avait même le même numéro de signal, 4, tout à fait à l'époque!)la source
a.out
plusieurs octets, en effet (l’9
instruction est compilée sur deux octets et l’assembleur ajoute également un en-tête et un pied de page pour rendre le programme exécutable). C'est pourquoi j'ai écrit le programme en langage assembleur, pas en code machine. Le programme en langage assembleur a un seul octet dans et compile dans un programme avec plus d'octets dans; c'est un problème de code-golf (minimiser la taille de la source), pas un problème de taille (minimiser la taille de l'exécutable), c'est donc la taille de la source sur 1 octet qui compte.C (x86_64, tcc ), 7 octets
Inspiré par cette réponse .
Essayez-le en ligne!
Comment ça fonctionne
L'assemblage généré ressemble à ceci.
Notez que TCC ne place pas la "fonction" définie dans un segment de données .
Après la compilation, _start indiquera main comme d'habitude. Lorsque le programme résultant est exécuté, il attend du code dans main et recherche le nombre entier 6 de little-endian (!) 6 , codé sous la forme 0x06 0x00 0x00 0x00 . Le premier octet - 0x06 - est un code opération non valide. Le programme se termine donc avec SIGILL .
C (x86_64, gcc ), 13 octets
Essayez-le en ligne!
Comment ça fonctionne
Sans le modificateur const , l'assemblage généré ressemble à ceci.
L'éditeur de liens de GCC considère la dernière ligne comme un indice que l'objet généré ne nécessite pas de pile exécutable. Puisque main est explicitement placé dans une section de données , le code opération qu’il contient n’est pas exécutable. Le programme se termine donc par SIGSEGV (erreur de segmentation).
En supprimant la deuxième ou la dernière ligne, l'exécutable généré fonctionnera comme prévu. La dernière ligne peut être ignorée avec l'indicateur de compilation
-zexecstack
( Essayez-le en ligne! ), Mais cela coûte 12 octets .Une alternative plus courte consiste à déclarer main avec le modificateur const , ce qui entraîne l'assemblage suivant.
Cela fonctionne sans les indicateurs du compilateur. Notez que
main=6;
cela écrirait la "fonction" définie dans les données , mais le modificateur const obligera GCC à l'écrire dans rodata , qui (du moins sur ma plate-forme) est autorisé à contenir du code.la source
main
s'agit d'un 6 et essaye de l'appeler (ce qui, je suppose, le ferait abandonner et essayer l'instruction)?main
ne pas être une fonction, mais seulement si vous activez les avertissements (-Wall
ou-pedantic
le ferez)..rodata
section à l'intérieur du segment de texte de l'exécutable, et je m'attends à ce que ce soit le cas sur à peu près n'importe quelle plate-forme. (Le chargeur de programme du noyau ne se soucie que des segments, pas des sections).06
ne s'agit que d'une instruction invalide dans x86-64. En mode 32 bits,PUSH ES
cette réponse ne fonctionne donc qu'avec les compilateurs dont le comportement par défaut est-m64
. Voir ref.x86asm.net/coder.html#x06 . La seule séquence d'octets dont le décodage est garanti en tant qu'instruction non autorisée sur tous les futurs processeurs x86 est le UD2 : 2 octets0F 0B
. Tout le reste pourrait être un préfixe futur ou un codage d’instruction. Néanmoins, une voix pour un moyen génial d’obtenir un compilateur C pour coller unemain
étiquette sur certains octets!Rapide, 5 octets
Access index 0 d'un tableau vide. Cet appel appelle
fatalError()
un message d'erreur et se bloque avec un SIGILL. Vous pouvez l'essayer ici .la source
fatalError()
se bloque intentionnellement en courantud2
. Je ne sais pas pourquoi ils ont choisi de le faire, mais ils pensaient peut-être que le message d'erreur "Instruction illégale" était logique, car le programme avait un comportement illégal.nil!
, mais le compilateur ne pouvait pas déduire le type de résultat. (Salut aussi JAL!)GNU C, 25 octets
GNU C (un dialecte spécifique de C avec extensions) contient une instruction pour planter le programme intentionnellement. L’implémentation exacte varie d’une version à l’autre, mais les développeurs essaient souvent d’implémenter le crash le moins cher possible, ce qui implique normalement l’utilisation d’une instruction illégale.
La version spécifique que j'avais l'habitude de tester est
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0
; Cependant, ce programme génère un SIGILL sur un assez grand nombre de plates-formes et est donc assez portable. De plus, il le fait via l'exécution d'une instruction illégale. Voici le code d'assemblage dans lequel ce qui précède est compilé avec les paramètres d'optimisation par défaut:ud2
est une instruction pour laquelle Intel garantit qu’il restera toujours indéfini.la source
main(){asm("ud2");}
00 00 0f 0b
c'est le langage machine pourud2
…00
octets; ils ne font pas partie du code machine pour UD2. BTW, comme je l'ai commenté sur la réponse de Dennis , il existe pour le moment des instructions illégales à un octet dans x86-64, mais elles ne sont pas garanties pour rester.00 00
décode la même chose en x86-64 (en tant queadd [rax], al
).00 00 0f 0b
généralement SIGSEGV avant SIGILL, à moins que vous ne disposiez d’un pointeur inscriptiblerax
.C (x86_64), 11, 30, 34 ou 34 + 15 = 49 octets
J'ai présenté quelques solutions qui utilisent des fonctions de bibliothèque pour lancer,
SIGILL
par divers moyens, mais on peut dire que c'est de la triche, car la fonction de bibliothèque résout le problème. Voici une gamme de solutions qui n'utilisent pas de fonctions de bibliothèque et émettent des hypothèses variables sur l'endroit où le système d'exploitation est prêt à vous permettre d'exécuter du code non exécutable. (Les constantes ici sont choisies pour x86_64, mais vous pouvez les changer pour obtenir des solutions qui fonctionnent pour la plupart des autres processeurs qui ont des instructions illégales.)06
est l'octet du plus petit numéro du code machine qui ne correspond pas à une instruction définie sur un processeur x86_64. Donc, tout ce que nous avons à faire est de l'exécuter. (Alternativement,2F
est également non défini et correspond à un seul caractère ASCII imprimable.) Ni l'un ni l'autre n'est garanti d'être toujours non défini, mais ils ne sont pas définis à ce jour.Le premier programme s'exécute ici à
2F
partir du segment de données en lecture seule. La plupart des linkers ne sont pas capables de produire un saut de travail de.text
vers.rodata
(ou leur équivalent de système d'exploitation) car ce n'est pas quelque chose qui serait utile dans un programme correctement segmenté; Je n'ai pas encore trouvé de système d'exploitation sur lequel cela fonctionne. Vous devez également tenir compte du fait que de nombreux compilateurs souhaitent que la chaîne en question soit une chaîne large, ce qui nécessiterait un complément.L
; Je suppose que tout système d'exploitation sur lequel cela fonctionne a une vue assez dépassée des choses et qu'il est donc construit par défaut pour une norme antérieure à C94. Il est possible que ce programme ne fonctionne nulle part, mais il est également possible que quelque part ce programme fonctionne. Je le répertorie donc dans cette collection de réponses potentielles plus douteuses à moins douteuses. (Après avoir posté cette réponse, Dennis a également mentionné la possibilitémain[]={6}
dans le chat, qui est de la même longueur et qui ne pose pas de problèmes de largeur de caractère, et a même laissé entendre que le potentiel existemain=6
. Je ne peux pas raisonnablement prétendre que ces réponses le mien, comme je n'y pensais pas moi-même)Le deuxième programme s'exécute ici à
06
partir du segment de données en lecture-écriture. Sur la plupart des systèmes d’exploitation, cela entraînera une erreur de segmentation, car les segments de données en écriture sont considérés comme un défaut de conception qui rend les exploitations probables. Cela n’a pas toujours été le cas, donc cela fonctionne probablement sur une version suffisamment ancienne de Linux, mais je ne peux pas le tester facilement.Le troisième programme s'exécute à
06
partir de la pile. Encore une fois, cela provoque une erreur de segmentation de nos jours, car la pile est normalement classée comme non inscriptible pour des raisons de sécurité. La documentation de l'éditeur de liens que j'ai beaucoup vue implique qu'il était légal d'exécuter à partir de la pile (contrairement aux deux cas précédents, il est parfois utile de le faire), donc bien que je ne puisse pas le tester, je suis assez sûr qu'il y a version de Linux (et probablement d’autres systèmes d’exploitation) sur laquelle cela fonctionne.Enfin, si vous donnez
-Wl,-z,execstack
(pénalité de 15 octets) àgcc
(si vous utilisez GNUld
dans le backend), il désactivera explicitement la protection de pile exécutable, permettant ainsi au troisième programme de fonctionner et donnant un signal d’opération non conforme comme prévu. Je l' ai testé et vérifié cette version 49 octets au travail. (Dennis mentionne dans le chat que cette option fonctionne apparemmentmain=6
, ce qui donnerait un score de 6 + 15. Je suis assez surpris que cela fonctionne, étant donné que le 6 n'est pas flagrant; l'option de lien fait apparemment plus que son nom suggère.)la source
const main=6;
fonctionne, ainsi que plusieurs variations. Cet éditeur de liens (qui, je suppose, est également votre éditeur de liens) est capable de générer un saut de.text
à.rodata
; le problème que vous aviez est que, sans celaconst
, vous sautez dans le segment de données en écriture (.data
), qui n’est pas exécutable sur du matériel moderne. Cela aurait fonctionné sur les anciens x86, où le matériel de protection de la mémoire ne pouvait pas marquer les pages comme lisibles, mais non exécutables.main
est nécessaire d’être une fonction (§5.1.2.2.1) - Je ne sais pas pourquoi gcc considère que la déclaration enmain
tant qu’objet de données mérite uniquement un avertissement, et uniquement avec-pedantic
la ligne de commande. Au début des années 90, quelqu'un a peut-être pensé que personne ne le ferait par accident, mais ce n'est pas comme si c'était utile de le faire exprès, sauf pour ce type de jeu.main[]="/"
attendiez à passer au segment de données en lecture seule, car les littéraux de chaîne vont en rodata. Vous avez été surpris par la différence entrechar *foo = "..."
etchar foo[] = "..."
.char *foo = "..."
est un sucre syntaxique pourconst char __inaccessible1[] = "..."; char *foo = (char *)&__inaccessible1[0];
, donc le littéral chaîne va dans rodata, etfoo
est une variable globale distincte et inscriptible qui pointe vers elle. Avecchar foo[] = "..."
, cependant, tout le tableau va dans le segment de données en écriture.GNU en tant que (x86_64), 3 octets
$ xxd sigill.S
$ as --64 sigill.S -o sigill.o; ld -S sigill.o -o sigill
$ ./sigill
$ objdump -d sigill
la source
asm-link
) pour les programmes de jouets mono-fichier construirait un exécutable à partir de cette source de la même manière, car, parld
défaut, le point d'entrée est situé au début du segment de texte ou quelque chose comme ça. Je ne pensais tout simplement pas à choisir la taille source asm pour celui-ci: PBash sur Raspbian sur QEMU, 4 (1?) Octets
Pas mon travail. Je rapporte simplement le travail d'un autre. Je ne suis même pas en mesure de tester l'allégation. Puisqu'une partie cruciale de ce défi semble être de trouver un environnement dans lequel ce signal sera émis et capté, je n'inclus pas la taille de QEMU, Raspbian ou bash.
Le 27 février 2013 à 20 h 49, l'utilisateur emlhalac a signalé qu'il avait reçu " des instructions illégales lorsqu'il tentait de chrooter " sur le forum Raspberry Pi.
produisant
J'imagine que des commandes beaucoup plus courtes produiront cette sortie, par exemple
tr
.EDIT: D'après le commentaire de @ fluffy's , la limite inférieure conjecturée de la longueur d'entrée a été réduite à "1?".
la source
[
commande gagnerait. :)Fichier COM MS-DOS x86, 2 octets
EDIT: Comme indiqué dans les commentaires, le DOS lui-même ne piège pas l'exception du processeur et se bloque simplement (pas seulement l'application, mais tout le système d'exploitation). L'exécution d'un système d'exploitation basé sur NT 32 bits, tel que Windows XP, déclenchera en effet un signal d'instruction illégal.
De la documentation :
Ce qui est assez explicite. Enregistrer sous un fichier .com et
s’exécuter dans n’importe quel émulateurDOS Les émulateurs DOS vont juste planter. Exécuter sous Windows XP, Vista ou 7 32 bits.la source
#UD
piège. (En outre, j'ai décidé de le tester, et il est apparu que mon émulateur DOS était projeté dans une boucle infinie.)C (Windows 32 bits), 34 octets
Cela ne fonctionne que si vous compilez sans optimisation (sinon, le code illégal de la
f
fonction est "optimisé").Le désassemblage de la
main
fonction ressemble à ceci:Nous pouvons voir qu'il utilise une
push
instruction avec une valeur littérale0b0f
(little-endian, donc ses octets sont permutés). L'call
instruction pousse une adresse de retour (de l'...
instruction) située sur la pile près du paramètre de la fonction. En utilisant un[-1]
déplacement, la fonction remplace l'adresse de retour afin qu'elle pointe 9 octets plus tôt, où se trouvent les octets0f 0b
.Ces octets provoquent une exception "instruction non définie", telle que conçue.
la source
Java,
504324 octetsCeci est un
java.util.function.Consumer<Runtime>
1 dont la commande est volée de la réponse de Fluffy . Cela fonctionne parce que vous devez l' appeler commewhateverNameYouGiveIt.accept(Runtime.getRuntime())
!Notez que cela va créer un nouveau processus et le faire lancer un SIGILL plutôt que de lancer un SIGILL lui-même.
1 - Techniquement, cela peut aussi être un
java.util.function.Function<Runtime, Process>
parce queRuntime#exec(String)
renvoie unjava.lang.Process
qui peut être utilisé pour contrôler le processus que vous venez de créer en exécutant une commande shell.Afin de faire quelque chose de plus impressionnant dans un langage aussi prolixe, voici un bonus de
7260 à48 octets:Celui-ci en est un autre
Consumer<Runtime>
qui passe par TOUS les processus (y compris lui-même), faisant que chacun d'eux jette un SIGILL. Mieux vaut se préparer à un accident violent.Et un autre bonus (a
Consumer<ANYTHING_GOES>
), qui prétend au moins lancer un SIGILL sur 20 octets:la source
Perl, 9 octets
Appelle simplement la fonction de bibliothèque appropriée pour signaler un processus et oblige le programme à se signaler lui-même
SIGILL
. Aucune instruction illégale réelle n'est impliquée ici, mais le résultat approprié est obtenu. (Je pense que cela rend le défi assez bon marché, mais si quelque chose est permis, c'est l'échappatoire que vous utiliseriez…)la source
+
. :)+
. Après avoir joué au golf pendant un certain temps, ils font+
parfois des démonstrations. Finalement, ils ont écrit suffisamment de programmes où ils avaient besoin d'éviter les espaces pour une raison ou une autre, ce qui en+
fait une habitude. (Il analyse également de manière moins ambiguë, car il permet de déclencher le cas particulier de l’analyseur des parenthèses.)Langage d'assemblage unifié (UAL) ARM, 3 octets
Par exemple:
Après exécution
nop
, le processeur interprète la.ARM.attributes
section en tant que code et rencontre une instruction illégale quelque part à cet endroit:Testé sur un Raspberry Pi 3.
la source
Microsoft C (Visual Studio 2005 et versions ultérieures), 16 octets
Je ne peux pas facilement tester cela, mais selon la documentation, il devrait produire une instruction illégale en essayant intentionnellement d'exécuter une instruction réservée au noyau à partir d'un programme en mode utilisateur. (Notez que, du fait que l'instruction illégale bloque le programme, nous n'avons pas à essayer de revenir
main
, ce qui signifie que cettemain
fonction de style K & R est valide. Visual Studio ne jamais être passé de C89 est normalement une mauvaise chose, mais il est entré utile ici.)la source
Ruby, 13 octets
Je suppose qu'il est prudent de supposer que nous exécutons ceci à partir d'un shell * nix. Les littéraux backtick exécutent la commande shell donnée.
$$
est le processus en cours d'exécution Ruby, et le#
est pour l'interpolation de chaîne.Sans appeler directement le shell:
Ruby, 17 octets
la source
N'importe quel shell (sh, bash, csh, etc.), n'importe quel POSIX (10 octets)
Réponse banale mais je n'avais vu personne le poster.
Envoie simplement SIGILL au processus en cours. Exemple de sortie sur OSX:
la source
kill -4 1
si la question n'est pas précise sur le programme qui lance le SIGILLYou will have root in your programs
- supprimez un octet de votre réponse et posez légèrement la question en même temps: D. BONUS: Vous devez tuerinit
init
est en réalité à l'abri des signaux qu'il n'a pas spécifiquement demandé de recevoir, même avec root. Vous pourriez peut-être contourner ce problème en utilisant un système d'exploitation POSIX différent, cependant.kill -4 2
alors: DELF + code machine x86, 45 octets
Ce devrait être le plus petit programme exécutable sur une machine Unix qui lance SIGILL (car Linux ne reconnaît pas l'exécutable s'il est réduit).
Compiler avec
nasm -f bin -o a.out tiny_sigill.asm
, testé sur une machine virtuelle x64.Binaire réel de 45 octets:
Liste de montage (voir source ci-dessous):
Disclaimer: code du tutoriel suivant sur l'écriture du plus petit programme d'assemblage pour renvoyer un nombre, mais en utilisant l'opcode ud2 au lieu de mov: http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
la source
AutoIt , 93 octets
Utilisation de l’assemblage en ligne de flatassembler:
Lorsqu'il est exécuté en mode interactif SciTE, il se bloque immédiatement. Le débogueur Windows devrait apparaître pendant une fraction de seconde. La sortie de la console ressemblera à ceci:
Où
-1073741795
est le code d'erreur indéfini émis par WinAPI. Cela peut être n'importe quel nombre négatif.Similaire avec mon propre assembleur LASM :
la source
NASM, 25 octets
Je ne sais pas comment cela fonctionne, mais seulement sur mon ordinateur (Linux x86_64).
Compiler et exécuter comme:
la source
ja 0
ud2
TI-83 Hex Assembly, 2 octets
Courir en tant que
Asm(prgmI)
. Exécute l'opcode 0xed77 non conforme. Je compte chaque paire de chiffres hexadécimaux comme un octet.la source
Python, 32 octets
la source
import os;os.kill(os.getpid(),4)
x86 .COM, 1 octet
ARPL
provoque#UD
en mode 16 bitsla source
Shell Linux, 9 octets
Envoie
SIGILL
au processus avec le PID 0. Je ne sais pas quel processus a le PID 0, mais il existe toujours.Essayez-le en ligne!
la source
man kill
:0 All processes in the current process group are signaled.
GNU C,
241918 octets-4 grâce à Dennis
-1 grâce à ceilingcat
Essayez-le en ligne! Cela suppose ASCII et x86_64. Il tente d'exécuter le code machine
27
, ce qui ... est illégal.shortC ,
1054 octetsÉquivalent au code GNU C ci-dessus. Essayez-le en ligne!
la source
L"\6"
est également illégal, en supposant que x86_64.L
n'est pas nécessaire.'
est 39 = 0x27 , pas 0x39 .MachineCode sur x86_64,
21 octetsEssayez-le en ligne!
0x07
Appelle simplement l'instruction x86_64 (ceilingcat a suggéré 0x07 au lieu de 0x27)la source