Skip to content

Repository files navigation

flashkit

Parse, analyze, decompile, and rebuild Adobe Flash SWF files and AVM2 bytecode.

flashkit is a pure-Python toolkit for working with the SWF container format and the AVM2 bytecode that runs ActionScript 3. It covers everything from low-level pool surgery to full AS3 source recovery, with a CLI for one-off questions and a programmatic API for building tools on top.

Install

pip install pyflashkit

Or from source:

git clone https://github.com/bitalizer/pyflashkit.git
cd pyflashkit
pip install -e .[dev] # ``[dev]`` adds pytest + pytest-cov

Python 3.10+. No runtime dependencies.

Quick start

fromflashkit.workspaceimportWorkspacefromflashkit.decompileimportdecompile_classws=Workspace()
ws.load_swf("application.swf")
# Inspect a classplayer=ws.get_class("PlayerManager")
print(player.super_name) # "EventDispatcher"print(player.fields[0].name, player.fields[0].type_name)
# Find every class extending Spriteforclsinws.find_classes(extends="Sprite"):
print(cls.qualified_name)
# Recover AS3 source from bytecodeprint(decompile_class(ws, name="PlayerManager"))

Features

  • SWF container — parse, build, and round-trip every standard tag.
  • AVM2 bytecode — parse to typed dataclasses, modify, write back with byte-perfect fidelity.
  • AS3 decompiler — CFG-based pipeline (basic blocks → dominators → loop nesting → stack simulation → structuring → idiom rewrites → AS3 source). Cross-block dataflow handles conditionals whose operands cross block boundaries.
  • Disassembler — raw and resolved instruction views.
  • Workspace — multi-SWF loading with cached cross-reference, string, field-access, inheritance, and call-graph indexes built in a single bytecode scan.
  • Analysis layer — register liveness, constant-argument inference at call sites, dead-class / dead-method detection, entry-point candidates, McCabe cyclomatic complexity.
  • CLIflashkit info / classes / class / strings / disasm / decompile / pool / tree / refs / callers / callees / fields / packages / extract / build.

CLI

flashkit info

$ flashkit info application.swf
File: application.swf
Format: SWF
SWF version: 40
Tags: 142
ABC blocks: 1
Classes: 823
Methods: 14210
Strings: 35482
Packages: 47

flashkit decompile

flashkit decompile app.swf --list # list classes
flashkit decompile app.swf --class PlayerManager # AS3 source for one class
flashkit decompile app.swf --class PlayerManager \
--method takeDamage # one method
flashkit decompile app.swf --all --outdir decompiled/ # whole SWF to disk

flashkit disasm

flashkit disasm app.swf --class PlayerManager
flashkit disasm app.swf --method-index 42
flashkit disasm app.swf --class Foo --raw # raw pool indices instead of names

Operands are resolved by default — getlex DevSettings, pushstring "noScale", setproperty scaleMode — so output reads next to AS3 source. Use --raw for pool-index debugging.

flashkit pool

Inspect any ABC constant pool.

flashkit pool app.swf multinames
flashkit pool app.swf strings -s "level"
flashkit pool app.swf namespaces -s flash
flashkit pool app.swf ints
flashkit pool app.swf doubles
flashkit pool app.swf namespace-sets

flashkit class

$ flashkit class application.swf PlayerManager
PlayerManager
Package: com.game
Extends: EventDispatcher
Implements: IDisposable, ITickable
Instance Fields (3)
mHealth: Number
mName: String
mLevel: int
Instance Methods (5)
init(): void
get name(): String
set name(value: String): void
takeDamage(amount: Number): void
serialize(): ByteArray

flashkit classes

flashkit classes app.swf # all classes
flashkit classes app.swf -s Manager # search by name
flashkit classes app.swf -p com.game # filter by package
flashkit classes app.swf -e Sprite # filter by superclass
flashkit classes app.swf -i # interfaces only
flashkit classes app.swf -v # verbose output

flashkit strings

flashkit strings app.swf # list all
flashkit strings app.swf -s config # search
flashkit strings app.swf -s config -v # with usage locations
flashkit strings app.swf -s "\\d+" -r # regex
flashkit strings app.swf -c # classify (URLs, debug)

flashkit tree / refs / callers / callees / fields

flashkit tree app.swf BaseEntity # show descendants
flashkit tree app.swf PlayerManager -a # show ancestors
flashkit refs app.swf Point # all references to a name
flashkit callers app.swf toString # call graph: who calls X
flashkit callees app.swf PlayerManager.init # call graph: what X calls
flashkit fields app.swf PlayerManager # field R/W summary
flashkit fields app.swf PlayerManager -f mHealth # readers/writers of one field
flashkit fields app.swf PlayerManager -m takeDamage # what fields a method touches

flashkit packages / extract / build / tags

flashkit tags app.swf # list raw SWF tags
flashkit packages app.swf # list packages
flashkit extract app.swf -o ./output # extract ABC blocks to disk
flashkit build app.swf -o rebuilt.swf # rebuild (compressed)
flashkit build app.swf -o out.swf -d # rebuild (decompressed)

Library

Workspace — load and query

fromflashkit.workspaceimportWorkspacews=Workspace()
ws.load_swf("application.swf")
ws.load_swz("module.swz")
print(ws.summary())
cls=ws.get_class("MyClass")
print(cls.name, cls.super_name, cls.interfaces)
print(cls.fields) # list of FieldInfoprint(cls.methods) # list of MethodInfoResolvedws.find_classes(extends="Sprite")
ws.find_classes(package="com.example", is_interface=True)

Decompiler

Three granularities, all accept either a Workspace or a parsed AbcFile:

