Last active
March 16, 2026 03:04
-
-
Save jLn0n/58068c181bdc4ad1906f6f53a87293cf to your computer and use it in GitHub Desktop.
imhex pattern formats
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma author jLn0n | |
| #pragma description Luau bytecode V6 | |
| import std.io; | |
| import std.mem; | |
| import std.math; | |
| import std.sys; | |
| import type.base; | |
| namespace VarInt32 { | |
| fn transform_bytes(ref auto bytes) { | |
| u128 res = 0; | |
| for(u8 i = 0, (i < sizeof(bytes)), i += 1) { | |
| res |= u128(bytes[i] & 0x7F) << i * 7; | |
| if ((bytes[i] & 0x80) == 0) break; | |
| } | |
| return res; | |
| }; | |
| fn transform(auto value) { | |
| return VarInt32::transform_bytes(value.bytes); | |
| }; | |
| fn format(auto value) { | |
| u128 res = VarInt32::transform_bytes(value.bytes); | |
| return std::format("{} ({:#x})", res, res); | |
| }; | |
| fn size(auto addy) { | |
| u8 len = 0; | |
| while (len < 5) { | |
| if ((std::mem::read_unsigned(addy + len, 1) & 0x80) == 0) { | |
| len += 1; | |
| break; | |
| } | |
| len += 1; | |
| } | |
| return len; | |
| }; | |
| } | |
| struct VarInt32 { | |
| u8 bytes[VarInt32::size($)] [[hidden]]; | |
| } [[sealed, format("VarInt32::format"), transform("VarInt32::transform")]]; | |
| namespace Luau { | |
| enum Opcode: u8 { | |
| NOP = 0, | |
| BREAK = 1, | |
| LOADNIL = 2, | |
| LOADB = 3, | |
| LOADN = 4, | |
| LOADK = 5, | |
| MOVE = 6, | |
| GETGLOBAL = 7, | |
| SETGLOBAL = 8, | |
| GETUPVAL = 9, | |
| SETUPVAL = 10, | |
| CLOSEUPVALS = 11, | |
| GETIMPORT = 12, | |
| GETTABLE = 13, | |
| SETTABLE = 14, | |
| GETTABLEKS = 15, | |
| SETTABLEKS = 16, | |
| GETTABLEN = 17, | |
| SETTABLEN = 18, | |
| NEWCLOSURE = 19, | |
| NAMECALL = 20, | |
| CALL = 21, | |
| RETURN = 22, | |
| JUMP = 23, | |
| JUMPBACK = 24, | |
| JUMPIF = 25, | |
| JUMPIFNOT = 26, | |
| JUMPIFEQ = 27, | |
| JUMPIFLE = 28, | |
| JUMPIFLT = 29, | |
| JUMPIFNOTEQ = 30, | |
| JUMPIFNOTLE = 31, | |
| JUMPIFNOTLT = 32, | |
| ADD = 33, | |
| SUB = 34, | |
| MUL = 35, | |
| DIV = 36, | |
| MOD = 37, | |
| POW = 38, | |
| ADDK = 39, | |
| SUBK = 40, | |
| MULK = 41, | |
| DIVK = 42, | |
| MODK = 43, | |
| POWK = 44, | |
| AND = 45, | |
| OR = 46, | |
| ANDK = 47, | |
| ORK = 48, | |
| CONCAT = 49, | |
| NOT = 50, | |
| MINUS = 51, | |
| LENGTH = 52, | |
| NEWTABLE = 53, | |
| DUPTABLE = 54, | |
| SETLIST = 55, | |
| FORNPREP = 56, | |
| FORNLOOP = 57, | |
| FORGLOOP = 58, | |
| FORGPREP_INEXT = 59, | |
| FASTCALL3 = 60, | |
| FORGPREP_NEXT = 61, | |
| DEP_FORGLOOP_NEXT = 62, | |
| GETVARARGS = 63, | |
| DUPCLOSURE = 64, | |
| PREPVARARGS = 65, | |
| LOADKX = 66, | |
| JUMPX = 67, | |
| FASTCALL = 68, | |
| COVERAGE = 69, | |
| CAPTURE = 70, | |
| SUBRK = 71, | |
| DIVRK = 72, | |
| FASTCALL1 = 73, | |
| FASTCALL2 = 74, | |
| FASTCALL2K = 75, | |
| FORGPREP = 76, | |
| JUMPXEQKNIL = 77, | |
| JUMPXEQKB = 78, | |
| JUMPXEQKN = 79, | |
| JUMPXEQKS = 80, | |
| IDIV = 81, | |
| IDIVK = 82, | |
| }; | |
| enum ConstantType: u8 { | |
| Nil = 0, | |
| Boolean = 1, | |
| Number = 2, | |
| String = 3, | |
| Import = 4, | |
| Table = 5, | |
| Closure = 6, | |
| Vector = 7, | |
| }; | |
| fn opcodeHasAux(Opcode opcode) { | |
| return ( | |
| opcode == Opcode::GETGLOBAL || | |
| opcode == Opcode::SETGLOBAL || | |
| opcode == Opcode::GETIMPORT || | |
| opcode == Opcode::GETTABLEKS || | |
| opcode == Opcode::SETTABLEKS || | |
| opcode == Opcode::NAMECALL || | |
| opcode == Opcode::JUMPIFEQ || | |
| opcode == Opcode::JUMPIFLE || | |
| opcode == Opcode::JUMPIFLT || | |
| opcode == Opcode::JUMPIFNOTEQ || | |
| opcode == Opcode::JUMPIFNOTLE || | |
| opcode == Opcode::JUMPIFNOTLT || | |
| opcode == Opcode::NEWTABLE || | |
| opcode == Opcode::SETLIST || | |
| opcode == Opcode::FORGLOOP || | |
| opcode == Opcode::FASTCALL3 || | |
| opcode == Opcode::LOADKX || | |
| opcode == Opcode::FASTCALL2 || | |
| opcode == Opcode::FASTCALL2K || | |
| opcode == Opcode::JUMPXEQKNIL || | |
| opcode == Opcode::JUMPXEQKB || | |
| opcode == Opcode::JUMPXEQKN || | |
| opcode == Opcode::JUMPXEQKS | |
| ); | |
| }; | |
| fn getInstructionPopulation(auto size, auto addy) { | |
| u32 result = size; | |
| for (u32 i = 0, i < size, i += 1) { | |
| u8 opcode = std::mem::read_unsigned(addy + (i * 4), 1); | |
| if (Luau::opcodeHasAux(opcode)) { | |
| i += 1; | |
| result -= 1; | |
| } | |
| } | |
| return result; | |
| }; | |
| fn format_LuauString(auto string) { | |
| if (string.size == 0) { | |
| return "None"; | |
| } | |
| return std::format("({} bytes) {}", sizeof(string.data), string.data); | |
| }; | |
| fn colorify_LuauString(auto string) { | |
| if (string.size < 1) { | |
| return "FFFFFF"; | |
| } | |
| u128 lsb_addy = $ - string.size; | |
| u8 lsb_size = std::math::min(string.size, 16); | |
| u128 lsb1 = std::mem::read_unsigned(lsb_addy, lsb_size, std::mem::Endian::Little); | |
| u128 lsb2 = std::mem::read_unsigned(lsb_addy, lsb_size, std::mem::Endian::Big); | |
| u128 rsb1 = 0; | |
| u128 rsb2 = 0; | |
| if (string.size > 16) { | |
| u8 rsb_size = std::math::min(string.size - 16, 16); | |
| u128 rsb_addy = $ - rsb_size; | |
| rsb1 = std::mem::read_unsigned(rsb_addy, rsb_size, std::mem::Endian::Little); | |
| rsb2 = std::mem::read_unsigned(rsb_addy, rsb_size, std::mem::Endian::Big); | |
| } | |
| u32 hash = (lsb1 ^ lsb2) | (string.size << 5); | |
| hash = ((hash << 5) - hash); | |
| if (string.size > 16) { | |
| hash ^= (rsb1 ^ rsb2); | |
| hash = ((hash << 5) - hash); | |
| } | |
| return std::format("{:08X}", hash); | |
| }; | |
| fn format_LuauConstant(auto constant) { | |
| str value = ""; | |
| if (constant.type == ConstantType::Nil) { | |
| value = "nil"; | |
| } else if (constant.type == ConstantType::String) { | |
| value = std::format("strings[{}]", constant.data); | |
| } else if (constant.type == ConstantType::Import) { | |
| value = std::format("import[{:#x}]", constant.data); | |
| } else if (constant.type == ConstantType::Table) { | |
| value = "{}"; | |
| } else if (constant.type == ConstantType::Closure) { | |
| value = std::format("proto[{}]", constant.data); | |
| } else if (constant.type == ConstantType::Vector) { | |
| value = std::format( | |
| "({}, {}, {}, {})", | |
| constant.data.x, | |
| constant.data.y, | |
| constant.data.z, | |
| constant.data.w | |
| ); | |
| } else { | |
| value = std::format("{}", constant.data); | |
| } | |
| return std::format("({}) {}", constant.type, value); | |
| }; | |
| fn colorify_LuauConstant(ConstantType type) { | |
| if (type == ConstantType::Nil) { | |
| return "9E9E9E"; | |
| } else if (type == ConstantType::Boolean) { | |
| return "FF6A00"; | |
| } else if (type == ConstantType::Number) { | |
| return "007BFF"; | |
| } else if (type == ConstantType::String) { | |
| return "00D84A"; | |
| } else if (type == ConstantType::Import) { | |
| return "A000FF"; | |
| } else if (type == ConstantType::Table) { | |
| return "00E5E5"; | |
| } else if (type == ConstantType::Closure) { | |
| return "FF007F"; | |
| } else if (type == ConstantType::Vector) { | |
| return "FFE600"; | |
| } | |
| return "FFFFFF"; | |
| }; | |
| fn format_LuauInstruction(auto instruction) { | |
| return std::format("{}", instruction.opcode); | |
| }; | |
| } | |
| struct Vector<T> { | |
| VarInt32 size; | |
| if (size > 0) { | |
| T values[size]; | |
| } | |
| }; | |
| namespace Luau { | |
| namespace Constants { | |
| struct String { | |
| VarInt32 size; | |
| if (size > 0) { | |
| char data[size]; | |
| } | |
| } [[sealed, format("Luau::format_LuauString"), color(Luau::colorify_LuauString(this))]]; | |
| struct Table { | |
| VarInt32 size; | |
| VarInt32 kidx[size]; | |
| }; | |
| struct Vector { | |
| float x, y, z, w; | |
| } [[static]]; | |
| } | |
| namespace Prototype { | |
| bitfield Flags { | |
| bool nativeModule: 1; | |
| bool nativeCold: 1; | |
| bool nativeFunction: 1; | |
| }; | |
| bitfield Constant { | |
| ConstantType type: 8; | |
| match (type) { | |
| (ConstantType::Nil): data: 0; | |
| (ConstantType::Boolean): bool data; | |
| (ConstantType::Number): double data; | |
| (ConstantType::String): VarInt32 data; | |
| (ConstantType::Import): u32 data; | |
| (ConstantType::Table): Constants::Table data; | |
| (ConstantType::Closure): VarInt32 data; | |
| (ConstantType::Vector): Constants::Vector data; | |
| } | |
| } [[sealed, format("Luau::format_LuauConstant"), color(Luau::colorify_LuauConstant(type))]]; | |
| bitfield Instruction { | |
| Luau::Opcode opcode: 8 [[color("00FFFF")]]; | |
| data: 24 [[color("008080")]]; | |
| if (Luau::opcodeHasAux(opcode)) { | |
| u32 aux [[color("40FF40")]]; | |
| } | |
| } [[format("Luau::format_LuauInstruction")]]; | |
| struct Instructions { | |
| VarInt32 size; | |
| Instruction data[Luau::getInstructionPopulation(size, $)]; | |
| }; | |
| namespace TypeInfo { | |
| struct Local { | |
| u8 type; | |
| u8 reg; | |
| VarInt32 startpc; | |
| VarInt32 endpc; | |
| }; | |
| } | |
| struct TypeInfo { | |
| VarInt32 size; | |
| if (size > 0) { | |
| VarInt32 upvalssize; | |
| VarInt32 localssize; | |
| u8 upvalues[upvalssize]; | |
| TypeInfo::Local locals[localssize]; | |
| } | |
| }; | |
| struct LineInfo<auto codesize> { | |
| bool enabled [[color("676740")]]; | |
| if (enabled) { | |
| u8 linegaplog2; | |
| u8 lineinfo[codesize]; | |
| u32 abslineinfo[((codesize - 1) >> linegaplog2) + 1]; | |
| } | |
| }; | |
| namespace DebugInfo { | |
| struct Local { | |
| VarInt32 name; | |
| VarInt32 startpc; | |
| VarInt32 endpc; | |
| u8 reg; | |
| }; | |
| struct Upvalue { | |
| VarInt32 name; | |
| }; | |
| } | |
| struct DebugInfo { | |
| bool enabled; | |
| if (enabled) { | |
| Vector<DebugInfo::Local> locals; | |
| Vector<DebugInfo::Upvalue> upvals; | |
| } | |
| }; | |
| } | |
| struct Prototype { | |
| u8 maxstacksize; | |
| u8 numparams; | |
| u8 numupvalues; | |
| bool isvararg; | |
| Prototype::Flags flags; | |
| Prototype::TypeInfo typeinfo; | |
| Prototype::Instructions code; | |
| Vector<Prototype::Constant> constants; | |
| Vector<VarInt32> protos; | |
| VarInt32 linedefined; | |
| VarInt32 debugnameindex; | |
| Prototype::LineInfo<code.size> lineinfo; | |
| Prototype::DebugInfo debuginfo; | |
| }; | |
| struct Header { | |
| u8 version; | |
| std::assert(version == 6, "Luau bytecode v6 is only supported."); | |
| u8 typesVersion; | |
| } [[static]]; | |
| struct File { | |
| Header header [[color("FF00FF")]]; | |
| Vector<Constants::String> strings [[color("80FF00")]]; | |
| if (header.typesVersion == 3) { | |
| // skipped | |
| u8 userdataRemapIndex; | |
| if (userdataRemapIndex != 0) { | |
| $ += VarInt32::size($); | |
| continue; | |
| } | |
| } | |
| Vector<Prototype> protos; | |
| VarInt32 mainproto; | |
| }; | |
| } | |
| Luau::File file @ 0; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment