From 718cdb0487c3b239530ad6dea43e0cc7afe9030b Mon Sep 17 00:00:00 2001 From: de4dot Date: Mon, 17 Oct 2011 07:28:53 +0200 Subject: [PATCH] Add validMask field to Int32/64Value --- blocks/blocks.csproj | 3 + blocks/cflow/BlockControlFlowDeobfuscator.cs | 448 ++------ blocks/cflow/InstructionEmulator.cs | 1013 ++++-------------- blocks/cflow/Int32Value.cs | 488 +++++++++ blocks/cflow/Int64Value.cs | 403 +++++++ blocks/cflow/Real8Value.cs | 53 + blocks/cflow/Value.cs | 45 +- 7 files changed, 1239 insertions(+), 1214 deletions(-) create mode 100644 blocks/cflow/Int32Value.cs create mode 100644 blocks/cflow/Int64Value.cs create mode 100644 blocks/cflow/Real8Value.cs diff --git a/blocks/blocks.csproj b/blocks/blocks.csproj index f36efde1..13fb747f 100644 --- a/blocks/blocks.csproj +++ b/blocks/blocks.csproj @@ -37,6 +37,9 @@ + + + diff --git a/blocks/cflow/BlockControlFlowDeobfuscator.cs b/blocks/cflow/BlockControlFlowDeobfuscator.cs index 8223f435..acb457fd 100644 --- a/blocks/cflow/BlockControlFlowDeobfuscator.cs +++ b/blocks/cflow/BlockControlFlowDeobfuscator.cs @@ -71,6 +71,12 @@ namespace de4dot.blocks.cflow { } } + bool emulateBranch(int stackArgs, Bool3 cond) { + if (cond == Bool3.Unknown) + return false; + return emulateBranch(stackArgs, cond == Bool3.True); + } + bool emulateBranch(int stackArgs, bool isTaken) { // Pop the arguments to the bcc instruction. The dead code remover will get rid of the // pop and any pushed arguments. Insert the pops just before the bcc instr. @@ -85,461 +91,133 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: If it's an unknown int32/64, push 1 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, int1.value == int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, long1.value == long2.value); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - return emulateBranch(2, real1.value == real2.value); - } - else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareEq((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareEq((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) return emulateBranch(2, true); - } - else { + else return false; - } } bool emulate_Bne_Un() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: If it's an unknown int32/64, push 1 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, (uint)int1.value != (uint)int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, (ulong)long1.value != (ulong)long2.value); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - return emulateBranch(2, real1.value != real2.value); - } - else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareNeq((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareNeq((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) return emulateBranch(2, false); - } - else { + else return false; - } } bool emulate_Bge() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, int1.value >= int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, long1.value >= long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == int.MaxValue) - return emulateBranch(2, true); // max >= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == int.MinValue) - return emulateBranch(2, true); // x >= min => true - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == long.MaxValue) - return emulateBranch(2, true); // max >= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == long.MinValue) - return emulateBranch(2, true); // x >= min => true - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareGe((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareGe((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Bge_Un() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, (uint)int1.value >= (uint)int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, (ulong)long1.value >= (ulong)long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if ((uint)int1.value == uint.MaxValue) - return emulateBranch(2, true); // max >= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if ((uint)int2.value == uint.MinValue) - return emulateBranch(2, true); // x >= min => true - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if ((ulong)long1.value == ulong.MaxValue) - return emulateBranch(2, true); // max >= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if ((ulong)long2.value == ulong.MinValue) - return emulateBranch(2, true); // x >= min => true - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareGe_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareGe_Un((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Bgt() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, int1.value > int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, long1.value > long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == int.MinValue) - return emulateBranch(2, false); // min > x => false - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == int.MaxValue) - return emulateBranch(2, false); // x > max => false - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == long.MinValue) - return emulateBranch(2, false); // min > x => false - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == long.MaxValue) - return emulateBranch(2, false); // x > max => false - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareGt((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareGt((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Bgt_Un() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, (uint)int1.value > (uint)int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, (ulong)long1.value > (ulong)long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if ((uint)int1.value == uint.MinValue) - return emulateBranch(2, false); // min > x => false - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if ((uint)int2.value == uint.MaxValue) - return emulateBranch(2, false); // x > max => false - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if ((ulong)long1.value == ulong.MinValue) - return emulateBranch(2, false); // min > x => false - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if ((ulong)long2.value == ulong.MaxValue) - return emulateBranch(2, false); // x > max => false - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareGt_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareGt_Un((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Ble() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, int1.value <= int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, long1.value <= long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == int.MinValue) - return emulateBranch(2, true); // min <= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == int.MaxValue) - return emulateBranch(2, true); // x <= max => true - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == long.MinValue) - return emulateBranch(2, true); // min <= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == long.MaxValue) - return emulateBranch(2, true); // x <= max => true - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareLe((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareLe((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Ble_Un() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, (uint)int1.value <= (uint)int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, (ulong)long1.value <= (ulong)long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if ((uint)int1.value == uint.MinValue) - return emulateBranch(2, true); // min <= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if ((uint)int2.value == uint.MaxValue) - return emulateBranch(2, true); // x <= max => true - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if ((ulong)long1.value == ulong.MinValue) - return emulateBranch(2, true); // min <= x => true - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if ((ulong)long2.value == ulong.MaxValue) - return emulateBranch(2, true); // x <= max => true - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareLe_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareLe_Un((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Blt() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, int1.value < int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, long1.value < long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == int.MaxValue) - return emulateBranch(2, false); // max < x => false - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == int.MinValue) - return emulateBranch(2, false); // x < min => false - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == long.MaxValue) - return emulateBranch(2, false); // max < x => false - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == long.MinValue) - return emulateBranch(2, false); // x < min => false - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareLt((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareLt((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Blt_Un() { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - //TODO: Support floats - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - return emulateBranch(2, (uint)int1.value < (uint)int2.value); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - return emulateBranch(2, (ulong)long1.value < (ulong)long2.value); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if ((uint)int1.value == uint.MaxValue) - return emulateBranch(2, false); // max < x => false - else - return false; - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if ((uint)int2.value == uint.MinValue) - return emulateBranch(2, false); // x < min => false - else - return false; - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if ((ulong)long1.value == ulong.MaxValue) - return emulateBranch(2, false); // max < x => false - else - return false; - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if ((ulong)long2.value == ulong.MinValue) - return emulateBranch(2, false); // x < min => false - else - return false; - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + return emulateBranch(2, Int32Value.compareLt_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + return emulateBranch(2, Int64Value.compareLt_Un((Int64Value)val1, (Int64Value)val2)); + else return false; - } } bool emulate_Brfalse() { var val1 = instructionEmulator.pop(); - //TODO: Support floats - if (val1.valueType == ValueType.Int32) - return emulateBranch(1, ((Int32Value)val1).value == 0); + return emulateBranch(1, Int32Value.compareFalse((Int32Value)val1)); else if (val1.valueType == ValueType.Int64) - return emulateBranch(1, ((Int64Value)val1).value == 0); + return emulateBranch(1, Int64Value.compareFalse((Int64Value)val1)); else if (val1.valueType == ValueType.Null) return emulateBranch(1, true); else @@ -549,12 +227,10 @@ namespace de4dot.blocks.cflow { bool emulate_Brtrue() { var val1 = instructionEmulator.pop(); - //TODO: Support floats - if (val1.valueType == ValueType.Int32) - return emulateBranch(1, ((Int32Value)val1).value != 0); + return emulateBranch(1, Int32Value.compareTrue((Int32Value)val1)); else if (val1.valueType == ValueType.Int64) - return emulateBranch(1, ((Int64Value)val1).value != 0); + return emulateBranch(1, Int64Value.compareTrue((Int64Value)val1)); else if (val1.valueType == ValueType.Null) return emulateBranch(1, false); else diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index 5a6298cc..ccfc45e6 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -35,50 +35,61 @@ namespace de4dot.blocks.cflow { this.variableDefinitions = variableDefinitions; valueStack.init(); - initValueList(args, parameterDefinitions.Count); + args.Clear(); + foreach (var arg in parameterDefinitions) + args.Add(getUnknownValue(arg.ParameterType)); if (initLocals) { locals.Clear(); - for (int i = 0; i < variableDefinitions.Count; i++) { - var localType = variableDefinitions[i].VariableType; - if (!localType.IsValueType) - locals.Add(NullValue.Instance); - else if (DotNetUtils.isAssembly(localType.Scope, "mscorlib")) { - switch (localType.FullName) { - case "System.Boolean": - case "System.Byte": - case "System.SByte": - case "System.Int16": - case "System.Int32": - case "System.UInt16": - case "System.UInt32": - locals.Add(new Int32Value(0)); - break; - case "System.Int64": - case "System.UInt64": - locals.Add(new Int64Value(0)); - break; - case "System.Single": - case "System.Double": - locals.Add(new Real8Value(0)); - break; - default: - locals.Add(new UnknownValue()); - break; - } - } - else - locals.Add(new UnknownValue()); - } + foreach (var local in variableDefinitions) + locals.Add(getDefaultValue(local.VariableType)); + } + else { + locals.Clear(); + foreach (var local in variableDefinitions) + locals.Add(getUnknownValue(local.VariableType)); } - else - initValueList(locals, variableDefinitions.Count); } - void initValueList(List list, int size) { - list.Clear(); - for (int i = 0; i < size; i++) - list.Add(new UnknownValue()); + static Value getDefaultValue(TypeReference typeReference) { + if (!typeReference.IsValueType) + return NullValue.Instance; + else if (DotNetUtils.isAssembly(typeReference.Scope, "mscorlib")) { + switch (typeReference.FullName) { + case "System.Boolean": + case "System.Byte": + case "System.SByte": + case "System.Int16": + case "System.Int32": + case "System.UInt16": + case "System.UInt32": + return new Int32Value(0); + case "System.Int64": + case "System.UInt64": + return new Int64Value(0); + case "System.Single": + case "System.Double": + return new Real8Value(0); + } + } + return new UnknownValue(); + } + + static Value getUnknownValue(TypeReference typeReference) { + if (DotNetUtils.isAssembly(typeReference.Scope, "mscorlib")) { + switch (typeReference.FullName) { + case "System.Boolean": return Int32Value.createUnknownBool(); + case "System.Byte": return Int32Value.createUnknownUInt8(); + case "System.SByte": return Int32Value.createUnknown(); + case "System.Int16": return Int32Value.createUnknown(); + case "System.Int32": return Int32Value.createUnknown(); + case "System.UInt16": return Int32Value.createUnknownUInt16(); + case "System.UInt32": return Int32Value.createUnknown(); + case "System.Int64": return Int64Value.createUnknown(); + case "System.UInt64": return Int64Value.createUnknown(); + } + } + return new UnknownValue(); } static Value getValue(List list, int i) { @@ -100,6 +111,12 @@ namespace de4dot.blocks.cflow { args[index] = value; } + Value getUnknownArg(int index) { + if (0 <= index && index < parameterDefinitions.Count) + return getUnknownValue(parameterDefinitions[index].ParameterType); + return new UnknownValue(); + } + Value getLocal(int i) { return getValue(locals, i); } @@ -113,6 +130,12 @@ namespace de4dot.blocks.cflow { locals[index] = value; } + Value getUnknownLocal(int index) { + if (0 <= index && index < variableDefinitions.Count) + return getUnknownValue(variableDefinitions[index].VariableType); + return new UnknownValue(); + } + public Value pop() { return valueStack.pop(); } @@ -197,6 +220,9 @@ namespace de4dot.blocks.cflow { case Code.Clt_Un: emulate_Clt_Un(instr); break; case Code.Unbox_Any:emulate_Unbox_Any(instr); break; + case Code.Call: emulate_Call(instr); break; + case Code.Callvirt: emulate_Callvirt(instr); break; + case Code.Add_Ovf: case Code.Add_Ovf_Un: case Code.Sub_Ovf: @@ -238,9 +264,7 @@ namespace de4dot.blocks.cflow { case Code.Br: case Code.Break: case Code.Br_S: - case Code.Call: case Code.Calli: - case Code.Callvirt: case Code.Castclass: case Code.Ckfinite: case Code.Constrained: @@ -364,204 +388,83 @@ namespace de4dot.blocks.cflow { } } - void switchUnknownToSecondOperand(ref Value op1, ref Value op2) { - if (op2.valueType == ValueType.Unknown) - return; - if (op1.valueType != ValueType.Unknown) - return; - Value tmp = op1; - op1 = op2; - op2 = tmp; - } - void emulate_Conv_U1(Instruction instr) { - //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - var val32 = (Int32Value)val1; - valueStack.push(new Int32Value((int)(byte)val32.value)); - break; - - case ValueType.Int64: - var val64 = (Int64Value)val1; - valueStack.push(new Int32Value((int)(byte)val64.value)); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int32Value((int)(byte)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int32Value.Conv_U1((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int32Value.Conv_U1((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int32Value.Conv_U1((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_I1(Instruction instr) { var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - var val32 = (Int32Value)val1; - valueStack.push(new Int32Value((int)(sbyte)val32.value)); - break; - - case ValueType.Int64: - var val64 = (Int64Value)val1; - valueStack.push(new Int32Value((int)(sbyte)val64.value)); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int32Value((int)(sbyte)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int32Value.Conv_I1((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int32Value.Conv_I1((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int32Value.Conv_I1((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_U2(Instruction instr) { - //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - var val32 = (Int32Value)val1; - valueStack.push(new Int32Value((int)(ushort)val32.value)); - break; - - case ValueType.Int64: - var val64 = (Int64Value)val1; - valueStack.push(new Int32Value((int)(ushort)val64.value)); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int32Value((int)(ushort)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int32Value.Conv_U2((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int32Value.Conv_U2((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int32Value.Conv_U2((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_I2(Instruction instr) { var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - var val32 = (Int32Value)val1; - valueStack.push(new Int32Value((int)(short)val32.value)); - break; - - case ValueType.Int64: - var val64 = (Int64Value)val1; - valueStack.push(new Int32Value((int)(short)val64.value)); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int32Value((int)(short)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int32Value.Conv_I2((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int32Value.Conv_I2((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int32Value.Conv_I2((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_U4(Instruction instr) { var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - valueStack.push(val1); - break; - - case ValueType.Int64: - var val64 = (Int64Value)val1; - valueStack.push(new Int32Value((int)(uint)val64.value)); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int32Value((int)(uint)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int32Value.Conv_U4((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int32Value.Conv_U4((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int32Value.Conv_U4((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_I4(Instruction instr) { var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - valueStack.push(val1); - break; - - case ValueType.Int64: - var val64 = (Int64Value)val1; - valueStack.push(new Int32Value((int)val64.value)); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int32Value((int)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int32Value.Conv_I4((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int32Value.Conv_I4((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int32Value.Conv_I4((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_U8(Instruction instr) { - //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - var val32 = (Int32Value)val1; - valueStack.push(new Int64Value((long)(ulong)(uint)val32.value)); - break; - - case ValueType.Int64: - valueStack.push(val1); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int64Value((long)(ulong)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int64Value.Conv_U8((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int64Value.Conv_U8((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int64Value.Conv_U8((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } void emulate_Conv_I8(Instruction instr) { var val1 = valueStack.pop(); switch (val1.valueType) { - case ValueType.Int32: - var val32 = (Int32Value)val1; - valueStack.push(new Int64Value((long)val32.value)); - break; - - case ValueType.Int64: - valueStack.push(val1); - break; - - case ValueType.Real8: - var valr8 = (Real8Value)val1; - valueStack.push(new Int64Value((long)valr8.value)); - break; - - default: - valueStack.pushUnknown(); - break; + case ValueType.Int32: valueStack.push(Int64Value.Conv_I8((Int32Value)val1)); break; + case ValueType.Int64: valueStack.push(Int64Value.Conv_I8((Int64Value)val1)); break; + case ValueType.Real8: valueStack.push(Int64Value.Conv_I8((Real8Value)val1)); break; + default: valueStack.pushUnknown(); break; } } @@ -569,311 +472,105 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - switchUnknownToSecondOperand(ref val1, ref val2); - - if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == 0) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value + int2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == 0) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - valueStack.push(new Int64Value(long1.value + long2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Real8Value(real1.value + real2.value)); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Add((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Add((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + valueStack.push(Real8Value.Add((Real8Value)val1, (Real8Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Sub(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value - int2.value)); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - valueStack.push(new Int64Value(long1.value - long2.value)); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Real8Value(real1.value - real2.value)); - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == 0) - valueStack.push(val1); - else - valueStack.pushUnknown(); - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == 0) - valueStack.push(val1); - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Sub((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Sub((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + valueStack.push(Real8Value.Sub((Real8Value)val1, (Real8Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Mul(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - switchUnknownToSecondOperand(ref val1, ref val2); - - if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == 1) - valueStack.push(val2); - else if (int1.value == 0) - valueStack.push(new Int32Value(0)); - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value * int2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == 1) - valueStack.push(val2); - else if (long1.value == 0) - valueStack.push(new Int64Value(0)); - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - valueStack.push(new Int64Value(long1.value * long2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Real8Value(real1.value * real2.value)); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Mul((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Mul((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + valueStack.push(Real8Value.Mul((Real8Value)val1, (Real8Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Div(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - try { - valueStack.push(new Int32Value(int1.value / int2.value)); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - try { - valueStack.push(new Int64Value(long1.value / long2.value)); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Real8Value(real1.value / real2.value)); - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == 1) - valueStack.push(val1); - else - valueStack.pushUnknown(); - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == 1) - valueStack.push(val1); - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Div((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Div((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + valueStack.push(Real8Value.Div((Real8Value)val1, (Real8Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Div_Un(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - try { - valueStack.push(new Int32Value((int)((uint)int1.value / (uint)int2.value))); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - try { - valueStack.push(new Int64Value((long)((ulong)long1.value / (ulong)long2.value))); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == 1) - valueStack.push(val1); - else - valueStack.pushUnknown(); - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == 1) - valueStack.push(val1); - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Div_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Div_Un((Int64Value)val1, (Int64Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Rem(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - try { - valueStack.push(new Int32Value(int1.value % int2.value)); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - try { - valueStack.push(new Int64Value(long1.value % long2.value)); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Real8Value(real1.value % real2.value)); - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == 1) - valueStack.push(new Int32Value(0)); - else - valueStack.pushUnknown(); - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == 1) - valueStack.push(new Int64Value(0)); - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Rem((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Rem((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + valueStack.push(Real8Value.Rem((Real8Value)val1, (Real8Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Rem_Un(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - try { - valueStack.push(new Int32Value((int)((uint)int1.value % (uint)int2.value))); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - try { - valueStack.push(new Int64Value((long)((ulong)long1.value % (ulong)long2.value))); - } - catch (ArithmeticException) { - valueStack.pushUnknown(); - } - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == 1) - valueStack.push(new Int32Value(0)); - else - valueStack.pushUnknown(); - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == 1) - valueStack.push(new Int64Value(0)); - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Rem_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Rem_Un((Int64Value)val1, (Int64Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Neg(Instruction instr) { var val1 = valueStack.pop(); if (val1.valueType == ValueType.Int32) - valueStack.push(new Int32Value(-((Int32Value)val1).value)); + valueStack.push(Int32Value.Neg((Int32Value)val1)); else if (val1.valueType == ValueType.Int64) - valueStack.push(new Int64Value(-((Int64Value)val1).value)); + valueStack.push(Int64Value.Neg((Int64Value)val1)); else if (val1.valueType == ValueType.Real8) - valueStack.push(new Real8Value(-((Real8Value)val1).value)); + valueStack.push(Real8Value.Neg((Real8Value)val1)); else valueStack.pushUnknown(); } @@ -882,116 +579,45 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - switchUnknownToSecondOperand(ref val1, ref val2); - - if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == 0) - valueStack.push(new Int32Value(0)); - else if (int1.value == -1) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value & int2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == 0) - valueStack.push(new Int64Value(0)); - else if (long1.value == -1) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - valueStack.push(new Int64Value(long1.value & long2.value)); - } - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.And((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.And((Int64Value)val1, (Int64Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Or(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - switchUnknownToSecondOperand(ref val1, ref val2); - - if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == -1) - valueStack.push(new Int32Value(-1)); - else if (int1.value == 0) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value | int2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == -1) - valueStack.push(new Int64Value(-1)); - else if (long1.value == 0) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - valueStack.push(new Int64Value(long1.value | long2.value)); - } - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Or((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Or((Int64Value)val1, (Int64Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Xor(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - switchUnknownToSecondOperand(ref val1, ref val2); - - if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == 0) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value ^ int2.value)); - } - else - valueStack.pushUnknown(); - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == 0) - valueStack.push(val2); - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - valueStack.push(new Int64Value(long1.value ^ long2.value)); - } - else - valueStack.pushUnknown(); - } - else { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Xor((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Xor((Int64Value)val1, (Int64Value)val2)); + else valueStack.pushUnknown(); - } } void emulate_Not(Instruction instr) { var val1 = valueStack.pop(); if (val1.valueType == ValueType.Int32) - valueStack.push(new Int32Value(~((Int32Value)val1).value)); + valueStack.push(Int32Value.Not((Int32Value)val1)); else if (val1.valueType == ValueType.Int64) - valueStack.push(new Int64Value(~((Int64Value)val1).value)); + valueStack.push(Int64Value.Not((Int64Value)val1)); else valueStack.pushUnknown(); } @@ -1000,22 +626,10 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - int bits; - if (val2.valueType == ValueType.Int32) - bits = ((Int32Value)val2).value; - else if (val2.valueType == ValueType.Int64) - bits = (int)((Int64Value)val2).value; - else { - valueStack.pushUnknown(); - return; - } - - if (bits == 0) - valueStack.push(val1); - else if (val1.valueType == ValueType.Int32) - valueStack.push(new Int32Value(((Int32Value)val1).value << bits)); - else if (val1.valueType == ValueType.Int64) - valueStack.push(new Int64Value(((Int64Value)val1).value << bits)); + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Shl((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int32) + valueStack.push(Int64Value.Shl((Int64Value)val1, (Int32Value)val2)); else valueStack.pushUnknown(); } @@ -1024,22 +638,10 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - int bits; - if (val2.valueType == ValueType.Int32) - bits = ((Int32Value)val2).value; - else if (val2.valueType == ValueType.Int64) - bits = (int)((Int64Value)val2).value; - else { - valueStack.pushUnknown(); - return; - } - - if (bits == 0) - valueStack.push(val1); - else if (val1.valueType == ValueType.Int32) - valueStack.push(new Int32Value(((Int32Value)val1).value >> bits)); - else if (val1.valueType == ValueType.Int64) - valueStack.push(new Int64Value(((Int64Value)val1).value >> bits)); + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Shr((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int32) + valueStack.push(Int64Value.Shr((Int64Value)val1, (Int32Value)val2)); else valueStack.pushUnknown(); } @@ -1048,22 +650,10 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - int bits; - if (val2.valueType == ValueType.Int32) - bits = ((Int32Value)val2).value; - else if (val2.valueType == ValueType.Int64) - bits = (int)((Int64Value)val2).value; - else { - valueStack.pushUnknown(); - return; - } - - if (bits == 0) - valueStack.push(val1); - else if (val1.valueType == ValueType.Int32) - valueStack.push(new Int32Value((int)((uint)((Int32Value)val1).value >> bits))); - else if (val1.valueType == ValueType.Int64) - valueStack.push(new Int64Value((long)((ulong)((Int64Value)val1).value >> bits))); + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Shr_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int32) + valueStack.push(Int64Value.Shr_Un((Int64Value)val1, (Int32Value)val2)); else valueStack.pushUnknown(); } @@ -1072,235 +662,62 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - //TODO: If it's an unknown int32/64, push 1 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value == int2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - valueStack.push(new Int32Value(long1.value == long2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Int32Value(real1.value == real2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) { + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Ceq((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Ceq((Int64Value)val1, (Int64Value)val2)); + else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) valueStack.push(new Int32Value(1)); - } - else { - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } + else + Int32Value.createUnknownBool(); } void emulate_Cgt(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - //TODO: If it's an unknown int32/64, push 0 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value > int2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - valueStack.push(new Int32Value(long1.value > long2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Int32Value(real1.value > real2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == int.MinValue) - valueStack.push(new Int32Value(0)); // min > x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == int.MaxValue) - valueStack.push(new Int32Value(0)); // x > max => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == long.MinValue) - valueStack.push(new Int32Value(0)); // min > x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == long.MaxValue) - valueStack.push(new Int32Value(0)); // x > max => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else { - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Cgt((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Cgt((Int64Value)val1, (Int64Value)val2)); + else + Int32Value.createUnknownBool(); } void emulate_Cgt_Un(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - //TODO: If it's an unknown int32/64, push 0 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value((uint)int1.value > (uint)int2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - valueStack.push(new Int32Value((ulong)long1.value > (ulong)long2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if ((uint)int1.value == uint.MinValue) - valueStack.push(new Int32Value(0)); // min > x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if ((uint)int2.value == uint.MaxValue) - valueStack.push(new Int32Value(0)); // x > max => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if ((ulong)long1.value == ulong.MinValue) - valueStack.push(new Int32Value(0)); // min > x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if ((ulong)long2.value == ulong.MaxValue) - valueStack.push(new Int32Value(0)); // x > max => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else { - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Cgt_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Cgt_Un((Int64Value)val1, (Int64Value)val2)); + else + Int32Value.createUnknownBool(); } void emulate_Clt(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - //TODO: If it's an unknown int32/64, push 0 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value(int1.value < int2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - valueStack.push(new Int32Value(long1.value < long2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) { - var real1 = (Real8Value)val1; - var real2 = (Real8Value)val2; - valueStack.push(new Int32Value(real1.value < real2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if (int1.value == int.MaxValue) - valueStack.push(new Int32Value(0)); // max < x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if (int2.value == int.MinValue) - valueStack.push(new Int32Value(0)); // x < min => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if (long1.value == long.MaxValue) - valueStack.push(new Int32Value(0)); // max < x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if (long2.value == long.MinValue) - valueStack.push(new Int32Value(0)); // x < min => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else { - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Clt((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Clt((Int64Value)val1, (Int64Value)val2)); + else + Int32Value.createUnknownBool(); } void emulate_Clt_Un(Instruction instr) { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - //TODO: If it's an unknown int32/64, push 0 if val1 is same ref as val2 - - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - var int2 = (Int32Value)val2; - valueStack.push(new Int32Value((uint)int1.value < (uint)int2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - var long2 = (Int64Value)val2; - valueStack.push(new Int32Value((ulong)long1.value < (ulong)long2.value ? 1 : 0)); - } - else if (val1.valueType == ValueType.Int32) { - var int1 = (Int32Value)val1; - if ((uint)int1.value == uint.MaxValue) - valueStack.push(new Int32Value(0)); // max < x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int32) { - var int2 = (Int32Value)val2; - if ((uint)int2.value == uint.MinValue) - valueStack.push(new Int32Value(0)); // x < min => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val1.valueType == ValueType.Int64) { - var long1 = (Int64Value)val1; - if ((ulong)long1.value == ulong.MaxValue) - valueStack.push(new Int32Value(0)); // max < x => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else if (val2.valueType == ValueType.Int64) { - var long2 = (Int64Value)val2; - if ((ulong)long2.value == ulong.MinValue) - valueStack.push(new Int32Value(0)); // x < min => false - else - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } - else { - valueStack.pushUnknown(); //TODO: Push int32 with bit 0 unknown - } + if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + valueStack.push(Int32Value.Clt_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + valueStack.push(Int64Value.Clt_Un((Int64Value)val1, (Int64Value)val2)); + else + Int32Value.createUnknownBool(); } void emulate_Unbox_Any(Instruction instr) { @@ -1325,12 +742,30 @@ namespace de4dot.blocks.cflow { void emulate_Ldarga(int index) { valueStack.pushUnknown(); - setArg(index, new UnknownValue()); + setArg(index, getUnknownArg(index)); } void emulate_Ldloca(int index) { valueStack.pushUnknown(); - setLocal(index, new UnknownValue()); + setLocal(index, getUnknownLocal(index)); + } + + void emulate_Call(Instruction instr) { + emulate_Call(instr, (MethodReference)instr.Operand); + } + + void emulate_Callvirt(Instruction instr) { + emulate_Call(instr, (MethodReference)instr.Operand); + } + + void emulate_Call(Instruction instr, MethodReference method) { + int pushes, pops; + DotNetUtils.calculateStackUsage(instr, false, out pushes, out pops); + valueStack.pop(pops); + if (pushes == 1) + valueStack.push(getUnknownValue(method.MethodReturnType.ReturnType)); + else + valueStack.push(pushes); } } } diff --git a/blocks/cflow/Int32Value.cs b/blocks/cflow/Int32Value.cs new file mode 100644 index 00000000..017b0c19 --- /dev/null +++ b/blocks/cflow/Int32Value.cs @@ -0,0 +1,488 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; + +namespace de4dot.blocks.cflow { + class Int32Value : Value { + const uint NO_UNKNOWN_BITS = uint.MaxValue; + public readonly int value; + public readonly uint validMask; + + public Int32Value(int value) + : base(ValueType.Int32) { + this.value = value; + this.validMask = NO_UNKNOWN_BITS; + } + + public Int32Value(int value, uint validMask) + : base(ValueType.Int32) { + this.value = value; + this.validMask = validMask; + } + + public bool hasUnknownBits() { + return validMask != NO_UNKNOWN_BITS; + } + + bool allBitsValid() { + return !hasUnknownBits(); + } + + bool isBitValid(int n) { + return isBitValid(validMask, n); + } + + static bool isBitValid(uint validMask, int n) { + return (validMask & (1U << n)) != 0; + } + + public static Int32Value createUnknownBool() { + return new Int32Value(0, NO_UNKNOWN_BITS << 1); + } + + public static Int32Value createUnknownUInt8() { + return new Int32Value(0, NO_UNKNOWN_BITS << 8); + } + + public static Int32Value createUnknownUInt16() { + return new Int32Value(0, NO_UNKNOWN_BITS << 16); + } + + public static Int32Value createUnknown() { + return new Int32Value(0, 0U); + } + + public bool isZero() { + return hasValue(0); + } + + public bool hasValue(int value) { + return allBitsValid() && this.value == value; + } + + public bool hasValue(uint value) { + return hasValue((int)value); + } + + public static Int32Value Conv_U1(Int32Value a) { + return Conv_U1(a.value, a.validMask); + } + + public static Int32Value Conv_U1(Int64Value a) { + return Conv_U1((int)a.value, (uint)a.validMask); + } + + public static Int32Value Conv_U1(int value, uint validMask) { + //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? + value = (int)(byte)value; + validMask |= NO_UNKNOWN_BITS << 8; + return new Int32Value(value, validMask); + } + + public static Int32Value Conv_U1(Real8Value a) { + //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? + return new Int32Value((int)(byte)a.value); + } + + public static Int32Value Conv_I1(Int32Value a) { + return Conv_I1(a.value, a.validMask); + } + + public static Int32Value Conv_I1(Int64Value a) { + return Conv_I1((int)a.value, (uint)a.validMask); + } + + public static Int32Value Conv_I1(int value, uint validMask) { + value = (int)(sbyte)value; + if (isBitValid(validMask, 7)) + validMask |= NO_UNKNOWN_BITS << 8; + else + validMask &= ~(NO_UNKNOWN_BITS << 8); + return new Int32Value(value, validMask); + } + + public static Int32Value Conv_I1(Real8Value a) { + return new Int32Value((int)(sbyte)a.value); + } + + public static Int32Value Conv_U2(Int32Value a) { + return Conv_U2(a.value, a.validMask); + } + + public static Int32Value Conv_U2(Int64Value a) { + return Conv_U2((int)a.value, (uint)a.validMask); + } + + public static Int32Value Conv_U2(int value, uint validMask) { + //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? + value = (int)(ushort)value; + validMask |= NO_UNKNOWN_BITS << 16; + return new Int32Value(value, validMask); + } + + public static Int32Value Conv_U2(Real8Value a) { + //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? + return new Int32Value((int)(ushort)a.value); + } + + public static Int32Value Conv_I2(Int32Value a) { + return Conv_I2(a.value, a.validMask); + } + + public static Int32Value Conv_I2(Int64Value a) { + return Conv_I2((int)a.value, (uint)a.validMask); + } + + public static Int32Value Conv_I2(int value, uint validMask) { + value = (int)(short)value; + if (isBitValid(validMask, 15)) + validMask |= NO_UNKNOWN_BITS << 16; + else + validMask &= ~(NO_UNKNOWN_BITS << 16); + return new Int32Value(value, validMask); + } + + public static Int32Value Conv_I2(Real8Value a) { + return new Int32Value((int)(short)a.value); + } + + public static Int32Value Conv_U4(Int32Value a) { + return a; + } + + public static Int32Value Conv_U4(Int64Value a) { + return new Int32Value((int)(uint)a.value, (uint)a.validMask); + } + + public static Int32Value Conv_U4(Real8Value a) { + return new Int32Value((int)(uint)a.value); + } + + public static Int32Value Conv_I4(Int32Value a) { + return a; + } + + public static Int32Value Conv_I4(Int64Value a) { + return new Int32Value((int)a.value, (uint)a.validMask); + } + + public static Int32Value Conv_I4(Real8Value a) { + return new Int32Value((int)a.value); + } + + public static Int32Value Add(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return new Int32Value(a.value + b.value); + return createUnknown(); + } + + public static Int32Value Sub(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return new Int32Value(a.value - b.value); + return createUnknown(); + } + + public static Int32Value Mul(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return new Int32Value(a.value * b.value); + if (a.isZero() || b.isZero()) + return new Int32Value(0); + if (a.hasValue(1)) + return b; + if (b.hasValue(1)) + return a; + return createUnknown(); + } + + public static Int32Value Div(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int32Value(a.value / b.value); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return a; + return createUnknown(); + } + + public static Int32Value Div_Un(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int32Value((int)((uint)a.value / (uint)b.value)); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return a; + return createUnknown(); + } + + public static Int32Value Rem(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int32Value(a.value % b.value); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return new Int32Value(0); + return createUnknown(); + } + + public static Int32Value Rem_Un(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int32Value((int)((uint)a.value % (uint)b.value)); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return new Int32Value(0); + return createUnknown(); + } + + public static Int32Value Neg(Int32Value a) { + if (a.allBitsValid()) + return new Int32Value(-a.value); + return createUnknown(); + } + + public static Int32Value And(Int32Value a, Int32Value b) { + int av = a.value, bv = b.value; + uint am = a.validMask, bm = b.validMask; + return new Int32Value(av & bv, (uint)((am & bm) | ((av & am) ^ am) | ((bv & bm) ^ bm))); + } + + public static Int32Value Or(Int32Value a, Int32Value b) { + int av = a.value, bv = b.value; + uint am = a.validMask, bm = b.validMask; + return new Int32Value(av | bv, (uint)((am & bm) | (av & am) | (bv & bm))); + } + + public static Int32Value Xor(Int32Value a, Int32Value b) { + if (ReferenceEquals(a, b)) + return new Int32Value(0); + int av = a.value, bv = b.value; + uint am = a.validMask, bm = b.validMask; + return new Int32Value(av ^ bv, (uint)(am & bm)); + } + + public static Int32Value Not(Int32Value a) { + return new Int32Value(~a.value, a.validMask); + } + + public static Int32Value Shl(Int32Value a, Int32Value b) { + if (b.hasUnknownBits()) + return createUnknown(); + if (b.value == 0) + return a; + if (b.value < 0 || b.value >= sizeof(int) * 8) + return createUnknown(); + int shift = b.value; + uint validMask = (a.validMask << shift) | (uint.MaxValue >> (sizeof(int) * 8 - shift)); + return new Int32Value(a.value << shift, validMask); + } + + public static Int32Value Shr(Int32Value a, Int32Value b) { + if (b.hasUnknownBits()) + return createUnknown(); + if (b.value == 0) + return a; + if (b.value < 0 || b.value >= sizeof(int) * 8) + return createUnknown(); + int shift = b.value; + uint validMask = a.validMask >> shift; + if (a.isBitValid(sizeof(int) * 8 - 1)) + validMask |= (uint.MaxValue << (sizeof(int) * 8 - shift)); + return new Int32Value(a.value >> shift, validMask); + } + + public static Int32Value Shr_Un(Int32Value a, Int32Value b) { + if (b.hasUnknownBits()) + return createUnknown(); + if (b.value == 0) + return a; + if (b.value < 0 || b.value >= sizeof(int) * 8) + return createUnknown(); + int shift = b.value; + uint validMask = (a.validMask >> shift) | (uint.MaxValue << (sizeof(int) * 8 - shift)); + return new Int32Value((int)((uint)a.value >> shift), validMask); + } + + static Int32Value create(Bool3 b) { + switch (b) { + case Bool3.False: return new Int32Value(0); + case Bool3.True: return new Int32Value(1); + default: return createUnknown(); + } + } + + public static Int32Value Ceq(Int32Value a, Int32Value b) { + return create(compareEq(a, b)); + } + + public static Int32Value Cgt(Int32Value a, Int32Value b) { + return create(compareGt(a, b)); + } + + public static Int32Value Cgt_Un(Int32Value a, Int32Value b) { + return create(compareGt_Un(a, b)); + } + + public static Int32Value Clt(Int32Value a, Int32Value b) { + return create(compareLt(a, b)); + } + + public static Int32Value Clt_Un(Int32Value a, Int32Value b) { + return create(compareLt_Un(a, b)); + } + + public static Bool3 compareEq(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value == b.value ? Bool3.True : Bool3.False; + if (ReferenceEquals(a, b)) + return Bool3.True; + if ((a.value & a.validMask & b.validMask) != (b.value & a.validMask & b.validMask)) + return Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 compareNeq(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value != b.value ? Bool3.True : Bool3.False; + if (ReferenceEquals(a, b)) + return Bool3.False; + if ((a.value & a.validMask & b.validMask) != (b.value & a.validMask & b.validMask)) + return Bool3.True; + return Bool3.Unknown; + } + + public static Bool3 compareGt(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value > b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MinValue)) + return Bool3.False; // min > x => false + if (b.hasValue(int.MaxValue)) + return Bool3.False; // x > max => false + return Bool3.Unknown; + } + + public static Bool3 compareGt_Un(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value > (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MinValue)) + return Bool3.False; // min > x => false + if (b.hasValue(uint.MaxValue)) + return Bool3.False; // x > max => false + return Bool3.Unknown; + } + + public static Bool3 compareGe(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value >= b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MaxValue)) + return Bool3.False; // max >= x => true + if (b.hasValue(int.MinValue)) + return Bool3.False; // x >= min => true + return Bool3.Unknown; + } + + public static Bool3 compareGe_Un(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value >= (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MaxValue)) + return Bool3.False; // max >= x => true + if (b.hasValue(uint.MinValue)) + return Bool3.False; // x >= min => true + return Bool3.Unknown; + } + + public static Bool3 compareLe(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value <= b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MinValue)) + return Bool3.False; // min <= x => true + if (b.hasValue(int.MaxValue)) + return Bool3.False; // x <= max => true + return Bool3.Unknown; + } + + public static Bool3 compareLe_Un(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value <= (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MinValue)) + return Bool3.False; // min <= x => true + if (b.hasValue(uint.MaxValue)) + return Bool3.False; // x <= max => true + return Bool3.Unknown; + } + + public static Bool3 compareLt(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value < b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MaxValue)) + return Bool3.False; // max < x => false + if (b.hasValue(int.MinValue)) + return Bool3.False; // x < min => false + return Bool3.Unknown; + } + + public static Bool3 compareLt_Un(Int32Value a, Int32Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value < (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MaxValue)) + return Bool3.False; // max < x => false + if (b.hasValue(uint.MinValue)) + return Bool3.False; // x < min => false + return Bool3.Unknown; + } + + public static Bool3 compareTrue(Int32Value a) { + if (a.allBitsValid()) + return a.value != 0 ? Bool3.True : Bool3.False; + if ((a.value & a.validMask) != 0) + return Bool3.True; + return Bool3.Unknown; + } + + public static Bool3 compareFalse(Int32Value a) { + if (a.allBitsValid()) + return a.value == 0 ? Bool3.True : Bool3.False; + if ((a.value & a.validMask) != 0) + return Bool3.False; + return Bool3.Unknown; + } + + public override string ToString() { + if (allBitsValid()) + return value.ToString(); + return string.Format("0x{0:X8}({1:X8})", value, validMask); + } + } +} diff --git a/blocks/cflow/Int64Value.cs b/blocks/cflow/Int64Value.cs new file mode 100644 index 00000000..0aff87d4 --- /dev/null +++ b/blocks/cflow/Int64Value.cs @@ -0,0 +1,403 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System; + +namespace de4dot.blocks.cflow { + class Int64Value : Value { + const ulong NO_UNKNOWN_BITS = ulong.MaxValue; + public readonly long value; + public readonly ulong validMask; + + public Int64Value(long value) + : base(ValueType.Int64) { + this.value = value; + this.validMask = NO_UNKNOWN_BITS; + } + + public Int64Value(long value, ulong validMask) + : base(ValueType.Int64) { + this.value = value; + this.validMask = validMask; + } + + bool hasUnknownBits() { + return validMask != NO_UNKNOWN_BITS; + } + + bool allBitsValid() { + return !hasUnknownBits(); + } + + bool isBitValid(int n) { + return isBitValid(validMask, n); + } + + static bool isBitValid(ulong validMask, int n) { + return (validMask & (1UL << n)) != 0; + } + + public static Int64Value createUnknown() { + return new Int64Value(0, 0UL); + } + + public bool isZero() { + return hasValue(0); + } + + public bool hasValue(long value) { + return allBitsValid() && this.value == value; + } + + public bool hasValue(ulong value) { + return hasValue((long)value); + } + + public static Int64Value Conv_U8(Int32Value a) { + //TODO: Doc says that result is sign-extended. You zero-extend it. Is that correct? + long value = (long)(ulong)(uint)a.value; + ulong validMask = a.validMask | (NO_UNKNOWN_BITS << 32); + return new Int64Value(value, validMask); + } + + public static Int64Value Conv_U8(Int64Value a) { + return a; + } + + public static Int64Value Conv_U8(Real8Value a) { + return new Int64Value((long)(ulong)a.value); + } + + public static Int64Value Conv_I8(Int32Value a) { + long value = a.value; + ulong validMask = a.validMask; + if (isBitValid(validMask, 31)) + validMask |= NO_UNKNOWN_BITS << 32; + else + validMask &= ~(NO_UNKNOWN_BITS << 32); + return new Int64Value(value, validMask); + } + + public static Int64Value Conv_I8(Int64Value a) { + return a; + } + + public static Int64Value Conv_I8(Real8Value a) { + return new Int64Value((long)a.value); + } + + public static Int64Value Add(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return new Int64Value(a.value + b.value); + return createUnknown(); + } + + public static Int64Value Sub(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return new Int64Value(a.value - b.value); + return createUnknown(); + } + + public static Int64Value Mul(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return new Int64Value(a.value * b.value); + if (a.isZero() || b.isZero()) + return new Int64Value(0); + if (a.hasValue(1)) + return b; + if (b.hasValue(1)) + return a; + return createUnknown(); + } + + public static Int64Value Div(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int64Value(a.value / b.value); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return a; + return createUnknown(); + } + + public static Int64Value Div_Un(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int64Value((int)((uint)a.value / (uint)b.value)); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return a; + return createUnknown(); + } + + public static Int64Value Rem(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int64Value(a.value % b.value); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return new Int64Value(0); + return createUnknown(); + } + + public static Int64Value Rem_Un(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) { + try { + return new Int64Value((int)((uint)a.value % (uint)b.value)); + } + catch (ArithmeticException) { + return createUnknown(); + } + } + if (b.hasValue(1)) + return new Int64Value(0); + return createUnknown(); + } + + public static Int64Value Neg(Int64Value a) { + if (a.allBitsValid()) + return new Int64Value(-a.value); + return createUnknown(); + } + + public static Int64Value And(Int64Value a, Int64Value b) { + long av = a.value, bv = b.value; + ulong am = a.validMask, bm = b.validMask; + return new Int64Value(av & bv, (am & bm) | (((ulong)av & am) ^ am) | (((ulong)bv & bm) ^ bm)); + } + + public static Int64Value Or(Int64Value a, Int64Value b) { + long av = a.value, bv = b.value; + ulong am = a.validMask, bm = b.validMask; + return new Int64Value(av | bv, (am & bm) | ((ulong)av & am) | ((ulong)bv & bm)); + } + + public static Int64Value Xor(Int64Value a, Int64Value b) { + if (ReferenceEquals(a, b)) + return new Int64Value(0); + long av = a.value, bv = b.value; + ulong am = a.validMask, bm = b.validMask; + return new Int64Value(av ^ bv, am & bm); + } + + public static Int64Value Not(Int64Value a) { + return new Int64Value(~a.value, a.validMask); + } + + public static Int64Value Shl(Int64Value a, Int32Value b) { + if (b.hasUnknownBits()) + return createUnknown(); + if (b.value == 0) + return a; + if (b.value < 0 || b.value >= sizeof(long) * 8) + return createUnknown(); + int shift = b.value; + ulong validMask = (a.validMask << shift) | (ulong.MaxValue >> (sizeof(long) * 8 - shift)); + return new Int64Value(a.value << shift, validMask); + } + + public static Int64Value Shr(Int64Value a, Int32Value b) { + if (b.hasUnknownBits()) + return createUnknown(); + if (b.value == 0) + return a; + if (b.value < 0 || b.value >= sizeof(long) * 8) + return createUnknown(); + int shift = b.value; + ulong validMask = a.validMask >> shift; + if (a.isBitValid(sizeof(long) * 8 - 1)) + validMask |= (ulong.MaxValue << (sizeof(long) * 8 - shift)); + return new Int64Value(a.value >> shift, validMask); + } + + public static Int64Value Shr_Un(Int64Value a, Int32Value b) { + if (b.hasUnknownBits()) + return createUnknown(); + if (b.value == 0) + return a; + if (b.value < 0 || b.value >= sizeof(long) * 8) + return createUnknown(); + int shift = b.value; + ulong validMask = (a.validMask >> shift) | (ulong.MaxValue << (sizeof(long) * 8 - shift)); + return new Int64Value((long)((ulong)a.value >> shift), validMask); + } + + static Int32Value create(Bool3 b) { + switch (b) { + case Bool3.False: return new Int32Value(0); + case Bool3.True: return new Int32Value(1); + default: return Int32Value.createUnknown(); + } + } + + public static Int32Value Ceq(Int64Value a, Int64Value b) { + return create(compareEq(a, b)); + } + + public static Int32Value Cgt(Int64Value a, Int64Value b) { + return create(compareGt(a, b)); + } + + public static Int32Value Cgt_Un(Int64Value a, Int64Value b) { + return create(compareGt_Un(a, b)); + } + + public static Int32Value Clt(Int64Value a, Int64Value b) { + return create(compareLt(a, b)); + } + + public static Int32Value Clt_Un(Int64Value a, Int64Value b) { + return create(compareLt_Un(a, b)); + } + + public static Bool3 compareEq(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value == b.value ? Bool3.True : Bool3.False; + if (ReferenceEquals(a, b)) + return Bool3.True; + if (((ulong)a.value & a.validMask & b.validMask) != ((ulong)b.value & a.validMask & b.validMask)) + return Bool3.False; + return Bool3.Unknown; + } + + public static Bool3 compareNeq(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value != b.value ? Bool3.True : Bool3.False; + if (ReferenceEquals(a, b)) + return Bool3.False; + if (((ulong)a.value & a.validMask & b.validMask) != ((ulong)b.value & a.validMask & b.validMask)) + return Bool3.True; + return Bool3.Unknown; + } + + public static Bool3 compareGt(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value > b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MinValue)) + return Bool3.False; // min > x => false + if (b.hasValue(int.MaxValue)) + return Bool3.False; // x > max => false + return Bool3.Unknown; + } + + public static Bool3 compareGt_Un(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value > (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MinValue)) + return Bool3.False; // min > x => false + if (b.hasValue(uint.MaxValue)) + return Bool3.False; // x > max => false + return Bool3.Unknown; + } + + public static Bool3 compareGe(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value >= b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MaxValue)) + return Bool3.False; // max >= x => true + if (b.hasValue(int.MinValue)) + return Bool3.False; // x >= min => true + return Bool3.Unknown; + } + + public static Bool3 compareGe_Un(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value >= (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MaxValue)) + return Bool3.False; // max >= x => true + if (b.hasValue(uint.MinValue)) + return Bool3.False; // x >= min => true + return Bool3.Unknown; + } + + public static Bool3 compareLe(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value <= b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MinValue)) + return Bool3.False; // min <= x => true + if (b.hasValue(int.MaxValue)) + return Bool3.False; // x <= max => true + return Bool3.Unknown; + } + + public static Bool3 compareLe_Un(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value <= (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MinValue)) + return Bool3.False; // min <= x => true + if (b.hasValue(uint.MaxValue)) + return Bool3.False; // x <= max => true + return Bool3.Unknown; + } + + public static Bool3 compareLt(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return a.value < b.value ? Bool3.True : Bool3.False; + if (a.hasValue(int.MaxValue)) + return Bool3.False; // max < x => false + if (b.hasValue(int.MinValue)) + return Bool3.False; // x < min => false + return Bool3.Unknown; + } + + public static Bool3 compareLt_Un(Int64Value a, Int64Value b) { + if (a.allBitsValid() && b.allBitsValid()) + return (uint)a.value < (uint)b.value ? Bool3.True : Bool3.False; + if (a.hasValue(uint.MaxValue)) + return Bool3.False; // max < x => false + if (b.hasValue(uint.MinValue)) + return Bool3.False; // x < min => false + return Bool3.Unknown; + } + + public static Bool3 compareTrue(Int64Value a) { + if (a.allBitsValid()) + return a.value != 0 ? Bool3.True : Bool3.False; + if (((ulong)a.value & a.validMask) != 0) + return Bool3.True; + return Bool3.Unknown; + } + + public static Bool3 compareFalse(Int64Value a) { + if (a.allBitsValid()) + return a.value == 0 ? Bool3.True : Bool3.False; + if (((ulong)a.value & a.validMask) != 0) + return Bool3.False; + return Bool3.Unknown; + } + + public override string ToString() { + if (allBitsValid()) + return value.ToString(); + return string.Format("0x{0:X8}L({1:X8})", value, validMask); + } + } +} diff --git a/blocks/cflow/Real8Value.cs b/blocks/cflow/Real8Value.cs new file mode 100644 index 00000000..8b5f3e6f --- /dev/null +++ b/blocks/cflow/Real8Value.cs @@ -0,0 +1,53 @@ +/* + Copyright (C) 2011 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +namespace de4dot.blocks.cflow { + class Real8Value : Value { + public readonly double value; + + public Real8Value(double value) + : base(ValueType.Real8) { + this.value = value; + } + + public static Real8Value Add(Real8Value a, Real8Value b) { + return new Real8Value(a.value + b.value); + } + + public static Real8Value Sub(Real8Value a, Real8Value b) { + return new Real8Value(a.value - b.value); + } + + public static Real8Value Mul(Real8Value a, Real8Value b) { + return new Real8Value(a.value * b.value); + } + + public static Real8Value Div(Real8Value a, Real8Value b) { + return new Real8Value(a.value / b.value); + } + + public static Real8Value Rem(Real8Value a, Real8Value b) { + return new Real8Value(a.value % b.value); + } + + public static Real8Value Neg(Real8Value a) { + return new Real8Value(-a.value); + } + } +} diff --git a/blocks/cflow/Value.cs b/blocks/cflow/Value.cs index beaf0beb..8bc992d9 100644 --- a/blocks/cflow/Value.cs +++ b/blocks/cflow/Value.cs @@ -28,6 +28,12 @@ namespace de4dot.blocks.cflow { String, } + enum Bool3 { + Unknown = -1, + False, + True, + } + abstract class Value { public readonly ValueType valueType; @@ -72,45 +78,6 @@ namespace de4dot.blocks.cflow { } } - class Int32Value : Value { - public readonly int value; - - public Int32Value(int value) - : base(ValueType.Int32) { - this.value = value; - } - - public override string ToString() { - return value.ToString(); - } - } - - class Int64Value : Value { - public readonly long value; - - public Int64Value(long value) - : base(ValueType.Int64) { - this.value = value; - } - - public override string ToString() { - return value.ToString() + "L"; - } - } - - class Real8Value : Value { - public readonly double value; - - public Real8Value(double value) - : base(ValueType.Real8) { - this.value = value; - } - - public override string ToString() { - return value.ToString() + "D"; - } - } - class StringValue : Value { public readonly string value;