Esempio n. 1
0
        public static TypeDefinition ImportFlagHelper(MetadataImage image, VMConstants constants)
        {
            // Clone flag helper class.
            var cloner         = new MemberCloner(image);
            var flagHelperType = cloner.CloneType(VmHelperType);

            image.Assembly.Modules[0].TopLevelTypes.Add(flagHelperType);

            // Obtain static cctor.
            var constructor  = flagHelperType.Methods.First(x => x.IsConstructor && x.IsStatic);
            var instructions = constructor.CilMethodBody.Instructions;

            instructions.Clear();

            // Assign values of flags to the fields.
            foreach (var entry in constants.Flags.OrderBy(x => x.Value))
            {
                instructions.Add(CilInstruction.Create(CilOpCodes.Ldc_I4, entry.Key));
                instructions.Add(CilInstruction.Create(CilOpCodes.Stsfld,
                                                       flagHelperType.Fields.First(x => x.Name == "FL_" + entry.Value.ToString())));
            }

            instructions.Add(CilInstruction.Create(CilOpCodes.Ret));

            return(flagHelperType);
        }
Esempio n. 2
0
        private void ApplyTransformations(ILCompilationUnit result, VMConstants constants)
        {
            var pipeline = new IILAstTransform[]
            {
                new StackFrameTransform(),
                new SsaTransform(),
                new TransformLoop("Expression Simplification", 5, new IChangeAwareILAstTransform[]
                {
                    new VariableInliner(),
                    new PushMinimizer(),
                    new LogicSimplifier(),
                    new FlagOperationSimplifier(constants),
                }),
                new PhiRemovalTransform(),
            };

            foreach (var transform in pipeline)
            {
                if (transform is TransformLoop loop)
                {
                    loop.TransformStart += (sender, args) => OnTransformStart(args);
                    loop.TransformEnd   += (sender, args) => OnTransformEnd(args);
                }

                Logger.Debug2(Tag, $"Applying {transform.Name}...");
                OnTransformStart(new ILTransformEventArgs(result, transform, 1));
                transform.ApplyTransformation(result, Logger);
                OnTransformEnd(new ILTransformEventArgs(result, transform, 1));
            }
        }
Esempio n. 3
0
        public static TypeDefinition ImportFlagHelper(ModuleDefinition module, VMConstants constants)
        {
            // Clone flag helper class.
            var cloner = new MemberCloner(module);

            cloner.Include(VmHelperType);
            var result         = cloner.Clone();
            var flagHelperType = result.ClonedMembers.OfType <TypeDefinition>().First();

            module.Assembly.Modules[0].TopLevelTypes.Add(flagHelperType);

            // Obtain static cctor.
            var constructor  = flagHelperType.Methods.First(x => x.IsConstructor && x.IsStatic);
            var instructions = constructor.CilMethodBody.Instructions;

            instructions.Clear();

            // Assign values of flags to the fields.
            foreach (var entry in constants.Flags.OrderBy(x => x.Value))
            {
                instructions.Add(CilInstruction.CreateLdcI4(entry.Key));
                instructions.Add(new CilInstruction(CilOpCodes.Stsfld,
                                                    flagHelperType.Fields.First(x => x.Name == "FL_" + entry.Value.ToString())));
            }

            instructions.Add(new CilInstruction(CilOpCodes.Ret));

            return(flagHelperType);
        }
Esempio n. 4
0
 public InferenceDisassembler(VMConstants constants, KoiStream koiStream)
 {
     Constants   = constants ?? throw new ArgumentNullException(nameof(constants));
     KoiStream   = koiStream ?? throw new ArgumentNullException(nameof(koiStream));
     _decoder    = new InstructionDecoder(constants, KoiStream.Contents.CreateReader());
     _processor  = new InstructionProcessor(this);
     _cfgBuilder = new ControlFlowGraphBuilder();
 }
