public void WriteTraitsInfo(TraitsInfo value) { WriteU30(value.Name.Index); WriteByte((Byte)((Byte)(value.Attributes) << 4 | (Byte)value.Kind)); switch (value.Kind) { case TraitKind.Slot: case TraitKind.Const: TraitField traitField = value.TraitField; WriteU30(traitField.SlotId); WriteU30(traitField.TypeName == null ? 0 : traitField.TypeName.Index); WriteU30(traitField.ValueIndex); if (traitField.ValueIndex > 0) { WriteByte((Byte)traitField.ValueKind); } break; case TraitKind.Method: TraitMethod traitMethod = value.TraitMethod; WriteU30(traitMethod.DispId); WriteU30(traitMethod.Method.Index); break; case TraitKind.Getter: TraitGetter traitGetter = value.Trait as TraitGetter; WriteU30(traitGetter.DispId); WriteU30(traitGetter.Method.Index); break; case TraitKind.Setter: TraitSetter traitSetter = value.Trait as TraitSetter; WriteU30(traitSetter.DispId); WriteU30(traitSetter.Method.Index); break; case TraitKind.Class: TraitClass traitClass = value.Trait as TraitClass; WriteU30(traitClass.SlotId); WriteU30(traitClass.Class.Index); break; case TraitKind.Function: TraitFunction traitFunction = value.Trait as TraitFunction; WriteU30(traitFunction.SlotId); WriteU30(traitFunction.Function.Index); break; default: throw new ArgumentOutOfRangeException(); } if (value.Attributes.HasFlag(TraitAttributes.Metadata)) { WriteU30Array(value.MetadataArray, true); } }
static void Main(string[] args) { #if DEBUG DateTime start = DateTime.Now; #endif try { #region Initialization try { bool hasMethod = false; if (args.Length < 1) { WriteInfo(); Console.WriteLine("[?] Use -help for some instructions."); return; } for (int i = 0, n = args.Length; i < n; ++i) { switch (args[i]) { case "-h": case "-help": case "-?": case "/?": case "/help": WriteInfo(); Usage(); return; // OPTIONS ///////////////////////////////////////////////////////////////////// case "-o": case "-output": _pathOutput = args[++i]; break; case "-q": case "-quiet": _isQuiet = true; break; // REPLACE //////////////////////////////////////////////////////////////////// case "-r": case "-replace": _action = Action.Replace; break; // REPLACE PROPERTIES //////////////////////////////////////////////////////// case "-sm": case "-static-method": if (hasMethod) { throw new Exception("Two methods defined."); } _isMethodStatic = true; _methodName = args[++i]; break; case "-m": case "-method": if (hasMethod) { throw new Exception("Two methods defined."); } _isMethodStatic = false; _methodName = args[++i]; break; case "-co": case "-constructor": if (hasMethod) { throw new Exception("Two methods defined."); } _isConstructor = true; _isMethodStatic = false; break; case "-sc": case "-static-constructor": if (hasMethod) { throw new Exception("Two methods defined."); } _isConstructor = true; _isMethodStatic = true; break; case "-c": case "-class": _className = args[++i]; break; case "-n": case "-namespace": _namespace = args[++i]; break; // INLINE /////////////////////////////////////////////////////////////////// case "-i": case "-inline": _action = Action.Inline; _pathSwf = args[++i]; break; // PATCH /////////////////////////////////////////////////////////////////// case "-optimize": _action = Action.Optimize; _pathSwf = args[++i]; break; // DASM //////////////////////////////////////////////////////////////////// case "-d": case "-dasm": _action = Action.Dasm; break; // DASM PROPERTIES //////////////////////////////////////////////////////// case "-a": case "-as3c": _dasmType = Dasm.As3c; break; case "-p": case "-plain": _dasmType = Dasm.Plain; break; default: if (File.Exists(args[i])) { try { BinaryReader fileHead = new BinaryReader(File.Open(args[i], FileMode.Open, FileAccess.Read)); if (fileHead.BaseStream.Length < 3) { throw new Exception("Invalid file given."); } byte[] head = fileHead.ReadBytes(3); fileHead.Close(); //TODO fix this ugly part... if ((head[0] == (byte)'C' || head[0] == (byte)'F') && head[1] == (byte)'W' && head[2] == (byte)'S') { if (_pathSwf != "") { throw new Exception("Two SWF files given."); } _pathSwf = args[i]; } else { if ("" != _pathAsm) { throw new Exception("Two ASM files given."); } _pathAsm = args[i]; } } catch (IOException io) { WriteInfo(); Console.Error.WriteLine("[-] Error: Can not open file {0}", args[i]); #if DEBUG Console.Error.WriteLine("[-] {0}", io.Message); #endif return; } catch (Exception e) { WriteInfo(); Console.Error.WriteLine("[-] Error: {0}", e.Message); #if DEBUG Console.Error.WriteLine("[-] {0}", e.StackTrace); #endif return; } } else { if (args[i].IndexOf(".") != -1) { WriteInfo(); Console.Error.WriteLine("[-] File {0} does not exist.", args[i]); return; } else { throw new Exception("Invalid argument given."); } } break; } } } catch (Exception e) { WriteInfo(); Console.Error.WriteLine("[-] Error: Invalid arguments. Use -help for help ..."); #if DEBUG Console.Error.WriteLine("[-] {0}", e.Message); Console.Error.WriteLine("[-] {0}", e.StackTrace); #endif return; } if (!_isQuiet) { Console.WriteLine("[i] As3c - ActionScript3 ASM compiler"); } Translator.InitTable(); #endregion #region Integrity scan for the Translator class (DEBUG only) #if DEBUG if (Translator.CheckIntegrity()) { if (!_isQuiet) { Console.WriteLine("[+] Integrity scan passed!"); } } else { Console.WriteLine("[-] Integrity scan failed...!"); } #endif #endregion FileStream fileInput; FileStream fileOutput; SwfFormat swf; switch (_action) { case Action.Dasm: #region Disassemble file swf = new SwfFormat(); fileInput = File.Open(_pathSwf, FileMode.Open, FileAccess.Read); swf.Read(fileInput); fileInput.Close(); fileInput.Dispose(); DisassemblerBase dasm; switch (_dasmType) { case Dasm.As3c: dasm = new DisassemblerAs3c(); break; case Dasm.Plain: dasm = new DisassemblerPlain(); break; default: throw new Exception(); } dasm.Parse(swf); if ("" == _pathOutput) { dasm.EmitToConsole(); } else { fileOutput = File.Open(_pathOutput, FileMode.OpenOrCreate, FileAccess.Write); dasm.EmitToStream(fileOutput); fileOutput.Close(); fileOutput.Dispose(); } #endregion break; case Action.Optimize: #region Optimize SWF swf = new SwfFormat(); fileInput = File.Open(_pathSwf, FileMode.Open, FileAccess.Read); swf.Read(fileInput); fileInput.Close(); fileInput.Dispose(); CompilerOptimize compOptimize = new CompilerOptimize(swf); compOptimize.Compile(); fileOutput = File.Open((_pathOutput != "") ? _pathOutput : _pathSwf, FileMode.OpenOrCreate, FileAccess.Write); swf.Write(fileOutput); fileOutput.Close(); fileOutput.Dispose(); #endregion break; case Action.Inline: #region Compile inline instructions swf = new SwfFormat(); fileInput = File.Open(_pathSwf, FileMode.Open, FileAccess.Read); swf.Read(fileInput); fileInput.Close(); fileInput.Dispose(); CompilerInline compInline = new CompilerInline(swf); compInline.Compile(); fileOutput = File.Open((_pathOutput != "") ? _pathOutput : _pathSwf, FileMode.OpenOrCreate, FileAccess.Write); swf.Write(fileOutput); fileOutput.Close(); fileOutput.Dispose(); #endregion break; case Action.Replace: #region Replace method body #region Simple pre-check if ("" == _pathAsm || !File.Exists(_pathAsm)) { Console.Error.WriteLine("[-] No valid ASM input given."); return; } if ("" == _pathSwf || !File.Exists(_pathSwf)) { Console.Error.WriteLine("[-] No valid SWF target given."); return; } if ("" == _className) { Console.Error.WriteLine("[-] No class given."); return; } if (!_isConstructor) { if ("" == _methodName) { Console.Error.WriteLine("[-] Need method name or constructor to replace"); return; } } #endregion swf = new SwfFormat(); fileInput = File.Open(_pathSwf, FileMode.Open, FileAccess.Read); swf.Read(fileInput); fileInput.Close(); fileInput.Dispose(); #region Body lookup Abc46 abc = null; SwfLibrary.Abc.MethodInfo method = null; MethodBodyInfo body = null; bool instanceFound = false; Abc46 currentAbc = null; string classFormat; if (_className.IndexOf(".") != -1) { classFormat = "public::" + _className.Substring(0, _className.LastIndexOf(".")) + "::" + _className.Substring(_className.LastIndexOf(".") + 1, _className.Length - _className.LastIndexOf(".") - 1); } else { classFormat = "public::" + _className; } string methodFormat = _namespace + "::" + _methodName; // Parse for all possibilities for (int i = 0, n = swf.AbcCount; i < n; ++i) { currentAbc = swf.GetAbcAt(i); for (int j = 0, m = currentAbc.Scripts.Count; j < m; ++j) { ScriptInfo script = (ScriptInfo)currentAbc.Scripts[j]; for (int k = 0, o = script.Traits.Count; k < o; ++k) { TraitInfo scriptTrait = (TraitInfo)script.Traits[k]; if (!(scriptTrait.Body is TraitClass)) { continue; } TraitClass classBody = (TraitClass)scriptTrait.Body; ClassInfo classInfo = (ClassInfo)currentAbc.Classes[(int)classBody.ClassI]; InstanceInfo instanceInfo = (InstanceInfo)currentAbc.Instances[(int)classBody.ClassI]; string instanceName = NameUtil.ResolveMultiname(currentAbc, instanceInfo.Name); if (classFormat == instanceName) { instanceFound = true; if (!_isQuiet) { Console.WriteLine("[+] Found class {0}", instanceName); } if (_isMethodStatic) { if (_isConstructor) { if (null != body) { Console.Error.WriteLine("[-] Can not explicitly determine method body."); return; } method = (SwfLibrary.Abc.MethodInfo)currentAbc.Methods[(int)classInfo.CInit]; body = FindBody(currentAbc, classInfo.CInit); abc = currentAbc; if (null != body) { if (!_isQuiet) { Console.WriteLine("[+] Found static class initializer."); } } } else { Console.Error.WriteLine("[-] Sorry, static methods do not work yet ..."); return; //TODO support static methods... } } else { if (_isConstructor) { if (null != body) { Console.Error.WriteLine("[-] Can not explicitly determine method body."); return; } method = (SwfLibrary.Abc.MethodInfo)currentAbc.Methods[(int)instanceInfo.IInit]; body = FindBody(currentAbc, instanceInfo.IInit); abc = currentAbc; if (null != body) { if (!_isQuiet) { Console.WriteLine("[+] Found class initializer."); } } } else { // here begins the ugly part ... for (int l = 0, p = instanceInfo.Traits.Count; l < p; ++l) { TraitInfo instanceTrait = (TraitInfo)instanceInfo.Traits[l]; if (!(instanceTrait.Body is TraitMethod)) { continue; } string methodName = NameUtil.ResolveMultiname(currentAbc, instanceTrait.Name); if ("" == _namespace) { if ("public::" + _methodName != methodName && "private::" + _methodName != methodName && "protected::" + _methodName != methodName && "protected$::" + _methodName != methodName && "internal::" + _methodName != methodName) { continue; } } else { if (methodName != methodFormat) { continue; } } if (null != body) { Console.Error.WriteLine("[-] Can not explicitly determine method body."); return; } TraitMethod methodBody = (TraitMethod)instanceTrait.Body; method = (SwfLibrary.Abc.MethodInfo)currentAbc.Methods[(int)methodBody.Method]; body = FindBody(currentAbc, methodBody.Method); abc = currentAbc; if (null != body) { if (!_isQuiet) { Console.WriteLine("[+] Found method {0}", methodName); } } } } } } } } } if (null == body) { Console.Error.WriteLine("[-] Could not find {0}.", (instanceFound) ? "method" : "class"); return; } #endregion // // We have valid body to replace. Start the parser. // ParserAs3c parser = new ParserAs3c(); parser.Parse(_pathAsm); // // Convert the parser instructions to actual bytecode for the AVM2. // CompilerAs3c compAs3c = new CompilerAs3c(); compAs3c.Compile(currentAbc, parser.Instructions, parser.Labels, false); // // Get body information (maxstack, localcount, maxscopedepth, initscopedepth) // We keep compiler directives in respect here ... // #region MethodBody information U30 maxStack; U30 maxScopeDepth; U30 localCount; U30 initScopeDepth = new U30(); if (parser.HasMaxStack) { maxStack = new U30(); maxStack.Value = parser.MaxStack; } else { maxStack = ByteCodeAnalyzer.CalcMaxStack(compAs3c.Code); if (maxStack.Value < method.ParameterCount.Value) { maxStack.Value = method.ParameterCount.Value; } } if (parser.HasLocalCount) { localCount = new U30(); localCount.Value = parser.LocalCount; } else { localCount = ByteCodeAnalyzer.CalcLocalCount(compAs3c.Code); } if (parser.HasInitScopeDepth) { initScopeDepth.Value = parser.InitScopeDepth; } else { initScopeDepth.Value = 1; } if (parser.HasMaxScopeDepth) { maxScopeDepth = new U30(); maxScopeDepth.Value = parser.MaxScopeDepth; } else { maxScopeDepth = ByteCodeAnalyzer.CalcScopeDepth(compAs3c.Code); maxScopeDepth.Value += initScopeDepth.Value; } if (!_isQuiet) { Console.WriteLine("[i] InitScopeDepth: {0}", (int)initScopeDepth); Console.WriteLine("[i] MaxScopeDepth: {0}", (int)maxScopeDepth); Console.WriteLine("[i] MaxStack: {0}", (int)maxStack); Console.WriteLine("[i] LocalCount: {0}", (int)localCount); } #endregion // // Replace the code of the body. // body.Code = compAs3c.Code; // // Update body information // body.MaxStack = maxStack; body.MaxScopeDepth = maxScopeDepth; body.InitScopeDepth = initScopeDepth; body.LocalCount = localCount; #region Write output if ("" == _pathOutput) { fileOutput = File.Open(_pathSwf, FileMode.OpenOrCreate, FileAccess.Write); } else { fileOutput = File.Open(_pathOutput, FileMode.OpenOrCreate, FileAccess.Write); } swf.Write(fileOutput); fileOutput.Close(); fileOutput.Dispose(); #endregion #endregion break; case Action.Usage: Usage(); break; } #region Program end if (!_isQuiet) { Console.WriteLine("[i] Done."); } #endregion } catch (IOException ioex) { Console.Error.WriteLine("[-] Error: {0}", ioex.Message); } catch (InstructionException iex) { #region Parser errors string message; switch (iex.ErrorType) { case InstructionException.Type.InvalidSyntax: message = "Error while parsing"; break; case InstructionException.Type.NotEnoughArguments: message = "Not enough arguments"; break; case InstructionException.Type.TooManyArguments: message = "Too many arguments"; break; case InstructionException.Type.UnknownType: message = "Unknown type"; break; case InstructionException.Type.LabelRedefined: message = "Label has already been defined"; break; case InstructionException.Type.LabelMissing: message = "Label has never been defined"; break; default: message = "Unknown error at"; break; } Console.Error.WriteLine("[-] " + message + ":"); if (null != iex.ParserInfo) { Console.Error.WriteLine("{0}({1}): {2}", iex.ParserInfo.FilePath, iex.ParserInfo.LineNumber, iex.ParserInfo.Line); } #endregion } catch (Exception ex) { Console.Error.WriteLine("[-] Unexpected error: {0}", ex.Message); #if DEBUG Console.Error.WriteLine("[-] Stacktrace: {0}", ex.StackTrace); #endif } #if DEBUG DateTime end = DateTime.Now; TimeSpan delta = end - start; Console.WriteLine("[i] Total: {0}sec {1}ms", delta.Seconds, delta.Milliseconds); #endif }
public TraitsInfo ReadTraitsInfo(Abc abc) { TraitsInfo result = new TraitsInfo(); result.Name = abc.ConstantPool.GetMultinameAt(ReadU30()); byte kindAndAttribute = ReadByte(); result.Kind = (TraitKind)(kindAndAttribute & 0xf); result.Attributes = (TraitAttributes)(kindAndAttribute >> 4); switch (result.Kind) { case TraitKind.Slot: case TraitKind.Const: var traitField = new TraitField(); traitField.SlotId = ReadU30(); traitField.TypeName = abc.ConstantPool.GetMultinameAt(ReadU30()); traitField.ValueIndex = ReadU30(); if (traitField.ValueIndex > 0) { traitField.ValueKind = (ValueKind)ReadByte(); } result.Trait = traitField; break; case TraitKind.Method: var traitMethod = new TraitMethod(); traitMethod.DispId = ReadU30(); traitMethod.Method = abc.MethodArray[ReadU30()]; result.Trait = traitMethod; break; case TraitKind.Getter: var traitGetter = new TraitGetter(); traitGetter.DispId = ReadU30(); traitGetter.Method = abc.MethodArray[ReadU30()]; result.Trait = traitGetter; break; case TraitKind.Setter: var traitSetter = new TraitSetter(); traitSetter.DispId = ReadU30(); traitSetter.Method = abc.MethodArray[ReadU30()]; result.Trait = traitSetter; break; case TraitKind.Class: var traitClass = new TraitClass(); traitClass.SlotId = ReadU30(); traitClass.Class = abc.AbcClassArray[ReadU30()].ClassInfo; result.Trait = traitClass; break; case TraitKind.Function: var traitFunction = new TraitFunction(); traitFunction.SlotId = ReadU30(); traitFunction.Function = abc.MethodArray[ReadU30()]; result.Trait = traitFunction; break; default: throw new ArgumentOutOfRangeException(); } if (result.Attributes.HasFlag(TraitAttributes.Metadata)) { result.MetadataArray = ReadU30Array(ReadU30()); } return(result); }
protected void FormatTraits(IHasTraits traitsObject) { ArrayList traitList = traitsObject.Traits; TraitInfo trait; for (int i = 0, n = traitList.Count; i < n; ++i) { trait = (TraitInfo)traitList[i]; if (trait.Body is TraitClass) { TraitClass classBody = (TraitClass)trait.Body; #region Class information ClassInfo classInfo = (ClassInfo)_abc.Classes[(int)classBody.ClassI]; InstanceInfo instanceInfo = (InstanceInfo)_abc.Instances[(int)classBody.ClassI]; string classDefinition = ""; if (0 != (instanceInfo.Flags & InstanceInfo.ClassInterface)) { classDefinition += "interface "; } else { if (0 != (instanceInfo.Flags & InstanceInfo.ClassFinal)) { classDefinition += "final "; } if (0 == (instanceInfo.Flags & InstanceInfo.ClassSealed)) { classDefinition += "dynamic "; } classDefinition += "class "; } //TODO add protected namespace lookup classDefinition += NameUtil.ResolveMultiname(_abc, (int)instanceInfo.Name) + " extends " + NameUtil.ResolveMultiname(_abc, (int)instanceInfo.SuperName) + " "; if (instanceInfo.Interfaces.Count > 0) { classDefinition += "implements "; for (int j = 0, m = instanceInfo.Interfaces.Count; j < m; ++j) { classDefinition += NameUtil.ResolveMultiname(_abc, (U30)instanceInfo.Interfaces[j]); if (j != m - 1) { classDefinition += ", "; } } } _output.Add(";" + classDefinition + "\r\n"); _output.Add(";cinit (static class initializer)\r\n"); FormatMethod(classInfo.CInit); _output.Add(";class members\r\n"); FormatTraits(classInfo); _output.Add(";iinit (instance initializer aka constructor)\r\n"); FormatMethod(instanceInfo.IInit); _output.Add(";instance members\r\n"); FormatTraits(instanceInfo); #endregion } else if (trait.Body is TraitConst) { } else if (trait.Body is TraitFunction) { } else if ((trait.Body is TraitMethod) || (trait.Body is TraitGetter) || (trait.Body is TraitSetter)) { TraitMethod methodBody = (TraitMethod)trait.Body; _output.Add(";" + NameUtil.ResolveMultiname(_abc, trait.Name) + "\r\n"); FormatMethod(methodBody.Method); } else { } } }