Beispiel #1
0
        private void WriteMethodBodies(ABCDataTypeWriter writer)
        {
            Method[] methods = this.methodMarshal.ToArray();
            writer.WriteU30Packed((uint)methods.Length);

            foreach (Method m in methods)
            {
                writer.WriteU30Packed((uint)this.methodMarshal.GetExistingIDFor(m));

                writer.WriteU30Packed(m.MaxStack);
                writer.WriteU30Packed(m.LocalCount);
                writer.WriteU30Packed(m.InitScopeDepth);
                writer.WriteU30Packed(m.MaxScopeDepth);

                byte[] bytecode = m.Bytes;

                writer.WriteU30Packed((uint)bytecode.Length);
                writer.Write(bytecode, 0, bytecode.Length);

                /* Exception handlers... */

                /* ISSUE 11: This is a fixed list from the moment the method is loaded. You can't add and
                 * remove from exception handlers since they are referenced from newexception opcodes
                 * by index into this list. Fix this by removing the list and marshalling the references
                 * when required. */
                writer.WriteU30Packed((uint)m.ExceptionHandlerCount);
                using (IEnumerator <ExceptionHandler> i = m.ExceptionHandlers)
                {
                    while (i.MoveNext())
                    {
                        ExceptionHandler eh = i.Current;

                        writer.WriteU30Packed((uint)((int)eh.From));
                        writer.WriteU30Packed((uint)((int)eh.To));
                        writer.WriteU30Packed((uint)((int)eh.Target));

                        if (eh.IsCatchAll)
                        {
                            writer.WriteU30Packed(0);
                        }
                        else
                        {
                            writer.WriteU30Packed((uint)this.multinameMarshal.GetIDFor(eh.CatchType));
                        }

                        writer.WriteU30Packed((uint)this.multinameMarshal.GetIDFor(eh.VarName));
                    }
                }

                /* Method traits... */
                writer.WriteU30Packed((uint)m.TraitCount);
                using (IEnumerator <Trait> i = m.Traits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }
        }
Beispiel #2
0
        private byte[] GenerateMethodInfo()
        {
            MemoryStream      buf    = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            Method[] methods = this.methodMarshal.ToArray();
            writer.WriteU30Packed((uint)methods.Length);
            foreach (Method method in methods)
            {
                writer.WriteU30Packed((uint)method.ParamCount);

                if (method.ReturnType != null)
                {
                    writer.WriteU30Packed((uint)this.MultinameID(method.ReturnType));
                }
                else
                {
                    writer.WriteU30Packed(0);
                }

                using (IEnumerator <Multiname> i = method.ParamTypes)
                {
                    while (i.MoveNext())
                    {
                        writer.WriteU30Packed((uint)this.MultinameID(i.Current));
                    }
                }

                writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(method.Name));
                writer.WriteUI8((uint)method.Flags);

                if (method.HasOptionalArgs)
                {
                    /* ISSUE 9 */
                    throw new SWFModellerException(
                              SWFModellerError.UnimplementedFeature,
                              "Optional arguments in methods.");
                }

                if (method.HasParamNames)
                {
                    /* ISSUE 12 */
                    throw new SWFModellerException(
                              SWFModellerError.UnimplementedFeature,
                              "Named method parameters");
                }
            }

            writer.Close(); /* Closes the buffer */
            return(buf.ToArray());
        }
Beispiel #3
0
        private byte[] GenerateMetadata(AbcCode code)
        {
            MemoryStream      buf    = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            writer.WriteU30Packed((uint)code.MetadataCount);

            foreach (string key in code.MetadataKeys)
            {
                writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(key));
                Dictionary <string, string> itemInfo = code.GetMetadata(key);
                writer.WriteU30Packed((uint)itemInfo.Count);
                foreach (string itemKey in itemInfo.Keys)
                {
                    writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemKey));
                    writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemInfo[itemKey]));
                }
            }

            writer.Close(); /* Closes the buffer */
            return(buf.ToArray());
        }
Beispiel #4
0
        private byte[] GenerateScriptInfo(AbcCode code)
        {
            MemoryStream      buf    = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            writer.WriteU30Packed((uint)code.ScriptCount);

            foreach (Script s in code.Scripts)
            {
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(s.Method));

                writer.WriteU30Packed((uint)s.TraitCount);
                using (IEnumerator <Trait> i = s.Traits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }

            writer.Close(); /* Closes the buffer */
            return(buf.ToArray());
        }
Beispiel #5
0
        private void WriteTraitInfo(ABCDataTypeWriter writer, Trait t)
        {
            writer.WriteU30Packed((uint)this.MultinameID(t.Name));
            writer.WriteUI8((uint)t.Kind);

            switch (t.Kind)
            {
            case TraitKind.Slot:
            case TraitKind.Const:
                SlotTrait st = (SlotTrait)t;
                writer.WriteU30Packed(st.SlotID);
                writer.WriteU30Packed((uint)this.MultinameID(st.TypeName));

                if (st.Val == null)
                {
                    writer.WriteU30Packed(0);
                }
                else
                {
                    switch (st.ValKind)
                    {
                    case ConstantKind.ConInt:
                        writer.WriteU30Packed((uint)this.intMarshal.GetIDFor((int)st.Val));
                        break;

                    case ConstantKind.ConUInt:
                        writer.WriteU30Packed((uint)this.uintMarshal.GetIDFor((uint)st.Val));
                        break;

                    case ConstantKind.ConDouble:
                        writer.WriteU30Packed((uint)this.doubleMarshal.GetIDFor((ulong)st.Val));
                        break;

                    case ConstantKind.ConUtf8:
                        writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor((string)st.Val));
                        break;

                    case ConstantKind.ConTrue:
                        /* Through observation, this always gets set to 11, I do not know why. It
                         * seems though that it should be any non-zero number. */
                        writer.WriteU30Packed(11);
                        break;

                    case ConstantKind.ConFalse:
                        /* Through observation, this always gets set to 11, I do not know why. It
                         * seems though that it should be any non-zero number. */
                        writer.WriteU30Packed(10);
                        break;

                    case ConstantKind.ConNull:
                        /* Through observation, this always gets set to 11, I do not know why. It
                         * seems though that it should be any non-zero number. */
                        writer.WriteU30Packed(12);
                        break;

                    case ConstantKind.ConUndefined:
                        /* Through observation, true, false and null all seem to have ignored but
                         * specific values. I haven't seen one for undefined, but I'm guessing it's
                         * 13. Really want to know what these number are. The don't seem to relate
                         * the string or multiname tables. */
                        writer.WriteU30Packed(13);
                        break;

                    case ConstantKind.ConNamespace:
                    case ConstantKind.ConPackageNamespace:
                    case ConstantKind.ConPackageInternalNs:
                    case ConstantKind.ConProtectedNamespace:
                    case ConstantKind.ConExplicitNamespace:
                    case ConstantKind.ConStaticProtectedNs:
                    case ConstantKind.ConPrivateNs:
                        writer.WriteU30Packed((uint)this.NamespaceID((Namespace)st.Val));
                        break;

                    default:
                        throw new SWFModellerException(
                                  SWFModellerError.Internal,
                                  "Unsupported constant type: " + st.ValKind.ToString());
                    }

                    writer.WriteUI8((uint)st.ValKind);
                }

                break;

            case TraitKind.Method:
            case TraitKind.Getter:
            case TraitKind.Setter:
                MethodTrait mt = (MethodTrait)t;
                if (mt.OverriddenMethod != null)
                {
                    writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(mt.OverriddenMethod));
                }
                else
                {
                    writer.WriteU30Packed(0);
                }

                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(mt.Fn));
                break;

            case TraitKind.Class:
                ClassTrait ct = (ClassTrait)t;
                writer.WriteU30Packed((uint)ct.SlotID);
                writer.WriteU30Packed((uint)this.classMarshal.GetIDFor(ct.As3class));
                break;

            case TraitKind.Function:
                FunctionTrait ft = (FunctionTrait)t;
                writer.WriteU30Packed((uint)ft.SlotID);
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(ft.Fn));
                break;

            default:
                /* ISSUE 73 */
                throw new SWFModellerException(
                          SWFModellerError.UnimplementedFeature,
                          "Unsupported trait kind: " + t.Kind.ToString());
            }
        }
