librex 0.0.42 → 0.0.43

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.
Files changed (34) hide show
  1. data/README.markdown +1 -1
  2. data/lib/rex/compat.rb +10 -0
  3. data/lib/rex/post/meterpreter/channels/pools/file.rb +1 -1
  4. data/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +20 -18
  5. data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +11 -22
  6. data/lib/rex/post/meterpreter/extensions/stdapi/fs/file_stat.rb +2 -1
  7. data/lib/rex/post/meterpreter/extensions/stdapi/railgun.rb.ts.rb +4 -0
  8. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb +27 -0
  9. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb.ut.rb +7 -0
  10. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_advapi32.rb +498 -242
  11. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_iphlpapi.rb +18 -18
  12. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +695 -694
  13. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb +6 -5
  14. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_ntdll.rb +24 -24
  15. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_shell32.rb +5 -4
  16. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_user32.rb +551 -551
  17. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_ws2_32.rb +93 -93
  18. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +56 -42
  19. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb.ut.rb +4 -4
  20. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_helper.rb.ut.rb +5 -5
  21. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper.rb +26 -0
  22. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper.rb.ut.rb +63 -0
  23. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +4 -4
  24. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +151 -96
  25. data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb.ut.rb +80 -5
  26. data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +3 -3
  27. data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +11 -11
  28. data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +3 -3
  29. data/lib/rex/post/meterpreter/packet.rb +12 -11
  30. data/lib/rex/proto/dhcp/server.rb +36 -42
  31. data/lib/rex/socket/range_walker.rb +1 -1
  32. data/lib/rex/text.rb +18 -1
  33. data/lib/rex/ui/text/table.rb +1 -1
  34. metadata +5 -3
@@ -45,22 +45,38 @@ class DLL
45
45
  attr_accessor :functions
46
46
  attr_reader :dll_path
47
47
 
48
- def initialize(dll_path, client, win_consts) #
48
+ def initialize(dll_path, win_consts)
49
49
  @dll_path = dll_path
50
- @client = client
50
+
51
+ # needed by DLLHelper
51
52
  @win_consts = win_consts
52
- if( @client.platform =~ /x64/i )
53
- @native = 'Q'
54
- else
55
- @native = 'V'
56
- end
53
+
57
54
  self.functions = {}
58
55
  end
59
56
 
60
- # adds a function to the DLL
57
+ def known_function_names
58
+ return functions.keys
59
+ end
60
+
61
+ def get_function(name)
62
+ return functions[name]
63
+ end
64
+
65
+ def call_function(func_symbol, args, client)
66
+ func_name = func_symbol.to_s
67
+
68
+ unless known_function_names.include? func_name
69
+ raise "DLL-function #{func_name} not found. Known functions: #{PP.pp(known_function_names, '')}"
70
+ end
71
+
72
+ function = get_function(func_name)
73
+
74
+ return process_function_call(function, args, client)
75
+ end
76
+
61
77
  # syntax for params:
62
- # add_function("MessageBoxW", # name
63
- # "DWORD", # return value
78
+ # add_function("MessageBoxW", # name
79
+ # "DWORD", # return value
64
80
  # [["DWORD","hWnd","in"], # params
65
81
  # ["PWCHAR","lpText","in"],
66
82
  # ["PWCHAR","lpCaption","in"],
@@ -84,9 +100,16 @@ class DLL
84
100
  private
85
101
 
86
102
  # called when a function like "MessageBoxW" is called
87
- def process_function_call(function, args)
103
+ def process_function_call(function, args, client)
88
104
  raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length
89
- #puts "process_function_call(function.windows_name,#{PP.pp(args, "")})"
105
+
106
+ if( client.platform =~ /x64/i )
107
+ native = 'Q'
108
+ else
109
+ native = 'V'
110
+ end
111
+
112
+ #puts "process_function_call(function.windows_name,#{PP.pp(args, "")})"
90
113
 
91
114
  # We transmit the immediate stack and three heap-buffers:
92
115
  # in, inout and out. The reason behind the separation is bandwidth.
@@ -114,14 +137,14 @@ class DLL
114
137
  buffer_size = args[param_idx]
115
138
  if param_desc[0] == "PDWORD"