Esempio n. 5
0
        public uint?ResolveExitKey(ILogger logger, KoiStream koiStream, VMConstants constants, VMFunction function)
        {
            // Strategy:
            //
            // Find CALL references to the function, and try every possible key in the key space that
            // decrypts the next instruction to either a PUSHR_xxxx R0 or a PUSHR_DWORD SP, as they
            // appear in the post-call to either store the return value, or adjust SP to clean up
            // the stack.

            // Since the actual decryption of the opcode only uses the 8 least significant bits, we can
            // make an optimisation that shaves off around 8 bits of the key space. By attempting to
            // decrypt the first byte using just the first 8 bits, and verifying whether this output
            // results to one of the instructions mentioned in the above, we can quickly rule out many
            // potential keys.

            var callReferences = function.References.Where(r => r.ReferenceType == FunctionReferenceType.Call).ToArray();

            if (callReferences.Length == 0)
            {
                logger.Warning(Tag, $"Cannot brute-force the exit key of function_{function.EntrypointAddress:X4} as it has no recorded call references.");
                return(null);
            }

            var data = koiStream.Data;

            // Find any call reference.
            foreach (var callReference in callReferences)
            {
                var  call         = callReference.Caller.Instructions[callReference.Offset];
                long targetOffset = call.Offset + call.Size;

                // Go over all possible LSBs.
                for (uint lsb = 0; lsb < byte.MaxValue; lsb++)
                {
                    // Check whether the LSB decodes to a PUSHR_xxxx.
                    if (IsPotentialLSB(constants, data[targetOffset], lsb))
                    {
                        // Go over all remaining 24 bits.
                        for (uint i = 0; i < 0x00FFFFFF; i++)
                        {
                            uint currentKey = (i << 8) | lsb;

                            // Try new key.
                            if (IsValidKey(constants, data, targetOffset, currentKey))
                            {
                                // We have found a key!
                                return(currentKey);
                            }
                        } // for all other 24 bits.
                    }     // if potential LSB
                }         // foreach LSB
            }             // foreach call reference

            return(null);
        }
Esempio n. 6
0
        public ILCompilationUnit BuildAst(
            ControlFlowGraph graph,
            IFrameLayout frameLayout,
            VMConstants constants)
        {
            var result = BuildBasicAst(graph, frameLayout);

            OnInitialAstBuilt(result);
            ApplyTransformations(result, constants);
            return(result);
        }
Esempio n. 7
0
        public CodeGenerationContext(CilMethodBody methodBody, VMConstants constants, CilVariable flagVariable,
                                     TypeDefinition flagHelperType)
        {
            MethodBody    = methodBody;
            Constants     = constants;
            _flagVariable = flagVariable;
            VmHelperType  = flagHelperType;

            ReferenceImporter = new ReferenceImporter(TargetModule);

            _arg0   = new CilVariable("__arg0", TargetModule.CorLibTypeFactory.UInt32);
            _arg1   = new CilVariable("__arg1", TargetModule.CorLibTypeFactory.UInt32);
            _result = new CilVariable("__result", TargetModule.CorLibTypeFactory.UInt32);
        }
Esempio n. 8
0
        public ILCompilationUnit BuildAst(
            ControlFlowGraph graph,
            IFrameLayout frameLayout,
            VMConstants constants)
        {
            Logger.Debug(Tag, "Building initial IL-AST...");
            var result = BuildBasicAst(graph, frameLayout);

            OnInitialAstBuilt(result);

            Logger.Debug(Tag, "Applying IL-AST transforms...");
            ApplyTransformations(result, constants);

            return(result);
        }
Esempio n. 9
0
        private static IFrameLayout InferLayoutFromLdftnReference(VMConstants constants, MetadataImage image, FunctionReference reference)
        {
            // LDFTN instructions reference a physical method, or an export defined in the export table containing
            // the signature of an intra-linked method. We can therefore reliably extract the necessary information
            // without too much guessing.

            var ldftn      = reference.Caller.Instructions[reference.Offset];
            var annotation = (LdftnAnnotation)ldftn.Annotation;

            var           parameterTypes = new List <TypeSignature>();
            TypeSignature returnType;
            bool          hasThis;

            if (annotation.IsIntraLinked)
            {
                returnType = ((ITypeDefOrRef)image.ResolveMember(annotation.Signature.ReturnToken))
                             .ToTypeSignature();

                foreach (var token in annotation.Signature.ParameterTokens)
                {
                    parameterTypes.Add(((ITypeDefOrRef)image.ResolveMember(token))
                                       .ToTypeSignature());
                }

                hasThis = (annotation.Signature.Flags & constants.FlagInstance) != 0;
            }
            else
            {
                var methodSig = (MethodSignature)annotation.Method.Signature;
                foreach (var parameter in methodSig.Parameters)
                {
                    parameterTypes.Add(parameter.ParameterType);
                }
                returnType = methodSig.ReturnType;
                hasThis    = methodSig.HasThis;
            }

            return(new DefaultFrameLayout(
                       image,
                       parameterTypes,
                       Array.Empty <TypeSignature>(),
                       returnType,
                       hasThis));
        }
Esempio n. 10
0
        private static bool IsValidKey(VMConstants constants, byte[] data, long offset, uint key)
        {
            // Opcode byte.
            byte pushRDword = DecryptByte(data[offset], ref key);

            if (!constants.OpCodes.TryGetValue(pushRDword, out var opCode))
            {
                return(false);
            }

            switch (opCode)
            {
            case ILCode.PUSHR_BYTE:
            case ILCode.PUSHR_WORD:
            case ILCode.PUSHR_DWORD:
            case ILCode.PUSHR_QWORD:
            case ILCode.PUSHR_OBJECT:
                break;

            default:
                return(false);
            }

            // Fixup byte.
            byte fixup = DecryptByte(data[offset + 1], ref key);

            // Register operand.
            byte rawRegister = DecryptByte(data[offset + 2], ref key);

            if (!constants.Registers.TryGetValue(rawRegister, out var register))
            {
                return(false);
            }

            switch (register)
            {
            case VMRegisters.R0:
            case VMRegisters.SP:
                return(true);
            }

            return(false);
        }
Esempio n. 11
0
        public IFrameLayout DetectFrameLayout(VMConstants constants, MetadataImage image, VMExportInfo export)
        {
            var parameterTypes = new List <TypeSignature>();

            foreach (var token in export.Signature.ParameterTokens)
            {
                parameterTypes.Add(((ITypeDefOrRef)image.ResolveMember(token))
                                   .ToTypeSignature());
            }

            var returnType = ((ITypeDefOrRef)image.ResolveMember(export.Signature.ReturnToken))
                             .ToTypeSignature();

            bool hasThis = (export.Signature.Flags & constants.FlagInstance) != 0;

            return(new DefaultFrameLayout(
                       image,
                       parameterTypes,
                       Array.Empty <TypeSignature>(),
                       returnType,
                       hasThis));
        }
Esempio n. 12
0
        private static bool IsPotentialLSB(VMConstants constants, byte encryptedOpCode, uint lsb)
        {
            byte pushRDword = DecryptByte(encryptedOpCode, ref lsb);

            if (!constants.OpCodes.TryGetValue(pushRDword, out var opCode))
            {
                return(false);
            }

            switch (opCode)
            {
            case ILCode.PUSHR_BYTE:
            case ILCode.PUSHR_WORD:
            case ILCode.PUSHR_DWORD:
            case ILCode.PUSHR_QWORD:
            case ILCode.PUSHR_OBJECT:
                return(true);

            default:
                return(false);
            }
        }
