fastlib 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.markdown +15 -0
  2. data/Rakefile +20 -0
  3. data/lib/fastlib.rb +312 -0
  4. metadata +72 -0
data/README.markdown ADDED
@@ -0,0 +1,15 @@
1
+ # FASTLIB
2
+
3
+ FASTLIB provides a method to encode large directories of libraries into a single archive file.
4
+ This is similar to capabilities like zip/ziprequire, except that it provides workarounds for
5
+ \_\_FILE\_\_ references, arbitrary compression, and arbitrary encoding of file and name contents.
6
+
7
+ # Usage
8
+
9
+ $ apt-get install fastlib
10
+ $ `gem env gemdir`/fastlib-0.0.1/lib/fastlib.rb dump lib/myarchive.fastlib lib/ lib/*
11
+ $ ruby -r rubygems -r fastlib -I lib/myarchive.fastlib application.rb
12
+
13
+
14
+ # Credits
15
+ Rapid7 LLC
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ task :build => :update do
4
+ Rake::Task['clean'].execute
5
+ puts "[*] Building fastlib.gemspec"
6
+ system "gem build fastlib.gemspec &> /dev/null"
7
+ end
8
+
9
+ task :release => :build do
10
+ puts "[*] Pushing fastlib to rubygems.org"
11
+ system "gem push fastlib-*.gem &> /dev/null"
12
+ Rake::Task['clean'].execute
13
+ end
14
+
15
+ task :clean do
16
+ system "rm -f *.gem >/dev/null 2>&1"
17
+ end
18
+
19
+ task :update do
20
+ end
data/lib/fastlib.rb ADDED
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # FASTLIB is a mechanism for loading large sets of libraries in a way that is
5
+ # faster and much more flexible than typical disk structures. FASTLIB includes
6
+ # hooks that can be used for both compression and encoding of Ruby libraries.
7
+ #
8
+
9
+ #
10
+ # This format was specifically created to improve the performance and
11
+ # AV-resistance of the Metasploit Framework and Rex libraries.
12
+ #
13
+
14
+
15
+ #
16
+ # This library is still in its early form; a large number of performance and
17
+ # compatiblity improvements are not yet included. Do not depend on the FASTLIB
18
+ # file format at this time.
19
+ #
20
+
21
+ require "find"
22
+
23
+ #
24
+ # Copyright (C) 2011 Rapid7. You can redistribute it and/or
25
+ # modify it under the terms of the ruby license.
26
+ #
27
+ #
28
+ # Roughly based on the rubyzip zip/ziprequire library:
29
+ # >> Copyright (C) 2002 Thomas Sondergaard
30
+ # >> rubyzip is free software; you can redistribute it and/or
31
+ # >> modify it under the terms of the ruby license.
32
+
33
+ module Kernel #:nodoc:all
34
+ alias :fastlib_original_require :require
35
+
36
+ #
37
+ # This method hooks the original Kernel.require to support
38
+ # loading files within FASTLIB archives
39
+ #
40
+ def require(name)
41
+ fastlib_require(name) || fastlib_original_require(name)
42
+ end
43
+
44
+ #
45
+ # This method handles the loading of FASTLIB archives
46
+ #
47
+ def fastlib_require(name)
48
+ name = name + ".rb" if not name =~ /\.rb$/
49
+ return false if fastlib_already_loaded?(name)
50
+ return false if fastlib_already_tried?(name)
51
+
52
+ # TODO: Implement relative path $: checks and adjust the
53
+ # search path within archives to match.
54
+
55
+ $:.grep( /^(.*)\.fastlib$/ ).each do |lib|
56
+ data = FastLib.load(lib, name)
57
+ next if not data
58
+ $" << name
59
+
60
+ # TODO: Implement a better stack trace that represents
61
+ # the original filename and line number.
62
+ Object.class_eval(data)
63
+ return true
64
+ end
65
+
66
+ $fastlib_miss << name
67
+
68
+ false
69
+ end
70
+
71
+ #
72
+ # This method determines whether the specific file name
73
+ # has already been loaded ($LOADED_FEATURES aka $")
74
+ #
75
+ def fastlib_already_loaded?(name)
76
+ re = Regexp.new("^" + Regexp.escape(name) + "$")
77
+ $".detect { |e| e =~ re } != nil
78
+ end
79
+
80
+ #
81
+ # This method determines whether the specific file name
82
+ # has already been attempted with the included FASTLIB
83
+ # archives.
84
+ #
85
+ # TODO: Ensure that this only applies to known FASTLIB
86
+ # archives and that newly included archives will
87
+ # be searched appropriately.
88
+ #
89
+ def fastlib_already_tried?(name)
90
+ $fastlib_miss ||= []
91
+ $fastlib_miss.include?(name)
92
+ end
93
+ end
94
+
95
+
96
+ #
97
+ # The FastLib class implements the meat of the FASTLIB archive format
98
+ #
99
+ class FastLib
100
+
101
+ VERSION = "0.0.1"
102
+
103
+ @@cache = {}
104
+
105
+ #
106
+ # This method returns the version of the fastlib library
107
+ #
108
+ def self.version
109
+ VERSION
110
+ end
111
+
112
+ #
113
+ # This method loads content from a specific archive file by name. If the
114
+ # noprocess argument is set to true, the contents will not be expanded to
115
+ # include workarounds for things such as __FILE__. This is useful when
116
+ # loading raw binary data where these strings may occur
117
+ #
118
+ def self.load(lib, name, noprocess=false)
119
+ data = ""
120
+ load_cache(lib)
121
+
122
+ return if not ( @@cache[lib] and @@cache[lib][name] )
123
+
124
+
125
+ ::File.open(lib, "rb") do |fd|
126
+ fd.seek(
127
+ @@cache[lib][:fastlib_header][0] +
128
+ @@cache[lib][:fastlib_header][1] +
129
+ @@cache[lib][name][0]
130
+ )
131
+ data = fastlib_filter( fd.read(@@cache[lib][name][1] ))
132
+ end
133
+
134
+ # Return the contents in raw or processed form
135
+ noprocess ? data : post_process(lib, name, data)
136
+ end
137
+
138
+ #
139
+ # This method caches the file list and offsets within the archive
140
+ #
141
+ def self.load_cache(lib)
142
+ return if @@cache[lib]
143
+ dict = {}
144
+ ::File.open(lib, 'rb') do |fd|
145
+ head = fd.read(4)
146
+ return if head != "FAST"
147
+ hlen = fd.read(4).unpack("N")[0]
148
+ dict[:fastlib_header] = [8, hlen]
149
+
150
+ nlen, doff, dlen = fd.read(12).unpack("N*")
151
+
152
+ while nlen > 0
153
+ name = fastlib_filter_name( fd.read(nlen) )
154
+ dict[name] = [doff, dlen]
155
+
156
+ nlen, doff, dlen = fd.read(12).unpack("N*")
157
+ end
158
+ @@cache[lib] = dict
159
+ end
160
+ end
161
+
162
+ #
163
+ # This method provides a way to hook the translation of file names
164
+ # from the dictionary in the file to the final string. This can be
165
+ # used to provide encryption or compression.
166
+ #
167
+ def self.fastlib_filter_name(name)
168
+ name
169
+ end
170
+
171
+ #
172
+ # This method provides a way to hook the translation of file content
173
+ # from the archive to the final content. This can be used to provide
174
+ # encryption or compression.
175
+ #
176
+ def self.fastlib_filter(data)
177
+ data
178
+ end
179
+
180
+ #
181
+ # This method provides a way to create a FASTLIB archive programatically,
182
+ # the key arguments are the name of the destination archive, the base
183
+ # directory that should be excluded from the archived path, and finally
184
+ # the list of specific files and directories to include in the archive.
185
+ #
186
+ def self.dump(lib, bdir, *dirs)
187
+ head = ""
188
+ data = ""
189
+ hidx = 0
190
+ didx = 0
191
+
192
+ bdir = bdir.gsub(/\/$/, '')
193
+ brex = /^#{Regexp.escape(bdir)}\//
194
+
195
+ dirs.each do |dir|
196
+ ::Find.find(dir).each do |path|
197
+ next if not ::File.file?(path)
198
+ name = fastlib_filter_name( path.sub( brex, "" ) )
199
+ buff = ""
200
+ ::File.open(path, "rb") do |fd|
201
+ buff = fd.read(fd.stat.size)
202
+ end
203
+
204
+ head << [ name.length, didx, buff.length ].pack("NNN")
205
+ head << name
206
+ hidx = hidx + 12 + name.length
207
+
208
+ data << fastlib_filter( buff )
209
+ didx = didx + buff.length
210
+ end
211
+ end
212
+
213
+ head << [0,0,0].pack("NNN")
214
+
215
+ ::File.open(lib, "wb") do |fd|
216
+ fd.write("FAST")
217
+ fd.write( [ head.length ].pack("N") )
218
+ fd.write( head )
219
+ fd.write( data )
220
+ end
221
+ end
222
+
223
+ #
224
+ # This archive provides a way to list the contents of an archive
225
+ # file, returning the names only in sorted order.
226
+ #
227
+ def self.list(lib)
228
+ load_cache(lib)
229
+ ( @@cache[lib] || {} ).keys.map{|x| x.to_s }.sort
230
+ end
231
+
232
+ # This method is called on the loaded
233
+
234
+ is required to expand __FILE__ and other inline
235
+ # dynamic constants to map to the correct location
236
+ def self.post_process(lib, name, data)
237
+ data.gsub('__FILE__', "'#{ ::File.expand_path(::File.join(::File.dirname(lib), name)) }'")
238
+ end
239
+
240
+ end
241
+
242
+
243
+ #
244
+ # Allow this library to be used as an executable to create and list
245
+ # FASTLIB archives
246
+ #
247
+ if __FILE__ == $0
248
+ cmd = ARGV.shift
249
+ unless ["dump", "list", "version"].include?(cmd)
250
+ $stderr.puts "Usage: #{$0} [dump|list|version] <arguments>"
251
+ exit(0)
252
+ end
253
+
254
+ case cmd
255
+ when "dump"
256
+ dst = ARGV.shift
257
+ dir = ARGV.shift
258
+ src = ARGV
259
+ unless dst and dir and src.length > 0
260
+ $stderr.puts "Usage: #{$0} dump destination.fastlib base_dir src1 src2 ... src99"
261
+ exit(0)
262
+ end
263
+ FastLib.dump(dst, dir, *src)
264
+
265
+ when "list"
266
+ src = ARGV.shift
267
+ unless src
268
+ $stderr.puts "Usage: #{$0} list src_lib "
269
+ exit(0)
270
+ end
271
+ $stdout.puts "Library: #{src}"
272
+ $stdout.puts "====================================================="
273
+ FastLib.list(src).each do |name|
274
+ $stdout.puts " - #{name}"
275
+ end
276
+ $stdout.puts ""
277
+
278
+ when "version"
279
+ $stdout.puts "FastLib Version #{FastLib.version}"
280
+ end
281
+
282
+ exit(0)
283
+ end
284
+
285
+ #
286
+ # FASTLIB archive format (subject to change without notice)
287
+ #
288
+ =begin
289
+
290
+ * All integers are 32-bit and in network byte order (big endian / BE)
291
+ * The file signature is 0x46415354 (big endian, use htonl() if necessary)
292
+ * The header is always 8 bytes into the archive (magic + header length)
293
+ * The data section is always 8 + header length into the archive
294
+ * The header entries always start with 'fastlib_header'
295
+ * The header entries always consist of 12 bytes + name length (no alignment)
296
+ * The header name data may be encoded, compressed, or transformed
297
+ * The data entries may be encoded, compressed, or transformed too
298
+
299
+
300
+ 4 bytes: "FAST"
301
+ 4 bytes: NBO header length
302
+ [
303
+ 4 bytes: name length (0 = End of Names)
304
+ 4 bytes: data offset
305
+ 4 bytes: data length
306
+ ]
307
+ [ Raw Data ]
308
+
309
+ =end
310
+
311
+
312
+
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlib
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - HD Moore
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-11-20 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: This gem provides a way to load libraries from an archive
23
+ email:
24
+ - hdm@metasploit.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.markdown
31
+ files:
32
+ - README.markdown
33
+ - Rakefile
34
+ - lib/fastlib.rb
35
+ has_rdoc: true
36
+ homepage: https://github.com/rapid7/fastlib
37
+ licenses:
38
+ - BSD
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ hash: 57
50
+ segments:
51
+ - 1
52
+ - 8
53
+ - 7
54
+ version: 1.8.7
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.4.2
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: FASTLIB archive library
71
+ test_files: []
72
+