116
139
  # bump up the size for an x64 pointer
117
- if( @native == 'Q' and buffer_size == 4 )
140
+ if( native == 'Q' and buffer_size == 4 )
118
141
  args[param_idx] = 8
119
142
  buffer_size = args[param_idx]
120
143
  end
121
144
 
122
- if( @native == 'Q' )
145
+ if( native == 'Q' )
123
146
  raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
124
- elsif( @native == 'V' )
147
+ elsif( native == 'V' )
125
148
  raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
126
149
  end
127
150
  end
@@ -157,43 +180,43 @@ class DLL
157
180
  if ["PDWORD", "PWCHAR", "PCHAR", "PBLOB"].include? param_desc[0]
158
181
  #puts " pointer"
159
182
  if args[param_idx] == nil # null pointer?
160
- buffer = [0].pack(@native) # type: DWORD (so the dll does not rebase it)
161
- buffer += [0].pack(@native) # value: 0
183
+ buffer = [0].pack(native) # type: DWORD (so the dll does not rebase it)
184
+ buffer += [0].pack(native) # value: 0
162
185
  elsif param_desc[2] == "in"
163
- buffer = [1].pack(@native)
164
- buffer += [in_only_layout[param_desc[1]].addr].pack(@native)
186
+ buffer = [1].pack(native)
187
+ buffer += [in_only_layout[param_desc[1]].addr].pack(native)
165
188
  elsif param_desc[2] == "out"
166
- buffer = [2].pack(@native)
167
- buffer += [out_only_layout[param_desc[1]].addr].pack(@native)
189
+ buffer = [2].pack(native)
190
+ buffer += [out_only_layout[param_desc[1]].addr].pack(native)
168
191
  elsif param_desc[2] == "inout"
169
- buffer = [3].pack(@native)
170
- buffer += [inout_layout[param_desc[1]].addr].pack(@native)
192
+ buffer = [3].pack(native)
193
+ buffer += [inout_layout[param_desc[1]].addr].pack(native)
171
194
  else
172
195
  raise "unexpected direction"
173
196
  end
174
197
  else
175
198
  #puts " not a pointer"
176
199
  # it's not a pointer (LPVOID is a pointer but is not backed by railgun memory, ala PBLOB)
177
- buffer = [0].pack(@native)
200
+ buffer = [0].pack(native)
178
201
  case param_desc[0]
179
202
  when "LPVOID", "HANDLE"
180
203
  num = param_to_number(args[param_idx])
181
- buffer += [num].pack(@native)
204
+ buffer += [num].pack(native)
182
205
  when "DWORD"
183
206
  num = param_to_number(args[param_idx])
184
- buffer += [num % 4294967296].pack(@native)
207
+ buffer += [num % 4294967296].pack(native)
185
208
  when "WORD"
186
209
  num = param_to_number(args[param_idx])
187
- buffer += [num % 65536].pack(@native)
210
+ buffer += [num % 65536].pack(native)
188
211
  when "BYTE"
189
212
  num = param_to_number(args[param_idx])
190
- buffer += [num % 256].pack(@native)
213
+ buffer += [num % 256].pack(native)
191
214
  when "BOOL"
192
215
  case args[param_idx]
193
216
  when true
194
- buffer += [1].pack(@native)
217
+ buffer += [1].pack(native)
195
218
  when false
196
- buffer += [0].pack(@native)
219
+ buffer += [0].pack(native)
197
220
  else
198
221
  raise "param #{param_desc[1]}: true or false expected"
199
222
  end
@@ -219,7 +242,7 @@ class DLL
219
242
  request.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, @dll_path )
220
243
  request.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.windows_name)
221
244
 
222
- response = @client.send_request(request)
245
+ response = client.send_request(request)
223
246
 
224
247
  #puts "receiving Stuff from meterpreter"
225
248
  #puts "out_only_layout:"
@@ -240,7 +263,7 @@ class DLL
240
263
  #process return value
241
264
  case function.return_type
242
265
  when "LPVOID", "HANDLE"
243
- if( @native == 'Q' )
266
+ if( native == 'Q' )
244
267
  return_hash["return"] = rec_return_value
245
268
  else
246
269
  return_hash["return"] = rec_return_value % 4294967296