Beispiel #6
0
        private byte[] GenerateClassInfo()
        {
            MemoryStream      buf    = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            AS3ClassDef[] classes = this.classMarshal.ToArray();

            writer.WriteU30Packed((uint)classes.Length);
#if DEBUG
            int _cid = 1;
            this.writeLog.AppendLine("Classes count " + classes.Length);
#endif
            /* First, instance info... */
            foreach (AS3ClassDef clazz in classes)
            {
#if DEBUG
                this.writeLog.AppendLine((_cid++) + " Class " + clazz.Name + " (mn ID " + (uint)this.MultinameID(clazz.Name) + ")");
#endif
                writer.WriteU30Packed((uint)this.MultinameID(clazz.Name));
                writer.WriteU30Packed((uint)this.MultinameID(clazz.Supername));
                writer.WriteUI8((uint)clazz.Flags);

                if (clazz.IsProtectedNS)
                {
                    writer.WriteU30Packed((uint)this.NamespaceID(clazz.ProtectedNS));
                }

                writer.WriteU30Packed((uint)clazz.InterfaceCount);

                using (IEnumerator <Multiname> i = clazz.Interfaces)
                {
                    while (i.MoveNext())
                    {
#if DEBUG
                        this.writeLog.AppendLine("    implements " + i.Current + " (mn ID " + (uint)this.MultinameID(i.Current) + ")");
#endif
                        writer.WriteU30Packed((uint)this.MultinameID(i.Current));
                    }
                }
#if DEBUG
                this.writeLog.AppendLine("    Iinit ID " + (uint)this.methodMarshal.GetIDFor(clazz.Iinit));
#endif
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Iinit));

                writer.WriteU30Packed((uint)clazz.InstanceTraitCount);
                using (IEnumerator <Trait> i = clazz.InstanceTraits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }

            /* Second, class info... */
            foreach (AS3ClassDef clazz in classes)
            {
#if DEBUG
                this.writeLog.AppendLine("Class cinit ID for '" + clazz.Name + "' " + (uint)this.methodMarshal.GetIDFor(clazz.Cinit));
#endif
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Cinit));

                writer.WriteU30Packed((uint)clazz.ClassTraitCount);
                using (IEnumerator <Trait> i = clazz.ClassTraits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }

            writer.Close(); /* Closes the buffer */
            return(buf.ToArray());
        }
Beispiel #7
0
        public void AssembleIfNecessary(DoABC codeTag, bool insertDebugCodes, string mainClassName, StringBuilder writeLog)
        {
            this.InsertDebugCodes = insertDebugCodes;

#if DEBUG
            this.writeLog = writeLog;
            if (this.writeLog == null)
            {
                /* Bit of a kludge. If we passed null for this, we don't want a log. We'd rather not
                 * put null tests everywhere though, so we create this string builder and log stuff
                 * to it knowing full well it will be discarded and is a waste of time. It's convenient,
                 * and it's only the debug build, so we don't really care about the waste. */
                this.writeLog = new StringBuilder();
            }
#endif

            /* Firstly, if any methods are altered, we need to decompile all of them since the
             * table references will all be wrong. Check all the methods for tampering... */

            AbcCode code = codeTag.Code;

            if (!code.IsTampered)
            {
                return;
            }

            /* At this point, all we know is that at least one piece of code
             * needs re-assembly. IsTampered doesn't meant everything is necessarily
             * tampered, although it probably is. */

            /* We've established that we actually do need to do some work */

            MemoryStream      buffer = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buffer);

            /* Ok, someone tampered with a method. Now we need to decompile them all and
             * re-build them... */
            foreach (Method m in code.Methods)
            {
                if (!m.Tampered)
                {
                    m.Tampered = true; /* Dissassembles the code and forces re-assembly. */
                }
            }

            /* Another side-effect of such meddling is the need to re-build the tables... */
            this.ReBuildTables(code, mainClassName);

            /* At this point, we will have a set of clean-built or untampered tables, and
             * calls to Method.Bytes will either return pre-built bytecode, or assemble it
             * on-demand for us. In other words, everything can just be dumped into a file. */

            writer.WriteUI16(AbcFileValues.MinorVersion);
            writer.WriteUI16(AbcFileValues.MajorVersion);

            byte[] methodInfoBytes = this.GenerateMethodInfo();
            byte[] metadataBytes   = this.GenerateMetadata(code);
            byte[] classInfo       = this.GenerateClassInfo();
            byte[] scriptInfo      = this.GenerateScriptInfo(code);

            /* When we write out the constant pool, the constants are fixed in the code object with
             * those values. In other words at this point we'd better be damn sure we've added all the
             * constants to the pools. */
            this.WriteConstantPool(writer, code);

            writer.Write(methodInfoBytes, 0, methodInfoBytes.Length);
            writer.Write(metadataBytes, 0, metadataBytes.Length);
            writer.Write(classInfo, 0, classInfo.Length);
            writer.Write(scriptInfo, 0, scriptInfo.Length);

            this.WriteMethodBodies(writer);

            writer.Close(); /* Also closes buffer. */
            codeTag.Bytecode = buffer.ToArray();
        }