Esempio n. 13
0
        public IFrameLayout DetectFrameLayout(VMConstants constants, MetadataImage image,
                                              VMFunction function)
        {
            if (function.References.Count == 0)
            {
                throw new ArgumentException("Can only infer frame layout of a function that is at least referenced once.");
            }

            var exceptions = new List <Exception>();

            // Order the references by reference type, as LDFTN references are more reliable.
            foreach (var reference in function.References.OrderBy(r => r.ReferenceType))
            {
                try
                {
                    switch (reference.ReferenceType)
                    {
                    case FunctionReferenceType.Call:
                        return(InferLayoutFromCallReference(image, reference));

                    case FunctionReferenceType.Ldftn:
                        return(InferLayoutFromLdftnReference(constants, image, reference));

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }

            throw new AggregateException(
                      $"Failed to infer the stack frame layout of function_{function.EntrypointAddress:X4}.", exceptions);
        }
Esempio n. 14
0
 public InstructionDecoder(VMConstants constants, IBinaryStreamReader reader)
     : this(constants, reader, 0)
 {
 }
Esempio n. 15
0
        public VMConstants CreateVmConstants()
        {
            var ilCodes   = CreateEnumDictionary <ILCode>();
            var flags     = CreateEnumDictionary <VMFlags>();
            var registers = CreateEnumDictionary <VMRegisters>();
            var vmCalls   = CreateEnumDictionary <VMCalls>();
            var eCalls    = CreateEnumDictionary <VMECallOpCode>();
            var ehTypes   = CreateEnumDictionary <EHType>();

            var constants = new VMConstants();
            var missing   = new List <string>();

            Process(OpCodes, ilCodes, constants.OpCodes);
            Process(Flags, flags, constants.Flags);
            Process(Registers, registers, constants.Registers);
            Process(VMCalls, vmCalls, constants.VMCalls);
            Process(ECalls, eCalls, constants.ECallOpCodes);
            Process(EHTypes, ehTypes, constants.EHTypes);

            missing.AddRange(ilCodes.Keys);
            missing.AddRange(flags.Keys);
            missing.AddRange(registers.Keys);
            missing.AddRange(vmCalls.Keys);
            missing.AddRange(eCalls.Keys);
            missing.AddRange(ehTypes.Keys);

            if (Misc.TryGetValue("HELPER_INIT", out byte value))
            {
                constants.HelperInit = value;
            }
            else
            {
                missing.Add("HELPER_INIT");
            }

            if (Misc.TryGetValue("FLAG_INSTANCE", out value))
            {
                constants.FlagInstance = value;
            }
            else
            {
                missing.Add("FLAG_INSTANCE");
            }

            int missingCount = missing.Count;

            if (missingCount > 0)
            {
                string suffix = string.Empty;
                if (missingCount > 5)
                {
                    missing.RemoveRange(5, missingCount - 6);
                    suffix = $" and {missingCount - 6} more";
                }

                throw new ArgumentException(
                          $"Incomplete configuration file. Missing constants {string.Join(", ", missing)}{suffix}.");
            }

            return(constants);
        }
Esempio n. 16
0
 public InstructionDecoder(VMConstants constants, IBinaryStreamReader reader, uint key)
 {
     _constants = constants;
     Reader     = reader;
     CurrentKey = key;
 }
Esempio n. 17
0
        private VMConstants AutoDetectConstants(DevirtualisationContext context)
        {
            bool rename = context.Options.RenameSymbols;

            var constants = new VMConstants();
            var fields    = FindConstantFieldsAndValues(context);

            foreach (var field in fields)
            {
                constants.ConstantFields.Add(field.Key, field.Value);
            }

            // TODO:
            // We assume that the constants appear in the same order as they were defined in the original source code.
            // This means the metadata tokens of the fields are also in increasing order. However, this could cause
            // problems when a fork of the obfuscation tool is made which scrambles the order.  A more robust way of
            // matching should be done that is order agnostic.

            var sortedFields = fields
                               .OrderBy(x => x.Key.MetadataToken.ToUInt32())
                               .ToArray();

            int currentIndex = 0;

            context.Logger.Debug2(Tag, "Resolving register mapping...");
            for (int i = 0; i < (int)VMRegisters.Max; i++, currentIndex++)
            {
                constants.Registers.Add(sortedFields[currentIndex].Value, (VMRegisters)i);
                if (rename)
                {
                    sortedFields[currentIndex].Key.Name = "REG_" + (VMRegisters)i;
                }
            }

            context.Logger.Debug2(Tag, "Resolving flag mapping...");
            for (int i = 1; i < (int)VMFlags.Max; i <<= 1, currentIndex++)
            {
                constants.Flags.Add(sortedFields[currentIndex].Value, (VMFlags)i);
                if (rename)
                {
                    sortedFields[currentIndex].Key.Name = "FLAG_" + (VMFlags)i;
                }
            }

            context.Logger.Debug2(Tag, "Resolving opcode mapping...");
            for (int i = 0; i < (int)ILCode.Max; i++, currentIndex++)
            {
                constants.OpCodes.Add(sortedFields[currentIndex].Value, (ILCode)i);
                if (rename)
                {
                    sortedFields[currentIndex].Key.Name = "OPCODE_" + (ILCode)i;
                }
            }

            context.Logger.Debug2(Tag, "Resolving vmcall mapping...");
            for (int i = 0; i < (int)VMCalls.Max; i++, currentIndex++)
            {
                constants.VMCalls.Add(sortedFields[currentIndex].Value, (VMCalls)i);
                if (rename)
                {
                    sortedFields[currentIndex].Key.Name = "VMCALL_" + (VMCalls)i;
                }
            }

            context.Logger.Debug2(Tag, "Resolving helper init ID...");
            if (rename)
            {
                sortedFields[currentIndex].Key.Name = "HELPER_INIT";
            }
            constants.HelperInit = sortedFields[currentIndex++].Value;

            context.Logger.Debug2(Tag, "Resolving ECall mapping...");
            for (int i = 0; i < 4; i++, currentIndex++)
            {
                constants.ECallOpCodes.Add(sortedFields[currentIndex].Value, (VMECallOpCode)i);
                if (rename)
                {
                    sortedFields[currentIndex].Key.Name = "ECALL_" + (VMECallOpCode)i;
                }
            }

            context.Logger.Debug2(Tag, "Resolving function signature flags...");
            sortedFields[currentIndex].Key.Name = "FLAG_INSTANCE";
            constants.FlagInstance = sortedFields[currentIndex++].Value;

            context.Logger.Debug2(Tag, "Resolving exception handler types...");
            for (int i = 0; i < (int)EHType.Max; i++, currentIndex++)
            {
                constants.EHTypes.Add(sortedFields[currentIndex].Value, (EHType)i);
                if (rename)
                {
                    sortedFields[currentIndex].Key.Name = "EH_" + (EHType)i;
                }
            }

            return(constants);
        }
Esempio n. 18
0
 public CilMethodBodyGenerator(VMConstants constants, TypeDefinition flagHelperType)
 {
     _constants      = constants ?? throw new ArgumentNullException(nameof(constants));
     _flagHelperType = flagHelperType ?? throw new ArgumentNullException(nameof(flagHelperType));
 }
Esempio n. 19
0
 public FlagOperationSimplifier(VMConstants constants)
 {
     _constants = constants;
 }
        public uint?ResolveExitKey(ILogger logger, KoiStream koiStream, VMConstants constants, VMFunction function)
        {
            // Strategy:
            //
            // Find CALL references to the function, and try every possible key in the key space that
            // decrypts the next instruction to either a PUSHR_xxxx R0 or a PUSHR_DWORD SP, as they
            // appear in the post-call to either store the return value, or adjust SP to clean up
            // the stack.

            // Since the actual decryption of the opcode only uses the 8 least significant bits, we can
            // make an optimisation that shaves off around 8 bits of the key space. By attempting to
            // decrypt the first byte using just the first 8 bits, and verifying whether this output
            // results to one of the instructions mentioned in the above, we can quickly rule out many
            // potential keys.

            var callReferences = function.References
                                 .Where(r => r.ReferenceType == FunctionReferenceType.Call)
                                 .ToArray();

            if (callReferences.Length == 0)
            {
                logger.Warning(Tag, $"Cannot brute-force the exit key of function_{function.EntrypointAddress:X4} as it has no recorded call references.");
                return(null);
            }

            var reader = koiStream.Contents.CreateReader();

            byte[] encryptedOpCodes = new byte[3];
            var    watch            = new Stopwatch();

            // Find any call reference.
            for (int i = 0; i < callReferences.Length; i++)
            {
                var callReference = callReferences[i];
                logger.Debug(Tag, $"Started bruteforcing key for call reference {i.ToString()} ({callReference.ToString()}).");
                watch.Restart();

                var  call         = callReference.Caller.Instructions[callReference.Offset];
                long targetOffset = call.Offset + call.Size;

                reader.FileOffset = (uint)targetOffset;
                reader.ReadBytes(encryptedOpCodes, 0, encryptedOpCodes.Length);

                // Go over all possible LSBs.
                for (uint lsb = 0; lsb < byte.MaxValue; lsb++)
                {
                    // Check whether the LSB decodes to a PUSHR_xxxx.
                    if (IsPotentialLSB(constants, encryptedOpCodes[0], lsb))
                    {
                        // Go over all remaining 24 bits.
                        for (uint j = 0; j < 0x00FFFFFF; j++)
                        {
                            uint currentKey = (j << 8) | lsb;

                            // Try new key.
                            if (IsValidKey(constants, encryptedOpCodes, currentKey))
                            {
                                // We have found a key!
                                watch.Stop();
                                logger.Debug(Tag, $"Found key after {watch.Elapsed.TotalSeconds:0.00}s.");

                                return(currentKey);
                            }
                        } // for all other 24 bits.
                    }     // if potential LSB
                }         // foreach LSB

                watch.Stop();
                logger.Debug(Tag, $"Exhausted key space after {watch.Elapsed.TotalSeconds:0.00}s without finding key.");
            }

            return(null);
        }