TypeDef GetKeyAttr(RPContext ctx) { if (keyAttrs == null) { keyAttrs = new Tuple <TypeDef, Func <int, int> > [0x10]; } int index = ctx.Random.NextInt32(keyAttrs.Length); if (keyAttrs[index] == null) { TypeDef rtType = ctx.Context.Registry.GetService <IRuntimeService>().GetRuntimeType("DotProtect.Runtime.RefProxyKey"); TypeDef injectedAttr = InjectHelper.Inject(rtType, ctx.Module); injectedAttr.Name = ctx.Name.RandomName(); injectedAttr.Namespace = string.Empty; Expression expression, inverse; var var = new Variable("{VAR}"); var result = new Variable("{RESULT}"); ctx.DynCipher.GenerateExpressionPair( ctx.Random, new VariableExpression { Variable = var }, new VariableExpression { Variable = result }, ctx.Depth, out expression, out inverse); var expCompiled = new DMCodeGen(typeof(int), new[] { Tuple.Create("{VAR}", typeof(int)) }) .GenerateCIL(expression) .Compile <Func <int, int> >(); MethodDef ctor = injectedAttr.FindMethod(".ctor"); MutationHelper.ReplacePlaceholder(ctor, arg => { var invCompiled = new List <Instruction>(); new CodeGen(arg, ctor, invCompiled).GenerateCIL(inverse); return(invCompiled.ToArray()); }); keyAttrs[index] = Tuple.Create(injectedAttr, expCompiled); ctx.Module.AddAsNonNestedType(injectedAttr); foreach (IDnlibDef def in injectedAttr.FindDefinitions()) { if (def.Name == "GetHashCode") { ctx.Name.MarkHelper(def, ctx.Marker, ctx.Protection); ((MethodDef)def).Access = MethodAttributes.Public; } else { ctx.Name.MarkHelper(def, ctx.Marker, ctx.Protection); } } } return(keyAttrs[index].Item1); }
public Instruction[] EmitDecode(MethodDef init, RPContext ctx, Instruction[] arg) { Tuple <MethodDef, Func <int, int> > key = GetKey(ctx, init); var repl = new List <Instruction>(); repl.AddRange(arg); repl.Add(Instruction.Create(OpCodes.Call, key.Item1)); return(repl.ToArray()); }
static ITypeDefOrRef Import(RPContext ctx, TypeDef typeDef) { ITypeDefOrRef retTypeRef = new Importer(ctx.Module, ImporterOptions.TryToUseTypeDefs).Import(typeDef); if (typeDef.Module != ctx.Module && ctx.Context.Modules.Contains((ModuleDefMD)typeDef.Module)) { ctx.Name.AddReference(typeDef, new TypeRefReference((TypeRef)retTypeRef, typeDef)); } return(retTypeRef); }
public Instruction[] EmitDecode(MethodDef init, RPContext ctx, Instruction[] arg) { Tuple <Expression, Func <int, int> > key = GetKey(ctx, init); var invCompiled = new List <Instruction>(); new CodeGen(arg, ctx.Method, invCompiled).GenerateCIL(key.Item1); init.Body.MaxStack += (ushort)ctx.Depth; return(invCompiled.ToArray()); }
Tuple <Expression, Func <int, int> > GetKey(RPContext ctx, MethodDef init) { Tuple <Expression, Func <int, int> > ret; if (!keys.TryGetValue(init, out ret)) { Func <int, int> keyFunc; Expression inverse; Compile(ctx, init.Body, out keyFunc, out inverse); keys[init] = ret = Tuple.Create(inverse, keyFunc); } return(ret); }
Tuple <MethodDef, Func <int, int> > GetKey(RPContext ctx, MethodDef init) { Tuple <MethodDef, Func <int, int> > ret; if (!keys.TryGetValue(init, out ret)) { Func <int, int> keyFunc; MethodDef native; Compile(ctx, out keyFunc, out native); keys[init] = ret = Tuple.Create(native, keyFunc); } return(ret); }
static int?TraceBeginning(RPContext ctx, int index, int argCount) { if (ctx.BranchTargets.Contains(ctx.Body.Instructions[index])) { return(null); } int currentStack = argCount; int currentIndex = index; while (currentStack > 0) { currentIndex--; Instruction currentInstr = ctx.Body.Instructions[currentIndex]; // Disrupt stack analysis :/ Used by array initializer if (currentInstr.OpCode == OpCodes.Pop || currentInstr.OpCode == OpCodes.Dup) { return(null); } // No branch instr. switch (currentInstr.OpCode.FlowControl) { case FlowControl.Call: case FlowControl.Break: case FlowControl.Meta: case FlowControl.Next: break; default: return(null); } int push, pop; currentInstr.CalculateStackUsage(out push, out pop); currentStack += pop; currentStack -= push; // No branch target if (ctx.BranchTargets.Contains(currentInstr) && currentStack != 0) { return(null); } } if (currentStack < 0) { return(null); } return(currentIndex); }
InitMethodDesc GetInitMethod(RPContext ctx, IRPEncoding encoding) { InitMethodDesc[] initDescs; if (!inits.TryGetValue(encoding, out initDescs)) { inits[encoding] = initDescs = new InitMethodDesc[ctx.InitCount]; } int index = ctx.Random.NextInt32(initDescs.Length); if (initDescs[index] == null) { TypeDef rtType = ctx.Context.Registry.GetService <IRuntimeService>().GetRuntimeType("DotProtect.Runtime.RefProxyStrong"); MethodDef injectedMethod = InjectHelper.Inject(rtType.FindMethod("Initialize"), ctx.Module); ctx.Module.GlobalType.Methods.Add(injectedMethod); injectedMethod.Access = MethodAttributes.PrivateScope; injectedMethod.Name = ctx.Name.RandomName(); ctx.Name.SetCanRename(injectedMethod, false); ctx.Marker.Mark(injectedMethod, ctx.Protection); var desc = new InitMethodDesc { Method = injectedMethod }; // Field name has five bytes, each bytes has different order & meaning int[] order = Enumerable.Range(0, 5).ToArray(); ctx.Random.Shuffle(order); desc.OpCodeIndex = order[4]; desc.TokenNameOrder = new int[4]; Array.Copy(order, 0, desc.TokenNameOrder, 0, 4); desc.TokenByteOrder = Enumerable.Range(0, 4).Select(x => x * 8).ToArray(); ctx.Random.Shuffle(desc.TokenByteOrder); var keyInjection = new int[9]; Array.Copy(desc.TokenNameOrder, 0, keyInjection, 0, 4); Array.Copy(desc.TokenByteOrder, 0, keyInjection, 4, 4); keyInjection[8] = desc.OpCodeIndex; MutationHelper.InjectKeys(injectedMethod, Enumerable.Range(0, 9).ToArray(), keyInjection); // Encoding MutationHelper.ReplacePlaceholder(injectedMethod, arg => { return(encoding.EmitDecode(injectedMethod, ctx, arg)); }); desc.Encoding = encoding; initDescs[index] = desc; } return(initDescs[index]); }
void ProcessInvoke(RPContext ctx, int instrIndex, int argBeginIndex) { Instruction instr = ctx.Body.Instructions[instrIndex]; var target = (IMethod)instr.Operand; MethodSig sig = CreateProxySignature(ctx, target, instr.OpCode.Code == Code.Newobj); TypeDef delegateType = GetDelegateType(ctx, sig); Tuple <Code, IMethod, IRPEncoding> key = Tuple.Create(instr.OpCode.Code, target, ctx.EncodingHandler); Tuple <FieldDef, MethodDef> proxy; if (!fields.TryGetValue(key, out proxy)) { // Create proxy field proxy = new Tuple <FieldDef, MethodDef>(CreateField(ctx, delegateType), null); fields[key] = proxy; } // Insert field load & replace instruction if (argBeginIndex == instrIndex) { ctx.Body.Instructions.Insert(instrIndex + 1, new Instruction(OpCodes.Call, delegateType.FindMethod("Invoke"))); instr.OpCode = OpCodes.Ldsfld; instr.Operand = proxy.Item1; } else { Instruction argBegin = ctx.Body.Instructions[argBeginIndex]; ctx.Body.Instructions.Insert(argBeginIndex + 1, new Instruction(argBegin.OpCode, argBegin.Operand)); argBegin.OpCode = OpCodes.Ldsfld; argBegin.Operand = proxy.Item1; instr.OpCode = OpCodes.Call; instr.Operand = delegateType.FindMethod("Invoke"); } var targetDef = target.ResolveMethodDef(); if (targetDef != null) { ctx.Context.Annotations.Set(targetDef, ReferenceProxyProtection.Targeted, ReferenceProxyProtection.Targeted); } }
void Compile(RPContext ctx, out Func <int, int> expCompiled, out MethodDef native) { var var = new Variable("{VAR}"); var result = new Variable("{RESULT}"); CorLibTypeSig int32 = ctx.Module.CorLibTypes.Int32; native = new MethodDefUser(ctx.Context.Registry.GetService <INameService>().RandomName(), MethodSig.CreateStatic(int32, int32), MethodAttributes.PinvokeImpl | MethodAttributes.PrivateScope | MethodAttributes.Static); native.ImplAttributes = MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | MethodImplAttributes.PreserveSig; ctx.Module.GlobalType.Methods.Add(native); ctx.Context.Registry.GetService <IMarkerService>().Mark(native, ctx.Protection); ctx.Context.Registry.GetService <INameService>().SetCanRename(native, false); x86Register?reg; var codeGen = new x86CodeGen(); Expression expression, inverse; do { ctx.DynCipher.GenerateExpressionPair( ctx.Random, new VariableExpression { Variable = var }, new VariableExpression { Variable = result }, ctx.Depth, out expression, out inverse); reg = codeGen.GenerateX86(inverse, (v, r) => { return(new[] { x86Instruction.Create(x86OpCode.POP, new x86RegisterOperand(r)) }); }); } while (reg == null); byte[] code = CodeGenUtils.AssembleCode(codeGen, reg.Value); expCompiled = new DMCodeGen(typeof(int), new[] { Tuple.Create("{VAR}", typeof(int)) }) .GenerateCIL(expression) .Compile <Func <int, int> >(); nativeCodes.Add(Tuple.Create(native, code, (MethodBody)null)); if (!addedHandler) { ctx.Context.CurrentModuleWriterListener.OnWriterEvent += InjectNativeCode; addedHandler = true; } }
static RPContext ParseParameters(ModuleDef module, DotProtectContext context, ProtectionParameters parameters, RPStore store) { var ret = new RPContext(); ret.Depth = parameters.GetParameter(context, module, "depth", 3); ret.InitCount = parameters.GetParameter(context, module, "initCount", 0x10); ret.Random = store.random; ret.Module = module; ret.Context = context; ret.Marker = context.Registry.GetService <IMarkerService>(); ret.DynCipher = context.Registry.GetService <IDynCipherService>(); ret.Name = context.Registry.GetService <INameService>(); ret.Delegates = store.delegates; return(ret); }
public Instruction[] EmitDecode(MethodDef init, RPContext ctx, Instruction[] arg) { Tuple <int, int> key = GetKey(ctx.Random, init); var ret = new List <Instruction>(); if (ctx.Random.NextBoolean()) { ret.Add(Instruction.Create(OpCodes.Ldc_I4, key.Item1)); ret.AddRange(arg); } else { ret.AddRange(arg); ret.Add(Instruction.Create(OpCodes.Ldc_I4, key.Item1)); } ret.Add(Instruction.Create(OpCodes.Mul)); return(ret.ToArray()); }
protected static MethodSig CreateProxySignature(RPContext ctx, IMethod method, bool newObj) { ModuleDef module = ctx.Module; if (newObj) { Debug.Assert(method.MethodSig.HasThis); Debug.Assert(method.Name == ".ctor"); TypeSig[] paramTypes = method.MethodSig.Params.Select(type => { if (ctx.TypeErasure && type.IsClassSig && method.MethodSig.HasThis) { return(module.CorLibTypes.Object); } return(type); }).ToArray(); TypeSig retType; if (ctx.TypeErasure) // newobj will not be used with value types { retType = module.CorLibTypes.Object; } else { TypeDef declType = method.DeclaringType.ResolveTypeDefThrow(); retType = Import(ctx, declType).ToTypeSig(); } return(MethodSig.CreateStatic(retType, paramTypes)); } else { IEnumerable <TypeSig> paramTypes = method.MethodSig.Params.Select(type => { if (ctx.TypeErasure && type.IsClassSig && method.MethodSig.HasThis) { return(module.CorLibTypes.Object); } return(type); }); if (method.MethodSig.HasThis && !method.MethodSig.ExplicitThis) { TypeDef declType = method.DeclaringType.ResolveTypeDefThrow(); if (ctx.TypeErasure && !declType.IsValueType) { paramTypes = new[] { module.CorLibTypes.Object } }
void Compile(RPContext ctx, CilBody body, out Func <int, int> expCompiled, out Expression inverse) { var var = new Variable("{VAR}"); var result = new Variable("{RESULT}"); Expression expression; ctx.DynCipher.GenerateExpressionPair( ctx.Random, new VariableExpression { Variable = var }, new VariableExpression { Variable = result }, ctx.Depth, out expression, out inverse); expCompiled = new DMCodeGen(typeof(int), new[] { Tuple.Create("{VAR}", typeof(int)) }) .GenerateCIL(expression) .Compile <Func <int, int> >(); }
FieldDef CreateField(RPContext ctx, TypeDef delegateType) { // Details will be filled in during metadata writing TypeDef randomType; do { randomType = ctx.Module.Types[ctx.Random.NextInt32(ctx.Module.Types.Count)]; } while (randomType.HasGenericParameters || randomType.IsGlobalModuleType || randomType.IsDelegate()); TypeSig fieldType = new CModOptSig(randomType, delegateType.ToTypeSig()); var field = new FieldDefUser("", new FieldSig(fieldType), FieldAttributes.Static | FieldAttributes.Assembly); field.CustomAttributes.Add(new CustomAttribute(GetKeyAttr(ctx).FindInstanceConstructors().First())); delegateType.Fields.Add(field); ctx.Marker.Mark(field, ctx.Protection); ctx.Name.SetCanRename(field, false); return(field); }
public override void Finalize(RPContext ctx) { foreach (var field in fields) { InitMethodDesc init = GetInitMethod(ctx, field.Key.Item3); byte opKey; do { // No zero bytes opKey = ctx.Random.NextByte(); } while (opKey == (byte)field.Key.Item1); TypeDef delegateType = field.Value.Item1.DeclaringType; MethodDef cctor = delegateType.FindOrCreateStaticConstructor(); cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, init.Method)); cctor.Body.Instructions.Insert(0, Instruction.CreateLdcI4(opKey)); cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldtoken, field.Value.Item1)); fieldDescs.Add(new FieldDesc { Field = field.Value.Item1, OpCode = field.Key.Item1, Method = field.Key.Item2, OpKey = opKey, InitDesc = init }); } foreach (TypeDef delegateType in ctx.Delegates.Values) { MethodDef cctor = delegateType.FindOrCreateStaticConstructor(); ctx.Marker.Mark(cctor, ctx.Protection); ctx.Name.SetCanRename(cctor, false); } ctx.Context.CurrentModuleWriterOptions.MetaDataOptions.Flags |= MetaDataFlags.PreserveExtraSignatureData; ctx.Context.CurrentModuleWriterListener.OnWriterEvent += EncodeField; encodeCtx = ctx; }
MethodDef CreateBridge(RPContext ctx, TypeDef delegateType, FieldDef field, MethodSig sig) { var method = new MethodDefUser(ctx.Name.RandomName(), sig); method.Attributes = MethodAttributes.PrivateScope | MethodAttributes.Static; method.ImplAttributes = MethodImplAttributes.Managed | MethodImplAttributes.IL; method.Body = new CilBody(); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, field)); for (int i = 0; i < method.Parameters.Count; i++) { method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg, method.Parameters[i])); } method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, delegateType.FindMethod("Invoke"))); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); delegateType.Methods.Add(method); ctx.Context.Registry.GetService <IMarkerService>().Mark(method, ctx.Protection); ctx.Name.SetCanRename(method, false); return(method); }
RPContext ParseParameters(MethodDef method, DotProtectContext context, ProtectionParameters parameters, RPStore store) { var ret = new RPContext(); ret.Mode = parameters.GetParameter(context, method, "mode", Mode.Mild); ret.Encoding = parameters.GetParameter(context, method, "encoding", EncodingType.Normal); ret.InternalAlso = parameters.GetParameter(context, method, "internal", false); ret.TypeErasure = parameters.GetParameter(context, method, "typeErasure", false); ret.Depth = parameters.GetParameter(context, method, "depth", 3); ret.Module = method.Module; ret.Method = method; ret.Body = method.Body; ret.BranchTargets = new HashSet <Instruction>( method.Body.Instructions .Select(instr => instr.Operand as Instruction) .Concat(method.Body.Instructions .Where(instr => instr.Operand is Instruction[]) .SelectMany(instr => (Instruction[])instr.Operand)) .Where(target => target != null)); ret.Protection = (ReferenceProxyProtection)Parent; ret.Random = store.random; ret.Context = context; ret.Marker = context.Registry.GetService <IMarkerService>(); ret.DynCipher = context.Registry.GetService <IDynCipherService>(); ret.Name = context.Registry.GetService <INameService>(); ret.Delegates = store.delegates; switch (ret.Mode) { case Mode.Mild: ret.ModeHandler = store.mild ?? (store.mild = new MildMode()); break; case Mode.Strong: ret.ModeHandler = store.strong ?? (store.strong = new StrongMode()); break; default: throw new UnreachableException(); } switch (ret.Encoding) { case EncodingType.Normal: ret.EncodingHandler = store.normal ?? (store.normal = new NormalEncoding()); break; case EncodingType.Expression: ret.EncodingHandler = store.expression ?? (store.expression = new ExpressionEncoding()); break; case EncodingType.x86: ret.EncodingHandler = store.x86 ?? (store.x86 = new x86Encoding()); if ((context.CurrentModule.Cor20HeaderFlags & ComImageFlags.ILOnly) != 0) { context.CurrentModuleWriterOptions.Cor20HeaderOptions.Flags &= ~ComImageFlags.ILOnly; } break; default: throw new UnreachableException(); } return(ret); }
void ProcessMethod(RPContext ctx) { for (int i = 0; i < ctx.Body.Instructions.Count; i++) { Instruction instr = ctx.Body.Instructions[i]; if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt || instr.OpCode.Code == Code.Newobj) { var operand = (IMethod)instr.Operand; var def = operand.ResolveMethodDef(); if (def != null && ctx.Context.Annotations.Get <object>(def, ReferenceProxyProtection.TargetExcluded) != null) { return; } // Call constructor if (instr.OpCode.Code != Code.Newobj && operand.Name == ".ctor") { continue; } // Internal reference option if (operand is MethodDef && !ctx.InternalAlso) { continue; } // No generic methods if (operand is MethodSpec) { continue; } // No generic types / array types if (operand.DeclaringType is TypeSpec) { continue; } // No varargs if (operand.MethodSig.ParamsAfterSentinel != null && operand.MethodSig.ParamsAfterSentinel.Count > 0) { continue; } TypeDef declType = operand.DeclaringType.ResolveTypeDefThrow(); // No delegates if (declType.IsDelegate()) { continue; } // No instance value type methods if (declType.IsValueType && operand.MethodSig.HasThis) { continue; } // No prefixed call if (i - 1 >= 0 && ctx.Body.Instructions[i - 1].OpCode.OpCodeType == OpCodeType.Prefix) { continue; } ctx.ModeHandler.ProcessCall(ctx, i); } } }
public int Encode(MethodDef init, RPContext ctx, int value) { Tuple <MethodDef, Func <int, int> > key = GetKey(ctx, init); return(key.Item2(value)); }
void ProcessBridge(RPContext ctx, int instrIndex) { Instruction instr = ctx.Body.Instructions[instrIndex]; var target = (IMethod)instr.Operand; TypeDef declType = target.DeclaringType.ResolveTypeDefThrow(); if (!declType.Module.IsILOnly) // Reflection doesn't like mixed mode modules. { return; } if (declType.IsGlobalModuleType) // Reflection doesn't like global methods too. { return; } Tuple <Code, IMethod, IRPEncoding> key = Tuple.Create(instr.OpCode.Code, target, ctx.EncodingHandler); Tuple <FieldDef, MethodDef> proxy; if (fields.TryGetValue(key, out proxy)) { if (proxy.Item2 != null) { instr.OpCode = OpCodes.Call; instr.Operand = proxy.Item2; return; } } else { proxy = new Tuple <FieldDef, MethodDef>(null, null); } MethodSig sig = CreateProxySignature(ctx, target, instr.OpCode.Code == Code.Newobj); TypeDef delegateType = GetDelegateType(ctx, sig); // Create proxy field if (proxy.Item1 == null) { proxy = new Tuple <FieldDef, MethodDef>( CreateField(ctx, delegateType), proxy.Item2); } // Create proxy bridge Debug.Assert(proxy.Item2 == null); proxy = new Tuple <FieldDef, MethodDef>( proxy.Item1, CreateBridge(ctx, delegateType, proxy.Item1, sig)); fields[key] = proxy; // Replace instruction instr.OpCode = OpCodes.Call; instr.Operand = proxy.Item2; var targetDef = target.ResolveMethodDef(); if (targetDef != null) { ctx.Context.Annotations.Set(targetDef, ReferenceProxyProtection.Targeted, ReferenceProxyProtection.Targeted); } }
public override void Finalize(RPContext ctx) { }
public override void ProcessCall(RPContext ctx, int instrIndex) { Instruction invoke = ctx.Body.Instructions[instrIndex]; var target = (IMethod)invoke.Operand; // Value type proxy is not supported in mild mode. if (target.DeclaringType.ResolveTypeDefThrow().IsValueType) { return; } // Skipping visibility is not supported in mild mode. if (!target.ResolveThrow().IsPublic&& !target.ResolveThrow().IsAssembly) { return; } Tuple <Code, TypeDef, IMethod> key = Tuple.Create(invoke.OpCode.Code, ctx.Method.DeclaringType, target); MethodDef proxy; if (!proxies.TryGetValue(key, out proxy)) { MethodSig sig = CreateProxySignature(ctx, target, invoke.OpCode.Code == Code.Newobj); proxy = new MethodDefUser(ctx.Name.RandomName(), sig); proxy.Attributes = MethodAttributes.PrivateScope | MethodAttributes.Static; proxy.ImplAttributes = MethodImplAttributes.Managed | MethodImplAttributes.IL; ctx.Method.DeclaringType.Methods.Add(proxy); // Fix peverify --- Non-virtual call to virtual methods must be done on this pointer if (invoke.OpCode.Code == Code.Call && target.ResolveThrow().IsVirtual) { proxy.IsStatic = false; sig.HasThis = true; sig.Params.RemoveAt(0); } ctx.Marker.Mark(proxy, ctx.Protection); ctx.Name.Analyze(proxy); ctx.Name.SetCanRename(proxy, false); proxy.Body = new CilBody(); for (int i = 0; i < proxy.Parameters.Count; i++) { proxy.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg, proxy.Parameters[i])); } proxy.Body.Instructions.Add(Instruction.Create(invoke.OpCode, target)); proxy.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); proxies[key] = proxy; } invoke.OpCode = OpCodes.Call; if (ctx.Method.DeclaringType.HasGenericParameters) { var genArgs = new GenericVar[ctx.Method.DeclaringType.GenericParameters.Count]; for (int i = 0; i < genArgs.Length; i++) { genArgs[i] = new GenericVar(i); } invoke.Operand = new MemberRefUser( ctx.Module, proxy.Name, proxy.MethodSig, new GenericInstSig((ClassOrValueTypeSig)ctx.Method.DeclaringType.ToTypeSig(), genArgs).ToTypeDefOrRef()); } else { invoke.Operand = proxy; } var targetDef = target.ResolveMethodDef(); if (targetDef != null) { ctx.Context.Annotations.Set(targetDef, ReferenceProxyProtection.Targeted, ReferenceProxyProtection.Targeted); } }
public abstract void Finalize(RPContext ctx);
public int Encode(MethodDef init, RPContext ctx, int value) { Tuple <int, int> key = GetKey(ctx.Random, init); return(value * key.Item2); }
public abstract void ProcessCall(RPContext ctx, int instrIndex);