Beispiel #8
0
        private byte[] GenerateClassInfo()
        {
            MemoryStream buf = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            AS3ClassDef[] classes = this.classMarshal.ToArray();

            writer.WriteU30Packed((uint)classes.Length);
            #if DEBUG
            int _cid = 1;
            this.writeLog.AppendLine("Classes count " + classes.Length);
            #endif
            /* First, instance info... */
            foreach (AS3ClassDef clazz in classes)
            {
            #if DEBUG
                this.writeLog.AppendLine((_cid++) + " Class " + clazz.Name + " (mn ID " + (uint)this.MultinameID(clazz.Name) + ")");
            #endif
                writer.WriteU30Packed((uint)this.MultinameID(clazz.Name));
                writer.WriteU30Packed((uint)this.MultinameID(clazz.Supername));
                writer.WriteUI8((uint)clazz.Flags);

                if (clazz.IsProtectedNS)
                {
                    writer.WriteU30Packed((uint)this.NamespaceID(clazz.ProtectedNS));
                }

                writer.WriteU30Packed((uint)clazz.InterfaceCount);

                using (IEnumerator<Multiname> i = clazz.Interfaces)
                {
                    while (i.MoveNext())
                    {
            #if DEBUG
                        this.writeLog.AppendLine("    implements " + i.Current + " (mn ID " + (uint)this.MultinameID(i.Current) + ")");
            #endif
                        writer.WriteU30Packed((uint)this.MultinameID(i.Current));
                    }
                }
            #if DEBUG
                this.writeLog.AppendLine("    Iinit ID " + (uint)this.methodMarshal.GetIDFor(clazz.Iinit));
            #endif
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Iinit));

                writer.WriteU30Packed((uint)clazz.InstanceTraitCount);
                using (IEnumerator<Trait> i = clazz.InstanceTraits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }

            /* Second, class info... */
            foreach (AS3ClassDef clazz in classes)
            {
            #if DEBUG
                this.writeLog.AppendLine("Class cinit ID for '" + clazz.Name + "' " + (uint)this.methodMarshal.GetIDFor(clazz.Cinit));
            #endif
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(clazz.Cinit));

                writer.WriteU30Packed((uint)clazz.ClassTraitCount);
                using (IEnumerator<Trait> i = clazz.ClassTraits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }

            writer.Close(); /* Closes the buffer */
            return buf.ToArray();
        }
Beispiel #9
0
        private void WriteTraitInfo(ABCDataTypeWriter writer, Trait t)
        {
            writer.WriteU30Packed((uint)this.MultinameID(t.Name));
            writer.WriteUI8((uint)t.Kind);

            switch (t.Kind)
            {
                case TraitKind.Slot:
                case TraitKind.Const:
                    SlotTrait st = (SlotTrait)t;
                    writer.WriteU30Packed(st.SlotID);
                    writer.WriteU30Packed((uint)this.MultinameID(st.TypeName));

                    if (st.Val == null)
                    {
                        writer.WriteU30Packed(0);
                    }
                    else
                    {
                        switch (st.ValKind)
                        {
                            case ConstantKind.ConInt:
                                writer.WriteU30Packed((uint)this.intMarshal.GetIDFor((int)st.Val));
                                break;

                            case ConstantKind.ConUInt:
                                writer.WriteU30Packed((uint)this.uintMarshal.GetIDFor((uint)st.Val));
                                break;

                            case ConstantKind.ConDouble:
                                writer.WriteU30Packed((uint)this.doubleMarshal.GetIDFor((ulong)st.Val));
                                break;

                            case ConstantKind.ConUtf8:
                                writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor((string)st.Val));
                                break;

                            case ConstantKind.ConTrue:
                                /* Through observation, this always gets set to 11, I do not know why. It
                                 * seems though that it should be any non-zero number. */
                                writer.WriteU30Packed(11);
                                break;

                            case ConstantKind.ConFalse:
                                /* Through observation, this always gets set to 11, I do not know why. It
                                 * seems though that it should be any non-zero number. */
                                writer.WriteU30Packed(10);
                                break;

                            case ConstantKind.ConNull:
                                /* Through observation, this always gets set to 11, I do not know why. It
                                 * seems though that it should be any non-zero number. */
                                writer.WriteU30Packed(12);
                                break;

                            case ConstantKind.ConUndefined:
                                /* Through observation, true, false and null all seem to have ignored but
                                 * specific values. I haven't seen one for undefined, but I'm guessing it's
                                 * 13. Really want to know what these number are. The don't seem to relate
                                 * the string or multiname tables. */
                                writer.WriteU30Packed(13);
                                break;

                            case ConstantKind.ConNamespace:
                            case ConstantKind.ConPackageNamespace:
                            case ConstantKind.ConPackageInternalNs:
                            case ConstantKind.ConProtectedNamespace:
                            case ConstantKind.ConExplicitNamespace:
                            case ConstantKind.ConStaticProtectedNs:
                            case ConstantKind.ConPrivateNs:
                                writer.WriteU30Packed((uint)this.NamespaceID((Namespace)st.Val));
                                break;
                            default:
                                throw new SWFModellerException(
                                        SWFModellerError.Internal,
                                        "Unsupported constant type: " + st.ValKind.ToString());
                        }

                        writer.WriteUI8((uint)st.ValKind);
                    }

                    break;

                case TraitKind.Method:
                case TraitKind.Getter:
                case TraitKind.Setter:
                    MethodTrait mt = (MethodTrait)t;
                    if (mt.OverriddenMethod != null)
                    {
                        writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(mt.OverriddenMethod));
                    }
                    else
                    {
                        writer.WriteU30Packed(0);
                    }

                    writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(mt.Fn));
                    break;

                case TraitKind.Class:
                    ClassTrait ct = (ClassTrait)t;
                    writer.WriteU30Packed((uint)ct.SlotID);
                    writer.WriteU30Packed((uint)this.classMarshal.GetIDFor(ct.As3class));
                    break;

                case TraitKind.Function:
                    FunctionTrait ft = (FunctionTrait)t;
                    writer.WriteU30Packed((uint)ft.SlotID);
                    writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(ft.Fn));
                    break;

                default:
                    /* ISSUE 73 */
                    throw new SWFModellerException(
                            SWFModellerError.UnimplementedFeature,
                            "Unsupported trait kind: " + t.Kind.ToString());
            }
        }
