furnace-avm2 0.9.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +3 -1
- data/bin/furnace-avm2 +27 -13
- data/bin/furnace-avm2-decompiler +14 -4
- data/bin/furnace-avm2-shell +4 -0
- data/furnace-avm2.gemspec +1 -1
- data/lib/furnace-avm2.rb +1 -1
- data/lib/furnace-avm2/abc.rb +3 -0
- data/lib/furnace-avm2/abc/metadata/exception_info.rb +7 -2
- data/lib/furnace-avm2/abc/metadata/method_body_info.rb +4 -2
- data/lib/furnace-avm2/abc/metadata/script_info.rb +8 -3
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_divide.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_equals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_modulo.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_not.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_strictequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic_opcode.rb +5 -0
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvalue.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvoid.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return_opcode.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/opcode.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_a.rb +4 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_b.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_s.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_d.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_i.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_o.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_s.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_u.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion_opcode.rb +10 -0
- data/lib/furnace-avm2/abc/primitives/opcode_sequence.rb +4 -0
- data/lib/furnace-avm2/abc/primitives/record.rb +21 -1
- data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +16 -8
- data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +7 -0
- data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +12 -4
- data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +10 -9
- data/lib/furnace-avm2/source/decompiler.rb +363 -166
- data/lib/furnace-avm2/source/implementation_tokens/case_token.rb +20 -0
- data/lib/furnace-avm2/source/implementation_tokens/catch_filter_token.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/catch_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/closure_token.rb +2 -0
- data/lib/furnace-avm2/source/implementation_tokens/control_flow_token.rb +0 -1
- data/lib/furnace-avm2/source/implementation_tokens/do_while_token.rb +16 -0
- data/lib/furnace-avm2/source/implementation_tokens/else_token.rb +3 -1
- data/lib/furnace-avm2/source/implementation_tokens/finally_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/label_name_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/this_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/try_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/unary_operator_token.rb +2 -2
- data/lib/furnace-avm2/source/implementation_tokens/unary_post_operator_token.rb +2 -2
- data/lib/furnace-avm2/transform.rb +2 -0
- data/lib/furnace-avm2/transform/ast_build.rb +234 -92
- data/lib/furnace-avm2/transform/ast_normalize.rb +4 -13
- data/lib/furnace-avm2/transform/cfg_build.rb +74 -33
- data/lib/furnace-avm2/transform/cfg_reduce.rb +457 -75
- data/lib/furnace-avm2/transform/nf_normalize.rb +69 -40
- data/lib/furnace-avm2/transform/propagate_constants.rb +49 -0
- data/lib/furnace-avm2/transform/propagate_labels.rb +31 -0
- data/lib/furnace-avm2/version.rb +1 -1
- data/test/basic.as +111 -3
- data/test/switch.as +27 -0
- metadata +907 -313
- data/Gemfile.lock +0 -19
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/bin/furnace-avm2
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "rubygems"
|
4
|
+
begin
|
5
|
+
require "bundler/setup"
|
6
|
+
rescue LoadError
|
7
|
+
end
|
4
8
|
|
5
9
|
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
6
10
|
|
@@ -30,7 +34,7 @@ EOS
|
|
30
34
|
opt :grep, "Search <s> in method names", :type => :string, :short => '-G'
|
31
35
|
|
32
36
|
opt :collect, "Collect failed methods instead of exiting", :default => false
|
33
|
-
opt :
|
37
|
+
opt :sort_by_size, "Sort methods by body size", :default => false
|
34
38
|
|
35
39
|
opt :disasm_before, "Disassemble methods before transforming", :default => false, :short => '-B'
|
36
40
|
opt :disasm_after, "Disassemble methods after transforming", :default => false, :short => '-A'
|
@@ -66,7 +70,7 @@ disasm = lambda do |body, after|
|
|
66
70
|
puts "Exceptions"
|
67
71
|
body.exceptions.each do |exception|
|
68
72
|
puts " #{exception.from_offset} -> #{exception.to_offset}: " <<
|
69
|
-
"catch(#{exception.
|
73
|
+
"catch(#{exception.exception.to_s} #{exception.variable.to_s}) #{exception.target_offset}"
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
@@ -75,7 +79,7 @@ end
|
|
75
79
|
|
76
80
|
failed = []
|
77
81
|
dced = []
|
78
|
-
|
82
|
+
by_body_size = {}
|
79
83
|
|
80
84
|
if opts[:fix_names]
|
81
85
|
abc.fix_names!
|
@@ -127,10 +131,8 @@ opts[:threads].times do
|
|
127
131
|
begin
|
128
132
|
disasm[body, false] if opts[:disasm_before]
|
129
133
|
|
130
|
-
if opts[:
|
131
|
-
|
132
|
-
smallest = body
|
133
|
-
end
|
134
|
+
if opts[:sort_by_size]
|
135
|
+
by_body_size[body] = body.code_length
|
134
136
|
end
|
135
137
|
|
136
138
|
if opts[:dce]
|
@@ -140,14 +142,14 @@ opts[:threads].times do
|
|
140
142
|
disasm[body, true] if opts[:disasm_after]
|
141
143
|
|
142
144
|
if opts[:ast]
|
143
|
-
ast = body.code_to_ast
|
145
|
+
ast, = body.code_to_ast
|
144
146
|
puts "Method #{body.method_idx}; AST"
|
145
147
|
puts ast.to_sexp
|
146
148
|
puts
|
147
149
|
end
|
148
150
|
|
149
151
|
if opts[:cfg]
|
150
|
-
cfg = body.code_to_cfg
|
152
|
+
cfg, = body.code_to_cfg
|
151
153
|
File.open("method-#{body.method_idx}.dot", "w") do |dot|
|
152
154
|
dot.write cfg.to_graphviz
|
153
155
|
end
|
@@ -158,7 +160,14 @@ opts[:threads].times do
|
|
158
160
|
"#{dominating.map(&:label).map(&:inspect).join(", ")}"
|
159
161
|
end
|
160
162
|
puts
|
161
|
-
|
163
|
+
=begin
|
164
|
+
puts "Method #{body.method_idx}; postdominators"
|
165
|
+
cfg.postdominators.each do |node, dominating|
|
166
|
+
puts "#{node.label.inspect} => " +
|
167
|
+
"#{dominating.map(&:label).map(&:inspect).join(", ")}"
|
168
|
+
end
|
169
|
+
puts
|
170
|
+
=end
|
162
171
|
puts "Method #{body.method_idx}; loops"
|
163
172
|
cfg.identify_loops.each do |header, body|
|
164
173
|
puts "#{header.label.inspect} => " +
|
@@ -168,7 +177,7 @@ opts[:threads].times do
|
|
168
177
|
end
|
169
178
|
|
170
179
|
if opts[:nf]
|
171
|
-
ast = body.code_to_nf
|
180
|
+
ast, = body.code_to_nf
|
172
181
|
puts "Method #{body.method_idx}; NF-AST"
|
173
182
|
puts ast.to_sexp
|
174
183
|
puts
|
@@ -200,8 +209,13 @@ if opts[:verbose]
|
|
200
209
|
end
|
201
210
|
end
|
202
211
|
|
203
|
-
if opts[:
|
204
|
-
puts "
|
212
|
+
if opts[:sort_by_size]
|
213
|
+
puts "Methods by body size:"
|
214
|
+
by_body_size.
|
215
|
+
sort_by { |(body, size)| size }.
|
216
|
+
each do |(body, size)|
|
217
|
+
puts "#{size}\tmethod ##{body.method_idx}"
|
218
|
+
end
|
205
219
|
end
|
206
220
|
|
207
221
|
if opts[:collect] && failed.any?
|
data/bin/furnace-avm2-decompiler
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "rubygems"
|
4
|
-
|
4
|
+
begin
|
5
|
+
require "bundler/setup"
|
6
|
+
rescue LoadError
|
7
|
+
end
|
5
8
|
|
6
9
|
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
7
10
|
|
@@ -102,7 +105,7 @@ start_time = Time.now
|
|
102
105
|
mutex = Mutex.new
|
103
106
|
|
104
107
|
roots = {}
|
105
|
-
workqueue = abc.instances
|
108
|
+
workqueue = abc.instances + abc.scripts
|
106
109
|
total_size = workqueue.size
|
107
110
|
|
108
111
|
last_percentage = 0
|
@@ -148,8 +151,14 @@ opts[:threads].times.map do
|
|
148
151
|
name = what.name
|
149
152
|
ns = name.ns
|
150
153
|
else # ScriptInfo
|
151
|
-
|
152
|
-
|
154
|
+
if what.has_name?
|
155
|
+
name = what.package_name.ns
|
156
|
+
ns = name.to_s.sub(/(^|\.)[^.]+$/, '')
|
157
|
+
else
|
158
|
+
index = abc.scripts.index(what)
|
159
|
+
name = "__global_name_#{index}"
|
160
|
+
ns = "__global_ns_#{index}"
|
161
|
+
end
|
153
162
|
end
|
154
163
|
next if shound_skip.(name)
|
155
164
|
|
@@ -164,6 +173,7 @@ opts[:threads].times.map do
|
|
164
173
|
|
165
174
|
source = what.decompile(options)
|
166
175
|
next unless source
|
176
|
+
next unless source.children.any?
|
167
177
|
|
168
178
|
text = source.to_text
|
169
179
|
|
data/bin/furnace-avm2-shell
CHANGED
data/furnace-avm2.gemspec
CHANGED
@@ -17,6 +17,6 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.add_runtime_dependency "furnace", '
|
20
|
+
s.add_runtime_dependency "furnace", '= 0.2.3'
|
21
21
|
s.add_runtime_dependency "trollop"
|
22
22
|
end
|
data/lib/furnace-avm2.rb
CHANGED
data/lib/furnace-avm2/abc.rb
CHANGED
@@ -16,7 +16,10 @@ require_relative "abc/opcodes/load_store_opcode"
|
|
16
16
|
require_relative "abc/opcodes/push_literal_opcode"
|
17
17
|
require_relative "abc/opcodes/control_transfer_opcode"
|
18
18
|
require_relative "abc/opcodes/function_invocation_opcode"
|
19
|
+
require_relative "abc/opcodes/function_return_opcode"
|
19
20
|
require_relative "abc/opcodes/property_opcode"
|
21
|
+
require_relative "abc/opcodes/type_conversion_opcode"
|
22
|
+
require_relative "abc/opcodes/arithmetic_opcode"
|
20
23
|
|
21
24
|
Dir[File.join(File.dirname(__FILE__), "abc", "opcodes", "*", "*.rb")].each do |file|
|
22
25
|
require file
|
@@ -3,8 +3,9 @@ module Furnace::AVM2::ABC
|
|
3
3
|
vuint30 :from_offset
|
4
4
|
vuint30 :to_offset
|
5
5
|
vuint30 :target_offset
|
6
|
-
|
7
|
-
const_ref :
|
6
|
+
|
7
|
+
const_ref :exception, :multiname
|
8
|
+
const_ref :variable, :multiname
|
8
9
|
|
9
10
|
attr_reader :from, :to, :target
|
10
11
|
|
@@ -25,5 +26,9 @@ module Furnace::AVM2::ABC
|
|
25
26
|
self.to_offset = @to.offset
|
26
27
|
self.target_offset = @target.offset
|
27
28
|
end
|
29
|
+
|
30
|
+
def range
|
31
|
+
from_offset..to_offset
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
@@ -21,10 +21,11 @@ module Furnace::AVM2::ABC
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def code_to_ast(options={
|
24
|
+
def code_to_ast(options={})
|
25
25
|
pipeline = Furnace::Transform::Pipeline.new([
|
26
26
|
Furnace::AVM2::Transform::ASTBuild.new(options),
|
27
|
-
Furnace::AVM2::Transform::ASTNormalize.new(options)
|
27
|
+
Furnace::AVM2::Transform::ASTNormalize.new(options),
|
28
|
+
Furnace::AVM2::Transform::PropagateConstants.new,
|
28
29
|
])
|
29
30
|
|
30
31
|
pipeline.run(code, self)
|
@@ -32,6 +33,7 @@ module Furnace::AVM2::ABC
|
|
32
33
|
|
33
34
|
def code_to_cfg
|
34
35
|
pipeline = Furnace::Transform::Pipeline.new([
|
36
|
+
Furnace::AVM2::Transform::PropagateLabels.new,
|
35
37
|
Furnace::AVM2::Transform::CFGBuild.new
|
36
38
|
])
|
37
39
|
|
@@ -8,7 +8,8 @@ module Furnace::AVM2::ABC
|
|
8
8
|
abc_array_of :trait, :nested, :class => TraitInfo
|
9
9
|
|
10
10
|
def collect_ns
|
11
|
-
options = { ns: Set.new
|
11
|
+
options = { ns: Set.new, no_ns: Set.new, names: {} }
|
12
|
+
options[:ns].add package_name.ns if has_name?
|
12
13
|
|
13
14
|
initializer.collect_ns(options) if initializer
|
14
15
|
traits.each { |trait| trait.collect_ns(options) }
|
@@ -20,14 +21,18 @@ module Furnace::AVM2::ABC
|
|
20
21
|
Furnace::AVM2::Tokens::PackageToken.new(self,
|
21
22
|
options.merge(collect_ns).merge(
|
22
23
|
package_type: :script,
|
23
|
-
package_name:
|
24
|
+
package_name: package_name)
|
24
25
|
)
|
25
26
|
end
|
26
27
|
|
27
|
-
def
|
28
|
+
def has_name?
|
28
29
|
non_init_traits.any?
|
29
30
|
end
|
30
31
|
|
32
|
+
def package_name
|
33
|
+
non_init_traits[0].name if non_init_traits.any?
|
34
|
+
end
|
35
|
+
|
31
36
|
def non_init_traits
|
32
37
|
traits.reject { |trait| trait.kind == :Class }
|
33
38
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Furnace::AVM2::ABC
|
2
|
-
class AS3IncLocalI <
|
2
|
+
class AS3IncLocalI < ArithmeticOpcode
|
3
3
|
instruction 0xc2
|
4
4
|
|
5
5
|
body do
|
@@ -10,6 +10,7 @@ module Furnace::AVM2::ABC
|
|
10
10
|
produce 0
|
11
11
|
|
12
12
|
type :integer
|
13
|
+
ast_type :inc_local
|
13
14
|
|
14
15
|
def parameters
|
15
16
|
[ body.reg_index ]
|