Exemplo n.º 1
0
        // Emulate one specific operation from the given method.
        private static void Explore(MethodDefinition methodDef, List <OpState> processingQueue,
                                    List <int> processedOps, OpState operation)
        {
            // Get specific values from current operation;
            int offset = operation.Offset;
            // Do not simulate processed instructions again.
            var ins = methodDef.Body.Instructions.First(o => o.Offset == offset);

            if (processedOps.Contains(ins.Offset))
            {
                return;
            }
            processedOps.Add(ins.Offset);

            List <Condition> conditions   = operation.Conditions;
            List <byte>      writtenBytes = operation.BytesWritten;

            switch (ins.OpCode.Code)
            {
            case Code.Dup:
                // Most of the time this element is a reference. References are clone-able without side-effects.
                object element = operation.StackPop();
                operation.StackPush(element);
                operation.StackPush(element);
                break;

            case Code.Ldnull:
                operation.StackPush("null");
                break;

            case Code.Ldc_I4:
                // The operand is a normal int.
                operation.StackPush(ins.Operand);
                break;

            case Code.Ldc_I4_S:
                // Combine 0 and the sbyte to force a conversion.
                // The operand is a signed byte.
                int intVal = 0 | (sbyte)ins.Operand;
                operation.StackPush(intVal);
                break;

            case Code.Ldc_R4:
                float flVal = (float)ins.Operand;
                operation.StackPush(flVal);
                break;

            case Code.Ldstr:
                operation.StackPush(ins.Operand);
                break;

            case Code.Ldc_I4_0:
            case Code.Ldc_I4_1:
            case Code.Ldc_I4_2:
            case Code.Ldc_I4_3:
            case Code.Ldc_I4_4:
            case Code.Ldc_I4_5:
            case Code.Ldc_I4_6:
            case Code.Ldc_I4_7:
            case Code.Ldc_I4_8:
                // OP2 is the second byte representing our opcode.
                // Ldc_i4_0 starts at OP2 = 0x16, so we subtract 0x16 from OP2 for each
                // matching opcode to find the index of the argument.
                int value = (int)(ins.OpCode.Op2 - 0x16);
                operation.StackPush(value);
                break;

            case Code.Ldc_I4_M1:
                // Pushes integer value -1 onto the stack.
                operation.StackPush(-1);
                break;

            case Code.Ldloca:
                operation.StackPush(String.Format("&{0}", (ins.Operand as VariableReference).ToString()));
                break;

            case Code.Ldloc:
            case Code.Ldloc_S:
            case Code.Ldloca_S:
                operation.StackPush((ins.Operand as VariableReference).ToString());
                break;

            case Code.Ldloc_0:
            case Code.Ldloc_1:
            case Code.Ldloc_2:
            case Code.Ldloc_3:
                // LdLoc_0 starts at 0x06 for OP2.
                int localIdx = (int)(ins.OpCode.Op2 - 0x06);
                operation.StackPush(String.Format("ldArg{0}", localIdx));
                break;

            case Code.Ldfld:
                var loadedObject = operation.StackPop();
                var field        = String.Format("{0}.{1}", loadedObject, (ins.Operand as FieldReference).Name);
                operation.StackPush(field);
                break;

            case Code.Ldsfld:
                operation.StackPush((ins.Operand as FieldReference).FullName);
                break;

            case Code.Ldarg:
            {
                var idx = (ins.Operand as ParameterReference).Index;
                if (idx == -1)
                {
                    operation.StackPush("this");
                }
                else
                {
                    operation.StackPush(String.Format("arg{0}", idx));
                }
            }
            break;

            case Code.Ldarg_0:
            case Code.Ldarg_1:
            case Code.Ldarg_2:
            case Code.Ldarg_3:
            case Code.Ldarg_S:
            {
                // OP2 is the second byte representing our opcode.
                // Ldarg_0 starts at OP2 = 2, so we subtract 2 from OP2 for each
                // matching opcode to find the index of the argument.
                operation.StackPush(String.Format("arg{0}", (ins.OpCode.Op2 - 2).ToString()));
            }
            break;

            case Code.Ldelem_Ref:
            {
                var idx = operation.StackPop();
                var arr = operation.StackPop();
                operation.StackPush(String.Format("{0}[{1}]", arr, idx));
            }
            break;

            case Code.Ldftn:
                operation.StackPush(String.Format("&({0})", ins.Operand));
                break;

            case Code.Ldtoken:
                // Not sure what to do with the operand, it could be anything.. A FieldDefinition or TypeDefinition..
                var valueType = ins.Operand;
                operation.StackPush(valueType);
                break;

            case Code.Newobj:
            {
                var mr       = ins.Operand as MethodReference;
                var numParam = mr.Parameters.Count;
                var stackIdx = operation.Stack.Count - numParam;
                var args     = operation.Stack.GetRange(stackIdx, numParam);
                operation.Stack.RemoveRange(stackIdx, numParam);
                var callString = String.Format("new {0}({1})",
                                               mr.DeclaringType.Name, String.Join(", ", args));
                operation.StackPush(callString);
                args.Insert(0, "this");

                var info = new CallInfo
                {
                    Conditions = new List <Condition>(conditions),
                    Method     = mr,
                    Arguments  = args,
                    String     = callString
                };
                // Process data collected up until now
                _onCall(info, writtenBytes);
            }
            break;

            case Code.Newarr:
                TypeReference operand     = ins.Operand as TypeDefinition ?? ins.Operand as TypeReference;
                var           arrayAmount = Int32.Parse(operation.StackPop().ToString());
                var           arrayName   = String.Format("new {0}[{1}]", operand.FullName, arrayAmount);
                var           openArray   = new OpenArray()
                {
                    StackName = arrayName,
                    Contents  = new List <object>(arrayAmount),
                };
                // Prefill the array with nulls
                for (int i = 0; i < arrayAmount; ++i)
                {
                    openArray.Contents.Push(null);
                }
                operation.StackPush(openArray);
                break;

            case Code.Brfalse:
            {
                var lhs   = operation.StackPop().ToString();
                var src   = ins.Offset;
                var tgt   = (ins.Operand as Instruction).Offset;
                var cond  = new Condition(src, lhs, Comparison.IsFalse);
                var ncond = new Condition(src, lhs, Comparison.IsTrue);
                Branch(tgt, operation, cond, ncond, processingQueue);
            }
            break;

            case Code.Brtrue:
            {
                var lhs   = operation.StackPop().ToString();
                var src   = ins.Offset;
                var tgt   = (ins.Operand as Instruction).Offset;
                var cond  = new Condition(src, lhs, Comparison.IsTrue);
                var ncond = new Condition(src, lhs, Comparison.IsFalse);
                Branch(tgt, operation, cond, ncond, processingQueue);
            }
            break;

            case Code.Beq:
            case Code.Bne_Un:
            case Code.Ble:
            case Code.Bge:
            case Code.Blt:
            case Code.Bgt:
            {
                var       rhs = operation.StackPop().ToString();
                var       lhs = operation.StackPop().ToString();
                var       src = ins.Offset;
                var       tgt = (ins.Operand as Instruction).Offset;
                Condition cond = null, ncond = null;
                switch (ins.OpCode.Code)
                {
                case Code.Beq:
                    cond  = new Condition(src, lhs, Comparison.Equal, rhs);
                    ncond = new Condition(src, lhs, Comparison.Inequal, rhs);
                    break;

                case Code.Bne_Un:
                    cond  = new Condition(src, lhs, Comparison.Inequal, rhs);
                    ncond = new Condition(src, lhs, Comparison.Equal, rhs);
                    break;

                case Code.Ble:
                    // x <= y --> y >= x; !(x <= y) --> x > y
                    cond  = new Condition(src, rhs, Comparison.GreaterThanEqual, lhs);
                    ncond = new Condition(src, lhs, Comparison.GreaterThan, rhs);
                    break;

                case Code.Bge:
                    cond = new Condition(src, lhs, Comparison.GreaterThanEqual, rhs);
                    // !(x >= y) --> y > x
                    ncond = new Condition(src, rhs, Comparison.GreaterThan, lhs);
                    break;

                case Code.Blt:
                    // x < y --> y > x; !(x < y) --> x >= y
                    cond  = new Condition(src, rhs, Comparison.GreaterThan, lhs);
                    ncond = new Condition(src, lhs, Comparison.GreaterThanEqual, rhs);
                    break;

                case Code.Bgt:
                    // !(x > y) --> y >= x
                    cond  = new Condition(src, lhs, Comparison.GreaterThan, rhs);
                    ncond = new Condition(src, rhs, Comparison.GreaterThanEqual, lhs);
                    break;
                }
                Branch(tgt, operation, cond, ncond, processingQueue);
            }
            break;

            case Code.Br:
                // Jump to other location (unconditionally).
                operation.Offset = (ins.Operand as Instruction).Offset;
                Explore(methodDef, processingQueue, processedOps, operation);
                return;

            case Code.Stsfld:
            {
                var arg = operation.StackPop();
                // Don't pop for the object pointer, because this is a static
                // set.
                var info = new StoreInfo
                {
                    Conditions = new List <Condition>(conditions),
                    Field      = ins.Operand as FieldReference,
                    Argument   = arg.ToString(),
                    RawObject  = arg,
                };
                _onStore(info, null);
            }
            break;

            case Code.Stfld:
            {
                var arg = operation.StackPop();
                /*var obj = */
                operation.StackPop();
                var info = new StoreInfo
                {
                    Conditions = new List <Condition>(conditions),
                    Field      = ins.Operand as FieldReference,
                    Argument   = arg.ToString(),
                    RawObject  = arg,
                };
                _onStore(info, null);
            }
            break;

            case Code.Stelem_Ref:
            {
                /*var val = */
                var arr_value = operation.StackPop();
                /*var idx = */
                var idx = Int32.Parse(operation.StackPop().ToString());
                /*var arr = */
                var arr = operation.StackPop();
                if (!(arr is OpenArray))
                {
                    throw new InvalidOperationException("The popped object must be of type OpenArray!");
                }
                (arr as OpenArray).Contents[idx] = arr_value;
            }
            break;

            case Code.Stelem_I4:
            {
                /*var val = */
                // This value is guaranteed to be an integer, because of the opcode.
                var arr_value = Int32.Parse(operation.StackPop().ToString());
                /*var idx = */
                var idx = Int32.Parse(operation.StackPop().ToString());
                /*var arr = */
                var arr = operation.StackPop();
                if (!(arr is OpenArray))
                {
                    throw new InvalidOperationException("The popped object must be of type OpenArray!");
                }
                (arr as OpenArray).Contents[idx] = arr_value;
            }
            break;

            case Code.Mul:
            {
                var rhs = operation.StackPop().ToString();
                var lhs = operation.StackPop().ToString();
                operation.StackPush(String.Format("{0} * {1}", lhs, rhs));
            }
            break;

            case Code.Call:
            case Code.Callvirt:
            {
                var mr   = ins.Operand as MethodReference;
                var args = new List <object>();
                for (var i = 0; i < mr.Parameters.Count; i++)
                {
                    args.Add(operation.StackPop());
                }
                if (mr.HasThis)
                {
                    if (operation.Stack.Count < 1)
                    {
                        throw new InvalidOperationException("The stack count should be 1 or higer");
                    }
                    args.Add(operation.StackPop());
                }
                args.Reverse();
                var callString = String.Format("{0}.{1}({2})",
                                               mr.HasThis ? args.First().ToString() : mr.DeclaringType.Name,
                                               mr.Name,
                                               String.Join(", ",
                                                           mr.HasThis ? args.Skip(1) : args));
                if (mr.ReturnType.FullName != "System.Void")
                {
                    operation.StackPush(callString);
                }

                var info = new CallInfo
                {
                    Conditions = new List <Condition>(conditions),
                    Method     = mr,
                    Arguments  = args,
                    String     = callString
                };
                // Process data collected up until now
                _onCall(info, writtenBytes);
            }
            break;
            }

            if (ins.Next != null)
            {
                // Update the current operation with next offset and requeue.
                operation.Offset = ins.Next.Offset;
                processingQueue.Add(operation);
            }
        }