fromflashkit.decompileimport (
decompile_class, decompile_method, decompile_method_body,
list_classes, ClassSummary, DecompilerCache,
)
src=decompile_class(ws, name="com.game.Player")
src=decompile_method(ws, class_name="com.game.Player", name="update")
# Typed metadata rows (also accept dict-style ``c["name"]`` for legacy code)forcinlist_classes(ws):
print(c.full_name, c.trait_count)
# Cache parses + decompilers across many lookups on the same SWFcache=DecompilerCache()
cache.list_classes("game.swf")
cache.decompile_class("game.swf", "Player")
cache.decompile_method("game.swf", "Player", "update")

Analysis

All indexes are built lazily on first access and cached on the workspace. One bytecode scan populates strings, references, and field access together.

# Inheritancews.get_subclasses("BaseEntity")
ws.get_descendants("BaseEntity") # transitivews.get_ancestors("PlayerManager")
ws.get_implementors("ISerializable")
# Call graphws.callers("toString")
ws.callees("PlayerManager.init")
# Referencesws.references_to("Point")
ws.references_from("PlayerManager")
ws.find_instantiators("Point")
ws.find_type_users("ByteArray")
# Stringsws.search_strings("config")
ws.classes_using_string("http://example.com")
ws.strings_in_class("PlayerManager")
# Field accessws.field_writers("PlayerManager", "mHealth")
ws.field_readers("PlayerManager", "mHealth")
ws.fields_written_by("PlayerManager", "takeDamage")
ws.fields_read_by("PlayerManager", "takeDamage")
ws.constructor_assignments("PlayerManager")
ws.field_access_summary("PlayerManager")
# Structuralws.find_classes_with_field_type("ByteArray")
ws.find_methods(return_type="String", name="get")
ws.find_fields(type_name="int")

Deeper analysis

fromflashkit.analysisimport (
method_liveness, ConstArgIndex,
find_dead_classes, find_dead_methods, entrypoint_candidates,
method_complexity,
)
# Per-method register liveness — useful for ``_loc3_`` rename heuristicsabc=ws.abc_blocks[0]
liv=method_liveness(abc, abc.method_bodies[0])
print(liv.read_counts, liv.write_counts)
# Constant-argument inference at every call siteconst_args=ConstArgIndex.from_workspace(ws)
print(const_args.distinct_arg_values("SetFlag", slot=0)) # e.g. {0, 1, 4, 8}# Dead-code detection (heuristic — AS3 dynamic dispatch can't be proven away)print(find_dead_classes(ws))
print(find_dead_methods(ws))
# Entry-point candidates — Sprite / MovieClip / EventDispatcher subclassesprint(entrypoint_candidates(ws))
# McCabe cyclomatic complexity per method bodymc=method_complexity(abc, abc.method_bodies[0])
print(mc.complexity, mc.block_count)

Parse SWF and ABC directly

fromflashkit.swfimportparse_swf, TAG_DO_ABC2fromflashkit.abcimportparse_abc, serialize_abcheader, tags, version, length=parse_swf(swf_bytes)
fortagintags:
iftag.tag_type==TAG_DO_ABC2:
null_idx=tag.payload.index(0, 4)
abc=parse_abc(tag.payload[null_idx+1:])
print(f"{len(abc.instances)} classes, {len(abc.methods)} methods")
# Round-trip fidelity: serialize(parse(data)) == dataassertserialize_abc(abc) ==tag.payload[null_idx+1:]

Build SWF programmatically

fromflashkit.abcimportAbcBuilder, serialize_abcfromflashkit.swfimportSwfBuilderb=AbcBuilder()
b.simple_class("Player", package="com.game",
fields=[("hp", "int"), ("name", "String")])
b.script()
abc_bytes=serialize_abc(b.build())
swf=SwfBuilder(version=40, width=800, height=600, fps=30)
swf.add_abc("GameCode", abc_bytes)
swf_bytes=swf.build(compress=True)

Disassemble method bodies

fromflashkit.abcimportdecode_instructions, resolve_instructionsforbodyinabc.method_bodies:
# Raw — pool indices as integersforinstrindecode_instructions(body.code):
print(f"0x{instr.offset:04X}{instr.mnemonic}{instr.operands}")
# Resolved — names / strings / literalsforrinresolve_instructions(abc, decode_instructions(body.code)):
print(f"0x{r.offset:04X}{r.mnemonic}{', '.join(r.operands)}")

AVM2 constants

The structural constants (multiname kinds, trait kinds, attribute flags, method/instance flags) are re-exported at the package level so a TraitInfo can be classified without reaching into the submodule:

fromflashkit.abcimport (
CONSTANT_QNAME, CONSTANT_TYPENAME,
TRAIT_SLOT, TRAIT_METHOD, TRAIT_GETTER,
ATTR_OVERRIDE, METHOD_HAS_PARAM_NAMES, INSTANCE_INTERFACE,
)

Project structure

flashkit/
cli/ CLI (one module per command)
swf/ SWF container (parse, build, tags)
abc/ AVM2 bytecode (parse, write, disasm, builder)
info/ Resolved class model (ClassInfo, FieldInfo, MethodInfo)
workspace/ File loading and class index
analysis/ Inheritance, call graph, references, strings,
field access, liveness, const-args, dead code,
complexity, method fingerprints, class graph
decompile/ CFG-based AS3 decompiler
graph/ CFG, dominators, loop detection (used by decompiler)

References

License

MIT

About

Python toolkit for SWF + AVM2 bytecode — parser, AS3 decompiler, disassembler, analysis indexes, and CLI.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages