Skip to content

Instantly share code, notes, and snippets.

@alexd2580
Last active September 21, 2023 15:19
Show Gist options
  • Save alexd2580/dea8bdbaa91fe59c4592e0961336e124 to your computer and use it in GitHub Desktop.
Save alexd2580/dea8bdbaa91fe59c4592e0961336e124 to your computer and use it in GitHub Desktop.
Selfchained polyglot in 12 languages
#ifdef X//[<?php echo"\rExecuted with PHP\n";system("a68g ".$argv[0].";rm .Random.seed");system("ruby ".$argv[0]);__halt_compiler();]#COMMENT
#++++++++++[->+>+++>+++++++>+++++++>++++++++>++++++++++>++++++++++++<<<<<<<]>
#>>-.>>>>.<+.--.>---.-.<++.-.<<<<++.>>>>>+++.<+++++.>---.<-.<<<<.>---.>>++.<<-.>+++.>----.<---.>+++++++.<<++.>+++++.<<<.<[
q="";""":";q=+1;q=%^;#=;q@
echo "Executed with BASH";cp "$0" "$0.lhs";runghc --ghc-arg=-cpp --ghc-arg=-pgmP --ghc-arg="python $0" "$0.lhs";rm "$0.lhs";exit 0
@;print"Executed with PERL\n";system("cp $0 $0.f90;gfortran -cpp $0.f90 -o f90 2>-;./f90;rm f90 $0.f90");q@"""
import sys,os;s=sys.argv
if len(s)<2:print("Executed with PYTHON",flush=True);os.system('cp {a} {a}.c;g++ -trigraphs {a}.c -o cpp 2>-;./cpp;rm cpp {a}.c'.format(a=s[0]));sys.exit(0)
with open(next(b for(a,b)in zip(s,s[1:])if a[0]!='-'and b[0]!='-'))as i,open(next(b for(a,b)in zip(s,s[1:])if a=='-o'),"a")as o:
[o.write(l)for l in i if l[0]!='#']
"""
#else
#define F!??/
program H!??/
character(len=:),allocatable::a!??/
print"(A)","Executed with FORTRAN"!??/
CALL system("python "//__FILE__)!??/
end program H!??/
#if 0
#define P(X) printf("Executed with C%s\n",X);fflush(stdout);
#include<stdio.h>
#include<stdlib.h>
main(int i,char*v[]){char a[99],c,c2;
#ifdef __cplusplus
P("++");snprintf(a,99,"gcc %s -o c -trigraphs 2>-;./c;rm c",__FILE__);system(a);
#else
if(i<2){P("");snprintf(a,99,"%s %s",v[0],__FILE__);system(a);}else{FILE*f=fopen(v[1],"r");int m[99]={0},p=0,s=0;for(c=fgetc(f);c!=-1;c=fgetc(f)){m[p+=(c==62)-(c==60)]+=(c==43)-(c==45);if(c==46)putchar(m[p]);if(c==91&&m[p]==0||c==93&&m[p]!=0){s=(c==91)-(c==93);do{fseek(f,-2*(c==93),1);c2=fgetc(f);s+=(c2==91)-(c2==93);}while(s!=0||c2==c||(c2!=91&&c2!=93));}}fclose(f);}
#endif
return 0;}
#endif//??/
#endif
#if 0
> import System.Process
> import System.Environment
> main=putStrLn"Executed with HASKELL">>getProgName>>=system.("php "++)
console.log "Executed with COFFEESCRIPT"
require('child_process').exec "perl "+process.argv[1],(f,o,e)->process.stdout.write o
"""
#endif//@;#^;puts"Executed with RUBY";a=__FILE__;b="#{a}.litcoffee";system("cp #{a} #{b};coffee #{b};rm #{b}")#]COMMENT print(("Executed with ALGOL68",newline))
@alexd2580
Copy link
Author

alexd2580 commented Sep 18, 2023

image

How to run

chmod +x lol
./lol

Requirements

  • gcc
  • g++
  • gfortran
  • php
  • python (3)
  • perl
  • ruby
  • ghc (runghc)
  • bash
  • coffee
  • algol68g

The brainfuck interpreter is contained within the C code, you can test the validity of the BFC code here: https://minond.xyz/brainfuck/

Tricks used

Algol68

This language has lots of different comment styles. Block comments can be wrapped in # or literally COMMENT. This is great since the first line starts with a # mark anyway. Unfortunately i did not spend enough time figuring out how to run syscalls from algol, so its execution is not actually chained, but instead sequenced from PHP.

Python

Treats # as comment line and """ as string expression. """ can be made into valid bash and ruby (?) by extending it to """:".
Except for the first code line after the header, all foreign code is "string-ed" out, and the preprocessor instructions are comments.

Fortran

Doesn't know trigraphs, ! is a comment.
Can use the C preprocessor to enter the same #ifdef scope. All foreign code is either not inside an enabled ifdef, or behind a #define.

C/C++

C++ has the _cplusplus macro. Both use the preprocessor, #defines and trigraphs to hide fortran code inside an enabled #ifdef block that is not visible to gcc and g++, but is only visible to gfortran.

Bash

Bash doesn't require the entire file to be valid bash if we can early exit using exit.
# are comments. Most languages come after bash, so exit skips them.

Brainfuck

BF ignores most letters and symbols. The only relevant ones are [ ] + - < > , .. We put the BF code as early as possible to prevent pollution of the BF tape with unintended use of these symbols. We leave the BF code in a state where the register index points to an empty (0) tape cell. Then, by putting the rest of the code between [ and ] we can do a jump to the end of the code. In between, all square brackets must come in pairs, the rest is irrelevant, because we don't execute it.

Literate Haskell

LHS ignores everything that is not marked with > at the start of the line. Unfortunately, it also uses a variant of the C preprocessor, though it is possible to run a custom preprocessor instead (which simply removes all preprocessor instructions) and only leaves LHS code. The replacement preprocessor is contained in the python code.

Coffeescript

Same here, literate CS only sees blocks that are indented. The rest is implicit comments.

Perl

q-strings - q= starts a string that ends at the next = sign. Luckily this looks exactly like an assignment in bash/ruby/python.
Numbers can be added to strings with no issue. # are comments unless they are in a string, duh. Most code is put inside a q@-string, while the bash/python/ruby glue code uses q= strings.

Ruby

%-strings - same as in perl, just with % as the string marker. q=%^;... looks like an assignment of %^ to q in bash, dunno what it does, but to ruby it looks like an assignment of a long %-string to q. This string wraps most foreign code. # are comments, thus skipping the preprocessor header.

PHP

Doesn't care about anything other than the PHP markers. To remove unwanted output we put the PHP at the start of the code, and print a carriage return \r, also terminating the PHP compiler early.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment