Fixed a spurious Invalid combination of type specifiers error when a typedef redefined an existing type name with an attribute in between, as in typedef int __attribute__((mode(__pointer__))) intptr_t;.
These are the syntax/semantic updates which may break current programs.
bool is a keyword (C23 6.7.2).
nullptr is a keyword (C23 6.4.4.6).
enum : { … } — C23/clang fixed-underlying-type enums. Only the anonymous form (colon straight after enum)
__attribute__((...)) stmt;
and attributes on labels now keep their attributes instead of being dropped.
typedef int __attribute__((mode(__pointer__))) intptr_t;
now parses as a redefinition rather than a bogus specifier list.
sizeof(+c) on a char is now 4, was 1.
_Complex actually works.
_Noreturn is parsed for real.
__builtin_bswap<N> is declared unsigned, as gcc and clang declare it.
__int128 is refused where the target has none — i386, armv7 and MSVC, per the new sizeof_int128
in the machine model.
(unsigned __int128)(0xHI ULL << 64 | 0xLO ULL)
rather than a bare decimal literal, which C silently truncates.
Opaque values can now be constructed along three independent axes.
identity
modular
mba
structure_state
scalar_state
environment
plugin
trivial
local
global
interprocedural.
The default for resilience is every level except trivial.
Trival contains invariants a C compiler may fold to a constant at -O2; these
are kept only for educational purposes.
See InitOpaque.
--Transform=InitOpaque) transform
using the two options:
--InitOpaqueInvariantKinds
--InitOpaqueInvariantResilience
--InitOpaqueMaxSize option sets the default composition size
for transformations that do not set their own. Defaults to 1.
--InitOpaqueStructs option is a deprecated spelling of
--InitOpaqueInvariantKinds, kept for compatibility;
it maps the old kind names onto the new ones. The input kind was removed.
Default none.
<T> that need them (AddOpaque, Flatten,
Merge, Virtualize, InitBranchFuns and
AntiBranchAnalysis):
--<T>OpaqueInvariantKinds
--<T>OpaqueInvariantResilience
--<T>OpaqueMaxSize
AddOpaque we get the three options
--AddOpaqueInvariantKinds,
--AddOpaqueInvariantResilience, and
--AddOpaqueMaxSize.
Each default to what InitOpaque set up.
--AddOpaqueStructs is now deprecated.
It maps onto the new kinds:
list/array→ structure_state
env→ environment
input was removed.
InitOpaque.
This option will be removed in a future version.
--Transform=InitOpaque --InitOpaqueList=true
option prints the whole opaque-invariant database.
This requires the --DeityMode=password to be set.
This allows the exact name of an opaque predicate to be passed to
--<T>Select.
--<Transform>Select option
(e.g. --AddOpaqueSelect=name) draws opaque invariants by exact name,
bypassing the kind and resilience selection.
This requires the --DeityMode=password to be set.
--InitOpaqueObfuscate option, on by default, applies light obfuscation to the
initialization of the opaque state variables.
--UpdateOpaqueObfuscate option, on by default, applies light obfuscation
to the opaque state updates.
--DeityMode=password option gives
access to additional Tigress features.
See AntiBranchAnalysis.
--AntiBranchAnalysisBranchFunFlatten has been renamed
--AntiBranchAnalysisFlatten.
--AntiBranchAnalysisFraction option controls how many of the
unconditional branches are encoded, defaulting to %100.
--AntiBranchAnalysisKinds,
the new kind
goto2push2 replaces
goto L with
push L; leal; jmp.
See CleanUp.
--CleanUpRenameKinds option chooses the style of the new identifiers:
short names (the default)
english, english2)
exebench, exebench2)
englishAbbr, exebenchAbbr).
--CleanUpKinds has two new kinds:
noLineNumbers drops the #line directives we emit before globals
noNestedBlocks flattens blocks-within-blocks.
See InitImplicitFlow.
callChain for
--InitImplicitFlowKinds copies a variable by passing it along a chain of calls.
--InitImplicitCallChainLength option sets the length of the call chain generated for
--InitImplicitFlowKinds=callChain, defaulting to
800.
--InitImplicitFlowCallChainName option sets the base name of the generated call-chain
functions, with the number appended, defaulting to
callChain.
See LowerType.
--Transform=LowerType is a new transformation. Its float2fixed converter
rewrites a function's float and double computation as
fixed-point integer arithmetic. This allows integer obfuscators like
EncodeArithmetic and EncodeData to transform code they
previously could not see at all.
See EncodeExternal.
--EncodeExternalKind=embed can now include source code from more common libraries:
random (PRNG)
checksum
parse
bignum
crypto.
--Allow128BitInts is a new top-level option saying whether the
target has 128-bit integers ( __int128). You normally do not set
it: it is read from the machine model that --Environment selects.
--Allow128BitInts=false makes the codec pick a
formulation that stays in 64 bits.
See Jit.
See Virtualize.
--VirtualizeDynamicBytecode, which makes the virtualized bytecode self-modifying,
is now false instead of hard.
See EncodeData.
--EncodeDataCodecs=rnc) is now correct
at every integer type; before 4.1 it silently computed wrong values at
unsigned short, unsigned int, long and
unsigned long. Its one remaining limitation is now documented: the codec
is exact only while the value being reconstructed stays below half the product of its
moduli, which a single operation on in-range values always satisfies, but a value
accumulated in a loop eventually will not. Prefer poly1 or
xor for such variables; both are exact for every value of every integer
type.
See EncodeLiterals.
--EncodeLiteralsStringKinds option chooses how string literals are
encoded (one is picked at random per run when several are given):
fsm (the default) rebuilds each string at runtime with a generated
decoder function.
chunk encodes each string in small pieces, so no readable text remains
in the generated source or the compiled binary.
Fixed a spurious Invalid combination of type specifiers error when a typedef redefined an existing type name with an attribute in between, as in typedef int __attribute__((mode(__pointer__))) intptr_t;.
Tigress no longer reports Invalid combination of type specifiers on the typedefs in system headers such as bits/floatn-common.h.
The JitDynamic transform now works on Darwin/ARM64, where the transformed program previously died with a bus error at the first block decode because Apple Silicon forbids a page that is writable and executable at the same time.
Generated code no longer uses the MSVC-only __inline specifier, which GCC did not understand and which left merged functions such as base64_decode_ctx_init as undefined references at link time.
Fixed the type checking of MBA plugin functions for comparison operators, which wrongly rejected valid declarations such as long mba_Eq_2(long a, long y) with a Wrong argument types error.
Fixed Merge passing an untyped 0 as the return-value argument of a merged void function, which broke compilation when EncodeLiterals was applied afterwards and turned that 0 into an opaque integer.
Fixed a failure when applying Jit to a program that had already been processed by Merge.
The Virtualize transform now handles !p for every pointer type, not just void *.
Fixed the ordering of ordinary labels around case and default labels in a switch, which came out reversed and could redirect a goto to the wrong place.
Fixed a crash caused by printing the wrong label for a goto when the target statement carried several labels, for example a label following a default: label in a switch.
Code generated by the Checksum transform now compiles under GCC 15 without the pointer type errors that previously required -fpermissive.
Fixed the ordering of generated declarations for functions carrying the two-argument __attribute__((__malloc__(deallocator, 1))), whose deallocator could be declared after its allocator and make GCC reject the output with 'free_buffer' undeclared here (not in a function).
Fixed Virtualize with --VirtualizeDynamicBytecode=true emitting the bytecode array twice, the second copy all zeros, which made the obfuscated program loop forever.
Fixed crashes, illegal instructions and wrong results in programs transformed with SelfModify on Darwin/ARM.
The Virtualize transform no longer generates casts from an integer to a pointer of a different size, which produced -Wint-to-pointer-cast warnings.
Fixed incompatible-pointer-type errors when compiling code from Virtualize that assigns to and reads from the virtual program counter.
On Darwin, preprocessing now passes -U__BLOCKS__, so system headers that declare Clang block-pointer parameters with ^, such as scandir_b in <dirent.h>, no longer cause a syntax error regardless of whether tigress.h was included first.
The #define _Noreturn workaround has been removed from tigress.h, so the C11 _Noreturn specifier is now parsed properly and its noreturn semantics survive into the obfuscated output.
Including <stdatomic.h> and using the C11 _Atomic type specifier no longer causes a parse error.
Fixed Merge failing with Cannot find the new copy of local variable on a function containing a variable-length array such as int arr[V], both on its own and combined with Flatten.
Fixed InitFunctions discarding the bounds of initialized arrays, so an array declared with a constant bound is no longer shrunk to a single element.
Fixed a syntax error on the C23 / clang fixed-underlying-type enum enum : <type> { ... }, which blocked any source that transitively included a modern macOS SDK header such as <mach/vm_types.h>.
The wrapper no longer prints an empty Using compiler: when --Compiler is omitted, and reports the compiler version consistently when --Compiler and --Environment are resolved from each other.
AddOpaque with a math opaque kind no longer inserts a predicate that reads uninitialized local variables, which was undefined behaviour and could change what the obfuscated program computed.
Fixed Optimize deleting a goto whose target is the next statement without moving that statement's labels or re-pointing the branches that named it, which left a branch pointing at a label that no longer existed.
Fixed Optimize rebuilding sizeof(expr) as _Alignof(expr), which gives the wrong value for anything whose size differs from its alignment.
--OptimizeDumpInterferenceGraph=true now writes the graph it promises, instead of doing nothing while --Verbosity=3 wrote the file whether or not it was asked for.
--EncodeDataCodecs=rnc no longer computes silently wrong values, and is now correct at every integer type; it was wrong at unsigned short, unsigned int, long and unsigned long.
Fixed a generated bit mask being built from a 32-bit int and then shifted by up to 63 places, undefined behaviour that put garbage values into the obfuscated program.
--CleanUpKinds=noExterns now also drops the system-header type definitions and static-inline helpers that came with the declarations it removes, so re-adding the #include files no longer causes redefinition errors.
Unary + now performs the C11 integer promotion, so sizeof(+c) on a char is sizeof(int); without it an obfuscated program could print different values from the original.
The __builtin_bswap<N> family is now declared unsigned, as GCC and clang declare it, instead of signed.
Long double floating constants no longer print a treating long double constant ... as double constant warning to the terminal, even at --Verbosity=0.
Fixed a segmentation fault in the prologue of a jitted function containing an indirect call under --JitObfuscateArguments, caused by a garbage frame size.
Added the C23 nullptr keyword, without which no program compiled against a C23 standard library could be obfuscated at all.
Fixed Virtualize aborting with findField: '_unsigned___int128' not found on a function whose body contains a 128-bit value.
Fixed an embedded library's unsigned char coming out signed, which made every EncodeExternal-embedded crypto hash produce the wrong digest.
Fixed struct-typed library globals being emitted in the wrong order, which crashed Tigress on every embedded library with global tables, such as libMath and libCrypto.
Fixed --VirtualizeAddOpaqueToBogusFuns=true corrupting the generated interpreter's dispatch table for the pointer-based dispatch methods, which made the obfuscated program die on a wild computed goto.
Jit now reports that it cannot represent a 128-bit integer constant, instead of silently truncating it to 64 bits and changing what the function computes.
Fixed --EncodeExternalKind=embed truncating to 32 bits the result of an embedded function called without a visible prototype, which crashed at run time with no diagnostic.
Fixed 128-bit integer constants being printed as bare decimal literals, which a C compiler silently truncates modulo 2^64; this made the rnc codec decode to the wrong value and could leave the obfuscated program looping forever.