Beispiel #10
0
        private void WriteMethodBodies(ABCDataTypeWriter writer)
        {
            Method[] methods = this.methodMarshal.ToArray();
            writer.WriteU30Packed((uint)methods.Length);

            foreach (Method m in methods)
            {
                writer.WriteU30Packed((uint)this.methodMarshal.GetExistingIDFor(m));

                writer.WriteU30Packed(m.MaxStack);
                writer.WriteU30Packed(m.LocalCount);
                writer.WriteU30Packed(m.InitScopeDepth);
                writer.WriteU30Packed(m.MaxScopeDepth);

                byte[] bytecode = m.Bytes;

                writer.WriteU30Packed((uint)bytecode.Length);
                writer.Write(bytecode, 0, bytecode.Length);

                /* Exception handlers... */
                /* ISSUE 11: This is a fixed list from the moment the method is loaded. You can't add and
                 * remove from exception handlers since they are referenced from newexception opcodes
                 * by index into this list. Fix this by removing the list and marshalling the references
                 * when required. */
                writer.WriteU30Packed((uint)m.ExceptionHandlerCount);
                using (IEnumerator<ExceptionHandler> i = m.ExceptionHandlers)
                {
                    while (i.MoveNext())
                    {
                        ExceptionHandler eh = i.Current;

                        writer.WriteU30Packed((uint)((int)eh.From));
                        writer.WriteU30Packed((uint)((int)eh.To));
                        writer.WriteU30Packed((uint)((int)eh.Target));

                        if (eh.IsCatchAll)
                        {
                            writer.WriteU30Packed(0);
                        }
                        else
                        {
                            writer.WriteU30Packed((uint)this.multinameMarshal.GetIDFor(eh.CatchType));
                        }

                        writer.WriteU30Packed((uint)this.multinameMarshal.GetIDFor(eh.VarName));
                    }
                }

                /* Method traits... */
                writer.WriteU30Packed((uint)m.TraitCount);
                using (IEnumerator<Trait> i = m.Traits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Writes out the constants to the SWF file. This will also re-set the tables in the
        /// code object, so be sure you don't have anything in there that you need.
        /// </summary>
        /// <param name="writer">Where to write the constants to.</param>
        /// <param name="code">The code object with the tables in it, that we'll
        /// overwrite.</param>
        private void WriteConstantPool(ABCDataTypeWriter writer, AbcCode code)
        {
            /* Integer constants */
            int[] ints = this.intMarshal.ToArray();
            writer.WriteU30Packed((uint)(ints.Length == 1 ? 0 : ints.Length));
            for (int i = 1; i < ints.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const int #" + i + ": " + ints[i]);
            #endif

                writer.WriteSI32(ints[i]);
            }

            code.IntConsts = ints;

            /* Unsigned integer constants */
            uint[] uints = this.uintMarshal.ToArray();
            writer.WriteU30Packed((uint)(uints.Length == 1 ? 0 : uints.Length));
            for (int i = 1; i < uints.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const uint #" + i + ": " + uints[i]);
            #endif
                writer.WriteUI32(uints[i]);
            }

            code.UIntConsts = uints;

            /* Double constants */
            ulong[] doubles = this.doubleMarshal.ToArray();
            writer.WriteU30Packed((uint)(doubles.Length == 1 ? 0 : doubles.Length));
            for (int i = 1; i < doubles.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const double (bits) #" + i + ": " + doubles[i]);
            #endif
                /* We hack this here instead of having a U64 type, because it's a hack around not
                 * treating double properly. There's no such thing as U64 in SWF. */
                ulong d = doubles[i];
                uint low = (uint)(d & 0x00000000FFFFFFFF);
                d >>= 32;
                uint high = (uint)(d & 0x00000000FFFFFFFF);

                writer.WriteUI32(high);
                writer.WriteUI32(low);
            }

            code.DoubleConsts = doubles;

            /* String constants */
            string[] strings = this.stringMarshal.ToArray();
            writer.WriteU30Packed((uint)(strings.Length == 1 ? 0 : strings.Length));
            for (int i = 1; i < strings.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const string #" + i + ": \"" + strings[i] + "\"");
            #endif
                writer.WriteString(strings[i]);
            }

            code.StringConsts = strings;

            /* Namespace constants */
            Namespace[] namespaces = this.nsMarshal.ToArray();
            writer.WriteU30Packed((uint)(namespaces.Length == 1 ? 0 : namespaces.Length));
            for (int i = 1; i < namespaces.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const ns #" + i + ": " + namespaces[i]);
            #endif
                Namespace ns = namespaces[i];
                writer.WriteUI8((uint)ns.Kind);
                writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(ns.Name));
            }

            code.SetNamespaces(namespaces);

            /* Namespace set constants */
            NamespaceSet[] namespaceSets = this.nsSetMarshal.ToArray();
            writer.WriteU30Packed((uint)(namespaceSets.Length == 1 ? 0 : namespaceSets.Length));
            for (int i = 1; i < namespaceSets.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const ns set #" + i + ": " + namespaceSets[i]);
            #endif
                NamespaceSet nss = namespaceSets[i];

                writer.WriteU30Packed((uint)nss.Count);

                foreach (Namespace ns in nss)
                {
                    writer.WriteU30Packed((uint)this.nsMarshal.GetExistingIDFor(ns));
                }
            }

            code.SetNamespaceSets(namespaceSets);

            /* Multiname constants */
            Multiname[] multinames = this.multinameMarshal.ToArray();
            writer.WriteU30Packed((uint)(multinames.Length == 1 ? 0 : multinames.Length));
            for (int i = 1; i < multinames.Length; i++) /* Omit value at [0] */
            {
            #if DEBUG
                this.writeLog.AppendLine("Const mn set #" + i + ": " + multinames[i]);
            #endif
                Multiname mn = multinames[i];

                writer.WriteUI8((uint)mn.Kind);

                switch (mn.Kind)
                {
                    case Multiname.MultinameKind.QName:
                    case Multiname.MultinameKind.QNameA:
                        uint nsIdx = (uint)this.nsMarshal.GetExistingIDFor(mn.NS);
                        uint nameIdx = (uint)this.stringMarshal.GetExistingIDFor(mn.Name);
                        writer.WriteU30Packed(nsIdx);
                        writer.WriteU30Packed(nameIdx);
                        break;

                    case Multiname.MultinameKind.RTQName:
                    case Multiname.MultinameKind.RTQNameA:
                        writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(mn.Name));
                        break;

                    case Multiname.MultinameKind.RTQNameL:
                    case Multiname.MultinameKind.RTQNameLA:
                        /* No data */
                        break;

                    case Multiname.MultinameKind.Multiname:
                    case Multiname.MultinameKind.MultinameA:
                        writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(mn.Name));
                        writer.WriteU30Packed((uint)this.nsSetMarshal.GetExistingIDFor(mn.Set));
                        break;

                    case Multiname.MultinameKind.MultinameL:
                    case Multiname.MultinameKind.MultinameLA:
                        writer.WriteU30Packed((uint)this.nsSetMarshal.GetExistingIDFor(mn.Set));
                        break;

                    default:
                        break;
                }
            }

            code.SetMultinames(multinames);
        }
Beispiel #12
0
        private byte[] GenerateScriptInfo(AbcCode code)
        {
            MemoryStream buf = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            writer.WriteU30Packed((uint)code.ScriptCount);

            foreach (Script s in code.Scripts)
            {
                writer.WriteU30Packed((uint)this.methodMarshal.GetIDFor(s.Method));

                writer.WriteU30Packed((uint)s.TraitCount);
                using (IEnumerator<Trait> i = s.Traits)
                {
                    while (i.MoveNext())
                    {
                        this.WriteTraitInfo(writer, i.Current);
                    }
                }
            }

            writer.Close(); /* Closes the buffer */
            return buf.ToArray();
        }
