Skip to content

Instantly share code, notes, and snippets.

@shinh
Last active December 26, 2020 18:15
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save shinh/54abf74d1600cc506a632bd211501db0 to your computer and use it in GitHub Desktop.
Save shinh/54abf74d1600cc506a632bd211501db0 to your computer and use it in GitHub Desktop.
bytecode polyglot - def con qual 2020 bytecoooding
#!/usr/bin/env ruby
# elisp, lua, python2, and jvm
# https://docs.google.com/spreadsheets/d/1l1N_wtK8xA7N-ezG5iUjDeg6iKQgVaYf8ckTSp30QIo/
$flag = File.read('flag').chomp
$ml_preamble = nil
$lua_preamble = nil
$ruby_preamble = nil
$node_preamble = ''
$jvm_preamble = nil
$python2_preamble = nil
$python3_preamble = nil
$elisp_preamble = nil
$ruby = "service/ruby-2.7.0/bin/ruby"
$bytenode = "bytenode"
$bytenode = "service/node-patched bytenode/cli.js"
def read_file(filename)
File.open(filename, 'r:binary') do |f|
f.read
end
end
def write_file(filename, s)
File.open(filename, 'w:binary') do |f|
f.write(s)
end
return filename
end
class BinaryConsumer
def initialize(b)
@b = b
@o = 0
end
def read(n)
r = @b[@o, n]
@o += n
return r
end
def offset
@o
end
end
def run_ocaml(filename)
pipe = IO.popen("ocaml #{filename}", "r+")
pipe.close_write
return pipe.read
end
def check_ocaml(filename)
out = run_ocaml(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def read_ocaml_int(code)
v = code[0].ord
if v >= 0x80
raise "unknown int: #{v}"
elsif v < 0x40
len = v.to_i + 1
r = 0
len.times do |i|
r <<= 8
r |= code[i + 1].ord
end
return r, len
else
return v - 0x40, 1
end
end
def ocaml_int_to_str(v)
if v < 64
return [v + 0x40].pack("c")
end
if v < 128
return [0, v].pack("c*")
end
if v < 4096
return [1, v / 256, v % 256].pack("c*")
end
raise "not supported #{v}"
end
def forge_ml(offset)
preamble_size = 12
cu_pos_off = 35
cu_size_off = 4
ml_source = %Q(print_endline (input_line (open_in "flag")))
write_file('read_flag.ml', ml_source)
if !system("ocamlc read_flag.ml")
raise "ocamlc failed"
end
check_ocaml("read_flag.cmo")
c = read_file('read_flag.cmo')
$ml_preamble = preamble = c[0, preamble_size]
cu_pos = c[preamble_size, 4].unpack1("L>")
cu = c[cu_pos..-1]
cu_size = cu[cu_size_off, 4].unpack1("L>")
code_pos, code_pos_len = read_ocaml_int(cu[cu_pos_off..-1])
code_size, code_size_len = read_ocaml_int(cu[cu_pos_off+code_pos_len..-1])
code = c[code_pos, code_size]
STDERR.puts "[+] ocaml cu_pos=#{cu_pos} cu_size=#{cu_size} code_pos=#{code_pos} code_size=#{code_size}"
new_code_pos_len = ocaml_int_to_str(offset)
cu[cu_pos_off, code_pos_len] = new_code_pos_len
cu[cu_size_off, 4] = [cu_size + new_code_pos_len.size - code_pos_len].pack("L>")
out = preamble
out += [cu_pos + offset - code_pos].pack("L>")
out += [0].pack("c") * (offset - code_pos)
out += code
out += cu
write_file("out.cmo", out)
check_ocaml("out.cmo")
return out
end
def run_lua(filename)
pipe = IO.popen("lua5.2 #{filename}", "r+")
return pipe.read
end
def check_lua(filename)
out = run_lua(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_lua(num_skip)
preamble_size = 18
code_off = 11
lua_source = %Q(r=io.open"flag","r"
print(r:read()))
write_file('read_flag.lua', lua_source)
if !system("luac5.2 read_flag.lua")
raise "luac failed"
end
check_lua("luac.out")
c = read_file('luac.out')
$lua_preamble = preamble = c[0, preamble_size]
off = preamble_size + code_off
code_size = c[off, 4].unpack1("L")
off += 4
bc = c[off, code_size * 4]
STDERR.puts "[lua] old bc_size=#{bc.size / 4}"
off += code_size * 4
const_size = c[off, 4].unpack1("L")
off += 4
rest = c[off..-1]
const_size.times do
typ = c[off].ord
off += 1
if typ == 0
STDERR.puts "[-] lua const nil"
elsif typ == 1
STDERR.puts "[-] lua const bool"
off += 1
elsif typ == 3
STDERR.puts "[-] lua const number"
off += 8
elsif typ == 4
str_size = c[off, 8].unpack1("Q")
off += 8
STDERR.puts "[-] lua const string #{c[off, str_size - 1]}"
off += str_size
else
raise "Unknown lua const type: #{typ}"
end
end
func_size = c[off, 4].unpack1("L")
off += 4
upvalue_size = c[off, 4].unpack1("L")
off += 4
off += upvalue_size * 2
STDERR.puts "[+] lua code_size=#{code_size} const_size=#{const_size} upvalue_size=#{upvalue_size}"
out = c.dup
out[preamble_size, 4] = [0].pack("L")
out[preamble_size + 4, 4] = [0].pack("L")
# numparams
out[preamble_size + 8] = [0].pack('c')
# is_vararg
out[preamble_size + 9] = [0].pack('c')
# maxstacksize
out[preamble_size + 10] = [6].pack('c')
op = proc{|o, a, b, c|
o | (a << 6) | (b << (6 + 8 + 9)) | (c << (6 + 8))
}
op_jmp = 23
skip = ''
if num_skip > 0
raise if num_skip <= 1
buf = [0] * 24
# buf[4] = 6 # for jvm
# # buf[5] = 20 # for node
# skip += buf.pack("c*")
skip_jmp = op[op_jmp, 0, 256, num_skip - 1 - 4]
STDERR.puts "[+] lua skip_jmp %0x" % skip_jmp
skip += [skip_jmp].pack("L")
skip += ([0] * (num_skip-4)).pack("L*")
end
bc2 = skip
todos = []
(bc.size / 4).times do |i|
todos << bc[i * 4, 4]
end
raise if todos.size != 12
todos[0] = [todos[0].unpack1("L") + (1 << 14)].pack("L")
todos[1] = [todos[1].unpack1("L") + (1 << 14)].pack("L")
todos[2] = [todos[2].unpack1("L") + (1 << 14)].pack("L")
todos[5] = [todos[5].unpack1("L") + (1 << 23)].pack("L")
todos[6] = [todos[6].unpack1("L") + (1 << 14)].pack("L")
todos[7] = [todos[7].unpack1("L") + (1 << 14)].pack("L")
todos[8] = [todos[8].unpack1("L") + (1 << 14)].pack("L")
jvm_start = 472
lua_start = 475
num_op = 12
lp = lua_start
while !todos.empty?
m = (lp - jvm_start) % 9
if m == 0
bc2 += [0].pack("L")
elsif m >= 1 && m <= 4
bc2 += todos.shift
elsif m == 5
bc2 += [op[op_jmp, 0, 256, 0]].pack("L")
else
bc2 += [0xff].pack("L")
end
#puts "#{lp} #{ans}"
lp += 4
end
# bc2 += [op[op_jmp, 0, 256, 400-1-4]].pack("L")
# bc2 += ([0] * 400).pack("L*")
# bc2 += bc
while bc2.size / 4 < 774 - 512
bc2 += ([0] * (1)).pack("L*")
end
# #bc2 += ([0] * (1+256+206 - 1 - 400 + 4)).pack("L*")
bc = bc2
STDERR.puts "[lua] new bc_size=#{bc.size / 4} rest=#{rest.size}"
out = preamble + out[preamble_size, code_off] + [bc.size / 4].pack("L") + bc
out += [const_size + 1].pack("L")
out += [4].pack("c")
strsz = 0x600
out += [strsz].pack("Q")
out += ([0] * strsz).pack("c*")
out += rest
write_file("out.luac", out)
#check_lua("out.luac")
return out
end
def run_ruby(filename)
ruby_run_source = %Q(
RubyVM::InstructionSequence.load_from_binary(File.open('#{filename}', 'r').readlines.join('')).eval
)
write_file("run_bc.rb", ruby_run_source)
pipe = IO.popen("#{$ruby} run_bc.rb", "r+")
return pipe.read
end
def check_ruby(filename)
out = run_ruby(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_ruby(offset)
preamble_size = 12
ruby_compile_source = %Q(
bc = RubyVM::InstructionSequence.compile(%Q(p File.read('flag')))
File.open('read_flag.rb.bc', 'w').write(bc.to_binary)
)
write_file('compile_read_flag.rb', ruby_compile_source)
if !system("#{$ruby} compile_read_flag.rb")
raise "ruby failed"
end
check_ruby("read_flag.rb.bc")
c = read_file("read_flag.rb.bc")
bc_size = c[preamble_size, 4].unpack1("L")
STDERR.puts "[+] ruby bc_size=#{bc_size}"
# Fails if we make bc_size larger!
c[preamble_size, 4] = [bc_size].pack("L")
out = c
write_file("out.rb.bc", out)
check_ruby("out.rb.bc")
return out
end
def run_node(filename)
pipe = IO.popen("#{$bytenode} #{filename}", "r+")
return pipe.read
end
def check_node(filename)
out = run_node(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_node(offset)
node_source = %Q(console.log(require("fs").readFileSync("flag")+0))
write_file('read_flag.js', node_source)
if !system("#{$bytenode} -c read_flag.js")
raise "bytenode failed"
end
check_node("read_flag.jsc")
c = read_file('read_flag.jsc')
# The check for magic_number was patched.
c[0, 4] = [0].pack("L")
# version_hash
c[4, 4] = [0].pack("L")
# source_hash
c[8, 4] = [0].pack("L")
# cpu_features
c[12, 4] = [0].pack("L")
# flags_hash
c[16, 4] = [0].pack("L")
extra_resv = 0
# num reservations offset
c[20, 4] = [5 + extra_resv].pack("L")
# code stub key
c[24, 4] = [0].pack("L")
# payload length
c[28, 4] = [0].pack("L")
# checksum1
c[32, 4] = [0].pack("L")
# checksum2
c[36, 4] = [0].pack("L")
out = c[0, 60]
out += ([0] * extra_resv).pack("L*")
out += c[60..-1]
#out += ([0] * 199).pack("L*")
write_file("out.jsc", out)
check_node("out.jsc")
return out
end
def run_python3(filename)
pipe = IO.popen("python3 #{filename}", "r+")
return pipe.read
end
def check_python3(filename)
out = run_python3(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_python3(offset)
preamble_size = 4
python_source = %Q(print(open('flag').read()))
write_file('read_flag.py', python_source)
if !system("python3 -m compileall read_flag.py")
raise "python failed"
end
check_python3("__pycache__/read_flag.cpython-36.pyc")
c = read_file("__pycache__/read_flag.cpython-36.pyc")
$python3_preamble = preamble = c[0, preamble_size]
# Arbitrary timestamp and size.
c[4, 4] = [0].pack("L")
c[8, 4] = [0].pack("L")
# argcount
c[12 + 1, 4] = [0].pack("L")
# kwonlyargcount
c[16 + 1, 4] = [0].pack("L")
# nlocals
c[20 + 1, 4] = [0].pack("L")
# stacksize
c[24 + 1, 4] = [0].pack("L")
# flags
c[28 + 1, 4] = [0].pack("L")
out = c
write_file("out.pyc", out)
check_python3("out.pyc")
return out
end
def run_python2(filename)
pipe = IO.popen("python2 #{filename}", "r+")
return pipe.read
end
def check_python2(filename)
out = run_python2(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_python2(offset)
preamble_size = 4
#python_source = %Q(print(open('flag').read()))
python_source = %Q(# coding=latin-1
import os
os.write(1, open('flag').read())
os._exit(0)
)
write_file('read_flag.py', python_source)
if !system("python2 -m compileall read_flag.py")
raise "python failed"
end
check_python2("read_flag.pyc")
c = read_file("read_flag.pyc")
$python2_preamble = preamble = c[0, preamble_size]
timestamp = c[4, 4].unpack("L*")[0]
argcount, nlocals, stacksize, flags = c[8 + 1, 4 * 4].unpack("L*")
puts "[+] python2 timestamp=#{timestamp} argcount=#{argcount} nlocals=#{nlocals} stacksize=#{stacksize} flags=#{flags}"
# Arbitrary timestamp.
c[4, 4] = [0].pack("L")
# argcount
c[8 + 1, 4] = [0].pack("L")
# nlocals
c[12 + 1, 4] = [0].pack("L")
# stacksize
c[16 + 1, 4] = [0].pack("L")
# flags
c[20 + 1, 4] = [0].pack("L")
bc_size = c[26, 4].unpack1("L")
STDERR.puts "[+] python2 bc_size=#{bc_size}"
bc = c[30, bc_size]
jmp_high = 6
jmp_low = 255
bc = [0x6e, jmp_low, jmp_high].pack("c*") + ([0] * (jmp_high * 256 + jmp_low)).pack("c*") + bc
bc += ([0] * 1500).pack("c*")
out = c[0, 26]
out += [bc.size].pack("L")
out += bc
out += c[26 + 4 + bc_size..-1]
write_file("out.pyc", out)
check_python2("out.pyc")
write_file("out.pyc.bc", out[4..-1])
return out
end
def gen_elisp_read_flag
bconst = 0xc0
bcall = 0x20
bcall1 = 0x21
bcall4 = 0x24
breturn = 0x87
bdiscard = 0x88
elc = []
elc << bconst + 26 + 1 # insert-file-contents
elc << bconst + 26 # string
elc << bconst + ?f.ord - ?a.ord
elc << bconst + ?l.ord - ?a.ord
elc << bconst + ?a.ord - ?a.ord
elc << bconst + ?g.ord - ?a.ord
elc << bcall4
elc << bcall1
elc << bdiscard
elc << bconst + 26 + 2 # print
elc << bconst + 26 + 3 # buffer-string
elc << bcall
elc << bcall1
elc << breturn
elc.pack("c*")
end
def run_elisp(filename)
system("cp #{filename} bytecode")
pipe = IO.popen("service/src/platforms/elisp/run", "r+")
return pipe.read
end
def check_elisp(filename)
# X crashes for some reason :(
return
out = run_elisp(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_elisp(offset)
out = ''
# To keep Java's constant pool small.
out += [1].pack("c*")
# bgoto
out += [0x82, offset % 256, offset / 256].pack("c*")
out += ([0] * (offset - out.size)).pack("c*")
out += gen_elisp_read_flag
write_file("out.elc", out)
check_elisp("out.elc")
return out
end
def run_jvm(filename)
if filename != "Bytecode.class"
system("cp #{filename} Bytecode.class")
end
pipe = IO.popen("java Bytecode", "r+")
return pipe.read
end
def check_jvm(filename)
out = run_jvm(filename)
if !out.include?($flag)
raise "No flag in #{out}"
end
end
def forge_jvm(offset)
preamble_size = 8
java_source = %Q(
import java.io.*;
public class Bytecode {
public static void main(String[] args) throws Exception {
FileReader r = new FileReader("flag");
BufferedReader b = new BufferedReader(r);
System.out.println(b.readLine());
}
}
)
write_file('Bytecode.java', java_source)
if !system("javac -g:none Bytecode.java")
raise "javac failed"
end
check_jvm("Bytecode.class")
c = read_file("Bytecode.class")
write_file("read_flag.class", c)
$jvm_preamble = preamble = c[0, preamble_size]
body = c[preamble_size..-1]
bc = BinaryConsumer.new(body)
num_consts = bc.read(2).unpack1("S>")
STDERR.puts "[+] java num_consts=#{num_consts}"
const_tag_float = 4
const_tag_double = 6
const_tag_string = 8
const_tag_name_and_type = 12
consts = []
dummy_str_idx = 44
consts << [const_tag_double]
consts << [const_tag_double]
consts << [const_tag_double]
consts << [const_tag_double]
consts << [const_tag_double]
consts << [const_tag_float]
consts << [const_tag_string, dummy_str_idx]
num_double = consts.count([const_tag_double])
idx_diff = consts.size + num_double
(num_consts - 1).times do
tag = bc.read(1).unpack1("c")
if tag == 1
len = bc.read(2).unpack1("s>")
str = bc.read(len)
STDERR.puts "[-] java UTF8 v=#{str}"
consts << [tag, str]
elsif tag == 7
idx = bc.read(2).unpack1("s>") + idx_diff
STDERR.puts "[-] java Classs idx=#{idx}"
consts << [tag, idx]
elsif tag == 8
idx = bc.read(2).unpack1("s>") + idx_diff
STDERR.puts "[-] java String idx=#{idx}"
consts << [tag, idx]
elsif tag == 9
ci = bc.read(2).unpack1("s>") + idx_diff
nti = bc.read(2).unpack1("s>") + idx_diff
STDERR.puts "[-] java Fieldref ci=#{ci} nti=#{nti}"
consts << [tag, ci, nti]
elsif tag == 10
ci = bc.read(2).unpack1("s>") + idx_diff
nti = bc.read(2).unpack1("s>") + idx_diff
STDERR.puts "[-] java Methodref ci=#{ci} nti=#{nti}"
consts << [tag, ci, nti]
elsif tag == 12
ni = bc.read(2).unpack1("s>") + idx_diff
di = bc.read(2).unpack1("s>") + idx_diff
STDERR.puts "[-] java NameAndType ni=#{ni} di=#{di}"
consts << [tag, ni, di]
else
raise "Unknown tag #{tag}"
end
end
0.times do
consts << [const_tag_string, dummy_str_idx]
end
nc = 13
nc.times {
consts << [const_tag_double]
}
num_double += nc
157.times do
consts << [const_tag_string, dummy_str_idx]
end
consts << [const_tag_double]
num_double += 1
consts << [const_tag_double]
num_double += 1
while consts.size + num_double < 0x181
consts << [const_tag_string, dummy_str_idx]
end
access_off = bc.offset
access_flags = bc.read(2).unpack1("s>")
this_flags = bc.read(2).unpack1("s>") + idx_diff
super_flags = bc.read(2).unpack1("s>") + idx_diff
STDERR.puts "[-] java access_flags=#{access_flags} this_flags=#{this_flags} super_flags=#{super_flags}"
interfaces_count = bc.read(2).unpack1("s>")
STDERR.puts "[-] java interfaces_count=#{interfaces_count}"
interfaces = []
interfaces_count.times do
interfaces << bc.read(2).unpack1("s>")
end
raise if interfaces_count > 0
fields_count = bc.read(2).unpack1("s>")
STDERR.puts "[-] java fields_count=#{fields_count}"
raise if fields_count > 0
method_off = bc.offset
methods_count = bc.read(2).unpack1("s>")
STDERR.puts "[-] java methods_count=#{methods_count}"
methods = []
methods_count.times do
af = bc.read(2).unpack1("s>")
ni = bc.read(2).unpack1("s>") + idx_diff
di = bc.read(2).unpack1("s>") + idx_diff
ac = bc.read(2).unpack1("s>")
STDERR.puts "[-] java method access_flags=#{af} name_index=#{ni} descriptor_index=#{di} attributes_count=#{ac}"
attrs = []
ac.times do
ani = bc.read(2).unpack1("s>") + idx_diff
attr_len = bc.read(4).unpack1("l>")
attr = bc.read(attr_len)
STDERR.puts "[-] java attribute_name_index=#{ani} attr_len=#{attr_len}"
attrs << [ani, attr]
end
methods << [af, ni, di, attrs]
end
fixup = proc{|mi, ai, off|
fc = methods[mi][3][ai][1]
fc[off, 2] = [fc[off, 2].unpack1("s>") + idx_diff].pack("s>")
}
fixup[0, 0, 9 + 1]
fixup[1, 0, 9 + 0]
fixup[1, 0, 9 + 3]
fixup[1, 0, 9 + 6]
fixup[1, 0, 9 + 10]
fixup[1, 0, 9 + 15]
fixup[1, 0, 9 + 19]
fixup[1, 0, 9 + 23]
fixup[1, 0, 9 + 26]
fixup[1, 1, 2]
attr_off = bc.offset
attributes_count = bc.read(2).unpack1("s>")
STDERR.puts "[-] java attributes_count=#{attributes_count}"
attributes = []
attributes_count.times do
ani = bc.read(2).unpack1("s>")
attr_len = bc.read(4).unpack1("l>")
attr = bc.read(attr_len)
STDERR.puts "[-] java attribute_name_index=#{ani} attr_len=#{attr_len}"
end
raise if preamble_size + bc.offset != c.size
out = preamble
# out += body[0, access_off]
out += [consts.size + 1 + num_double].pack("s>")
consts.each do |tag, *ci|
out += [tag].pack("c")
if tag == 1
str = ci[0]
out += [str.size].pack("s>")
out += str
elsif tag == 4
out += [0].pack("l>")
elsif tag == 5 || tag == 6
out += [0].pack("q>")
elsif tag == 7
out += ci.pack("s>*")
elsif tag == 8
out += ci.pack("s>*")
elsif tag == 9
out += ci.pack("s>*")
elsif tag == 10
out += ci.pack("s>*")
elsif tag == 12
out += ci.pack("s>*")
else
raise "Unknown tag #{tag}"
end
end
out += [access_flags, this_flags, super_flags].pack("s>*")
out += [interfaces_count, fields_count].pack("s>*")
out += [methods.size].pack("s>")
methods.each do |af, ni, di, attrs|
out += [af, ni, di, attrs.size].pack("s>*")
attrs.each do |ani, attr|
out += [ani].pack("s>")
out += [attr.size].pack("l>")
out += attr
end
end
target_size = 4000
dummy_attr_len = target_size - out.size - 2 - 2 - 4
out += [1].pack("s>")
out += [dummy_str_idx].pack("s>")
out += [dummy_attr_len].pack("l>")
out += ([0] * dummy_attr_len).pack("c*")
write_file("out.class", out)
check_jvm("out.class")
return out
end
def polyglot(bcs)
out = ''
5000.times do |i|
o = 0
winner = nil
code_exist = false
bcs.each do |name, bc, preamble|
c = bc[preamble.size + i]
next if !c
c = c.ord
raise "too large for 4k limit #{name}" if i > 4000
code_exist = true
if c != o
if o == 0
STDERR.puts "[-] use #{name} for i=#{i} v=%02x" % c
o = c
winner = name
elsif c != 0
raise "conflict at #{i} #{name}(#{c}) vs #{winner}(#{o})"
end
end
end
break if !code_exist
out << [o].pack("c")
end
write_file('polyglot.bc', out)
exts = {
'ocaml' => 'cmo',
'lua' => 'luac',
'python3' => 'pyc',
'python2' => 'pyc',
'java' => 'class',
'node' => 'jsc',
}
bcs.each do |name, bc, preamble|
STDERR.puts "[+] #{name} testing"
checker = method('check_' + name)
filename = "polyglot_#{name}.#{exts[name]}"
write_file(filename, preamble + out)
checker.call(filename)
STDERR.puts "[+] #{name} OK!"
end
return out
end
# Restriction: first 4B must be cu offset in BE.
ocaml_bc = forge_ml(0xc00)
# Restriction: first 8B is non-negative in LE.
# Next 3B are
# numparams = 0
# is_vararg = 1
# maxstacksize = 3
# Note the skip jmp size is sensitive for python2's stack.
lua_bc = forge_lua(118) # for py2
#lua_bc = forge_lua(800 - 40 - 256) # for jvm
#lua_bc = forge_lua(800 - 40)
# Restriction: first 4B is bytecode size in LE.
ruby_bc = forge_ruby(4000)
# Restriction: first 8B is abitrary.
# TYPE_CODE = e3 [1B]
# argc = 0
# kwonlyargcount = 0
# nlocals = 0
# stacksize = 0
# flags = 0
# TYPE_STRING = 74 [1B]
py3_bc = forge_python3(4000)
# Restriction: first 4B is abitrary.
# TYPE_CODE = e3 [1B]
# argc = 0
# nlocals = 0
# stacksize = 0
# flags = 0
# TYPE_STRING = 74 [1B]
py2_bc = forge_python2(4000)
jvm_bc = forge_jvm(2000)
# Restriction: first 4B is bd 03 de c0.
node_bc = forge_node(2000)
# Restriction: first 4B is jmp to the actual bytecode.
elisp_bc = forge_elisp(3900-56+2)
bcs = [
#['ocaml', ocaml_bc, $ml_preamble],
['elisp', elisp_bc, ''],
#['node', node_bc, $node_preamble],
['jvm', jvm_bc, $jvm_preamble],
['python2', py2_bc, $python2_preamble],
['lua', lua_bc, $lua_preamble],
]
polyglot(bcs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment