.NET Reactor: Fix CflowConstantsInliner corrupting exception handlers - #40
Closed
GarethWright wants to merge 1 commit into
Closed
GarethWright wants to merge 1 commit into
GarethWright wants to merge 1 commit into
Conversation
InlineAllConstants replaced whole Instruction objects when inlining the
cflow constant fields:
instrs[i] = Instruction.CreateLdcI4(value);
instrs[i + 1] = Instruction.Create(OpCodes.Nop);
If the original ldsfld/ldfld was an exception-handler boundary or a branch
target, that reference kept pointing at the now-detached instruction, which
is no longer part of the method body. InstructionListParser then threw
KeyNotFoundException from instrToIndex[...] while rebuilding the method's
blocks, aborting deobfuscation of the whole assembly.
Write the constant in place (OpCode/Operand) so exception handlers and
branches keep referring to a live instruction. The emitted IL is unchanged.
The trigger is not version-specific: it only requires an inlined
cflow-constant field access to coincide with an exception-handler boundary
or a branch target.
GarethWright
force-pushed
the
fix/cflow-constants-inliner-instruction-identity
branch
from
September 21, 2026 16:38
52de45c to
15d0765
Compare
Author
|
Closing this and reopening as a fresh PR with a tidied-up description. No change to the code. |
GarethWright
deleted the
fix/cflow-constants-inliner-instruction-identity
branch
September 21, 2026 16:40
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
CflowConstantsInliner.InlineAllConstantsreplaces wholeInstructionobjects when inlining the cflow constant fields:If the original
ldsfld/ldfldhappens to be an exception-handler boundary (e.g.HandlerEnd) or a branch target, the handler/branch keeps referencing the old, now-detached instruction, which is no longer inbody.Instructions.InstructionListParserthen throwsKeyNotFoundExceptionfrominstrToIndex[...](viaMarkAsBranchTarget/GetInstrIndex) when it rebuilds that method's blocks. Because this happens during normal method deobfuscation, it aborts deobfuscation of the entire assembly, not just the affected method.Observed stack trace against current
master:Fix
Write the constant in place (
OpCode/Operand) instead of substituting newInstructionobjects, so exception handlers and branches keep pointing at a live instruction. The emitted IL is unchanged — only object identity is preserved.Trigger condition
The bug isn't specific to a particular .NET Reactor version. It only requires an inlined cflow-constant field access to coincide with an exception-handler boundary or a branch target, which is why it shows up on some protected assemblies and not others.
Testing
KeyNotFoundExceptioncrash → deobfuscates and saves normally.