node-marshal 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/COPYING +23 -23
- data/README.rdoc +56 -49
- data/bin/noderbc +63 -63
- data/bin/noderbc.bat +0 -0
- data/ext/node-marshal/base85r.c +194 -194
- data/ext/node-marshal/extconf.rb +2 -2
- data/ext/node-marshal/node193.h +321 -321
- data/ext/node-marshal/node220.h +347 -347
- data/ext/node-marshal/node230.h +361 -361
- data/ext/node-marshal/nodedump.c +2356 -2296
- data/ext/node-marshal/nodedump.h +60 -60
- data/ext/node-marshal/nodeinfo.c +619 -615
- data/lib/node-marshal.rb +227 -227
- data/test/lifegame.rb +145 -145
- data/test/test_base.rb +226 -226
- data/test/test_complex.rb +136 -136
- data/test/test_lifegame.rb +68 -68
- data/test/test_namedarg.rb +54 -0
- data/test/test_obfuscator.rb +36 -36
- data/test/test_qcall.rb +52 -52
- data/test/tinytet.rb +79 -79
- metadata +4 -3
data/lib/node-marshal.rb
CHANGED
@@ -1,227 +1,227 @@
|
|
1
|
-
require_relative '../ext/node-marshal/nodemarshal.so'
|
2
|
-
begin
|
3
|
-
require 'zlib'
|
4
|
-
rescue LoadError
|
5
|
-
# If zlib library is absent in the system no support for
|
6
|
-
# compression will be provided in
|
7
|
-
end
|
8
|
-
|
9
|
-
# Implementation of Array::to_h method for Ruby 1.9 (and probably 2.0)
|
10
|
-
# Don't use for Ruby 2.2.x and Ruby 2.3.x
|
11
|
-
if !defined?([].to_h)
|
12
|
-
class Array
|
13
|
-
def to_h
|
14
|
-
h = {}
|
15
|
-
a = self
|
16
|
-
a.each do |x|
|
17
|
-
raise "wrong array length" if x.length != 2
|
18
|
-
h[x[0]] = x[1];
|
19
|
-
end
|
20
|
-
return h
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class NodeMarshal
|
26
|
-
# call-seq:
|
27
|
-
# obj.to_compiled_rb(outfile, opts)
|
28
|
-
#
|
29
|
-
# Transforms node to the Ruby file
|
30
|
-
# - +outfile+ -- name of the output file
|
31
|
-
# - +opts+ -- Hash with options (+:compress+, +:so_path+)
|
32
|
-
# +:compress+ can be +true+ or +false+, +:so_path+ is a test string
|
33
|
-
# with the command for nodemarshal.so inclusion (default is
|
34
|
-
# <tt>require_relative '../ext/node-marshal/nodemarshal.so'</tt>)
|
35
|
-
#
|
36
|
-
# See also NodeMarshal::compile_rb_file
|
37
|
-
def to_compiled_rb(outfile, *args)
|
38
|
-
compress = true
|
39
|
-
so_path = "require_relative '../ext/node-marshal/nodemarshal.so'"
|
40
|
-
if args.length > 0
|
41
|
-
opts = args[0]
|
42
|
-
if opts.has_key?(:compress)
|
43
|
-
compress = opts[:compress]
|
44
|
-
end
|
45
|
-
if opts.has_key?(:so_path)
|
46
|
-
so_path = opts[:so_path]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
# Compression
|
50
|
-
if compress
|
51
|
-
if !defined?(Zlib)
|
52
|
-
raise "Compression is not supported: Zlib is absent"
|
53
|
-
end
|
54
|
-
zlib_include = "require 'zlib'"
|
55
|
-
data_txt = NodeMarshal.base85r_encode(Zlib::deflate(self.to_bin))
|
56
|
-
data_bin = "Zlib::inflate(NodeMarshal.base85r_decode(data_txt))"
|
57
|
-
else
|
58
|
-
zlib_include = "# No compression"
|
59
|
-
data_txt = self.to_text
|
60
|
-
data_bin = "NodeMarshal.base85r_decode(data_txt)"
|
61
|
-
end
|
62
|
-
# Document header
|
63
|
-
txt = <<EOS
|
64
|
-
# Ruby compressed source code
|
65
|
-
# RUBY_PLATFORM: #{RUBY_PLATFORM}
|
66
|
-
# RUBY_VERSION: #{RUBY_VERSION}
|
67
|
-
#{zlib_include}
|
68
|
-
#{so_path}
|
69
|
-
data_txt = <<DATABLOCK
|
70
|
-
#{data_txt}
|
71
|
-
DATABLOCK
|
72
|
-
data_bin = #{data_bin}
|
73
|
-
node = NodeMarshal.new(:binmemory, data_bin)
|
74
|
-
node.filename = __FILE__
|
75
|
-
node.filepath = File.expand_path(node.filename)
|
76
|
-
node.compile.eval
|
77
|
-
EOS
|
78
|
-
# Process input arguments
|
79
|
-
if outfile != nil
|
80
|
-
File.open(outfile, 'w') {|fp| fp << txt}
|
81
|
-
end
|
82
|
-
return txt
|
83
|
-
end
|
84
|
-
|
85
|
-
# call-seq:
|
86
|
-
# NodeMarshal::compile_rb_file(outfile, inpfile, opts)
|
87
|
-
#
|
88
|
-
# Reads +.rb+ file (Ruby source) and compiles it to .rb file containing
|
89
|
-
# compressed AST node and its loader. This functions is an envelope for
|
90
|
-
# NodeMarshal#to_compiled_rb
|
91
|
-
def self.compile_rb_file(outfile, inpfile, *args)
|
92
|
-
node = NodeMarshal.new(:srcfile, inpfile)
|
93
|
-
node.to_compiled_rb(outfile, *args)
|
94
|
-
return true
|
95
|
-
end
|
96
|
-
|
97
|
-
# call-seq:
|
98
|
-
# obj.replace_symbols(syms_subs)
|
99
|
-
#
|
100
|
-
# Replaces some symbols inside parsed AST to user-defined aliases.
|
101
|
-
# It is designed to make code obfuscation easier. Be careful when using
|
102
|
-
# this ability: it is possible to break external libraries calls,
|
103
|
-
# operators overloading and some metaprogramming techniques.
|
104
|
-
# - +syms_subs+ -- Hash with the table of aliases. Keys are original names,
|
105
|
-
# values are aliases. Keys and values MUST BE strings (not symbols!).
|
106
|
-
def replace_symbols(syms_subs)
|
107
|
-
# Check input data
|
108
|
-
# a) type
|
109
|
-
if !(syms_subs.is_a?(Hash))
|
110
|
-
raise "symb_subs must be a hash"
|
111
|
-
end
|
112
|
-
# b) uniqueness of values inside the hash
|
113
|
-
values = syms_subs.values
|
114
|
-
if values.size != values.uniq.size
|
115
|
-
raise ArgumentError, "values (new names) must be unique"
|
116
|
-
end
|
117
|
-
# c) uniqueness of values after replacement
|
118
|
-
# TODO: MAKE IT!!!
|
119
|
-
# Use NodeMarshal C part to replace the symbols
|
120
|
-
self.to_hash # To initialize Hash with preparsed Ruby AST NODE
|
121
|
-
syms_subs.each do |key, value|
|
122
|
-
change_symbol(key, value)
|
123
|
-
end
|
124
|
-
self
|
125
|
-
end
|
126
|
-
|
127
|
-
# call-seq:
|
128
|
-
# obj.get_safe_symbols(our_symbols)
|
129
|
-
#
|
130
|
-
# Returns an array that contains strings with the names of symbols that are safe
|
131
|
-
# to change. It excludes symbols that are present in the table of literals (and their derivatives
|
132
|
-
# such as @x and x=). Such operation is useful for attr_readed, attr_writer and another similar
|
133
|
-
# metaprogramming techniques handling
|
134
|
-
#
|
135
|
-
# - +our_symbols+ symbols created during node creation (must be found manually by the user
|
136
|
-
# by means of Symbol.all_symbols calling BEFORE and AFTER node creation.
|
137
|
-
def get_safe_symbols(our_symbols)
|
138
|
-
self.to_hash # To initialize Hash with preparsed Ruby AST NODE
|
139
|
-
symbolic_literals = self.literals.select {|x| x.is_a?(Symbol)}.map {|x| x.to_s}
|
140
|
-
fixed_symbols = [] + symbolic_literals
|
141
|
-
fixed_symbols += symbolic_literals.map {|x| "@#{x}"}
|
142
|
-
fixed_symbols += symbolic_literals.map {|x| "#{x}="}
|
143
|
-
our_symbols = our_symbols.dup
|
144
|
-
our_symbols -= fixed_symbols
|
145
|
-
end
|
146
|
-
|
147
|
-
# call-seq:
|
148
|
-
# obj.get_aliases_table(our_symbols)
|
149
|
-
#
|
150
|
-
# Returns a hash that has {"old_sym_name"=>"new_sym_name",...} format.
|
151
|
-
# "new_sym_name" are generated automatically.
|
152
|
-
#
|
153
|
-
# - +our_symbols+ -- An array that contains the list of symbols (AS STRINGS,
|
154
|
-
# NOT AS SYMBOLS) that can be renamed.
|
155
|
-
def get_aliases_table(our_symbols)
|
156
|
-
symbols_ary = get_safe_symbols(our_symbols)
|
157
|
-
pos = 0;
|
158
|
-
aliases_ary = symbols_ary.map do |sym|
|
159
|
-
pos += 1
|
160
|
-
if sym.length > 1 && sym[0..1] == '@@'
|
161
|
-
"@@q#{pos}"
|
162
|
-
elsif sym[0] == '@'
|
163
|
-
"@q#{pos}"
|
164
|
-
elsif sym[0] =~ /[A-Z]/
|
165
|
-
"Q#{pos}"
|
166
|
-
elsif sym[0] =~ /[a-z]/
|
167
|
-
"q#{pos}"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
[symbols_ary, aliases_ary].transpose.to_h
|
171
|
-
end
|
172
|
-
|
173
|
-
# call-seq:
|
174
|
-
# obj.rename_ivars
|
175
|
-
def rename_ivars(*args)
|
176
|
-
if args.size == 0
|
177
|
-
excl_names = []
|
178
|
-
else
|
179
|
-
excl_names = args[0]
|
180
|
-
end
|
181
|
-
|
182
|
-
to_hash
|
183
|
-
syms = @nodehash[:symbols].select {|x| (x =~ /@[^@]/) == 0}
|
184
|
-
pos = 1;
|
185
|
-
syms_new = syms.map do |x|
|
186
|
-
if excl_names.find_index(x[1..-1]) != nil
|
187
|
-
str = x
|
188
|
-
else
|
189
|
-
str = "@ivar#{pos}"
|
190
|
-
end
|
191
|
-
pos = pos + 1;
|
192
|
-
str
|
193
|
-
end
|
194
|
-
syms_subs = [syms, syms_new].transpose.to_h
|
195
|
-
replace_symbols(syms_subs)
|
196
|
-
self
|
197
|
-
end
|
198
|
-
|
199
|
-
# call-seq:
|
200
|
-
# obj.rebuild
|
201
|
-
#
|
202
|
-
# Rebuilds the node by converting it to the binary dump and further restoring
|
203
|
-
# of it from this dump. It doesn't change the original node and returns rebuilt
|
204
|
-
# node.
|
205
|
-
def rebuild
|
206
|
-
NodeMarshal.new(:binmemory, to_bin)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# Designed for the logging of symbols table changes. When an example of
|
211
|
-
# SymbolsLogger is created the current global table of symbols is saved
|
212
|
-
# inside it. The created example can be used for finding new symbols in
|
213
|
-
# the global table. This is useful for code obfuscation.
|
214
|
-
class SymbolsLogger
|
215
|
-
def initialize
|
216
|
-
@symtbl_old = Symbol.all_symbols
|
217
|
-
end
|
218
|
-
|
219
|
-
def new_symbols
|
220
|
-
symtbl_new = Symbol.all_symbols
|
221
|
-
symtbl_new - @symtbl_old
|
222
|
-
end
|
223
|
-
|
224
|
-
def update
|
225
|
-
@symtbl_old = Symbol.all_symbols
|
226
|
-
end
|
227
|
-
end
|
1
|
+
require_relative '../ext/node-marshal/nodemarshal.so'
|
2
|
+
begin
|
3
|
+
require 'zlib'
|
4
|
+
rescue LoadError
|
5
|
+
# If zlib library is absent in the system no support for
|
6
|
+
# compression will be provided in
|
7
|
+
end
|
8
|
+
|
9
|
+
# Implementation of Array::to_h method for Ruby 1.9 (and probably 2.0)
|
10
|
+
# Don't use for Ruby 2.2.x and Ruby 2.3.x
|
11
|
+
if !defined?([].to_h)
|
12
|
+
class Array
|
13
|
+
def to_h
|
14
|
+
h = {}
|
15
|
+
a = self
|
16
|
+
a.each do |x|
|
17
|
+
raise "wrong array length" if x.length != 2
|
18
|
+
h[x[0]] = x[1];
|
19
|
+
end
|
20
|
+
return h
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class NodeMarshal
|
26
|
+
# call-seq:
|
27
|
+
# obj.to_compiled_rb(outfile, opts)
|
28
|
+
#
|
29
|
+
# Transforms node to the Ruby file
|
30
|
+
# - +outfile+ -- name of the output file
|
31
|
+
# - +opts+ -- Hash with options (+:compress+, +:so_path+)
|
32
|
+
# +:compress+ can be +true+ or +false+, +:so_path+ is a test string
|
33
|
+
# with the command for nodemarshal.so inclusion (default is
|
34
|
+
# <tt>require_relative '../ext/node-marshal/nodemarshal.so'</tt>)
|
35
|
+
#
|
36
|
+
# See also NodeMarshal::compile_rb_file
|
37
|
+
def to_compiled_rb(outfile, *args)
|
38
|
+
compress = true
|
39
|
+
so_path = "require_relative '../ext/node-marshal/nodemarshal.so'"
|
40
|
+
if args.length > 0
|
41
|
+
opts = args[0]
|
42
|
+
if opts.has_key?(:compress)
|
43
|
+
compress = opts[:compress]
|
44
|
+
end
|
45
|
+
if opts.has_key?(:so_path)
|
46
|
+
so_path = opts[:so_path]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# Compression
|
50
|
+
if compress
|
51
|
+
if !defined?(Zlib)
|
52
|
+
raise "Compression is not supported: Zlib is absent"
|
53
|
+
end
|
54
|
+
zlib_include = "require 'zlib'"
|
55
|
+
data_txt = NodeMarshal.base85r_encode(Zlib::deflate(self.to_bin))
|
56
|
+
data_bin = "Zlib::inflate(NodeMarshal.base85r_decode(data_txt))"
|
57
|
+
else
|
58
|
+
zlib_include = "# No compression"
|
59
|
+
data_txt = self.to_text
|
60
|
+
data_bin = "NodeMarshal.base85r_decode(data_txt)"
|
61
|
+
end
|
62
|
+
# Document header
|
63
|
+
txt = <<EOS
|
64
|
+
# Ruby compressed source code
|
65
|
+
# RUBY_PLATFORM: #{RUBY_PLATFORM}
|
66
|
+
# RUBY_VERSION: #{RUBY_VERSION}
|
67
|
+
#{zlib_include}
|
68
|
+
#{so_path}
|
69
|
+
data_txt = <<DATABLOCK
|
70
|
+
#{data_txt}
|
71
|
+
DATABLOCK
|
72
|
+
data_bin = #{data_bin}
|
73
|
+
node = NodeMarshal.new(:binmemory, data_bin)
|
74
|
+
node.filename = __FILE__
|
75
|
+
node.filepath = File.expand_path(node.filename)
|
76
|
+
node.compile.eval
|
77
|
+
EOS
|
78
|
+
# Process input arguments
|
79
|
+
if outfile != nil
|
80
|
+
File.open(outfile, 'w') {|fp| fp << txt}
|
81
|
+
end
|
82
|
+
return txt
|
83
|
+
end
|
84
|
+
|
85
|
+
# call-seq:
|
86
|
+
# NodeMarshal::compile_rb_file(outfile, inpfile, opts)
|
87
|
+
#
|
88
|
+
# Reads +.rb+ file (Ruby source) and compiles it to .rb file containing
|
89
|
+
# compressed AST node and its loader. This functions is an envelope for
|
90
|
+
# NodeMarshal#to_compiled_rb
|
91
|
+
def self.compile_rb_file(outfile, inpfile, *args)
|
92
|
+
node = NodeMarshal.new(:srcfile, inpfile)
|
93
|
+
node.to_compiled_rb(outfile, *args)
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
|
97
|
+
# call-seq:
|
98
|
+
# obj.replace_symbols(syms_subs)
|
99
|
+
#
|
100
|
+
# Replaces some symbols inside parsed AST to user-defined aliases.
|
101
|
+
# It is designed to make code obfuscation easier. Be careful when using
|
102
|
+
# this ability: it is possible to break external libraries calls,
|
103
|
+
# operators overloading and some metaprogramming techniques.
|
104
|
+
# - +syms_subs+ -- Hash with the table of aliases. Keys are original names,
|
105
|
+
# values are aliases. Keys and values MUST BE strings (not symbols!).
|
106
|
+
def replace_symbols(syms_subs)
|
107
|
+
# Check input data
|
108
|
+
# a) type
|
109
|
+
if !(syms_subs.is_a?(Hash))
|
110
|
+
raise "symb_subs must be a hash"
|
111
|
+
end
|
112
|
+
# b) uniqueness of values inside the hash
|
113
|
+
values = syms_subs.values
|
114
|
+
if values.size != values.uniq.size
|
115
|
+
raise ArgumentError, "values (new names) must be unique"
|
116
|
+
end
|
117
|
+
# c) uniqueness of values after replacement
|
118
|
+
# TODO: MAKE IT!!!
|
119
|
+
# Use NodeMarshal C part to replace the symbols
|
120
|
+
self.to_hash # To initialize Hash with preparsed Ruby AST NODE
|
121
|
+
syms_subs.each do |key, value|
|
122
|
+
change_symbol(key, value)
|
123
|
+
end
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# call-seq:
|
128
|
+
# obj.get_safe_symbols(our_symbols)
|
129
|
+
#
|
130
|
+
# Returns an array that contains strings with the names of symbols that are safe
|
131
|
+
# to change. It excludes symbols that are present in the table of literals (and their derivatives
|
132
|
+
# such as @x and x=). Such operation is useful for attr_readed, attr_writer and another similar
|
133
|
+
# metaprogramming techniques handling
|
134
|
+
#
|
135
|
+
# - +our_symbols+ symbols created during node creation (must be found manually by the user
|
136
|
+
# by means of Symbol.all_symbols calling BEFORE and AFTER node creation.
|
137
|
+
def get_safe_symbols(our_symbols)
|
138
|
+
self.to_hash # To initialize Hash with preparsed Ruby AST NODE
|
139
|
+
symbolic_literals = self.literals.select {|x| x.is_a?(Symbol)}.map {|x| x.to_s}
|
140
|
+
fixed_symbols = [] + symbolic_literals
|
141
|
+
fixed_symbols += symbolic_literals.map {|x| "@#{x}"}
|
142
|
+
fixed_symbols += symbolic_literals.map {|x| "#{x}="}
|
143
|
+
our_symbols = our_symbols.dup
|
144
|
+
our_symbols -= fixed_symbols
|
145
|
+
end
|
146
|
+
|
147
|
+
# call-seq:
|
148
|
+
# obj.get_aliases_table(our_symbols)
|
149
|
+
#
|
150
|
+
# Returns a hash that has {"old_sym_name"=>"new_sym_name",...} format.
|
151
|
+
# "new_sym_name" are generated automatically.
|
152
|
+
#
|
153
|
+
# - +our_symbols+ -- An array that contains the list of symbols (AS STRINGS,
|
154
|
+
# NOT AS SYMBOLS) that can be renamed.
|
155
|
+
def get_aliases_table(our_symbols)
|
156
|
+
symbols_ary = get_safe_symbols(our_symbols)
|
157
|
+
pos = 0;
|
158
|
+
aliases_ary = symbols_ary.map do |sym|
|
159
|
+
pos += 1
|
160
|
+
if sym.length > 1 && sym[0..1] == '@@'
|
161
|
+
"@@q#{pos}"
|
162
|
+
elsif sym[0] == '@'
|
163
|
+
"@q#{pos}"
|
164
|
+
elsif sym[0] =~ /[A-Z]/
|
165
|
+
"Q#{pos}"
|
166
|
+
elsif sym[0] =~ /[a-z]/
|
167
|
+
"q#{pos}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
[symbols_ary, aliases_ary].transpose.to_h
|
171
|
+
end
|
172
|
+
|
173
|
+
# call-seq:
|
174
|
+
# obj.rename_ivars
|
175
|
+
def rename_ivars(*args)
|
176
|
+
if args.size == 0
|
177
|
+
excl_names = []
|
178
|
+
else
|
179
|
+
excl_names = args[0]
|
180
|
+
end
|
181
|
+
|
182
|
+
to_hash
|
183
|
+
syms = @nodehash[:symbols].select {|x| (x =~ /@[^@]/) == 0}
|
184
|
+
pos = 1;
|
185
|
+
syms_new = syms.map do |x|
|
186
|
+
if excl_names.find_index(x[1..-1]) != nil
|
187
|
+
str = x
|
188
|
+
else
|
189
|
+
str = "@ivar#{pos}"
|
190
|
+
end
|
191
|
+
pos = pos + 1;
|
192
|
+
str
|
193
|
+
end
|
194
|
+
syms_subs = [syms, syms_new].transpose.to_h
|
195
|
+
replace_symbols(syms_subs)
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
199
|
+
# call-seq:
|
200
|
+
# obj.rebuild
|
201
|
+
#
|
202
|
+
# Rebuilds the node by converting it to the binary dump and further restoring
|
203
|
+
# of it from this dump. It doesn't change the original node and returns rebuilt
|
204
|
+
# node.
|
205
|
+
def rebuild
|
206
|
+
NodeMarshal.new(:binmemory, to_bin)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Designed for the logging of symbols table changes. When an example of
|
211
|
+
# SymbolsLogger is created the current global table of symbols is saved
|
212
|
+
# inside it. The created example can be used for finding new symbols in
|
213
|
+
# the global table. This is useful for code obfuscation.
|
214
|
+
class SymbolsLogger
|
215
|
+
def initialize
|
216
|
+
@symtbl_old = Symbol.all_symbols
|
217
|
+
end
|
218
|
+
|
219
|
+
def new_symbols
|
220
|
+
symtbl_new = Symbol.all_symbols
|
221
|
+
symtbl_new - @symtbl_old
|
222
|
+
end
|
223
|
+
|
224
|
+
def update
|
225
|
+
@symtbl_old = Symbol.all_symbols
|
226
|
+
end
|
227
|
+
end
|