Exemplo n.º 1
0
        private void PerformFlowControl(VMFunction function, ILInstruction instruction, List <ProgramState> nextStates, ProgramState next)
        {
            switch (instruction.OpCode.FlowControl)
            {
            case ILFlowControl.Next:
            {
                // Normal flow.
                nextStates.Add(next);
                break;
            }

            case ILFlowControl.Jump:
            {
                function.BlockHeaders.Add((long)next.IP);

                // Unconditional jump target.
                var metadata = InferJumpTargets(instruction);
                if (metadata != null)
                {
                    next.IP = metadata.InferredJumpTargets[0];
                    function.BlockHeaders.Add((long)next.IP);
                    nextStates.Add(next);
                }

                break;
            }

            case ILFlowControl.ConditionalJump:
            {
                // We need to consider that the condition might be true or false.

                // Conditional branch(es):
                var metadata = InferJumpTargets(instruction);
                if (metadata != null)
                {
                    foreach (var target in metadata.InferredJumpTargets)
                    {
                        var branch = next.Copy();
                        branch.IP = target;
                        nextStates.Add(branch);
                        function.BlockHeaders.Add((long)branch.IP);
                    }
                }

                // Fall through branch:
                nextStates.Add(next);
                function.BlockHeaders.Add((long)next.IP);

                break;
            }

            default:
            {
                throw new ArgumentOutOfRangeException();
            }
            }
        }
Exemplo n.º 2
0
        private IEnumerable <ProgramState> ProcessCall(VMFunction function, ILInstruction instruction, ProgramState next)
        {
            var symbolicAddress = next.Stack.Pop();

            instruction.Dependencies.AddOrMerge(0, symbolicAddress);

            uint address = (uint)symbolicAddress.InferStackValue().U8;

            if (address >= KoiStream.Contents.GetPhysicalSize())
            {
                Logger.Warning(Tag,
                               $"Call instruction at IL_{instruction.Offset:X4} "
                               + $"transfers control to a function outside of the KoiVM stream (IL_{address:X4}.");
            }

            var callee = _disassembler.GetOrCreateFunctionInfo(address, next.Key);

            callee.References.Add(new FunctionReference(function, instruction.Offset, FunctionReferenceType.Call, callee));
            instruction.Annotation = new CallAnnotation
            {
                Function          = callee,
                InferredPopCount  = 1,
                InferredPushCount = 1,
            };

            if (!callee.ExitKey.HasValue)
            {
                // Exit key of called function is not known yet.
                // We cannot continue disassembly yet because of the encryption used in KoiVM.
                function.UnresolvedOffsets.Add(instruction.Offset);
                Logger.Debug(Tag,
                             $"Stopped at call instruction at IL_{instruction.Offset:X4} "
                             + $"as exit key of function_{address:X4} is not known yet.");
                return(Enumerable.Empty <ProgramState>());
            }
            else
            {
                // Exit key is known, we can continue disassembly!
                function.UnresolvedOffsets.Remove(instruction.Offset);
                next.Key = callee.ExitKey.Value;

                return(new[] { next });
            }
        }
Exemplo n.º 3
0
        private void ProcessRet(VMFunction function, ILInstruction instruction, ProgramState next)
        {
            // Pop return address.
            var symbolicReturnAddress = next.Stack.Pop();

            instruction.Dependencies.AddOrMerge(0, symbolicReturnAddress);

            // Add metadata.
            instruction.Annotation = new Annotation
            {
                InferredPopCount  = instruction.Dependencies.Count,
                InferredPushCount = 0
            };

            if (!next.IgnoreExitKey)
            {
                // Returns indicate the end of the method, and therefore also determine the encryption key of the
                // instruction after a call instruction. Store this information so it can be used to continue
                // disassembly at these points later in time.

                if (function.ExitKey.HasValue)
                {
                    if (function.ExitKey != next.Key)
                    {
                        Logger.Debug2(Tag,
                                      $"Resolved an alternative exit key ({next.Key:X8}) at offset " +
                                      $"IL_{instruction.Offset:X4}for function_{function.EntrypointAddress:X4}.");
                    }
                }
                else
                {
                    Logger.Debug2(Tag, $"Inferred exit key {next.Key:X8} at offset IL_{instruction.Offset:X4}.");
                    function.ExitKey = next.Key;
                }
            }
        }
Exemplo n.º 4
0
        public IList <ProgramState> GetNextStates(VMFunction function, ILInstruction instruction, ProgramState next)
        {
            var nextStates = new List <ProgramState>(1);
            var metadata   = instruction.Annotation as VCallAnnotation;

            int stackSize = next.Stack.Count;

            var symbolicVCallValue = next.Stack.Pop();

            instruction.Dependencies.AddOrMerge(0, symbolicVCallValue);
            var vcall = metadata?.VMCall ?? Constants.VMCalls[symbolicVCallValue.InferStackValue().U1];

            bool returnNextState = true;

            switch (vcall)
            {
            case VMCalls.BOX:
                ProcessBox(instruction, next);
                break;

            case VMCalls.CAST:
                ProcessCast(instruction, next);
                break;

            case VMCalls.CKFINITE:
                ProcessCkFinite(instruction, next);
                break;

            case VMCalls.CKOVERFLOW:
                ProcessCkOverflow(instruction, next);
                break;

            case VMCalls.ECALL:
                ProcessECall(instruction, next);
                break;

            case VMCalls.INITOBJ:
                ProcessInitObj(instruction, next);
                break;

            case VMCalls.LOCALLOC:
                ProcessLocalloc(instruction, next);
                break;

            case VMCalls.LDFLD:
                ProcessLdfld(instruction, next);
                break;

            case VMCalls.LDFTN:
                ProcessLdftn(function, instruction, next);
                break;

            case VMCalls.RANGECHK:
                ProcessRangeChk(instruction, next);
                break;

            case VMCalls.SIZEOF:
                ProcessSizeOf(instruction, next);
                break;

            case VMCalls.STFLD:
                ProcessStfld(instruction, next);
                break;

            case VMCalls.THROW:
                ProcessThrow(instruction, next);
                returnNextState = false;
                break;

            case VMCalls.TOKEN:
                ProcessToken(instruction, next);
                break;

            case VMCalls.UNBOX:
                ProcessUnbox(instruction, next);
                break;

            case VMCalls.EXIT:
            case VMCalls.BREAK:
                throw new NotSupportedException($"VCALL {vcall} is not supported.");

            default:
                throw new ArgumentOutOfRangeException();
            }

            if (returnNextState)
            {
                nextStates.Add(next);
            }

            if ((next.Stack.Count - stackSize) != instruction.Annotation.InferredStackDelta)
            {
                // Should not happen, but sanity checks are always nice to check whether we have implemented the
                // vcall processors correctly.
                throw new DisassemblyException($"VCall at offset IL_{instruction.Offset:X4} ({vcall}) inferred stack delta does not match the emulated stack delta.");
            }

            return(nextStates);
        }
Exemplo n.º 5
0
        private void ProcessLdftn(VMFunction currentFunction, ILInstruction instruction, ProgramState next)
        {
            var symbolicMethod = next.Stack.Pop();
            var symbolicObject = next.Stack.Pop();

            instruction.Dependencies.AddOrMerge(1, symbolicMethod);
            instruction.Dependencies.AddOrMerge(2, symbolicObject);

            var methodSlot = symbolicMethod.InferStackValue();

            if (symbolicObject.Type == VMType.Object)
            {
                // This is a virtual dispatched ldftn.

                var method = (IMethodDescriptor)KoiStream.ResolveReference(Logger, instruction.Offset, methodSlot.U4,
                                                                           TableIndex.Method,
                                                                           TableIndex.MemberRef,
                                                                           TableIndex.MethodSpec);

                instruction.Annotation = new LdftnAnnotation(method, true);
            }
            else
            {
                // This is a static ldftn.

                var obj = symbolicObject.InferStackValue();
                if (obj.U8 != 0)
                {
                    // We are dealing with intra-linked methods.

                    // Pop entry key.
                    var symbolicEntryKey = next.Stack.Pop();
                    instruction.Dependencies.AddOrMerge(3, symbolicEntryKey);
                    uint entryKey = symbolicEntryKey.InferStackValue().U4;

                    // Obtain export containing signature.
                    uint exportId   = obj.U4;
                    var  exportInfo = KoiStream.Exports[exportId];

                    // Get the function at the pushed address and register reference.
                    uint codeAddress = (uint)methodSlot.U8;
                    var  function    = _disassembler.GetOrCreateFunctionInfo(codeAddress, entryKey);
                    function.References.Add(new FunctionReference(
                                                currentFunction,
                                                instruction.Offset,
                                                FunctionReferenceType.Ldftn,
                                                function));

                    instruction.Annotation = new LdftnAnnotation(function, exportInfo.Signature);
                }
                else
                {
                    // Resolve method.
                    var method = (IMethodDescriptor)KoiStream.ResolveReference(Logger, instruction.Offset, methodSlot.U4,
                                                                               TableIndex.Method,
                                                                               TableIndex.MemberRef,
                                                                               TableIndex.MethodSpec);

                    instruction.Annotation = new LdftnAnnotation(method, false);
                }
            }

            next.Stack.Push(new SymbolicValue(instruction, VMType.Pointer));

            instruction.Annotation.InferredPopCount  = instruction.Dependencies.Count;
            instruction.Annotation.InferredPushCount = 1;
        }