Beispiel #13
0
        private byte[] GenerateMethodInfo()
        {
            MemoryStream buf = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            Method[] methods = this.methodMarshal.ToArray();
            writer.WriteU30Packed((uint)methods.Length);
            foreach (Method method in methods)
            {
                writer.WriteU30Packed((uint)method.ParamCount);

                if (method.ReturnType != null)
                {
                    writer.WriteU30Packed((uint)this.MultinameID(method.ReturnType));
                }
                else
                {
                    writer.WriteU30Packed(0);
                }

                using (IEnumerator<Multiname> i = method.ParamTypes)
                {
                    while (i.MoveNext())
                    {
                        writer.WriteU30Packed((uint)this.MultinameID(i.Current));
                    }
                }

                writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(method.Name));
                writer.WriteUI8((uint)method.Flags);

                if (method.HasOptionalArgs)
                {
                    /* ISSUE 9 */
                    throw new SWFModellerException(
                            SWFModellerError.UnimplementedFeature,
                            "Optional arguments in methods.");
                }

                if (method.HasParamNames)
                {
                    /* ISSUE 12 */
                    throw new SWFModellerException(
                            SWFModellerError.UnimplementedFeature,
                            "Named method parameters");
                }
            }

            writer.Close(); /* Closes the buffer */
            return buf.ToArray();
        }
Beispiel #14
0
        private byte[] GenerateMetadata(AbcCode code)
        {
            MemoryStream buf = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            writer.WriteU30Packed((uint)code.MetadataCount);

            foreach (string key in code.MetadataKeys)
            {
                writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(key));
                Dictionary<string, string> itemInfo = code.GetMetadata(key);
                writer.WriteU30Packed((uint)itemInfo.Count);
                foreach (string itemKey in itemInfo.Keys)
                {
                    writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemKey));
                    writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor(itemInfo[itemKey]));
                }
            }

            writer.Close(); /* Closes the buffer */
            return buf.ToArray();
        }
Beispiel #15
0
        public void AssembleIfNecessary(DoABC codeTag, bool insertDebugCodes, string mainClassName, StringBuilder writeLog)
        {
            this.InsertDebugCodes = insertDebugCodes;

            #if DEBUG
            this.writeLog = writeLog;
            if (this.writeLog == null)
            {
                /* Bit of a kludge. If we passed null for this, we don't want a log. We'd rather not
                 * put null tests everywhere though, so we create this string builder and log stuff
                 * to it knowing full well it will be discarded and is a waste of time. It's convenient,
                 * and it's only the debug build, so we don't really care about the waste. */
                this.writeLog = new StringBuilder();
            }
            #endif

            /* Firstly, if any methods are altered, we need to decompile all of them since the
             * table references will all be wrong. Check all the methods for tampering... */

            AbcCode code = codeTag.Code;

            if (!code.IsTampered)
            {
                return;
            }

            /* At this point, all we know is that at least one piece of code
             * needs re-assembly. IsTampered doesn't meant everything is necessarily
             * tampered, although it probably is. */

            /* We've established that we actually do need to do some work */

            MemoryStream buffer = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buffer);

            /* Ok, someone tampered with a method. Now we need to decompile them all and
             * re-build them... */
            foreach (Method m in code.Methods)
            {
                if (!m.Tampered)
                {
                    m.Tampered = true; /* Dissassembles the code and forces re-assembly. */
                }
            }

            /* Another side-effect of such meddling is the need to re-build the tables... */
            this.ReBuildTables(code, mainClassName);

            /* At this point, we will have a set of clean-built or untampered tables, and
             * calls to Method.Bytes will either return pre-built bytecode, or assemble it
             * on-demand for us. In other words, everything can just be dumped into a file. */

            writer.WriteUI16(AbcFileValues.MinorVersion);
            writer.WriteUI16(AbcFileValues.MajorVersion);

            byte[] methodInfoBytes = this.GenerateMethodInfo();
            byte[] metadataBytes = this.GenerateMetadata(code);
            byte[] classInfo = this.GenerateClassInfo();
            byte[] scriptInfo = this.GenerateScriptInfo(code);

            /* When we write out the constant pool, the constants are fixed in the code object with
             * those values. In other words at this point we'd better be damn sure we've added all the
             * constants to the pools. */
            this.WriteConstantPool(writer, code);

            writer.Write(methodInfoBytes, 0, methodInfoBytes.Length);
            writer.Write(metadataBytes, 0, metadataBytes.Length);
            writer.Write(classInfo, 0, classInfo.Length);
            writer.Write(scriptInfo, 0, scriptInfo.Length);

            this.WriteMethodBodies(writer);

            writer.Close(); /* Also closes buffer. */
            codeTag.Bytecode = buffer.ToArray();
        }
