librex 0.0.42 → 0.0.43
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -1
- data/lib/rex/compat.rb +10 -0
- data/lib/rex/post/meterpreter/channels/pools/file.rb +1 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/dir.rb +20 -18
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +11 -22
- data/lib/rex/post/meterpreter/extensions/stdapi/fs/file_stat.rb +2 -1
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun.rb.ts.rb +4 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb +27 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb.ut.rb +7 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_advapi32.rb +498 -242
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_iphlpapi.rb +18 -18
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +695 -694
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb +6 -5
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_ntdll.rb +24 -24
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_shell32.rb +5 -4
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_user32.rb +551 -551
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_ws2_32.rb +93 -93
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +56 -42
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb.ut.rb +4 -4
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_helper.rb.ut.rb +5 -5
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper.rb +26 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper.rb.ut.rb +63 -0
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/multicall.rb +4 -4
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +151 -96
- data/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb.ut.rb +80 -5
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +3 -3
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +11 -11
- data/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +3 -3
- data/lib/rex/post/meterpreter/packet.rb +12 -11
- data/lib/rex/proto/dhcp/server.rb +36 -42
- data/lib/rex/socket/range_walker.rb +1 -1
- data/lib/rex/text.rb +18 -1
- data/lib/rex/ui/text/table.rb +1 -1
- 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,
|
48
|
+
def initialize(dll_path, win_consts)
|
49
49
|
@dll_path = dll_path
|
50
|
-
|
50
|
+
|
51
|
+
# needed by DLLHelper
|
51
52
|
@win_consts = win_consts
|
52
|
-
|
53
|
-
@native = 'Q'
|
54
|
-
else
|
55
|
-
@native = 'V'
|
56
|
-
end
|
53
|
+
|
57
54
|
self.functions = {}
|
58
55
|
end
|
59
56
|
|
60
|
-
|
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",
|
63
|
-
# "DWORD",
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
161
|
-
buffer += [0].pack(
|
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(
|
164
|
-
buffer += [in_only_layout[param_desc[1]].addr].pack(
|
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(
|
167
|
-
buffer += [out_only_layout[param_desc[1]].addr].pack(
|
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(
|
170
|
-
buffer += [inout_layout[param_desc[1]].addr].pack(
|
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(
|
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(
|
204
|
+
buffer += [num].pack(native)
|
182
205
|
when "DWORD"
|
183
206
|
num = param_to_number(args[param_idx])
|
184
|
-
buffer += [num % 4294967296].pack(
|
207
|
+
buffer += [num % 4294967296].pack(native)
|
185
208
|
when "WORD"
|
186
209
|
num = param_to_number(args[param_idx])
|
187
|
-
buffer += [num % 65536].pack(
|
210
|
+
buffer += [num % 65536].pack(native)
|
188
211
|
when "BYTE"
|
189
212
|
num = param_to_number(args[param_idx])
|
190
|
-
buffer += [num % 256].pack(
|
213
|
+
buffer += [num % 256].pack(native)
|
191
214
|
when "BOOL"
|
192
215
|
case args[param_idx]
|
193
216
|
when true
|
194
|
-
buffer += [1].pack(
|
217
|
+
buffer += [1].pack(native)
|
195
218
|
when false
|
196
|
-
buffer += [0].pack(
|
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 =
|
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(
|
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 => '#{
|
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],
|
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
|
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],
|
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.
|
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
|
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
|
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
|
48
|
+
assert(actual_string =~ /^#{target_string}/,
|
49
49
|
"asciiz_to_str should preserve string before zero")
|
50
50
|
|
51
|
-
assert(
|
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
|
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
|
45
|
-
@parent
|
46
|
-
@client
|
47
|
-
|
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
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
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(
|
120
|
+
def memread(address, length)
|
68
121
|
|
69
|
-
raise "Invalid parameters." if(
|
122
|
+
raise "Invalid parameters." if(not address or not length)
|
70
123
|
|
71
|
-
request = Packet.create_request(
|
124
|
+
request = Packet.create_request('stdapi_railgun_memread')
|
72
125
|
|
73
|
-
request.add_tlv(
|
74
|
-
request.add_tlv(
|
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(
|
77
|
-
if(
|
78
|
-
return response.get_tlv_value(
|
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(
|
138
|
+
def memwrite(address, data, length)
|
86
139
|
|
87
|
-
raise "Invalid parameters." if(
|
140
|
+
raise "Invalid parameters." if(not address or not data or not length)
|
88
141
|
|
89
|
-
request = Packet.create_request(
|
142
|
+
request = Packet.create_request('stdapi_railgun_memwrite')
|
90
143
|
|
91
|
-
request.add_tlv(
|
92
|
-
request.add_tlv(
|
93
|
-
request.add_tlv(
|
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(
|
96
|
-
if(
|
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
|
-
|
106
|
-
|
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=
|
113
|
-
|
114
|
-
if
|
115
|
-
|
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
|
-
|
187
|
+
|
188
|
+
dlls[dll_name] = DLL.new(windows_name, constant_manager)
|
118
189
|
end
|
119
190
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
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
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
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
|
-
|
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 (
|
249
|
+
# The multi-call shorthand (["kernel32", "ExitProcess", [0]])
|
196
250
|
def multi(functions)
|
197
|
-
@multicaller.
|
198
|
-
|
251
|
+
if @multicaller.nil?
|
252
|
+
@multicaller = MultiCaller.new(client, self)
|
253
|
+
end
|
199
254
|
|
200
|
-
|
201
|
-
|
255
|
+
return @multicaller.call(functions)
|
256
|
+
end
|
202
257
|
end
|
203
258
|
|
204
259
|
end; end; end; end; end; end
|