Exemplo n.º 6
0
        public IList <ProgramState> GetNextStates(
            VMFunction function,
            ProgramState currentState,
            ILInstruction instruction,
            uint nextKey)
        {
            var nextStates = new List <ProgramState>(1);
            var next       = currentState.Copy();

            next.IP += (ulong)instruction.Size;
            next.Key = nextKey;

            if (instruction.OpCode.AffectsFlags)
            {
                next.Registers[VMRegisters.FL] = new SymbolicValue(instruction, VMType.Byte);
            }

            switch (instruction.OpCode.Code)
            {
            case ILCode.CALL:
                nextStates.AddRange(ProcessCall(function, instruction, next));
                break;

            case ILCode.RET:
                ProcessRet(function, instruction, next);
                function.BlockHeaders.Add((long)next.IP);
                break;

            case ILCode.VCALL:
                // VCalls have embedded opcodes with different behaviours.
                nextStates.AddRange(_vCallProcessor.GetNextStates(function, instruction, next));
                break;

            case ILCode.TRY:
                // TRY opcodes have a very distinct behaviour from the other common opcodes.
                nextStates.AddRange(ProcessTry(instruction, next));
                foreach (var state in nextStates)
                {
                    function.BlockHeaders.Add((long)state.IP);
                }
                break;

            case ILCode.LEAVE:
                nextStates.AddRange(ProcessLeave(instruction, next));
                function.BlockHeaders.Add((long)next.IP);
                break;

            case ILCode.POP when(VMRegisters) instruction.Operand == VMRegisters.SP:
                nextStates.Add(ProcessPopSp(instruction, next));

                break;

            case ILCode.SWT:
                nextStates.AddRange(ProcessSwt(instruction, next));
                function.BlockHeaders.UnionWith(nextStates.Select(s => (long)s.IP));
                break;

            default:
            {
                // Push/pop necessary values from stack.
                int initial = next.Stack.Count;
                PopSymbolicValues(instruction, next);
                int popCount = initial - next.Stack.Count;

                initial = next.Stack.Count;
                PushSymbolicValues(instruction, next);
                int pushCount = next.Stack.Count - initial;

                // Apply control flow.
                PerformFlowControl(function, instruction, nextStates, next);

                if (instruction.Annotation == null)
                {
                    instruction.Annotation = new Annotation();
                }

                instruction.Annotation.InferredPopCount  = popCount;
                instruction.Annotation.InferredPushCount = pushCount;
                break;
            }
            }

            return(nextStates);
        }
Exemplo n.º 7
0
 public FunctionEventArgs(VMFunction function)
 {
     Function = function;
 }
Exemplo n.º 8
0
        private void ProcessLdftn(VMFunction currentFunction, ILInstruction instruction, ProgramState next)
        {
            var symbolicMethod = next.Stack.Pop();
            var symbolicObject = next.Stack.Pop();

            instruction.Dependencies.AddOrMerge(1, symbolicMethod);
            instruction.Dependencies.AddOrMerge(2, symbolicObject);

            var methodSlot = symbolicMethod.InferStackValue();

            if (symbolicObject.Type == VMType.Object)
            {
                // TODO: get base method.
                throw new DisassemblyException(
                          $"Failed to process the LDFTN instruction at offset IL_{instruction.Offset:X4}.",
                          new NotSupportedException("LDFTN instructions based on objects is not supported yet."));
            }
            else
            {
                var obj = symbolicObject.InferStackValue();
                if (obj.U8 != 0)
                {
                    // We are dealing with intra-linked methods.

                    // Pop entry key.
                    var symbolicEntryKey = next.Stack.Pop();
                    instruction.Dependencies.AddOrMerge(3, symbolicEntryKey);
                    uint entryKey = symbolicEntryKey.InferStackValue().U4;

                    // Obtain export containing signature.
                    uint exportId   = obj.U4;
                    var  exportInfo = KoiStream.Exports[exportId];

                    // Get the function at the pushed address and register reference.
                    uint codeAddress = (uint)methodSlot.U8;
                    var  function    = _disassembler.GetOrCreateFunctionInfo(codeAddress, entryKey);
                    function.References.Add(new FunctionReference(
                                                currentFunction,
                                                instruction.Offset,
                                                FunctionReferenceType.Ldftn,
                                                function));

                    instruction.Annotation = new LdftnAnnotation(function, exportInfo.Signature);
                }
                else
                {
                    // Resolve method.
                    var method = (ICallableMemberReference)KoiStream.ResolveReference(Logger, instruction.Offset, methodSlot.U4,
                                                                                      MetadataTokenType.Method,
                                                                                      MetadataTokenType.MemberRef,
                                                                                      MetadataTokenType.MethodSpec);

                    instruction.Annotation = new LdftnAnnotation(method);
                }
            }

            next.Stack.Push(new SymbolicValue(instruction, VMType.Pointer));

            instruction.Annotation.InferredPopCount  = instruction.Dependencies.Count;
            instruction.Annotation.InferredPushCount = 1;
        }