Beispiel #16
0
        private void AssembleMethod(Method method)
        {
            MemoryStream      buf    = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            this.methodMarshal.Register(method);

            using (IEnumerator <ExceptionHandler> i = method.ExceptionHandlers)
            {
                while (i.MoveNext())
                {
                    ExceptionHandler eh = i.Current;
                    this.RegisterMultiname(eh.CatchType);
                    this.RegisterMultiname(eh.VarName);
                }
            }

            bool insertDebugCodes = this.InsertDebugCodes;

            if (insertDebugCodes)
            {
                /* Quick check to see if the code already has them, in which case we
                 * won't bother. */
                foreach (Opcode op in method.Opcodes)
                {
                    if (op.Mnemonic == Opcode.Mnemonics.DebugFile || op.Mnemonic == Opcode.Mnemonics.DebugLine)
                    {
                        insertDebugCodes = false;
                        break;
                    }
                }
            }

            IEnumerable <Opcode> opcodes = method.Opcodes;

            if (insertDebugCodes)
            {
                List <Opcode> debugged = new List <Opcode>();

                debugged.Add(Opcode.CreateDebugFile(method.SourceFile));

                /* Let's just check this string won't break things... */
                uint srcPos = (uint)this.stringMarshal.GetIDFor(method.SourceFile);
                if (srcPos > 255)
                {
                    /* ISSUE 10: Best fix may be to strip out debug opcodes. */
                    throw new SWFModellerException(
                              SWFModellerError.CodeMerge,
                              "Marshaller failed to keep debug string at low index");
                }

                uint lineNumber = 1; /* For our fake debug info. */
                foreach (Opcode op in opcodes)
                {
                    debugged.Add(Opcode.CreateDebugLine(lineNumber++));
                    debugged.Add(op);
                }

                opcodes = debugged;
            }

            Dictionary <Opcode, int> offsetMap = new Dictionary <Opcode, int>();
            Dictionary <Opcode, int> lenMap    = new Dictionary <Opcode, int>();
            List <KeyValuePair <Opcode, KeyValuePair <Opcode, int> > > unresolvedOffsets = new List <KeyValuePair <Opcode, KeyValuePair <Opcode, int> > >();

            foreach (Opcode op in opcodes)
            {
                int startOffset = (int)writer.Offset;
                offsetMap.Add(op, startOffset);

                writer.WriteUI8(op.Instruction);

                if (op.Mnemonic == Opcode.Mnemonics.LookupSwitch)
                {
                    for (int i = 0; i < op.Args.Length; i++)
                    {
                        int argOffset = (int)writer.Offset - startOffset;
                        KeyValuePair <Opcode, int> sourceInfo = new KeyValuePair <Opcode, int>(op, argOffset);
                        object argOb = op.Args[i];
                        if (i == 1)
                        {
                            writer.WriteU30Packed((uint)(op.Args.Length - 3));
                        }
                        else
                        {
                            unresolvedOffsets.Add(new KeyValuePair <Opcode, KeyValuePair <Opcode, int> >((Opcode)argOb, sourceInfo));
                            writer.WriteSI24(0x00FFDEAD); /* Because dead beef won't fit in 3 bytes. */
                        }
                    }

                    lenMap.Add(op, 0);
                    continue;
                }

                Opcode.ArgType[] types = op.ArgTypes;
                if (types == null)
                {
                    /* done. */
                    lenMap.Add(op, (int)writer.Offset - startOffset);
                    continue;
                }

                if (types.Length != op.Args.Length)
                {
                    throw new SWFModellerException(
                              SWFModellerError.Internal,
                              "Arg mismatch in op " + op + " (" + types.Length + " expected)");
                }

                for (int i = 0; i < op.Args.Length; i++)
                {
                    object         argOb   = op.Args[i];
                    Opcode.ArgType argType = types[i];

                    switch (argType)
                    {
                    case Opcode.ArgType.MultinameU30:
                        writer.WriteU30Packed((uint)this.MultinameID((Multiname)argOb));
                        break;

                    case Opcode.ArgType.CountU30:
                    case Opcode.ArgType.UintU30:
                    case Opcode.ArgType.IntU30:
                    case Opcode.ArgType.LineNumberU30:
                    case Opcode.ArgType.RegisterU30:
                    case Opcode.ArgType.ObjectRegisterU30:
                    case Opcode.ArgType.PropertyRegisterU30:
                    case Opcode.ArgType.ShortU30:
                    case Opcode.ArgType.DoubleU30:
                        writer.WriteU30Packed((uint)argOb);
                        break;

                    case Opcode.ArgType.ByteU8:
                    case Opcode.ArgType.DebugU8:
                    case Opcode.ArgType.StackU8:
                        writer.WriteUI8((byte)argOb);
                        break;

                    case Opcode.ArgType.StringU8:
                        uint spos = (uint)this.stringMarshal.GetIDFor((string)argOb);
                        if (spos > 255)
                        {
                            /* ISSUE 10: Best fix may be to strip out debug opcodes. */
                            throw new SWFModellerException(
                                      SWFModellerError.Internal,
                                      "Marshaller failed to keep debug string at low index");
                        }

                        writer.WriteUI8(spos);
                        break;

                    case Opcode.ArgType.StringU30:
                        writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor((string)argOb));
                        break;

                    case Opcode.ArgType.ClassU30:
                        writer.WriteU30Packed((uint)this.classMarshal.GetIDFor((AS3ClassDef)argOb));
                        break;

                    case Opcode.ArgType.ByteS8:
                        writer.WriteSI8((int)argOb);
                        break;

                    case Opcode.ArgType.ShortS30:
                        writer.WriteSI32((int)argOb);     /* Spec says this signed value is a U30. Stupid spec. */
                        break;

                    case Opcode.ArgType.OffsetS24:
                        if (argOb is Opcode)
                        {
                            int argOffset = (int)writer.Offset - startOffset;
                            KeyValuePair <Opcode, int> sourceInfo = new KeyValuePair <Opcode, int>(op, argOffset);
                            unresolvedOffsets.Add(new KeyValuePair <Opcode, KeyValuePair <Opcode, int> >((Opcode)argOb, sourceInfo));
                            writer.WriteSI24(0x00FFDEAD);     /* Because dead beef won't fit in 3 bytes. */
                        }
                        else
                        {
                            /* ISSUE 73 */
                            throw new SWFModellerException(
                                      SWFModellerError.UnimplementedFeature,
                                      "Unsupported op arg type in " + op.Mnemonic.ToString() + ": " + argType);
                        }

                        break;

                    case Opcode.ArgType.ExceptionU30:
                        writer.WriteU30Packed((uint)argOb);     /* Reference into the fixed list of exception handlers. TODO: Make it not so fixed. */
                        break;

                    case Opcode.ArgType.SlotU30:
                        writer.WriteU30Packed((uint)argOb);     /* Reference into the fixed list of exception handlers. TODO: Make it not so fixed. */
                        break;

                    case Opcode.ArgType.NamespaceU30:
                    case Opcode.ArgType.MethodU30:
                    case Opcode.ArgType.DebugTypeU30:
                    default:
                        /* ISSUE 73 */
                        throw new SWFModellerException(
                                  SWFModellerError.UnimplementedFeature,
                                  "Unsupported op arg type in " + op.Mnemonic.ToString() + ": " + argType);
                    }
                }

                lenMap.Add(op, (int)writer.Offset - startOffset);
            }

            using (IEnumerator <ExceptionHandler> i = method.ExceptionHandlers)
            {
                while (i.MoveNext())
                {
                    ExceptionHandler eh = i.Current;
                    eh.From   = offsetMap[eh.From];
                    eh.To     = offsetMap[eh.To];
                    eh.Target = offsetMap[eh.Target];
                }
            }

            writer.Close(); /* Also closes the buffer. */

            byte[] byteBuffer = buf.ToArray();

            foreach (KeyValuePair <Opcode, KeyValuePair <Opcode, int> > link in unresolvedOffsets)
            {
                KeyValuePair <Opcode, int> sourceInfo = link.Value;
                int srcOffset = offsetMap[sourceInfo.Key];
                int argOffset = srcOffset + sourceInfo.Value;
                int val       = offsetMap[link.Key] - srcOffset - lenMap[sourceInfo.Key];
                new ABCDataTypeWriter(new MemoryStream(byteBuffer, argOffset, 3)).WriteSI24(val);
            }

            method.Bytes = byteBuffer;
        }
