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.
- 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
|