librex 0.0.42 → 0.0.43

Sign up to get free protection for your applications and to get access to all the features.
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