Beispiel #17
0
        /// <summary>
        /// Writes out the constants to the SWF file. This will also re-set the tables in the
        /// code object, so be sure you don't have anything in there that you need.
        /// </summary>
        /// <param name="writer">Where to write the constants to.</param>
        /// <param name="code">The code object with the tables in it, that we'll
        /// overwrite.</param>
        private void WriteConstantPool(ABCDataTypeWriter writer, AbcCode code)
        {
            /* Integer constants */
            int[] ints = this.intMarshal.ToArray();
            writer.WriteU30Packed((uint)(ints.Length == 1 ? 0 : ints.Length));
            for (int i = 1; i < ints.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const int #" + i + ": " + ints[i]);
#endif

                writer.WriteSI32(ints[i]);
            }

            code.IntConsts = ints;

            /* Unsigned integer constants */
            uint[] uints = this.uintMarshal.ToArray();
            writer.WriteU30Packed((uint)(uints.Length == 1 ? 0 : uints.Length));
            for (int i = 1; i < uints.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const uint #" + i + ": " + uints[i]);
#endif
                writer.WriteUI32(uints[i]);
            }

            code.UIntConsts = uints;

            /* Double constants */
            ulong[] doubles = this.doubleMarshal.ToArray();
            writer.WriteU30Packed((uint)(doubles.Length == 1 ? 0 : doubles.Length));
            for (int i = 1; i < doubles.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const double (bits) #" + i + ": " + doubles[i]);
#endif

                /* We hack this here instead of having a U64 type, because it's a hack around not
                 * treating double properly. There's no such thing as U64 in SWF. */
                ulong d   = doubles[i];
                uint  low = (uint)(d & 0x00000000FFFFFFFF);
                d >>= 32;
                uint high = (uint)(d & 0x00000000FFFFFFFF);

                writer.WriteUI32(high);
                writer.WriteUI32(low);
            }

            code.DoubleConsts = doubles;

            /* String constants */
            string[] strings = this.stringMarshal.ToArray();
            writer.WriteU30Packed((uint)(strings.Length == 1 ? 0 : strings.Length));
            for (int i = 1; i < strings.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const string #" + i + ": \"" + strings[i] + "\"");
#endif
                writer.WriteString(strings[i]);
            }

            code.StringConsts = strings;

            /* Namespace constants */
            Namespace[] namespaces = this.nsMarshal.ToArray();
            writer.WriteU30Packed((uint)(namespaces.Length == 1 ? 0 : namespaces.Length));
            for (int i = 1; i < namespaces.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const ns #" + i + ": " + namespaces[i]);
#endif
                Namespace ns = namespaces[i];
                writer.WriteUI8((uint)ns.Kind);
                writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(ns.Name));
            }

            code.SetNamespaces(namespaces);

            /* Namespace set constants */
            NamespaceSet[] namespaceSets = this.nsSetMarshal.ToArray();
            writer.WriteU30Packed((uint)(namespaceSets.Length == 1 ? 0 : namespaceSets.Length));
            for (int i = 1; i < namespaceSets.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const ns set #" + i + ": " + namespaceSets[i]);
#endif
                NamespaceSet nss = namespaceSets[i];

                writer.WriteU30Packed((uint)nss.Count);

                foreach (Namespace ns in nss)
                {
                    writer.WriteU30Packed((uint)this.nsMarshal.GetExistingIDFor(ns));
                }
            }

            code.SetNamespaceSets(namespaceSets);

            /* Multiname constants */
            Multiname[] multinames = this.multinameMarshal.ToArray();
            writer.WriteU30Packed((uint)(multinames.Length == 1 ? 0 : multinames.Length));
            for (int i = 1; i < multinames.Length; i++) /* Omit value at [0] */
            {
#if DEBUG
                this.writeLog.AppendLine("Const mn set #" + i + ": " + multinames[i]);
#endif
                Multiname mn = multinames[i];

                writer.WriteUI8((uint)mn.Kind);

                switch (mn.Kind)
                {
                case Multiname.MultinameKind.QName:
                case Multiname.MultinameKind.QNameA:
                    uint nsIdx   = (uint)this.nsMarshal.GetExistingIDFor(mn.NS);
                    uint nameIdx = (uint)this.stringMarshal.GetExistingIDFor(mn.Name);
                    writer.WriteU30Packed(nsIdx);
                    writer.WriteU30Packed(nameIdx);
                    break;

                case Multiname.MultinameKind.RTQName:
                case Multiname.MultinameKind.RTQNameA:
                    writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(mn.Name));
                    break;

                case Multiname.MultinameKind.RTQNameL:
                case Multiname.MultinameKind.RTQNameLA:
                    /* No data */
                    break;

                case Multiname.MultinameKind.Multiname:
                case Multiname.MultinameKind.MultinameA:
                    writer.WriteU30Packed((uint)this.stringMarshal.GetExistingIDFor(mn.Name));
                    writer.WriteU30Packed((uint)this.nsSetMarshal.GetExistingIDFor(mn.Set));
                    break;

                case Multiname.MultinameKind.MultinameL:
                case Multiname.MultinameKind.MultinameLA:
                    writer.WriteU30Packed((uint)this.nsSetMarshal.GetExistingIDFor(mn.Set));
                    break;

                default:
                    break;
                }
            }

            code.SetMultinames(multinames);
        }