@@ -307,7 +330,7 @@ class DLL
307
330
  # puts("
308
331
  #=== START of proccess_function_call snapshot ===
309
332
  # {
310
- # :platform => '#{@native == 'Q' ? 'x64/win64' : 'x86/win32'}',
333
+ # :platform => '#{native == 'Q' ? 'x64/win64' : 'x86/win32'}',
311
334
  # :name => '#{function.windows_name}',
312
335
  # :params => #{function.params},
313
336
  # :return_type => '#{function.return_type}',
@@ -335,15 +358,6 @@ class DLL
335
358
  return return_hash
336
359
  end
337
360
 
338
- # process_function_call
339
-
340
- # we fake having methods like "MessageBoxW" by intercepting "method-not-found"-exceptions
341
- def method_missing(func_symbol, *args)
342
- func_name = func_symbol.to_s
343
- raise "DLL-function #{func_name} not found. Known functions: #{PP.pp(@functions.keys, "")}" unless @functions.has_key? func_name
344
- function = @functions[func_name]
345
- return process_function_call(function, args)
346
- end
347
361
  end
348
362
 
349
363
  end; end; end; end; end; end;
@@ -18,7 +18,7 @@ class DLL::UnitTest < Test::Unit::TestCase
18
18
 
19
19
  def test_add_function
20
20
  mock_function_descriptions.each do |func|
21
- dll = DLL.new(func[:dll_name], make_mock_client(func[:platform]), nil)
21
+ dll = DLL.new(func[:dll_name], nil)
22
22
  dll.add_function(func[:name], func[:return_type], func[:params])
23
23
 
