public BlockStatement ConvertBlock(Instruction[] instructions, Stack <Expression> stack, out int consumed) { var statements = new List <Statement>(); for (consumed = 0; consumed < instructions.Length; consumed++) { Instruction instruction = instructions[consumed]; if (instruction == null) { Console.WriteLine($"Unimplemented instruction! Stack: {stack.Count}"); continue; } if (Jumper.IsValid(instruction.OP)) { throw new NotImplementedException("Control Flow handling is still under construction."); } Statement stmt = instruction.AcceptVisitor(this, stack); if (stmt != null) { statements.Add(stmt); } } return(new BlockStatement(statements)); }
public void RemoveRange(int index, int count) { if ((index + count) <= _instructions.Count) { for (int i = 0; i < count; i++) { ASInstruction instruction = _instructions[index]; _indices.Remove(instruction); _instructions.RemoveAt(index); List <ASInstruction> group = _opGroups[instruction.OP]; if (group.Count == 1) { _opGroups.Remove(instruction.OP); } else { group.Remove(instruction); } // TODO: Recalibrate switch exits. Jumper entry = GetJumperEntry(instruction); if (entry != null) { if (index != _instructions.Count) { ASInstruction exit = _instructions[index]; JumpExits[entry] = exit; } else { JumpExits[entry] = null; } } if (Jumper.IsValid(instruction.OP)) { JumpExits.Remove((Jumper)instruction); } else if (instruction.OP == OPCode.LookUpSwitch) { SwitchExits.Remove((LookUpSwitchIns)instruction); } } for (int i = index; i < _indices.Count; i++) { ASInstruction toPull = _instructions[i]; _indices[toPull] -= count; } } }
public static ControlFlowGraph Create(LingoCode code) { //TODO: Remove temp comment rambling //TODO: For later tranforms, check BBs which are "pure" and only exist as a exit and as "a jumper". BB.Body.Count == 1 //TODO: Number the blocks topologically or nah? BB.Ordinal? var builder = new ControlFlowGraphBuilder(); var forwardBranches = new Dictionary <Instruction, BasicBlock>(code.JumpExits.Count); var backEdges = new Dictionary <Jumper, BasicBlock>(); Range currentBlockRange = default; //0..0 for (int i = 0; i < code.Count; i++) { Instruction instruction = code[i]; //We are hunting here for those dangling JumpExits. If we find one which is a back-edge exit, we break and leave that to be targeted for future. if (code.JumpExits.ContainsValue(instruction)) //TODO: if the jumpExit OP = Return -> BBKind.Exit, or have another round for transformations like that? //TODO: Check how much ContainsValue slows this down { currentBlockRange = currentBlockRange.End..i; if (!currentBlockRange.Equals(i..i)) { builder.CurrentBlock.Body = code[currentBlockRange]; } if (forwardBranches.TryGetValue(instruction, out var successor)) { //Resolving the forward-edge if (builder.CurrentBlock.FallThrough == null) //TODO: Doesn't feel right? Maybe abuse the shit out of CurrentBlock getter-setter? { builder.AppendBlock(successor); //If } else { builder.AddBlock(successor); //usually If-Else } } else { //Back-edge target var nextBlock = new BasicBlock(); backEdges.Add(code.GetJumperEntry(instruction), nextBlock); builder.AppendBlock(nextBlock); } } if (Jumper.IsValid(instruction.OP)) { Jumper jumper = (Jumper)instruction; currentBlockRange = currentBlockRange.End..(i + 1); builder.CurrentBlock.Body = code[currentBlockRange]; if (code.IsBackwardsJump(jumper)) { //Fallthrough back-edge jumper builder.LinkBlocks(builder.CurrentBlock, backEdges[jumper]); } else { //Forward jump to successor if (!forwardBranches.TryGetValue(code.JumpExits[jumper], out BasicBlock successor)) { forwardBranches.Add(code.JumpExits[jumper], successor = new BasicBlock()); } builder.LinkBlocks(builder.CurrentBlock, successor); if (Jumper.IsConditional(instruction.OP)) { builder.AppendBlock(new BasicBlock(), isConditionalBranch: true); } } } } currentBlockRange = currentBlockRange.End..code.Count; builder.CurrentBlock.Body = code[currentBlockRange]; builder.AppendBlock(new BasicBlock(BasicBlockKind.Exit)); //TODO: return(new ControlFlowGraph() { Blocks = builder.Blocks }); }
public override void WriteTo(FlashWriter output) { var marks = new Dictionary <ASInstruction, long>(); var sharedExits = new Dictionary <ASInstruction, List <ASInstruction> >(); var forwardCaseExits = new Dictionary <ASInstruction, Tuple <LookUpSwitchIns, int> >(); foreach (ASInstruction instruction in _instructions) { long previousPosition = output.Position; marks.Add(instruction, previousPosition); instruction.WriteTo(output); List <ASInstruction> jumpers = null; if (sharedExits.TryGetValue(instruction, out jumpers)) { foreach (ASInstruction jumper in jumpers) { long position = marks[jumper]; var fixedOffset = (uint)(previousPosition - (position + 4)); ((Jumper)jumper).Offset = fixedOffset; Rewrite(output, jumper, position); } sharedExits.Remove(instruction); } Tuple <LookUpSwitchIns, int> switchIdentity = null; if (forwardCaseExits.TryGetValue(instruction, out switchIdentity)) { int caseIndex = switchIdentity.Item2; LookUpSwitchIns lookUpSwitch = switchIdentity.Item1; long position = marks[lookUpSwitch]; var fixedOffset = (uint)(previousPosition - position); if (caseIndex == lookUpSwitch.CaseOffsets.Count) { lookUpSwitch.DefaultOffset = fixedOffset; } else { lookUpSwitch.CaseOffsets[caseIndex] = fixedOffset; } Rewrite(output, lookUpSwitch, position); } if (instruction.OP == OPCode.LookUpSwitch) { bool requiresRewrite = false; var lookUpSwitch = (LookUpSwitchIns)instruction; ASInstruction[] cases = SwitchExits[lookUpSwitch]; for (int i = 0; i < cases.Length; i++) { ASInstruction exit = cases[i]; if (exit.OP != OPCode.Label) { forwardCaseExits.Add(exit, Tuple.Create(lookUpSwitch, i)); } else { requiresRewrite = true; long exitPosition = marks[exit]; long jumpCount = (previousPosition - (exitPosition + 1)); uint fixedOffset = (uint)(uint.MaxValue - jumpCount); if (i == (cases.Length - 1)) { lookUpSwitch.DefaultOffset = fixedOffset; } else { lookUpSwitch.CaseOffsets[i] = fixedOffset; } } } if (requiresRewrite) { Rewrite(output, lookUpSwitch, previousPosition); } } else if (Jumper.IsValid(instruction.OP)) { var jumper = (Jumper)instruction; if (jumper.Offset == 0) { continue; } ASInstruction exit = null; if (!JumpExits.TryGetValue(jumper, out exit)) { // An exit for this jump instruction could not be found, perhaps its' offset exceeds the index limit? continue; } else if (exit.OP != OPCode.Label || !marks.ContainsKey(exit)) // Forward jumps can have a label for an exit, as long as it is located ahead. { jumpers = null; if (!sharedExits.TryGetValue(exit, out jumpers)) { jumpers = new List <ASInstruction>(); sharedExits.Add(exit, jumpers); } jumpers.Add(jumper); } else // Backward jumps must always have a label for an exit, and must be behind this instruction. { long exitPosition = marks[exit]; long jumpCount = (output.Position - (exitPosition + 1)); var fixedOffset = (uint)(uint.MaxValue - jumpCount); jumper.Offset = fixedOffset; Rewrite(output, jumper, previousPosition); } } } }
private void LoadInstructions() { var caseIndices = new Dictionary <long, int>(); var marks = new Dictionary <long, ASInstruction>(); var sharedExits = new Dictionary <long, List <Jumper> >(); var forwardCaseExits = new Dictionary <long, LookUpSwitchIns>(); using (var input = new FlashReader(_body.Code)) { while (input.IsDataAvailable) { long previousPosition = input.Position; var instruction = ASInstruction.Create(_abc, input); marks[previousPosition] = instruction; _indices.Add(instruction, _indices.Count); _instructions.Add(instruction); List <ASInstruction> instructions = null; if (!_opGroups.TryGetValue(instruction.OP, out instructions)) { instructions = new List <ASInstruction>(); _opGroups.Add(instruction.OP, instructions); } instructions.Add(instruction); List <Jumper> jumpers = null; if (sharedExits.TryGetValue(previousPosition, out jumpers)) { // This is an exit position for a jump instruction, or more. foreach (Jumper jumper in jumpers) { JumpExits.Add(jumper, instruction); } sharedExits.Remove(previousPosition); } LookUpSwitchIns lookUpSwitch = null; if (forwardCaseExits.TryGetValue(previousPosition, out lookUpSwitch)) { int index = caseIndices[previousPosition]; SwitchExits[lookUpSwitch][index] = instruction; caseIndices.Remove(previousPosition); forwardCaseExits.Remove(previousPosition); } if (instruction.OP == OPCode.LookUpSwitch) { lookUpSwitch = (LookUpSwitchIns)instruction; var offsets = new List <uint>(lookUpSwitch.CaseOffsets); offsets.Add(lookUpSwitch.DefaultOffset); var cases = new ASInstruction[offsets.Count]; for (int i = 0; i < offsets.Count; i++) { ASInstruction exit = null; long exitPosition = (previousPosition + offsets[i]); if (exitPosition <= input.Length) { caseIndices.Add(exitPosition, i); forwardCaseExits.Add(exitPosition, lookUpSwitch); } else { cases[i] = exit; exit = marks[(exitPosition - uint.MaxValue) - 1]; } } SwitchExits.Add(lookUpSwitch, cases); } else if (Jumper.IsValid(instruction.OP)) { var jumper = (Jumper)instruction; if (jumper.Offset == 0) { continue; } long exitPosition = (input.Position + jumper.Offset); if (exitPosition == input.Length) { // Jump exit does not exist at this (non-existent)index, do not look for exit. continue; } else if (exitPosition < input.Length) // Forward jump. { jumpers = null; if (!sharedExits.TryGetValue(exitPosition, out jumpers)) { jumpers = new List <Jumper>(); sharedExits.Add(exitPosition, jumpers); } jumpers.Add(jumper); } else // Backwards jump. { var label = (LabelIns)marks[(exitPosition - uint.MaxValue) - 1]; JumpExits.Add(jumper, label); } } } } }
public void Deobfuscate() { var machine = new ASMachine(this, _body.LocalCount); var cleaned = new List <ASInstruction>(_instructions.Count); var valuePushers = new Stack <ASInstruction>(_body.MaxStack); var localReferences = new Dictionary <int, List <ASInstruction> >(); var swappedValues = new Dictionary <ASInstruction, ASInstruction[]>(); KeyValuePair <Jumper, ASInstruction>[] jumpExits = JumpExits.ToArray(); KeyValuePair <LookUpSwitchIns, ASInstruction[]>[] switchExits = SwitchExits.ToArray(); for (int i = 0; i < _instructions.Count; i++) { ASInstruction instruction = _instructions[i]; if (Jumper.IsValid(instruction.OP)) { i += GetFinalJumpCount(machine, (Jumper)instruction, cleaned, localReferences, valuePushers); } else { instruction.Execute(machine); #region Arithmetic Optimization if (Computation.IsValid(instruction.OP)) { object result = machine.Values.Pop(); ASInstruction rightPusher = valuePushers.Pop(); ASInstruction leftPusher = valuePushers.Pop(); if (!Local.IsValid(leftPusher.OP) && !Local.IsValid(rightPusher.OP) && result != null) { // Constant values found, push result instead of having it do the calculation everytime. cleaned.Remove(leftPusher); cleaned.Remove(rightPusher); instruction = Primitive.Create(_abc, result); foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in jumpExits) { if (leftPusher == jumpExit.Value) { JumpExits[jumpExit.Key] = instruction; // Do not break, another jump instruction can share the same exit. } } foreach (KeyValuePair <LookUpSwitchIns, ASInstruction[]> switchExit in switchExits) { ASInstruction[] cases = switchExit.Value; for (int j = 0; j < cases.Length; j++) { ASInstruction exit = cases[j]; if (leftPusher == exit) { cases[j] = instruction; } } } instruction.Execute(machine); } else { // Do not attempt to optimize when a local is being use, as these values can change. valuePushers.Push(leftPusher); valuePushers.Push(rightPusher); machine.Values.Push(result); } } #endregion cleaned.Add(instruction); ASInstruction[] swaps = null; List <ASInstruction> references = null; if (Local.IsValid(instruction.OP)) { var local = (Local)instruction; if (!localReferences.TryGetValue(local.Register, out references)) { references = new List <ASInstruction>(); localReferences[local.Register] = references; } references.Add(instruction); } else if (instruction.OP == OPCode.Swap) { swaps = new ASInstruction[2]; swappedValues[instruction] = swaps; } int popCount = instruction.GetPopCount(); for (int j = 0; j < popCount; j++) { ASInstruction pusher = valuePushers.Pop(); references?.Add(pusher); if (swaps != null) { swaps[j] = pusher; } } int pushCount = instruction.GetPushCount(); for (int j = 0; j < pushCount; j++) { valuePushers.Push(instruction); } } } // Remove dead locals. foreach (KeyValuePair <int, List <ASInstruction> > local in localReferences) { if (local.Key == 0) { continue; // Scope } if (local.Key <= (_body.Method.Parameters.Count)) { continue; // Register == Param #(Non-zero based) } List <ASInstruction> references = localReferences[local.Key]; bool isNeeded = false; foreach (ASInstruction reference in references) { // This checks if the local is being referenced by something else that retreives the value. if (Local.IsValid(reference.OP) && !Local.IsSetLocal(reference.OP)) { isNeeded = true; break; } } if (!isNeeded) { foreach (ASInstruction reference in references) { if (reference.OP == OPCode.Swap) { ASInstruction[] swaps = swappedValues[reference]; foreach (ASInstruction swap in swaps) { cleaned.Remove(swap); } } cleaned.Remove(reference); } } } jumpExits = JumpExits.ToArray(); // Global property could have been updated. foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in jumpExits) { if (IndexOf(jumpExit.Key) > IndexOf(jumpExit.Value)) { // This is not a forward jump instruction, since the exit appears before the jump. continue; } // True if it still has the jump instruction, but the instruction // needed to determine the final instruction to jump is not present. if (cleaned.Contains(jumpExit.Key) && !cleaned.Contains(jumpExit.Value)) { // Start at the index of the instruction that should come after the final instruction to jump. for (int j = (_indices[jumpExit.Value] + 1); j < _instructions.Count; j++) { ASInstruction afterEnd = _instructions[j]; int exitIndex = cleaned.IndexOf(afterEnd); if (exitIndex != -1) // Does this instruction exist in the cleaned output?; Otherwise, get instruction that comes after it. { JumpExits[jumpExit.Key] = cleaned[exitIndex]; break; } } } } _instructions.Clear(); _instructions.AddRange(cleaned); Recalibrate(); }