Beispiel #18
0
        private void AssembleMethod(Method method)
        {
            MemoryStream buf = new MemoryStream();
            ABCDataTypeWriter writer = new ABCDataTypeWriter(buf);

            this.methodMarshal.Register(method);

            using (IEnumerator<ExceptionHandler> i = method.ExceptionHandlers)
            {
                while (i.MoveNext())
                {
                    ExceptionHandler eh = i.Current;
                    this.RegisterMultiname(eh.CatchType);
                    this.RegisterMultiname(eh.VarName);
                }
            }

            bool insertDebugCodes = this.InsertDebugCodes;

            if (insertDebugCodes)
            {
                /* Quick check to see if the code already has them, in which case we
                 * won't bother. */
                foreach (Opcode op in method.Opcodes)
                {
                    if (op.Mnemonic == Opcode.Mnemonics.DebugFile || op.Mnemonic == Opcode.Mnemonics.DebugLine)
                    {
                        insertDebugCodes = false;
                        break;
                    }
                }
            }

            IEnumerable<Opcode> opcodes = method.Opcodes;

            if (insertDebugCodes)
            {
                List<Opcode> debugged = new List<Opcode>();

                debugged.Add(Opcode.CreateDebugFile(method.SourceFile));

                /* Let's just check this string won't break things... */
                uint srcPos = (uint)this.stringMarshal.GetIDFor(method.SourceFile);
                if (srcPos > 255)
                {
                    /* ISSUE 10: Best fix may be to strip out debug opcodes. */
                    throw new SWFModellerException(
                            SWFModellerError.CodeMerge,
                            "Marshaller failed to keep debug string at low index");
                }

                uint lineNumber = 1; /* For our fake debug info. */
                foreach (Opcode op in opcodes)
                {
                    debugged.Add(Opcode.CreateDebugLine(lineNumber++));
                    debugged.Add(op);
                }

                opcodes = debugged;
            }

            Dictionary<Opcode, int> offsetMap = new Dictionary<Opcode, int>();
            Dictionary<Opcode, int> lenMap = new Dictionary<Opcode, int>();
            List<KeyValuePair<Opcode, KeyValuePair<Opcode, int>>> unresolvedOffsets = new List<KeyValuePair<Opcode, KeyValuePair<Opcode, int>>>();

            foreach (Opcode op in opcodes)
            {
                int startOffset = (int)writer.Offset;
                offsetMap.Add(op, startOffset);

                writer.WriteUI8(op.Instruction);

                if (op.Mnemonic == Opcode.Mnemonics.LookupSwitch)
                {
                    for (int i = 0; i < op.Args.Length; i++)
                    {
                        int argOffset = (int)writer.Offset - startOffset;
                        KeyValuePair<Opcode, int> sourceInfo = new KeyValuePair<Opcode, int>(op, argOffset);
                        object argOb = op.Args[i];
                        if (i == 1)
                        {
                            writer.WriteU30Packed((uint)(op.Args.Length - 3));
                        }
                        else
                        {
                            unresolvedOffsets.Add(new KeyValuePair<Opcode, KeyValuePair<Opcode, int>>((Opcode)argOb, sourceInfo));
                            writer.WriteSI24(0x00FFDEAD); /* Because dead beef won't fit in 3 bytes. */
                        }
                    }

                    lenMap.Add(op, 0);
                    continue;
                }

                Opcode.ArgType[] types = op.ArgTypes;
                if (types == null)
                {
                    /* done. */
                    lenMap.Add(op, (int)writer.Offset - startOffset);
                    continue;
                }

                if (types.Length != op.Args.Length)
                {
                    throw new SWFModellerException(
                            SWFModellerError.Internal,
                            "Arg mismatch in op " + op + " (" + types.Length + " expected)");
                }

                for (int i = 0; i < op.Args.Length; i++)
                {
                    object argOb = op.Args[i];
                    Opcode.ArgType argType = types[i];

                    switch (argType)
                    {
                        case Opcode.ArgType.MultinameU30:
                            writer.WriteU30Packed((uint)this.MultinameID((Multiname)argOb));
                            break;

                        case Opcode.ArgType.CountU30:
                        case Opcode.ArgType.UintU30:
                        case Opcode.ArgType.IntU30:
                        case Opcode.ArgType.LineNumberU30:
                        case Opcode.ArgType.RegisterU30:
                        case Opcode.ArgType.ObjectRegisterU30:
                        case Opcode.ArgType.PropertyRegisterU30:
                        case Opcode.ArgType.ShortU30:
                        case Opcode.ArgType.DoubleU30:
                            writer.WriteU30Packed((uint)argOb);
                            break;

                        case Opcode.ArgType.ByteU8:
                        case Opcode.ArgType.DebugU8:
                        case Opcode.ArgType.StackU8:
                            writer.WriteUI8((byte)argOb);
                            break;

                        case Opcode.ArgType.StringU8:
                            uint spos = (uint)this.stringMarshal.GetIDFor((string)argOb);
                            if (spos > 255)
                            {
                                /* ISSUE 10: Best fix may be to strip out debug opcodes. */
                                throw new SWFModellerException(
                                        SWFModellerError.Internal,
                                        "Marshaller failed to keep debug string at low index");
                            }

                            writer.WriteUI8(spos);
                            break;

                        case Opcode.ArgType.StringU30:
                            writer.WriteU30Packed((uint)this.stringMarshal.GetIDFor((string)argOb));
                            break;

                        case Opcode.ArgType.ClassU30:
                            writer.WriteU30Packed((uint)this.classMarshal.GetIDFor((AS3ClassDef)argOb));
                            break;

                        case Opcode.ArgType.ByteS8:
                            writer.WriteSI8((int)argOb);
                            break;

                        case Opcode.ArgType.ShortS30:
                            writer.WriteSI32((int)argOb); /* Spec says this signed value is a U30. Stupid spec. */
                            break;

                        case Opcode.ArgType.OffsetS24:
                            if (argOb is Opcode)
                            {
                                int argOffset = (int)writer.Offset - startOffset;
                                KeyValuePair<Opcode, int> sourceInfo = new KeyValuePair<Opcode, int>(op, argOffset);
                                unresolvedOffsets.Add(new KeyValuePair<Opcode, KeyValuePair<Opcode, int>>((Opcode)argOb, sourceInfo));
                                writer.WriteSI24(0x00FFDEAD); /* Because dead beef won't fit in 3 bytes. */
                            }
                            else
                            {
                                /* ISSUE 73 */
                                throw new SWFModellerException(
                                        SWFModellerError.UnimplementedFeature,
                                        "Unsupported op arg type in " + op.Mnemonic.ToString() + ": " + argType);
                            }

                            break;

                        case Opcode.ArgType.ExceptionU30:
                            writer.WriteU30Packed((uint)argOb); /* Reference into the fixed list of exception handlers. TODO: Make it not so fixed. */
                            break;

                        case Opcode.ArgType.SlotU30:
                            writer.WriteU30Packed((uint)argOb); /* Reference into the fixed list of exception handlers. TODO: Make it not so fixed. */
                            break;

                        case Opcode.ArgType.NamespaceU30:
                        case Opcode.ArgType.MethodU30:
                        case Opcode.ArgType.DebugTypeU30:
                        default:
                            /* ISSUE 73 */
                            throw new SWFModellerException(
                                    SWFModellerError.UnimplementedFeature,
                                    "Unsupported op arg type in "+op.Mnemonic.ToString()+": " + argType);
                    }
                }

                lenMap.Add(op, (int)writer.Offset - startOffset);
            }

            using (IEnumerator<ExceptionHandler> i = method.ExceptionHandlers)
            {
                while (i.MoveNext())
                {
                    ExceptionHandler eh = i.Current;
                    eh.From = offsetMap[eh.From];
                    eh.To = offsetMap[eh.To];
                    eh.Target = offsetMap[eh.Target];
                }
            }

            writer.Close(); /* Also closes the buffer. */

            byte[] byteBuffer = buf.ToArray();

            foreach (KeyValuePair<Opcode, KeyValuePair<Opcode, int>> link in unresolvedOffsets)
            {
                KeyValuePair<Opcode, int> sourceInfo = link.Value;
                int srcOffset = offsetMap[sourceInfo.Key];
                int argOffset = srcOffset + sourceInfo.Value;
                int val = offsetMap[link.Key] - srcOffset - lenMap[sourceInfo.Key];
                new ABCDataTypeWriter(new MemoryStream(byteBuffer, argOffset, 3)).WriteSI24(val);
            }

            method.Bytes = byteBuffer;
        }