librex 0.0.32 → 0.0.33
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.
- data/README.markdown +1 -1
- data/lib/rex/exploitation/egghunter.rb +90 -1
- data/lib/rex/exploitation/javascriptosdetect.rb +9 -1
- data/lib/rex/io/ring_buffer.rb.ut.rb +134 -0
- data/lib/rex/io/stream_server.rb +11 -2
- data/lib/rex/parser/nessus_xml.rb +4 -2
- data/lib/rex/parser/nexpose_xml.rb +45 -15
- data/lib/rex/parser/nmap_nokogiri.rb +385 -0
- data/lib/rex/parser/nmap_xml.rb +71 -51
- data/lib/rex/parser/nokogiri_doc_mixin.rb +99 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file_stat.rb +1 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +1 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb.ut.rb +4 -128
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/mock_magic.rb +146 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb.ut.rb +63 -0
- data/lib/rex/post/meterpreter/packet_dispatcher.rb +8 -2
- data/lib/rex/proto/http/handler/proc.rb +1 -1
- data/lib/rex/proto/http/server.rb +15 -10
- data/lib/rex/socket/parameters.rb +16 -0
- data/lib/rex/socket/ssl_tcp_server.rb +47 -38
- data/lib/rex/ui/text/dispatcher_shell.rb +49 -29
- data/lib/rex/zip.rb +3 -0
- data/lib/rex/zip/archive.rb +13 -89
- data/lib/rex/zip/entry.rb +11 -1
- data/lib/rex/zip/jar.rb +224 -0
- metadata +9 -3
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env rubyj
|
2
|
+
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..','..','..','..','..', 'lib'))
|
4
|
+
|
5
|
+
require 'rex/post/meterpreter/extensions/stdapi/railgun/railgun'
|
6
|
+
require 'rex/post/meterpreter/extensions/stdapi/railgun/mock_magic'
|
7
|
+
require 'test/unit'
|
8
|
+
require 'benchmark'
|
9
|
+
|
10
|
+
module Rex
|
11
|
+
module Post
|
12
|
+
module Meterpreter
|
13
|
+
module Extensions
|
14
|
+
module Stdapi
|
15
|
+
module Railgun
|
16
|
+
class Railgun::UnitTest < Test::Unit::TestCase
|
17
|
+
|
18
|
+
include MockMagic
|
19
|
+
|
20
|
+
def test_add_dll
|
21
|
+
railgun = Railgun.new(make_mock_client())
|
22
|
+
|
23
|
+
target_dll_name = 'discordia'
|
24
|
+
target_windows_name = 'C:\look\behind\you'
|
25
|
+
|
26
|
+
railgun.add_dll(target_dll_name, target_windows_name)
|
27
|
+
|
28
|
+
actual_dll = railgun.get_dll(target_dll_name);
|
29
|
+
|
30
|
+
assert_not_nil(actual_dll,
|
31
|
+
"add_dll should make a DLL accessible via get_dll")
|
32
|
+
|
33
|
+
assert_equal(actual_dll.dll_path, target_windows_name,
|
34
|
+
"add_dll should set a dll path when specified")
|
35
|
+
|
36
|
+
#
|
37
|
+
# wrapper = railgun.send(target_dll_name.to_sym)
|
38
|
+
#
|
39
|
+
# assert_same(wrapper._dll, actual_dll,
|
40
|
+
# "railgun instance responds with dll wrapper as expected")
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_add_function
|
44
|
+
mock_function_descriptions.each do |func|
|
45
|
+
railgun = Railgun.new(make_mock_client(func[:platform]))
|
46
|
+
|
47
|
+
dll_name = func[:dll_name]
|
48
|
+
function_name = func[:name]
|
49
|
+
|
50
|
+
railgun.add_dll(dll_name)
|
51
|
+
railgun.add_function(dll_name, function_name, func[:return_type], func[:params])
|
52
|
+
|
53
|
+
assert(railgun.get_dll(dll_name).functions.has_key?(function_name),
|
54
|
+
"add_function should add a function to the DLL specified")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -60,11 +60,17 @@ module PacketDispatcher
|
|
60
60
|
|
61
61
|
bytes = 0
|
62
62
|
raw = packet.to_r
|
63
|
+
err = nil
|
63
64
|
|
64
65
|
if (raw)
|
66
|
+
|
65
67
|
begin
|
66
68
|
bytes = self.sock.write(raw)
|
67
69
|
rescue ::Exception => e
|
70
|
+
err = e
|
71
|
+
end
|
72
|
+
|
73
|
+
if bytes.to_i == 0
|
68
74
|
# Mark the session itself as dead
|
69
75
|
self.alive = false
|
70
76
|
|
@@ -72,7 +78,7 @@ module PacketDispatcher
|
|
72
78
|
@finish = true
|
73
79
|
|
74
80
|
# Reraise the error to the top-level caller
|
75
|
-
raise
|
81
|
+
raise err if err
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
@@ -113,7 +119,7 @@ module PacketDispatcher
|
|
113
119
|
waiter = add_response_waiter(packet)
|
114
120
|
|
115
121
|
# Transmit the packet
|
116
|
-
if (send_packet(packet) <= 0)
|
122
|
+
if (send_packet(packet).to_i <= 0)
|
117
123
|
# Remove the waiter if we failed to send the packet.
|
118
124
|
remove_response_waiter(waiter)
|
119
125
|
return nil
|
@@ -38,7 +38,7 @@ class Handler::Proc < Handler
|
|
38
38
|
rescue Errno::EPIPE
|
39
39
|
elog("Proc::on_request: Client closed connection prematurely", LogSource)
|
40
40
|
rescue
|
41
|
-
elog("Proc::on_request: #{$!}\n\n#{$@.join("\n")}", LogSource)
|
41
|
+
elog("Proc::on_request: #{$!.class}: #{$!}\n\n#{$@.join("\n")}", LogSource)
|
42
42
|
if self.server and self.server.context
|
43
43
|
exploit = self.server.context['MsfExploit']
|
44
44
|
if exploit
|
@@ -99,15 +99,17 @@ class Server
|
|
99
99
|
# Initializes an HTTP server as listening on the provided port and
|
100
100
|
# hostname.
|
101
101
|
#
|
102
|
-
def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil)
|
102
|
+
def initialize(port = 80, listen_host = '0.0.0.0', ssl = false, context = {}, comm = nil, ssl_cert = nil)
|
103
103
|
self.listen_host = listen_host
|
104
104
|
self.listen_port = port
|
105
|
+
self.ssl = ssl
|
105
106
|
self.context = context
|
107
|
+
self.comm = comm
|
108
|
+
self.ssl_cert = ssl_cert
|
109
|
+
|
106
110
|
self.listener = nil
|
107
111
|
self.resources = {}
|
108
112
|
self.server_name = DefaultServer
|
109
|
-
self.ssl = ssl
|
110
|
-
self.comm = comm
|
111
113
|
end
|
112
114
|
|
113
115
|
#
|
@@ -134,6 +136,7 @@ class Server
|
|
134
136
|
'LocalPort' => self.listen_port,
|
135
137
|
'Context' => self.context,
|
136
138
|
'SSL' => self.ssl,
|
139
|
+
'SSLCert' => self.ssl_cert,
|
137
140
|
'Comm' => self.comm
|
138
141
|
)
|
139
142
|
|
@@ -219,7 +222,7 @@ class Server
|
|
219
222
|
# Adds Server headers and stuff.
|
220
223
|
#
|
221
224
|
def add_response_headers(resp)
|
222
|
-
resp['Server'] = self.server_name
|
225
|
+
resp['Server'] = self.server_name if not resp['Server']
|
223
226
|
end
|
224
227
|
|
225
228
|
#
|
@@ -256,7 +259,7 @@ class Server
|
|
256
259
|
cli.send_response(resp)
|
257
260
|
end
|
258
261
|
|
259
|
-
attr_accessor :listen_port, :listen_host, :server_name, :context, :ssl, :comm
|
262
|
+
attr_accessor :listen_port, :listen_host, :server_name, :context, :ssl, :comm, :ssl_cert
|
260
263
|
attr_accessor :listener, :resources
|
261
264
|
|
262
265
|
protected
|
@@ -275,10 +278,7 @@ protected
|
|
275
278
|
#
|
276
279
|
def on_client_data(cli)
|
277
280
|
begin
|
278
|
-
|
279
|
-
# XXX: Handle ParseCode::Partial
|
280
|
-
#
|
281
|
-
data = cli.get
|
281
|
+
data = cli.read(65535)
|
282
282
|
|
283
283
|
raise ::EOFError if not data
|
284
284
|
raise ::EOFError if data.empty?
|
@@ -286,8 +286,13 @@ protected
|
|
286
286
|
case cli.request.parse(data)
|
287
287
|
when Packet::ParseCode::Completed
|
288
288
|
dispatch_request(cli, cli.request)
|
289
|
-
|
290
289
|
cli.reset_cli
|
290
|
+
|
291
|
+
when Packet::ParseCode::Partial
|
292
|
+
# Return and wait for the on_client_data handler to be called again
|
293
|
+
# The Request object tracks the state of the request for us
|
294
|
+
return
|
295
|
+
|
291
296
|
when Packet::ParseCode::Error
|
292
297
|
close_client(cli)
|
293
298
|
end
|
@@ -63,6 +63,10 @@ class Rex::Socket::Parameters
|
|
63
63
|
#
|
64
64
|
# Specify SSL2, SSL3, or TLS1 (SSL3 is default)
|
65
65
|
#
|
66
|
+
# SSLCert
|
67
|
+
#
|
68
|
+
# A file containing an SSL certificate (for server sockets)
|
69
|
+
#
|
66
70
|
# Proxies
|
67
71
|
#
|
68
72
|
# List of proxies to use.
|
@@ -139,6 +143,14 @@ class Rex::Socket::Parameters
|
|
139
143
|
self.ssl_version = hash['SSLVersion']
|
140
144
|
end
|
141
145
|
|
146
|
+
if (hash['SSLCert'] and ::File.file?(hash['SSLCert']))
|
147
|
+
begin
|
148
|
+
self.ssl_cert = ::File.read(hash['SSLCert'])
|
149
|
+
rescue ::Exception => e
|
150
|
+
elog("Failed to read cert: #{e.class}: #{e}", LogSource)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
142
154
|
if hash['Proxies']
|
143
155
|
self.proxies = hash['Proxies'].split('-').map{|a| a.strip}.map{|a| a.split(':').map{|b| b.strip}}
|
144
156
|
end
|
@@ -325,6 +337,10 @@ class Rex::Socket::Parameters
|
|
325
337
|
#
|
326
338
|
attr_accessor :ssl_version
|
327
339
|
#
|
340
|
+
# The SSL certificate, in pem format, stored as a string. See +SslTcpServer#make_ssl+
|
341
|
+
#
|
342
|
+
attr_accessor :ssl_cert
|
343
|
+
#
|
328
344
|
# Whether we should use IPv6
|
329
345
|
#
|
330
346
|
attr_accessor :v6
|
@@ -47,7 +47,7 @@ module Rex::Socket::SslTcpServer
|
|
47
47
|
|
48
48
|
def initsock(params = nil)
|
49
49
|
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
50
|
-
self.sslctx = makessl()
|
50
|
+
self.sslctx = makessl(params.ssl_cert)
|
51
51
|
super
|
52
52
|
end
|
53
53
|
|
@@ -99,43 +99,52 @@ module Rex::Socket::SslTcpServer
|
|
99
99
|
end
|
100
100
|
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
102
|
+
#
|
103
|
+
# Create a new ssl context. If +ssl_cert+ is not given, generates a new
|
104
|
+
# key and a leaf certificate with random values.
|
105
|
+
#
|
106
|
+
def makessl(ssl_cert=nil)
|
107
|
+
|
108
|
+
if ssl_cert
|
109
|
+
cert = OpenSSL::X509::Certificate.new(ssl_cert)
|
110
|
+
key = OpenSSL::PKey::RSA.new(ssl_cert)
|
111
|
+
else
|
112
|
+
key = OpenSSL::PKey::RSA.new(1024){ }
|
113
|
+
cert = OpenSSL::X509::Certificate.new
|
114
|
+
cert.version = 2
|
115
|
+
cert.serial = rand(0xFFFFFFFF)
|
116
|
+
# name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
|
117
|
+
subject = OpenSSL::X509::Name.new([
|
118
|
+
["C","US"],
|
119
|
+
['ST', Rex::Text.rand_state()],
|
120
|
+
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
121
|
+
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
122
|
+
["CN", Rex::Text.rand_hostname],
|
123
|
+
])
|
124
|
+
issuer = OpenSSL::X509::Name.new([
|
125
|
+
["C","US"],
|
126
|
+
['ST', Rex::Text.rand_state()],
|
127
|
+
["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
128
|
+
["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
|
129
|
+
["CN", Rex::Text.rand_hostname],
|
130
|
+
])
|
131
|
+
|
132
|
+
cert.subject = subject
|
133
|
+
cert.issuer = issuer
|
134
|
+
cert.not_before = Time.now - (3600 * 365)
|
135
|
+
cert.not_after = Time.now + (3600 * 365)
|
136
|
+
cert.public_key = key.public_key
|
137
|
+
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
|
138
|
+
cert.extensions = [
|
139
|
+
ef.create_extension("basicConstraints","CA:FALSE"),
|
140
|
+
ef.create_extension("subjectKeyIdentifier","hash"),
|
141
|
+
ef.create_extension("extendedKeyUsage","serverAuth"),
|
142
|
+
ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
|
143
|
+
]
|
144
|
+
ef.issuer_certificate = cert
|
145
|
+
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
|
146
|
+
cert.sign(key, OpenSSL::Digest::SHA1.new)
|
147
|
+
end
|
139
148
|
|
140
149
|
ctx = OpenSSL::SSL::SSLContext.new()
|
141
150
|
ctx.key = key
|
@@ -35,6 +35,8 @@ module DispatcherShell
|
|
35
35
|
#
|
36
36
|
# Returns nil for an empty set of commands.
|
37
37
|
#
|
38
|
+
# This method should be overridden
|
39
|
+
#
|
38
40
|
def commands
|
39
41
|
end
|
40
42
|
|
@@ -84,7 +86,9 @@ module DispatcherShell
|
|
84
86
|
# Displays the help banner. With no arguments, this is just a list of
|
85
87
|
# all commands grouped by dispatcher. Otherwise, tries to use a method
|
86
88
|
# named cmd_#{+cmd+}_help for the first dispatcher that has a command
|
87
|
-
# named +cmd+.
|
89
|
+
# named +cmd+. If no such method exists, uses +cmd+ as a regex to
|
90
|
+
# compare against each enstacked dispatcher's name and dumps commands
|
91
|
+
# of any that match.
|
88
92
|
#
|
89
93
|
def cmd_help(cmd=nil, *ignored)
|
90
94
|
if cmd
|
@@ -103,6 +107,16 @@ module DispatcherShell
|
|
103
107
|
break
|
104
108
|
end
|
105
109
|
end
|
110
|
+
|
111
|
+
unless cmd_found
|
112
|
+
# We didn't find a cmd, try it as a dispatcher name
|
113
|
+
shell.dispatcher_stack.each do |dispatcher|
|
114
|
+
if dispatcher.name =~ /#{cmd}/i
|
115
|
+
print_line(dispatcher.help_to_s)
|
116
|
+
cmd_found = help_found = true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
106
120
|
print_error("No help for #{cmd}, try -h") if cmd_found and not help_found
|
107
121
|
print_error("No such command") if not cmd_found
|
108
122
|
else
|
@@ -127,6 +141,37 @@ module DispatcherShell
|
|
127
141
|
|
128
142
|
alias cmd_? cmd_help
|
129
143
|
|
144
|
+
#
|
145
|
+
# Return a pretty, user-readable table of commands provided by this
|
146
|
+
# dispatcher.
|
147
|
+
#
|
148
|
+
def help_to_s(opts={})
|
149
|
+
# If this dispatcher has no commands, we can't do anything useful.
|
150
|
+
return "" if commands.nil? or commands.length == 0
|
151
|
+
|
152
|
+
# Display the commands
|
153
|
+
tbl = Table.new(
|
154
|
+
'Header' => "#{self.name} Commands",
|
155
|
+
'Indent' => opts['Indent'] || 4,
|
156
|
+
'Columns' =>
|
157
|
+
[
|
158
|
+
'Command',
|
159
|
+
'Description'
|
160
|
+
],
|
161
|
+
'ColProps' =>
|
162
|
+
{
|
163
|
+
'Command' =>
|
164
|
+
{
|
165
|
+
'MaxWidth' => 12
|
166
|
+
}
|
167
|
+
})
|
168
|
+
|
169
|
+
commands.sort.each { |c|
|
170
|
+
tbl << c
|
171
|
+
}
|
172
|
+
|
173
|
+
return "\n" + tbl.to_s + "\n"
|
174
|
+
end
|
130
175
|
|
131
176
|
#
|
132
177
|
# No tab completion items by default
|
@@ -391,45 +436,20 @@ module DispatcherShell
|
|
391
436
|
#
|
392
437
|
# Return a readable version of a help banner for all of the enstacked
|
393
438
|
# dispatchers.
|
439
|
+
#
|
440
|
+
# See +CommandDispatcher#help_to_s+
|
394
441
|
#
|
395
442
|
def help_to_s(opts = {})
|
396
443
|
str = ''
|
397
444
|
|
398
445
|
dispatcher_stack.reverse.each { |dispatcher|
|
399
|
-
|
400
|
-
next if ((dispatcher.respond_to?('commands') == false) or
|
401
|
-
(dispatcher.commands == nil) or
|
402
|
-
(dispatcher.commands.length == 0))
|
403
|
-
|
404
|
-
# Display the commands
|
405
|
-
tbl = Table.new(
|
406
|
-
'Header' => "#{dispatcher.name} Commands",
|
407
|
-
'Indent' => opts['Indent'] || 4,
|
408
|
-
'Columns' =>
|
409
|
-
[
|
410
|
-
'Command',
|
411
|
-
'Description'
|
412
|
-
],
|
413
|
-
'ColProps' =>
|
414
|
-
{
|
415
|
-
'Command' =>
|
416
|
-
{
|
417
|
-
'MaxWidth' => 12
|
418
|
-
}
|
419
|
-
})
|
420
|
-
|
421
|
-
dispatcher.commands.sort.each { |c|
|
422
|
-
tbl << c
|
423
|
-
}
|
424
|
-
|
425
|
-
str << "\n" + tbl.to_s + "\n"
|
446
|
+
str << dispatcher.help_to_s
|
426
447
|
}
|
427
448
|
|
428
449
|
return str
|
429
450
|
end
|
430
451
|
|
431
452
|
|
432
|
-
|
433
453
|
#
|
434
454
|
# Returns nil for an empty set of blocked commands.
|
435
455
|
#
|
data/lib/rex/zip.rb
CHANGED
data/lib/rex/zip/archive.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
##
|
2
|
-
# $Id: archive.rb
|
2
|
+
# $Id: archive.rb 12718 2011-05-25 16:45:20Z egypt $
|
3
3
|
##
|
4
4
|
|
5
5
|
module Rex
|
@@ -9,14 +9,20 @@ module Zip
|
|
9
9
|
# This represents an entire archive.
|
10
10
|
#
|
11
11
|
class Archive
|
12
|
+
|
13
|
+
# An array of the Entry objects stored in this Archive.
|
12
14
|
attr_reader :entries
|
13
15
|
|
16
|
+
|
14
17
|
def initialize(compmeth=CM_DEFLATE)
|
15
18
|
@compmeth = compmeth
|
16
19
|
@entries = []
|
17
20
|
end
|
18
21
|
|
19
22
|
|
23
|
+
#
|
24
|
+
# Create a new Entry and add it to the archive.
|
25
|
+
#
|
20
26
|
def add_file(fname, fdata=nil, xtra=nil, comment=nil)
|
21
27
|
if (not fdata)
|
22
28
|
begin
|
@@ -45,6 +51,9 @@ class Archive
|
|
45
51
|
end
|
46
52
|
|
47
53
|
|
54
|
+
#
|
55
|
+
# Write the compressed file to +fname+.
|
56
|
+
#
|
48
57
|
def save_to(fname)
|
49
58
|
f = File.open(fname, 'wb')
|
50
59
|
f.write(pack)
|
@@ -52,6 +61,9 @@ class Archive
|
|
52
61
|
end
|
53
62
|
|
54
63
|
|
64
|
+
#
|
65
|
+
# Compress this archive and return the resulting zip file as a String.
|
66
|
+
#
|
55
67
|
def pack
|
56
68
|
ret = ''
|
57
69
|
|
@@ -92,93 +104,5 @@ class Archive
|
|
92
104
|
|
93
105
|
end
|
94
106
|
|
95
|
-
class Jar < Archive
|
96
|
-
attr_accessor :manifest
|
97
|
-
|
98
|
-
def build_manifest(opts={})
|
99
|
-
main_class = opts[:main_class] || nil
|
100
|
-
existing_manifest = nil
|
101
|
-
|
102
|
-
@manifest = "Manifest-Version: 1.0\r\n"
|
103
|
-
@manifest << "Main-Class: #{main_class}\r\n" if main_class
|
104
|
-
@manifest << "\r\n"
|
105
|
-
@entries.each { |e|
|
106
|
-
existing_manifest = e if e.name == "META-INF/MANIFEST.MF"
|
107
|
-
next unless e.name =~ /\.class$/
|
108
|
-
@manifest << "Name: #{e.name}\r\n"
|
109
|
-
#@manifest << "SHA1-Digest: #{Digest::SHA1.base64digest(e.data)}\r\n"
|
110
|
-
@manifest << "\r\n"
|
111
|
-
}
|
112
|
-
if existing_manifest
|
113
|
-
existing_manifest.data = @manifest
|
114
|
-
else
|
115
|
-
add_file("META-INF/", '')
|
116
|
-
add_file("META-INF/MANIFEST.MF", @manifest)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def to_s
|
121
|
-
pack
|
122
|
-
end
|
123
|
-
|
124
|
-
def length
|
125
|
-
pack.length
|
126
|
-
end
|
127
|
-
|
128
|
-
#
|
129
|
-
# Add multiple files from an array
|
130
|
-
#
|
131
|
-
# +files+ should be structured like so:
|
132
|
-
# [
|
133
|
-
# [ "path", "to", "file1" ],
|
134
|
-
# [ "path", "to", "file2" ]
|
135
|
-
# ]
|
136
|
-
# and +path+ should be the location on the file system to find the files to
|
137
|
-
# add. +base_dir+ will be prepended to the path inside the jar.
|
138
|
-
#
|
139
|
-
# Example:
|
140
|
-
# <code>
|
141
|
-
# war = Rex::Zip::Jar.new
|
142
|
-
# war.add_file("WEB-INF/", '')
|
143
|
-
# war.add_file("WEB-INF/", "web.xml", web_xml)
|
144
|
-
# war.add_file("WEB-INF/classes/", '')
|
145
|
-
# files = [
|
146
|
-
# [ "servlet", "examples", "HelloWorld.class" ],
|
147
|
-
# [ "Foo.class" ],
|
148
|
-
# [ "servlet", "Bar.class" ],
|
149
|
-
# ]
|
150
|
-
# war.add_files(files, "./class_files/", "WEB-INF/classes/")
|
151
|
-
# </code>
|
152
|
-
#
|
153
|
-
# The above code would create a jar with the following structure from files
|
154
|
-
# found in ./class_files/ :
|
155
|
-
#
|
156
|
-
# +- WEB-INF/
|
157
|
-
# +- web.xml
|
158
|
-
# +- classes/
|
159
|
-
# +- Foo.class
|
160
|
-
# +- servlet/
|
161
|
-
# +- Bar.class
|
162
|
-
# +- examples/
|
163
|
-
# +- HelloWorld.class
|
164
|
-
#
|
165
|
-
def add_files(files, path, base_dir="")
|
166
|
-
files.each do |file|
|
167
|
-
# Add all of the subdirectories if they don't already exist
|
168
|
-
1.upto(file.length - 1) do |idx|
|
169
|
-
full = base_dir + file[0,idx].join("/") + "/"
|
170
|
-
if !(entries.map{|e|e.name}.include?(full))
|
171
|
-
add_file(full, '')
|
172
|
-
end
|
173
|
-
end
|
174
|
-
# Now add the actual file, grabbing data from the filesystem
|
175
|
-
fd = File.open(File.join( path, file ), "rb")
|
176
|
-
data = fd.read(fd.stat.size)
|
177
|
-
fd.close
|
178
|
-
add_file(base_dir + file.join("/"), data)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
107
|
end
|
184
108
|
end
|