protected ILExpression CreateLabeledExpression(GMCode code) { int absolute = GMCodeUtil.getBranchOffset(CurrentRaw) + CurrentPC; ILExpression e = new ILExpression(code, GetLabel(absolute)); e.Extra = (int)(CurrentRaw & 0xFFFF); e.ILRanges.Add(new ILRange(CurrentPC, CurrentPC)); return(e); }
public static Instruction DissasembleFromReader(int address, BinaryReader r) { Instruction ret = new Instruction(address); uint raw = r.ReadUInt32(); ret.Code = GMCodeUtil.getFromRaw(raw); if (DissasembleRawCode(ret, raw)) { switch (ret.Code) { case GMCode.Call: case GMCode.Pop: ret.Operand = r.ReadInt32(); break; case GMCode.Push: { switch (ret.Types[0]) { case GM_Type.Long: ret.Operand = r.ReadInt64(); break; case GM_Type.Double: ret.Operand = r.ReadDouble(); break; case GM_Type.Float: ret.Operand = r.ReadSingle(); break; case GM_Type.Bool: ret.Operand = r.ReadInt32() != 0 ? true : false; // tested, yess this is silly break; case GM_Type.String: case GM_Type.Var: case GM_Type.Int: ret.Operand = r.ReadInt32(); break; case GM_Type.Short: break; // already read in DissasembleRawCode } } break; } Debug.Assert(ret.Operand != null); } return(ret); }
public ILValue(object o, GM_Type type) { if (o is short) { this.Value = (int)((short)o); } else if (type == GM_Type.String) { this.ValueText = GMCodeUtil.EscapeString(o as string); } else { this.Value = o; } Type = type; }
void WriteOperand(ITextOutput output, bool escapeString = true) { if (Operand == null) { output.Write("%NULL_OPERAND%"); } else if (Operand is ILLabel) { output.Write((Operand as ILLabel).Name); } else if (escapeString) { if (Operand is string) { output.Write(GMCodeUtil.EscapeString((string)Operand)); } else if (Operand is ILValue) { ILValue val = Operand as ILValue; if (escapeString && val.Type == GM_Type.String) { output.Write(val.ValueText); } else { output.Write(val.ToString()); } } else { output.Write(Operand.ToString()); } } else { output.Write(Operand.ToString()); } }
public ILValue(string i) { this.Value = i; Type = GM_Type.String; this.ValueText = GMCodeUtil.EscapeString(i as string); }
// This pass accepts index or instance values being protected override ILExpression CreateExpression(List <ILNode> list) { ILExpression e = null; OldCode nOpCode = (OldCode)(CurrentRaw >> 24); GM_Type[] types = ReadTypes(CurrentRaw); switch (nOpCode) // the bit switch { case OldCode.Conv: e = CreateExpression(GMCode.Conv, types); break; case OldCode.Popz: e = CreateExpression(GMCode.Popz, types); break; case OldCode.Mul: e = CreateExpression(GMCode.Mul, types); break; case OldCode.Div: e = CreateExpression(GMCode.Div, types); break; case OldCode.Rem: e = CreateExpression(GMCode.Rem, types); break; case OldCode.Mod: e = CreateExpression(GMCode.Mod, types); break; case OldCode.Add: e = CreateExpression(GMCode.Add, types); break; case OldCode.Sub: e = CreateExpression(GMCode.Sub, types); break; case OldCode.And: e = CreateExpression(GMCode.And, types); break; case OldCode.Or: e = CreateExpression(GMCode.Or, types); break; case OldCode.Xor: e = CreateExpression(GMCode.Xor, types); break; case OldCode.Neg: e = CreateExpression(GMCode.Neg, types); break; case OldCode.Not: e = CreateExpression(GMCode.Not, types); break; case OldCode.Sal: e = CreateExpression(GMCode.Sal, types); break; // case GMCode.S: e = CreateExpression(GMCode.Sal, types); break; // case GMCode.S: e = CreateExpression(GMCode.Sal, types); break; // case GMCode.shr: e = CreateExpression(GMCode.Saa, types); break; // hack, handle shift right case OldCode.Slt: e = CreateExpression(GMCode.Slt, types); break; case OldCode.Sle: e = CreateExpression(GMCode.Sle, types); break; case OldCode.Seq: e = CreateExpression(GMCode.Seq, types); break; case OldCode.Sne: e = CreateExpression(GMCode.Sne, types); break; case OldCode.Sge: e = CreateExpression(GMCode.Sge, types); break; case OldCode.Sgt: e = CreateExpression(GMCode.Sgt, types); break; case OldCode.Dup: e = CreateExpression(GMCode.Dup, types); e.Operand = (int)(CurrentRaw & 0xFFFF); // dup type break; case OldCode.Call: e = CreateExpression(GMCode.CallUnresolved, types); e.Operand = ILCall.CreateCall(File.Strings[r.ReadInt32()].String, (int)(CurrentRaw & 0xFFFF)); break; case OldCode.Ret: e = CreateExpression(GMCode.Ret, types); break; case OldCode.Exit: e = CreateExpression(GMCode.Exit, types); break; case OldCode.B: e = CreateLabeledExpression(GMCode.B); break; case OldCode.Bt: e = CreateLabeledExpression(GMCode.Bt); break; case OldCode.Bf: e = CreateLabeledExpression(GMCode.Bf); break; // We have to fix these to a lopp to emulate a while latter case OldCode.Pushenv: { // Debug.WriteLine("Popenv: Address: {0}, Extra: {1} {1:X8} Calc: {2}", CurrentPC, CurrentRaw, GMCodeUtil.getBranchOffset(CurrentRaw)); int sextra = CurrentPC + GMCodeUtil.getBranchOffset(CurrentRaw); e = CreateExpression(GMCode.Pushenv, types, GetLabel(sextra + 1)); // we are one instruction after the pop pushEnviroment.Add(sextra, GetLabel(CurrentPC)); // record the pop position } break; case OldCode.Popenv: { // We convert this to a Branch so the loop detecter will find it e = CreateExpression(GMCode.Popenv, types); // e = CreateExpression(GMCode.Popenv, types); if (CurrentRaw == 0xbcf00000) // its a break, ugh, old break code ugh { foreach (var last in list.Reverse <ILNode>().OfType <ILExpression>()) { if (last.Code == GMCode.Pushenv) { e.Operand = last.Operand; return(e); } } Debug.Assert(false); } else { // some reason its the negitive offset? // int offset = GMCodeUtil.getBranchOffset(CurrentRaw) - currentPC; ILLabel endOfEnviroment; if (pushEnviroment.TryGetValue(CurrentPC, out endOfEnviroment)) // this is the code { e.Operand = endOfEnviroment; // not a break, set the label BACK to the push as we are simulating a loop } else { throw new Exception("This MUST be a break"); } } } break; case OldCode.Pop: e = CreateExpression(GMCode.Pop, types); e.Operand = BuildUnresolvedVar(r.ReadInt32()); break; case OldCode.Push: e = CreatePushExpression(GMCode.Push, types); break; case OldCode.Break: e = CreateExpression(GMCode.Break, types); break; default: throw new Exception("Bad opcode"); } return(e); }
protected override ILExpression CreateExpression(List <ILNode> list) { ILExpression e = null; NewOpcode nOpCode = (NewOpcode)(CurrentRaw >> 24); GM_Type[] types = ReadTypes(CurrentRaw); switch (nOpCode) // the bit switch { case NewOpcode.conv: e = CreateExpression(GMCode.Conv, types); break; case NewOpcode.popz: e = CreateExpression(GMCode.Popz, types); break; case NewOpcode.mul: e = CreateExpression(GMCode.Mul, types); break; case NewOpcode.div: e = CreateExpression(GMCode.Div, types); break; case NewOpcode.rem: e = CreateExpression(GMCode.Rem, types); break; case NewOpcode.mod: e = CreateExpression(GMCode.Mod, types); break; case NewOpcode.@add: e = CreateExpression(GMCode.Add, types); break; case NewOpcode.sub: e = CreateExpression(GMCode.Sub, types); break; case NewOpcode.and: e = CreateExpression(GMCode.And, types); break; case NewOpcode.or: e = CreateExpression(GMCode.Or, types); break; case NewOpcode.xor: e = CreateExpression(GMCode.Xor, types); break; case NewOpcode.neg: e = CreateExpression(GMCode.Neg, types); break; case NewOpcode.not: e = CreateExpression(GMCode.Not, types); break; case NewOpcode.shl: e = CreateExpression(GMCode.Sal, types); break; // case NewOpcode.shr: e = CreateExpression(GMCode.Saa, types); break; // hack, handle shift right case NewOpcode.@set: switch ((CurrentRaw >> 8) & 0xFF) { case 1: e = CreateExpression(GMCode.Slt, types); break; case 2: e = CreateExpression(GMCode.Sle, types); break; case 3: e = CreateExpression(GMCode.Seq, types); break; case 4: e = CreateExpression(GMCode.Sne, types); break; case 5: e = CreateExpression(GMCode.Sge, types); break; case 6: e = CreateExpression(GMCode.Sgt, types); break; default: throw new Exception("Bad condition"); } break; case NewOpcode.dup: e = CreateExpression(GMCode.Dup, types); e.Operand = (int)(CurrentRaw & 0xFFFF); // dup type break; case NewOpcode.call: e = CreateExpression(GMCode.CallUnresolved, types); e.Operand = ILCall.CreateCall(File.Strings[r.ReadInt32()].String, (int)(CurrentRaw & 0xFFFF)); // since we can have var args on alot of functions, extra is used break; case NewOpcode.ret: e = CreateExpression(GMCode.Ret, types); break; case NewOpcode.exit: e = CreateExpression(GMCode.Exit, types); break; case NewOpcode.b: e = CreateLabeledExpression(GMCode.B); break; case NewOpcode.bt: e = CreateLabeledExpression(GMCode.Bt); break; case NewOpcode.bf: e = CreateLabeledExpression(GMCode.Bf); break; // We have to fix these to a lopp to emulate a while latter case NewOpcode.pushenv: { // Debug.WriteLine("Popenv: Address: {0}, Extra: {1} {1:X8} Calc: {2}",i.Address, raw, GMCodeUtil.getBranchOffset(raw)); int sextra = CurrentPC + GMCodeUtil.getBranchOffset(CurrentRaw); e = CreateExpression(GMCode.Pushenv, types, GetLabel(sextra + 1)); // we are one instruction after the pop pushEnviroment.Add(sextra, GetLabel(CurrentPC)); // record the pop position } break; case NewOpcode.popenv: { // We convert this to a Branch so the loop detecter will find it e = CreateExpression(GMCode.B, types); // e = CreateExpression(GMCode.Popenv, types); if (CurrentRaw == 0xBBF00000) // its a break, ugh { foreach (var last in list.Reverse <ILNode>().OfType <ILExpression>()) { if (last.Code == GMCode.Pushenv) { e.Operand = last.Operand; return(e); } } Debug.Assert(false); } else { // some reason its the negitive offset? // int offset = GMCodeUtil.getBranchOffset(CurrentRaw) - currentPC; ILLabel endOfEnviroment; if (pushEnviroment.TryGetValue(CurrentPC, out endOfEnviroment)) // this is the code { e.Operand = endOfEnviroment; // not a break, set the label BACK to the push as we are simulating a loop } else { throw new Exception("This MUST be a break"); } } } break; case NewOpcode.pop: e = CreateExpression(GMCode.Pop, types); e.Operand = BuildUnresolvedVar(r.ReadInt32()); break; case NewOpcode.pushi: Debug.Assert(types[0] == GM_Type.Short); e = CreatePushExpression(GMCode.Push, types); break; // push int? ah like a pushe case NewOpcode.push: e = CreatePushExpression(GMCode.Push, types); break; // generic push is fine right? case NewOpcode.pushl: // local e = CreatePushExpression(GMCode.Push, types); break; // local? -7 case NewOpcode.pushg: // Global e = CreatePushExpression(GMCode.Push, types); break; // global? -5 // id is the last bit? case NewOpcode.pushb: // builtin .. seeing a patern hummmm // Built in vars? always -1? e = CreatePushExpression(GMCode.Push, types); break; // case NewOpcode.call2: e = CreateExpression(GMCode.Sal, types, operand); break; case NewOpcode.@break: e = CreateExpression(GMCode.Break, types); break; default: throw new Exception("Bad opcode"); } return(e); }
// I had multipul passes on this so trying to combine it all to do one pass public static SortedList <int, Instruction> Dissasemble(BinaryReader r, long length, List <string> StringList, List <string> InstanceList = null) { if (r == null) { throw new ArgumentNullException("stream"); } if (r.BaseStream.CanRead != true) { throw new IOException("Cannot read stream"); } int pc = 0; long lastpc = r.BaseStream.Length / 4; SortedList <int, Instruction> list = new SortedList <int, Instruction>(); Dictionary <int, Label> labels = new Dictionary <int, Label>(); Dictionary <int, Label> pushEnviroment = new Dictionary <int, Label>(); List <Instruction> branches = new List <Instruction>(); Func <Instruction, int, Label> GiveInstructionALabel = (Instruction inst, int laddress) => { Label label; if (!labels.TryGetValue(laddress, out label)) { labels.Add(laddress, label = new Label(laddress)); } inst.Operand = label; branches.Add(inst); // add it to the branches to handle latter return(label); }; Instruction prev = null; StringBuilder sb = new StringBuilder(50); while (r.BaseStream.Position < length) { Instruction i = DissasembleFromReader(pc, r); pc += i.Size; switch (i.Code) { case GMCode.Pushenv: { Label endOfEnviroment = GiveInstructionALabel(i, i.Extra + 1); // skip the pop i.Operand = endOfEnviroment; pushEnviroment.Add(i.Address, endOfEnviroment); branches.Add(i); } break; case GMCode.Popenv: { Label endOfEnviroment; if (pushEnviroment.TryGetValue(i.Extra - 1, out endOfEnviroment)) { i.Operand = GiveInstructionALabel(i, i.Extra - 1); // not a break, set the label BACK to the push as we are simulating a loop } else { foreach (Instruction ii in list.Values.Reverse()) { if (ii.Code == GMCode.Pushenv) { i.Operand = GiveInstructionALabel(i, ii.Address); // we want to make these continues break; } } } branches.Add(i); Debug.Assert(i.Operand != null); } break; case GMCode.Bf: case GMCode.Bt: case GMCode.B: GiveInstructionALabel(i, i.Extra); break; case GMCode.Push: if (i.Types[0] == GM_Type.Var) { // i.Operand = StringList[(int)i.Operand]; Don't want to touch as it might sb.Clear(); sb.Append(GMCodeUtil.lookupInstance(i.Extra, InstanceList)); sb.Append('.'); sb.Append(StringList[(int)i.Operand & 0xFFFFF]); i.OperandText = sb.ToString(); } else if (i.Types[0] == GM_Type.String) { i.Operand = StringList[(int)i.Operand]; i.OperandText = GMCodeUtil.EscapeString(i.Operand as string); } break; case GMCode.Pop: // technicaly pop is always a var, but eh //i.Operand = StringList[(int)i.Operand]; Don't do it this wa as we might need to find out of its an array sb.Clear(); sb.Append(GMCodeUtil.lookupInstance(i.Extra, InstanceList)); sb.Append('.'); sb.Append(StringList[(int)i.Operand & 0xFFFFF]); i.OperandText = sb.ToString(); break; case GMCode.Call: i.OperandText = StringList[(int)i.Operand]; i.Operand = i.OperandText; break; } i.Previous = prev; if (prev == null) { prev = i; } else { prev.Next = i; } list.Add(i.Address, i); } if (list.Count == 0) { return(list); // return list, its empty } Instruction last = list.Last().Value; if (last.Code != GMCode.Exit || last.Code != GMCode.Ret) { int address = last.Address + last.Size; list.Add(address, new Instruction(address, GMCode.Exit)); } // must be done for graph and in case we have a branch that goes right outside. Its implied in any event foreach (var i in branches) { Label l = i.Operand as Label; if (l.Origin == null) // Link the label { l.Origin = list[l.Address]; list[l.Address].Label = l; } } return(list); }
/// <summary> /// Diassembles a raw opcode. /// </summary> /// <param name="i"></param> /// <param name="raw"></param> /// <returns>Returns truee if operand is needed</returns> public static bool DissasembleRawCode(Instruction i, uint raw) { i.Code = GMCodeUtil.getFromRaw(raw); i.Types = null; i.Operand = null; i.OperandText = null; // we clear eveything just in case i._extra = (short)(0xFFFF & raw); // default for almost eveything switch (i.Code) { case GMCode.Call: i.Types = new GM_Type[] { (GM_Type)((raw >> 16) & 15) }; return(true); case GMCode.Exit: case GMCode.Ret: case GMCode.Not: case GMCode.Neg: case GMCode.Popz: i.Types = new GM_Type[] { (GM_Type)((raw >> 16) & 15) }; break; case GMCode.Pop: i.Types = new GM_Type[] { (GM_Type)((raw >> 16) & 15), (GM_Type)((raw >> 20) & 15) }; return(true); case GMCode.Push: i.Types = new GM_Type[] { (GM_Type)((raw >> 16) & 15) }; // this should be in the operand, but just in case make sure extra is right if (i.Types[0] == GM_Type.Short) { i.Operand = i._extra; // convert it to int break; // set the operand ourselfs don't need it } else { return(true); // need to read an operand } case GMCode.Add: case GMCode.Sub: case GMCode.Mul: case GMCode.Div: case GMCode.Mod: case GMCode.Or: case GMCode.And: case GMCode.Dup: case GMCode.Sal: case GMCode.Seq: case GMCode.Sge: case GMCode.Sgt: case GMCode.Sle: case GMCode.Sne: case GMCode.Conv: case GMCode.Rem: case GMCode.Slt: case GMCode.Xor: i.Types = new GM_Type[] { (GM_Type)((raw >> 16) & 15), (GM_Type)((raw >> 20) & 15) }; break; case GMCode.Break: // i._extra = (int)(0x00FFFFFFF & raw); // never seen the need for more than this break; case GMCode.B: case GMCode.Bf: case GMCode.Bt: i._extra = i.Address + GMCodeUtil.getBranchOffset(raw); break; case GMCode.Popenv: // Debug.WriteLine("Popenv: Address: {0}, Extra: {1} {1:X8} Calc: {2}",i.Address, raw, GMCodeUtil.getBranchOffset(raw)); if (0xBCF00000 == raw) // its a popbreak { i._extra = 0; } else { i._extra = i.Address + GMCodeUtil.getBranchOffset(raw); } break; case GMCode.Pushenv: // Debug.WriteLine("Pushenv: Address: {0}, Extra: {1} {1:X8} Calc: {2}",i.Address, raw, GMCodeUtil.getBranchOffset(raw)); i._extra = i.Address + GMCodeUtil.getBranchOffset(raw); break; case GMCode.BadOp: throw new Exception("Bad opcode?"); default: throw new Exception("Unkonwn opcode"); } return(false); }