24
24
  assert(dll.functions.has_key?(func[:name]),
@@ -26,14 +26,14 @@ class DLL::UnitTest < Test::Unit::TestCase
26
26
  end
27
27
  end
28
28
 
29
- def test_method_missing
29
+ def test_call_function
30
30
  mock_function_descriptions.each do |func|
31
31
  client = make_mock_client(func[:platform], func[:request_to_client], func[:response_from_client])
32
- dll = DLL.new(func[:dll_name], client, nil)
32
+ dll = DLL.new(func[:dll_name], nil)
33
33
 
34
34
  dll.add_function(func[:name], func[:return_type], func[:params])
35
35
 
36
- actual_returned_hash = dll.send(:method_missing, func[:name].to_sym, *func[:ruby_args])
36
+ actual_returned_hash = dll.call_function(func[:name].to_sym, func[:ruby_args], client)
37
37
 
38
38
  assert(func[:returned_hash].has_key?('GetLastError'),
39
39
  "process_function_call should add the result of GetLastError to the key GetLastError")
@@ -28,10 +28,10 @@ class DLLHelper::UnitTest < Test::Unit::TestCase
28
28
  # converts ruby string to zero-terminated ASCII string
29
29
  zero_terminated_ascii_attempt = TEST_DLL_HELPER.str_to_ascii_z(original_string)
30
30
 
31
- assert(zero_terminated_ascii_attempt.end_with?("\x00"),
31
+ assert(zero_terminated_ascii_attempt =~ /\x00$/,
32
32
  "str_to_ascii_z should result in a 0 terminated string")
33
33
 
34
- assert(zero_terminated_ascii_attempt.start_with?(original_string),
34
+ assert(zero_terminated_ascii_attempt =~ /^#{original_string}/,
35
35
  "str_to_ascii_z should still start with original string")
36
36
 
37
37
  assert_equal(original_string.length + 1, zero_terminated_ascii_attempt.length,
@@ -45,10 +45,10 @@ class DLLHelper::UnitTest < Test::Unit::TestCase
45
45
 
46
46
  actual_string = TEST_DLL_HELPER.asciiz_to_str(zero_terminated_string)
47
47
 
48
- assert(actual_string.start_with?(target_string),
48
+ assert(actual_string =~ /^#{target_string}/,
49
49
  "asciiz_to_str should preserve string before zero")
50
50
 
51
- assert(!actual_string.end_with?(post_zero_noise),
51
+ assert(actual_string !~ /#{post_zero_noise}$/,
52
52
  "asciiz_to_str should ignore characters after zero")
53
53
 
54
54
  assert_equal(target_string, actual_string,
@@ -65,7 +65,7 @@ class DLLHelper::UnitTest < Test::Unit::TestCase
65
65
  target_zero_terminated_unicode = Rex::Text.to_unicode(ruby_string) + "\x00\x00"
66
66
  actual_zero_terminated_unicode = TEST_DLL_HELPER.str_to_uni_z(ruby_string)
67
67
 
68
- assert(actual_zero_terminated_unicode.end_with?("\x00\x00"),
68
+ assert(actual_zero_terminated_unicode =~ /\x00\x00$/,
69
69
  "str_to_uni_z should result in a double-zero terminated string")
70
70
 
71
71
  assert_equal(target_zero_terminated_unicode, actual_zero_terminated_unicode,
@@ -0,0 +1,26 @@
1
+ module Rex
2
+ module Post
3
+ module Meterpreter
4
+ module Extensions
5
+ module Stdapi
6
+ module Railgun
7
+ class DLLWrapper
8
+ attr_reader :_client, :_dll
9
+
10
+ def initialize(dll, client)
11
+ @_dll = dll
12
+ @_client = client
13
+ end
14
+
15
+ # For backwards compatability. People check if functions are added this way
16
+ # XXX: Depricate this
17
+ def functions
18
+ # warn 'Depricated.'
19
+ _dll.functions
20
+ end
21
+
22
+ def method_missing(sym, *args)
23
+ _dll.call_function(sym, args, _client)
24
+ end
25
+ end
26
+ end; end; end; end; end; end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', '..', '..','..','..','..','..', 'lib'))
4
+
5
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll'
6
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper'
7
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/mock_magic'
8
+ require 'test/unit'
9
+
10
+ module Rex
11
+ module Post
12
+ module Meterpreter
13
+ module Extensions
14
+ module Stdapi
15
+ module Railgun
16
+ class DLLWrapper::UnitTest < Test::Unit::TestCase
17
+
18
+ include MockMagic
19
+
20
+ def test_functions
21
+ mock_function_descriptions.each do |func|
22
+ client = make_mock_client(func[:platform], func[:request_to_client], func[:response_from_client])
23
+ dll = DLL.new(func[:dll_name], client)
24
+
25
+ dll_wrapper = DLLWrapper.new(dll, client)
26
+
27
+ # This represents how people check if a function doesn't exist
28
+ assert(!dll_wrapper.functions[func[:name]], 'Function non-existence can be chucked via .functions')
29
+
30
+ dll.add_function(func[:name], func[:return_type], func[:params])
31
+
32
+ # This represents how people check if a function exist
33
+ assert(dll_wrapper.functions[func[:name]], 'Function existence can be chucked via .functions')
34
+
35
+ actual_returned_hash = dll_wrapper.send(:method_missing, func[:name].to_sym, *func[:ruby_args])
36
+
37
+ assert_equal(func[:returned_hash], actual_returned_hash,
38
+ "method_missing should result in a successful call to specified function")
39
+ end
40
+ end
41
+
42
+ def test_method_missing
43
+ mock_function_descriptions.each do |func|
44
+ client = make_mock_client(func[:platform], func[:request_to_client], func[:response_from_client])
45
+ dll = DLL.new(func[:dll_name], client)
46
+
47
+ dll.add_function(func[:name], func[:return_type], func[:params])
48
+
49
+ dll_wrapper = DLLWrapper.new(dll, client)
50
+
51
+ actual_returned_hash = dll_wrapper.send(:method_missing, func[:name].to_sym, *func[:ruby_args])
52
+
53
+ assert_equal(func[:returned_hash], actual_returned_hash,
54
+ "method_missing should result in a successful call to specified function")
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -41,10 +41,10 @@ class MultiCaller
41
41
 
42
42
  include DLLHelper
43
43
 
44
- def initialize( client, parent, win_consts )
45
- @parent = parent
46
- @client = client
47
- @win_consts = win_consts
44
+ def initialize( client, parent )
45
+ @parent = parent
46
+ @client = client
47
+
48
48
  if( @client.platform =~ /x64/i )
49
49
  @native = 'Q'
50
50
  else
@@ -26,6 +26,10 @@
26
26
  # sf - Sept 2010 - Modified for x64 support and merged into the stdapi extension.
27
27
  #
28
28
 
29
+ #
30
+ # chao - June 2011 - major overhaul of dll lazy loading, caching, and bit of everything
31
+ #
32
+
29
33
  require 'pp'
30
34
  require 'enumerator'
31
35
 
@@ -35,6 +39,7 @@ require 'rex/post/meterpreter/extensions/stdapi/railgun/util'
35
39
  require 'rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager'
36
40
  require 'rex/post/meterpreter/extensions/stdapi/railgun/multicall'
37
41
  require 'rex/post/meterpreter/extensions/stdapi/railgun/dll'
42
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper'
38
43
 
39
44
  module Rex
40
45
  module Post
@@ -43,128 +48,180 @@ module Extensions
43
48
  module Stdapi
44
49
  module Railgun
45
50
 
51
+
46
52
  #
47
53
  # The Railgun class to dynamically expose the Windows API.
48
54
  #
49
55
  class Railgun
50
- def initialize( client )
56
+ # If you want to add additional DLL definitions to be preloaded
57
+ # create a definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/'
58
+ # Naming is important and should follow convention.
59
+ # For example, if your dll's name was "my_dll"
60
+ # file name - def_my_dll.rb
61
+ # class name - Def_my_dll
62
+ # entry below - 'my_dll'
63
+ BUILTIN_DLLS = [
64
+ 'kernel32',
65
+ 'ntdll',
66
+ 'user32',
67
+ 'ws2_32',
68
+ 'iphlpapi',
69
+ 'advapi32',
70
+ 'shell32',
71
+ 'netapi32',
72
+ ].freeze
51
73
 
52
- @client = client
53
- @dll = ::Hash.new
54
-
55
- @win_consts = WinConstManager.new()
56
-
57
- @constants_loaded = false
74
+ ##
75
+ # dlls
76
+ #
77
+ # Returns a hash containing DLLs added to this instance with self.add_dll
78
+ # as well as references to any frozen cached dlls added directly in self.get_dll
79
+ # and copies of any frozen dlls (added directly with self.add_function)
80
+ # that the user attempted to modify with self.add_function
81
+ #
82
+ # Keys are friendly DLL names and values are the corresponding DLL instance
83
+ attr_accessor :dlls
58
84
 
59
- # Load the multi-caller
60
- @multicaller = MultiCaller.new( @client, self, @win_consts )
85
+ ##
86
+ # client
87
+ #
88
+ # Contains a reference to the client that corresponds to this instance of railgun
89
+ attr_accessor :client
61
90
 
62
- # Load utility class
63
- @util = Util.new( self, @client.platform )
64
- end
91
+ ##
92
+ # @@cached_dlls
93
+ #
94
+ # These DLLs are loaded lazily and then shared amongst all railgun instances.
95
+ # For safety reasons this variable should only be read/written within get_dll.
96
+ @@cached_dlls = {}
97
+
98
+ # if you are going to touch @@cached_dlls, wear protection
99
+ @@cache_semaphore = Mutex.new
65
100
 
101
+ def initialize(client)
102
+ self.client = client
103
+ self.dlls = {}
104
+ end
105
+
106
+ def util
107
+ if @util.nil?
108
+ Util.new(self, client.platform)
109
+ end
110
+
111
+ return @util
112
+ end
113
+
114
+ def constant_manager
115
+ # Loads lazily
116
+ return ApiConstants.manager
117
+ end
118
+
66
119
  # read data from a memory address on the host (useful for working with LPVOID parameters)
67
- def memread( address, length )
120
+ def memread(address, length)
68
121
 
69
- raise "Invalid parameters." if( not address or not length )
122
+ raise "Invalid parameters." if(not address or not length)
70
123
 
71
- request = Packet.create_request( 'stdapi_railgun_memread' )
124
+ request = Packet.create_request('stdapi_railgun_memread')
72
125
 
73
- request.add_tlv( TLV_TYPE_RAILGUN_MEM_ADDRESS, address )
74
- request.add_tlv( TLV_TYPE_RAILGUN_MEM_LENGTH, length )
126
+ request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
127
+ request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)
75
128
 
76
- response = client.send_request( request )
77
- if( response.result == 0 )
78
- return response.get_tlv_value( TLV_TYPE_RAILGUN_MEM_DATA )
129
+ response = client.send_request(request)
130
+ if(response.result == 0)
131
+ return response.get_tlv_value(TLV_TYPE_RAILGUN_MEM_DATA)
79
132
  end
80
133
 
81
134
  return nil
82
135
  end
83
136
 
84
137
  # write data to a memory address on the host (useful for working with LPVOID parameters)
85
- def memwrite( address, data, length )
138
+ def memwrite(address, data, length)
86
139
 
87
- raise "Invalid parameters." if( not address or not data or not length )
140
+ raise "Invalid parameters." if(not address or not data or not length)
88
141
 
89
- request = Packet.create_request( 'stdapi_railgun_memwrite' )
142
+ request = Packet.create_request('stdapi_railgun_memwrite')
90
143
 
91
- request.add_tlv( TLV_TYPE_RAILGUN_MEM_ADDRESS, address )
92
- request.add_tlv( TLV_TYPE_RAILGUN_MEM_DATA, data )
93
- request.add_tlv( TLV_TYPE_RAILGUN_MEM_LENGTH, length )
144
+ request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
145
+ request.add_tlv(TLV_TYPE_RAILGUN_MEM_DATA, data)
146
+ request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)
94
147
 
95
- response = client.send_request( request )
96
- if( response.result == 0 )
148
+ response = client.send_request(request)
149
+ if(response.result == 0)
97
150
  return true
98
151
  end
99
152
 
100
153
  return false
101
154
  end
102
155
 
103
- # adds a function to an existing DLL-definition
156
+ # adds a function to an existing DLL-definition.
157
+ # if the DLL-definition is frozen (idealy this should be true for all cached dlls)
158
+ # an unfrozen copy is created and used henceforth for this instance.
104
159
  def add_function(dll_name, function_name, return_type, params, windows_name=nil)
105
- raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(@dll.keys, "")}" unless @dll.has_key? dll_name
106
- @dll[dll_name].add_function(function_name, return_type, params, windows_name)
160
+
161
+ unless known_dll_names.include?(dll_name)
162
+ raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, "")}"
163
+ end
164
+
165
+ dll = get_dll(dll_name)
166
+
167
+ # For backwards compatibility, we ensure the dll is thawed
168
+ if dll.frozen?
169
+ # dup will copy values, but not the frozen status
170
+ dll = dll.dup
171
+
172
+ # Update local dlls with the modifiable duplicate
173
+ dlls[dll_name] = dll
174
+ end
175
+
176
+ dll.add_function(function_name, return_type, params, windows_name)
107
177
  end
108
178
 
109
179
  # adds a function to an existing DLL-definition
110
180
  # you can override the dll name if you want to include a path or the DLL name contains
111
181
  # non-ruby-approved characters
112
- def add_dll(dll_name, windows_name=nil)
113
- raise "DLL #{dll_name} already exists. Existing DLLs: #{PP.pp(@dll.keys, "")}" unless not @dll.has_key? dll_name
114
- if( windows_name == nil )
115
- windows_name = dll_name
182
+ def add_dll(dll_name, windows_name=dll_name)
183
+
184
+ if dlls.has_key? dll_name
185
+ raise "A DLL of name #{dll_name} has already been loaded."
116
186
  end
117
- @dll[dll_name] = DLL.new(windows_name, @client, @win_consts)
187
+
188
+ dlls[dll_name] = DLL.new(windows_name, constant_manager)
118
189
  end
119
190
 
120
- def get_dll( dll_name )
121
- # sf: we now lazy load the module definitions as needed to avoid the performance hit
122
- # to stdapi if we do it upon initilization (the user may never use railgun or else
123
- # require only a portion of the modules exposed by railgun so no need to pre load them)
124
- if( not @dll.has_key?( dll_name ) )
125
-
126
- # the constants are also lazy loaded the first time we call const() or any API function...
127
- if( not @constants_loaded )
128
- ApiConstants.add_constants( @win_consts )
129
- @constants_loaded = true
130
- end
191
+
192
+ def known_dll_names
193
+ return BUILTIN_DLLS | dlls.keys
194
+ end
131
195
 
132
- case dll_name
133
- when 'kernel32'
134
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32'
135
- Def::Def_kernel32.add_imports(self)
136
- when 'ntdll'
137
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_ntdll'
138
- Def::Def_ntdll.add_imports(self)
139
- when 'user32'
140
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_user32'
141
- Def::Def_user32.add_imports(self)
142
- when 'ws2_32'
143
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_ws2_32'
144
- Def::Def_ws2_32.add_imports(self)
145
- when 'iphlpapi'
146
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_iphlpapi'
147
- Def::Def_iphlpapi.add_imports(self)
148
- when 'netapi32'
149
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32'
150
- Def::Def_netapi32.add_imports(self)
151
- when 'advapi32'
152
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_advapi32'
153
- Def::Def_advapi32.add_imports(self)
154
- when 'shell32'
155
- require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_shell32'
156
- Def::Def_shell32.add_imports(self)
157
- end
158
-
159
- if( @dll.has_key?( dll_name ) )
160
- return @dll[dll_name]
196
+ # Attempts to provide a DLL instance of the given name. Handles lazy loading and caching
197
+ # Note that if a DLL of the given name does not exist, then nil is returned
198
+ def get_dll(dll_name)
199
+
200
+ # If the DLL is not local, we now either load it from cache or load it lazily.
201
+ # In either case, a reference to the dll is stored in the collection "dlls"
202
+ # If the DLL can not be found/created, no actions are taken
203
+ unless dlls.has_key? dll_name
204
+ # We read and write to @@cached_dlls and rely on state consistency
205
+ @@cache_semaphore.synchronize do
206
+ if @@cached_dlls.has_key? dll_name
207
+ dlls[dll_name] = @@cached_dlls[dll_name]
208
+ elsif BUILTIN_DLLS.include? dll_name
209
+ # I highly doubt this case will ever occur, but I am paranoid
210
+ if dll_name !~ /^\w+$/
211
+ raise "DLL name #{dll_name} is bad. Correct Railgun::BUILTIN_DLLS"
212
+ end
213
+
214
+ require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_' << dll_name
215
+ dll = Def.const_get('Def_' << dll_name).create_dll.freeze
216
+
217
+ @@cached_dlls[dll_name] = dll
218
+ dlls[dll_name] = dll
219
+ end
161
220
  end
162
-
163
- else
164
- return @dll[dll_name]
221
+
165
222
  end
166
-
167
- return nil
223
+
224
+ return dlls[dll_name]
168
225
  end
169
226
 
170
227
  # we fake having members like user32 and kernel32.
@@ -175,30 +232,28 @@ class Railgun
175
232
  def method_missing(dll_symbol, *args)
176
233
  dll_name = dll_symbol.to_s
177
234
 
178
- self.get_dll( dll_name )
179
-
180
- raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(@dll.keys, "")}" unless @dll.has_key? dll_name
235
+ unless known_dll_names.include? dll_name
236
+ raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, '')}"
237
+ end
181
238
 
182
- return @dll[dll_name]
239
+ dll = get_dll(dll_name)
240
+
241
+ return DLLWrapper.new(dll, client)
183
242
  end
184
243
 
185
244
  # Give the programmer access to constants
186
245
  def const(str)
187
- # the constants are also lazy loaded the first time we call const() or any API function...
188
- if( not @constants_loaded )
189
- ApiConstants.add_constants( @win_consts )
190
- @constants_loaded = true
191
- end
192
- return @win_consts.parse(str)
246
+ return constant_manager.parse(str)
193
247
  end
194
248
 
195
- # The multi-call shorthand ( ["kernel32", "ExitProcess", [0]] )
249
+ # The multi-call shorthand (["kernel32", "ExitProcess", [0]])
196
250
  def multi(functions)
197
- @multicaller.call(functions)
198
- end
251
+ if @multicaller.nil?
252
+ @multicaller = MultiCaller.new(client, self)
253
+ end
199
254
 
200
- attr_accessor :client, :dll, :multicaller, :win_consts, :util
201
-
255
+ return @multicaller.call(functions)
256
+ end
202
257
  end
203
258
 
204
259
  end; end; end; end; end; end