Skip to content

Instantly share code, notes, and snippets.

@Justasic
Last active May 1, 2023 16:00
Show Gist options
  • Save Justasic/3b5ad2c351851623e6e423b4a429c1f3 to your computer and use it in GitHub Desktop.
Save Justasic/3b5ad2c351851623e6e423b4a429c1f3 to your computer and use it in GitHub Desktop.
ImHex (https://imhex.werwolv.net/) Telegram TLO object decoding (TLO schema v2 should match what Telegram uses)
#include <std/mem.pat>
#include <std/io.pat>
#include <std/sys.pat>
enum ConstructorIDType : u32 {
CID_TLSTYPE = 0x12eb4386,
CID_NATCONST = 0x8ce940b1,
CID_NATVAR = 0x4e8a14f0,
CID_EXPRNAT = 0xdcb49bd8,
CID_EXPRTYPE = 0xecc9da78,
CID_TYPEVAR = 0x0142ceae,
CID_TYPEEXPR = 0xc1863d08,
CID_TLSARG = 0x29dfe61b,
CID_ARRAY = 0xd9fb20de,
CID_COMBINATORLEFT = 0x4c12c6d9,
CID_COMBINATORLEFTBUILTIN = 0xcd211f63,
CID_COMBINATORRIGHT = 0x2c064372,
CID_COMBINATOR = 0x5c0a1ed5,
CID_SCHEMAv2 = 0x3a2f9be2
};
// These flags are defined in types and expressions
bitfield TypeFlags
{
tf_bare : 1; // Is the type expression bare
tf_nocons : 1; // unknown?
padding : 8;
tf_empty : 1; // unknown?
padding : 6;
tf_optvar : 1; // Is the argument optional? (eg. wrapped in '{}' or flagged)
tf_excl : 1; // Is the argument a forwarded function? (via !)
padding : 1;
tf_optfield : 1; // Is the argument hidden with a field mask
tf_novar : 1; // unknown? only used with "Maybe" and "Bool" types
padding : 3;
tf_defcon : 1; // Does the type have a default constructor (e.g. constructor that will be used if no magic is presented.)
padding : 1;
tf_forwarded: 1;
padding : 4;
};
// These flags only make sense for functions, not types
bitfield CombinatorFlags
{
cf_read : 1; // Does the function represent a read query
cf_write : 1; // Does the function represent a write query
cf_internal : 1; // Is the function used only for internal engine interconnection
cf_kphp : 1; // Is the function processed by kPHP RPC server
};
struct TLString
{
if (std::mem::read_signed($, sizeof(u8)) == 254)
{
$ += 1; // Skip 254
u24 length;
}
else
u8 length;
char value[this.length];
padding[-(this.length + 1) & 3];
};
// tls.type#12eb4386 name:int id:string constructors_num:int flags:int arity:int params_type:long = tls.Type;
struct TLSType
{
ConstructorIDType ConstructorID;
s32 name;
TLString id;
s32 constructors_num;
s32 flags;
s32 arity;
s64 params_type;
};
// tls.natConst#8ce940b1 value:int = tls.NatExpr;
struct NatConst
{
ConstructorIDType ConstructorID;
s32 value;
};
// tls.natVar#4e8a14f0 dif:int var_num:int = tls.NatExpr;
struct NatVar
{
ConstructorIDType ConstructorID;
s32 dif;
s32 var_num;
};
struct NatExpr
{
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_NATVAR)
NatVar;
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_NATCONST)
NatConst;
else
std::assert(false, std::format("Invalid ExprNat value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $));
};
// tls.exprNat#dcb49bd8 _:tls.NatExpr = tls.Expr;
struct ExprNat
{
ConstructorIDType ConstructorID;
NatExpr underscore;
};
using TypeVar;
using typeExpr;
using Array;
struct TypeExpr
{
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_TYPEVAR)
TypeVar;
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_TYPEEXPR)
typeExpr;
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_ARRAY)
Array;
else
std::assert(false, std::format("Invalid TypeExpr value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $));
};
// tls.exprType#ecc9da78 _:tls.TypeExpr = tls.Expr;
struct ExprType
{
ConstructorIDType ConstructorID;
TypeExpr underscore;
};
// tls.typeVar#0142ceae var_num:int flags:int = tls.TypeExpr;
struct TypeVar
{
ConstructorIDType ConstructorID;
s32 var_num;
s32 flags;
};
struct Expr
{
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_EXPRTYPE)
ExprType;
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_EXPRNAT)
ExprNat;
else
std::assert(false, std::format("Invalid typeExpr value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $));
};
// tls.typeExpr#c1863d08 name:int flags:int children_num:# children:children_num*[tls.Expr] = tls.TypeExpr;
struct typeExpr
{
ConstructorIDType ConstructorID;
s32 name;
s32 flags;
u32 children_num;
Expr children[this.children_num];
};
bitfield ArgFlags
{
padding : 1;
exist_bit : 1;
var_bit : 1;
padding : 29;
};
// tls.arg#29dfe61b id:string flags:# var_num:flags.2?int exist_var_num:flags.1?int
// exist_var_bit:flags.1?int type:tls.TypeExpr = tls.Arg;
struct Arg
{
ConstructorIDType ConstructorID;
TLString id;
ArgFlags flags;
if (flags.var_bit)
s32 var_num;
if (flags.exist_bit)
{
s32 exist_var_num;
s32 exist_var_bit;
}
TypeExpr type;
};
// tls.array#d9fb20de multiplicity:tls.NatExpr args_num:# args:args_num*[tls.Arg] = tls.TypeExpr;
struct Array
{
ConstructorIDType ConstructorID;
NatExpr multiplicity;
u32 args_num;
Arg args[this.args_num];
};
// tls.combinatorLeft#4c12c6d9 args_num:# args:args_num*[tls.Arg] = tls.CombinatorLeft;
struct TLSCombinerLeft
{
ConstructorIDType ConstructorID;
u32 args_num;
Arg args[this.args_num];
};
// tls.combinatorLeftBuiltin#cd211f63 = tls.CombinatorLeft;
struct combinatorLeftBuiltin
{
ConstructorIDType ConstructorID;
};
struct CombinatorLeft
{
if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_COMBINATORLEFTBUILTIN)
combinatorLeftBuiltin;
else if (u32(std::mem::read_signed($, sizeof(u32))) == ConstructorIDType::CID_COMBINATORLEFT)
TLSCombinerLeft;
else
std::assert(false, std::format("Invalid combinator left value CID: {:#x} @ {:#x}", u32(std::mem::read_signed($, sizeof(u32))), $));
};
// tls.combinatorRight#2c064372 value:tls.TypeExpr = tls.CombinatorRight;
struct CombinatorRight
{
ConstructorIDType ConstructorID;
TypeExpr value;
};
// tls.combinator#5c0a1ed5 name:int id:string type_name:int
// left:tls.CombinatorLeft right:tls.CombinatorRight = tls.Combinator;
struct Combinator
{
ConstructorIDType ConstructorID;
s32 name;
TLString id;
s32 type_name;
CombinatorLeft left;
CombinatorRight right;
};
// tls.schema_v2 version:int date:int types_num:# types:types_num*[tls.Type]
// constructor_num:# constructors:constructor_num*[tls.Combinator]
// functions_num:# functions:functions_num*[tls.Combinator] = tls.Schema;
struct schemav2
{
ConstructorIDType ConstructorID;
s32 version;
s32 date;
u32 types_num;
TLSType types[this.types_num];
u32 constructor_num;
Combinator constructors[this.constructor_num];
u32 functions_num;
Combinator functions[this.functions_num];
};
schemav2 schema @ 0x00;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment