Rejig platform to use windows instead of win32/win64
parent
699a8e91d2
commit
022830634b
|
@ -284,7 +284,7 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
#
|
||||
# Load the stdapi extension.
|
||||
#
|
||||
def load_stdapi()
|
||||
def load_stdapi
|
||||
original = console.disable_output
|
||||
console.disable_output = true
|
||||
console.run_single('load stdapi')
|
||||
|
@ -294,9 +294,8 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
#
|
||||
# Load the priv extension.
|
||||
#
|
||||
def load_priv()
|
||||
def load_priv
|
||||
original = console.disable_output
|
||||
|
||||
console.disable_output = true
|
||||
console.run_single('load priv')
|
||||
console.disable_output = original
|
||||
|
@ -310,7 +309,6 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
|
||||
begin
|
||||
self.machine_id = self.core.machine_id(timeout)
|
||||
self.payload_uuid ||= self.core.uuid(timeout)
|
||||
|
||||
return true
|
||||
rescue ::Rex::Post::Meterpreter::RequestError
|
||||
|
@ -325,40 +323,6 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
def update_session_info
|
||||
username = self.sys.config.getuid
|
||||
sysinfo = self.sys.config.sysinfo
|
||||
tuple = self.platform.split('/')
|
||||
|
||||
#
|
||||
# Windows meterpreter currently needs 'win32' or 'win64' to be in the
|
||||
# second half of the platform tuple, in order for various modules and
|
||||
# library code match on that specific string.
|
||||
#
|
||||
if self.platform !~ /win32|win64/
|
||||
|
||||
platform = case self.sys.config.sysinfo['OS']
|
||||
when /windows/i
|
||||
Msf::Module::Platform::Windows
|
||||
when /darwin/i
|
||||
Msf::Module::Platform::OSX
|
||||
when /freebsd/i
|
||||
Msf::Module::Platform::FreeBSD
|
||||
when /netbsd/i
|
||||
Msf::Module::Platform::NetBSD
|
||||
when /openbsd/i
|
||||
Msf::Module::Platform::OpenBSD
|
||||
when /sunos/i
|
||||
Msf::Module::Platform::Solaris
|
||||
when /android/i
|
||||
Msf::Module::Platform::Android
|
||||
else
|
||||
Msf::Module::Platform::Linux
|
||||
end.realname.downcase
|
||||
|
||||
#
|
||||
# This normalizes the platform from 'python/python' to 'python/linux'
|
||||
#
|
||||
self.platform = "#{tuple[0]}/#{platform}"
|
||||
end
|
||||
|
||||
|
||||
safe_info = "#{username} @ #{sysinfo['Computer']}"
|
||||
safe_info.force_encoding("ASCII-8BIT") if safe_info.respond_to?(:force_encoding)
|
||||
|
@ -505,8 +469,33 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
sock
|
||||
end
|
||||
|
||||
attr_accessor :platform
|
||||
attr_accessor :binary_suffix
|
||||
#
|
||||
# Get a string representation of the current session platform
|
||||
#
|
||||
def platform
|
||||
# TODO: talk about this with the devs because we seem to rely on this
|
||||
# value when populating the DB before the session is even fully established.
|
||||
if self.payload_uuid
|
||||
# return the actual platform of the current session if it's there
|
||||
self.payload_uuid.to_platform
|
||||
else
|
||||
# otherwise just use the base for the session type tied to this handler
|
||||
self.base_platform
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Get the value to use for file suffixes based on the platform
|
||||
#
|
||||
def binary_suffix
|
||||
self.payload_uuid.binary_suffix
|
||||
end
|
||||
|
||||
# This is the base platform for the original payload, required for when the
|
||||
# session is first created thanks to the fact that the DB session recording
|
||||
# happens before the session is even established.
|
||||
attr_accessor :base_platform
|
||||
|
||||
attr_accessor :console # :nodoc:
|
||||
attr_accessor :skip_ssl
|
||||
attr_accessor :skip_cleanup
|
||||
|
|
|
@ -19,8 +19,7 @@ class Meterpreter_Java_Java < Msf::Sessions::Meterpreter
|
|||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'java/java'
|
||||
self.binary_suffix = 'jar'
|
||||
self.base_platform = 'java/java'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,8 +19,7 @@ class Meterpreter_mipsbe_Linux < Msf::Sessions::Meterpreter
|
|||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'mipsbe/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
self.base_platform = 'mipsbe/linux'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ module MeterpreterOptions
|
|||
session.load_session_info
|
||||
end
|
||||
|
||||
if session.platform =~ /win32|win64/i
|
||||
if session.platform =~ /windows/i
|
||||
session.load_priv rescue nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,8 +19,7 @@ class Meterpreter_Php_Php < Msf::Sessions::Meterpreter
|
|||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'php/php'
|
||||
self.binary_suffix = 'php'
|
||||
self.base_platform = 'php/php'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -86,8 +86,7 @@ class Meterpreter_Python_Python < Msf::Sessions::Meterpreter
|
|||
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'python/python'
|
||||
self.binary_suffix = 'py'
|
||||
self.base_platform = 'python/python'
|
||||
end
|
||||
|
||||
def lookup_error(error_code)
|
||||
|
@ -116,5 +115,6 @@ class Meterpreter_Python_Python < Msf::Sessions::Meterpreter
|
|||
false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,8 +19,7 @@ class Meterpreter_x64_Mettle_Linux < Msf::Sessions::Meterpreter
|
|||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x64/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
self.base_platform = 'x64/linux'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ module Sessions
|
|||
class Meterpreter_x64_Win < Msf::Sessions::Meterpreter
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x64/win64'
|
||||
self.binary_suffix = 'x64.dll'
|
||||
self.base_platform = 'x64/windows'
|
||||
end
|
||||
|
||||
def lookup_error(code)
|
||||
|
|
|
@ -13,8 +13,7 @@ module Sessions
|
|||
class Meterpreter_x86_BSD < Msf::Sessions::Meterpreter
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x86/bsd'
|
||||
self.binary_suffix = 'bso'
|
||||
self.base_platform = 'x86/bsd'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ module Sessions
|
|||
class Meterpreter_x86_Linux < Msf::Sessions::Meterpreter
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x86/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
self.base_platform = 'x86/linux'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,8 +19,7 @@ class Meterpreter_x86_Mettle_Linux < Msf::Sessions::Meterpreter
|
|||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x86/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
self.base_platform = 'x86/linux'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ module Sessions
|
|||
class Meterpreter_x86_Win < Msf::Sessions::Meterpreter
|
||||
def initialize(rstream,opts={})
|
||||
super
|
||||
self.platform = 'x86/win32'
|
||||
self.binary_suffix = 'x86.dll'
|
||||
self.base_platform = 'x86/windows'
|
||||
end
|
||||
|
||||
def lookup_error(code)
|
||||
|
|
|
@ -298,6 +298,52 @@ class Msf::Payload::UUID
|
|||
].join("/")
|
||||
end
|
||||
|
||||
#
|
||||
# Return a string that represents the Meterpreter platform
|
||||
#
|
||||
def to_platform
|
||||
# mini-patch for x86_64 so that it renders x64 instead. This is
|
||||
# mostly to keep various external modules happy.
|
||||
arch = self.arch
|
||||
if arch == ARCH_X86_64
|
||||
arch = ARCH_X64
|
||||
end
|
||||
"#{arch}/#{self.platform}"
|
||||
end
|
||||
|
||||
#
|
||||
# TODO: Not sure if this is the best place for this to go. Open to
|
||||
# suggestions for moving it elsewhere
|
||||
#
|
||||
def binary_suffix
|
||||
# generate a file/binary suffix based on the current platform
|
||||
case self.platform
|
||||
when 'windows'
|
||||
# with windows, we also need to care about arch
|
||||
if self.arch == ARCH_X86
|
||||
'x86.dll'
|
||||
else
|
||||
'x64.dll'
|
||||
end
|
||||
when 'android', 'java'
|
||||
'jar'
|
||||
when 'ruby'
|
||||
'rb'
|
||||
when 'linux' , 'aix' , 'hpux' , 'irix' , 'unix'
|
||||
'lso'
|
||||
when 'bsd' , 'openbsd' , 'bsdi' , 'netbsd' , 'freebsd'
|
||||
'bso'
|
||||
when 'php'
|
||||
'php'
|
||||
when 'python'
|
||||
'py'
|
||||
when 'nodejs', 'js'
|
||||
'js'
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Provides a hash representation of a UUID
|
||||
#
|
||||
|
|
|
@ -431,40 +431,42 @@ class ClientCore < Extension
|
|||
# Migrates the meterpreter instance to the process specified
|
||||
# by pid. The connection to the server remains established.
|
||||
#
|
||||
def migrate(pid, writable_dir = nil, opts = {})
|
||||
def migrate(target_pid, writable_dir = nil, opts = {})
|
||||
keepalive = client.send_keepalives
|
||||
client.send_keepalives = false
|
||||
process = nil
|
||||
target_process = nil
|
||||
current_process = nil
|
||||
binary_suffix = nil
|
||||
old_platform = client.platform
|
||||
old_binary_suffix = client.binary_suffix
|
||||
|
||||
# Load in the stdapi extension if not allready present so we can determine the target pid architecture...
|
||||
client.core.use('stdapi') if not client.ext.aliases.include?('stdapi')
|
||||
|
||||
# Determine the architecture for the pid we are going to migrate into...
|
||||
current_pid = client.sys.process.getpid
|
||||
|
||||
# Find the current and target process instances
|
||||
client.sys.process.processes.each { | p |
|
||||
if p['pid'] == pid
|
||||
process = p
|
||||
break
|
||||
if p['pid'] == target_pid
|
||||
target_process = p
|
||||
elsif p['pid'] == current_pid
|
||||
current_process = p
|
||||
end
|
||||
}
|
||||
|
||||
# We cant migrate into a process that does not exist.
|
||||
unless process
|
||||
unless target_process
|
||||
raise RuntimeError, 'Cannot migrate into non existent process', caller
|
||||
end
|
||||
|
||||
# We cannot migrate into a process that we are unable to open
|
||||
# On linux, arch is empty even if we can access the process
|
||||
if client.platform =~ /win/
|
||||
if process['arch'] == nil || process['arch'].empty?
|
||||
if target_process['arch'] == nil || target_process['arch'].empty?
|
||||
raise RuntimeError, "Cannot migrate into this process (insufficient privileges)", caller
|
||||
end
|
||||
end
|
||||
|
||||
# And we also cannot migrate into our own current process...
|
||||
if process['pid'] == client.sys.process.getpid
|
||||
if current_process['pid'] == target_process['pid']
|
||||
raise RuntimeError, 'Cannot migrate into current process', caller
|
||||
end
|
||||
|
||||
|
@ -481,7 +483,7 @@ class ClientCore < Extension
|
|||
# Rex::Post::FileStat#writable? isn't available
|
||||
end
|
||||
|
||||
blob = generate_payload_stub(process)
|
||||
blob = generate_payload_stub(target_process)
|
||||
|
||||
# Build the migration request
|
||||
request = Packet.create_request('core_migrate')
|
||||
|
@ -507,16 +509,23 @@ class ClientCore < Extension
|
|||
request.add_tlv(TLV_TYPE_MIGRATE_SOCKET_PATH, socket_path, false, client.capabilities[:zlib])
|
||||
end
|
||||
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_PID, pid )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_PID, target_pid )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_LEN, blob.length )
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_PAYLOAD, blob, false, client.capabilities[:zlib])
|
||||
|
||||
if process['arch'] == ARCH_X86_64
|
||||
if target_process['arch'] == ARCH_X86_64
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 2 ) # PROCESS_ARCH_X64
|
||||
|
||||
else
|
||||
request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 1 ) # PROCESS_ARCH_X86
|
||||
end
|
||||
|
||||
# if we change architecture, we need to change UUID as well
|
||||
if current_process['arch'] != target_process['arch']
|
||||
client.payload_uuid.arch = target_process['arch']
|
||||
request.add_tlv( TLV_TYPE_UUID, client.payload_uuid.to_raw )
|
||||
end
|
||||
|
||||
# Send the migration request. Timeout can be specified by the caller, or set to a min
|
||||
# of 60 seconds.
|
||||
timeout = [(opts[:timeout] || 0), 60].max
|
||||
|
@ -558,30 +567,6 @@ class ClientCore < Extension
|
|||
end
|
||||
end
|
||||
|
||||
# Update the meterpreter platform/suffix for loading extensions as we may
|
||||
# have changed target architecture
|
||||
# sf: this is kinda hacky but it works. As ruby doesnt let you un-include a
|
||||
# module this is the simplest solution I could think of. If the platform
|
||||
# specific modules Meterpreter_x64_Win/Meterpreter_x86_Win change
|
||||
# significantly we will need a better way to do this.
|
||||
|
||||
case client.platform
|
||||
when /win/i
|
||||
if process['arch'] == ARCH_X86_64
|
||||
client.platform = 'x64/win64'
|
||||
client.binary_suffix = 'x64.dll'
|
||||
else
|
||||
client.platform = 'x86/win32'
|
||||
client.binary_suffix = 'x86.dll'
|
||||
end
|
||||
when /linux/i
|
||||
client.platform = 'x86/linux'
|
||||
client.binary_suffix = 'lso'
|
||||
else
|
||||
client.platform = old_platform
|
||||
client.binary_suffix = old_binary_suffix
|
||||
end
|
||||
|
||||
# Load all the extensions that were loaded in the previous instance (using the correct platform/binary_suffix)
|
||||
client.ext.aliases.keys.each { |e|
|
||||
client.core.use(e)
|
||||
|
|
|
@ -361,7 +361,7 @@ class DLL
|
|||
# puts("
|
||||
#=== START of proccess_function_call snapshot ===
|
||||
# {
|
||||
# :platform => '#{native == 'Q' ? 'x64/win64' : 'x86/win32'}',
|
||||
# :platform => '#{native == 'Q' ? 'x64/windows' : 'x86/windows'}',
|
||||
# :name => '#{function.windows_name}',
|
||||
# :params => #{function.params},
|
||||
# :return_type => '#{function.return_type}',
|
||||
|
|
|
@ -46,7 +46,7 @@ module MockMagic
|
|||
end
|
||||
end
|
||||
|
||||
def make_mock_client(platform = "x86/win32", target_request_tlvs = [], response_tlvs = [])
|
||||
def make_mock_client(platform = "x86/windows", target_request_tlvs = [], response_tlvs = [])
|
||||
check_request = lambda do |request|
|
||||
target_request_tlvs.each_pair do |type, target_value|
|
||||
assert_equal(target_value, request.get_tlv_value(type),
|
||||
|
@ -62,7 +62,7 @@ module MockMagic
|
|||
def mock_function_descriptions
|
||||
[
|
||||
{
|
||||
:platform => "x86/win32",
|
||||
:platform => "x86/windows",
|
||||
:name => "LookupAccountSidA",
|
||||
:params => [
|
||||
["PCHAR","lpSystemName","in"],
|
||||
|
@ -101,7 +101,7 @@ module MockMagic
|
|||
},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'LookupAccountSidA',
|
||||
:params => [
|
||||
["PCHAR", "lpSystemName", "in"],
|
||||
|
@ -140,7 +140,7 @@ module MockMagic
|
|||
},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptAcquireContextW',
|
||||
:params => [["PDWORD", "phProv", "out"], ["PWCHAR", "pszContainer", "in"], ["PWCHAR", "pszProvider", "in"], ["DWORD", "dwProvType", "in"], ["DWORD", "dwflags", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -163,7 +163,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "phProv"=>1371080},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptCreateHash',
|
||||
:params => [["LPVOID", "hProv", "in"], ["DWORD", "Algid", "in"], ["LPVOID", "hKey", "in"], ["DWORD", "dwFlags", "in"], ["PDWORD", "phHash", "out"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -186,7 +186,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "phHash"=>1370736},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptHashData',
|
||||
:params => [["LPVOID", "hHash", "in"], ["PWCHAR", "pbData", "in"], ["DWORD", "dwDataLen", "in"], ["DWORD", "dwFlags", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -209,7 +209,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptDeriveKey',
|
||||
:params => [["LPVOID", "hProv", "in"], ["DWORD", "Algid", "in"], ["LPVOID", "hBaseData", "in"], ["DWORD", "dwFlags", "in"], ["PDWORD", "phKey", "inout"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -232,7 +232,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "phKey"=>1416352},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptDecrypt',
|
||||
:params => [["LPVOID", "hKey", "in"], ["LPVOID", "hHash", "in"], ["BOOL", "Final", "in"], ["DWORD", "dwFlags", "in"], ["PBLOB", "pbData", "inout"], ["PDWORD", "pdwDataLen", "inout"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -255,7 +255,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "pbData"=>"q\x00u\x00x\x00", "pdwDataLen"=>6},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptDestroyHash',
|
||||
:params => [["LPVOID", "hHash", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -278,7 +278,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptDestroyKey',
|
||||
:params => [["LPVOID", "hKey", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -301,7 +301,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x86/win32',
|
||||
:platform => 'x86/windows',
|
||||
:name => 'CryptReleaseContext',
|
||||
:params => [["LPVOID", "hProv", "in"], ["DWORD", "dwFlags", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -324,7 +324,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptAcquireContextW',
|
||||
:params => [["PDWORD", "phProv", "out"], ["PWCHAR", "pszContainer", "in"], ["PWCHAR", "pszProvider", "in"], ["DWORD", "dwProvType", "in"], ["DWORD", "dwflags", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -347,7 +347,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "phProv"=>1756800},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptCreateHash',
|
||||
:params => [["LPVOID", "hProv", "in"], ["DWORD", "Algid", "in"], ["LPVOID", "hKey", "in"], ["DWORD", "dwFlags", "in"], ["PDWORD", "phHash", "out"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -370,7 +370,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "phHash"=>1680128},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptHashData',
|
||||
:params => [["LPVOID", "hHash", "in"], ["PWCHAR", "pbData", "in"], ["DWORD", "dwDataLen", "in"], ["DWORD", "dwFlags", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -393,7 +393,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptDeriveKey',
|
||||
:params => [["LPVOID", "hProv", "in"], ["DWORD", "Algid", "in"], ["LPVOID", "hBaseData", "in"], ["DWORD", "dwFlags", "in"], ["PDWORD", "phKey", "inout"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -416,7 +416,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "phKey"=>1680240},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptDecrypt',
|
||||
:params => [["LPVOID", "hKey", "in"], ["LPVOID", "hHash", "in"], ["BOOL", "Final", "in"], ["DWORD", "dwFlags", "in"], ["PBLOB", "pbData", "inout"], ["PDWORD", "pdwDataLen", "inout"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -439,7 +439,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true, "pbData"=>"b\x00a\x00z\x00", "pdwDataLen"=>6},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptDestroyHash',
|
||||
:params => [["LPVOID", "hHash", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -462,7 +462,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptDestroyKey',
|
||||
:params => [["LPVOID", "hKey", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
@ -485,7 +485,7 @@ module MockMagic
|
|||
:returned_hash => {"GetLastError"=>0, "return"=>true},
|
||||
},
|
||||
{
|
||||
:platform => 'x64/win64',
|
||||
:platform => 'x64/windows',
|
||||
:name => 'CryptReleaseContext',
|
||||
:params => [["LPVOID", "hProv", "in"], ["DWORD", "dwFlags", "in"]],
|
||||
:return_type => 'BOOL',
|
||||
|
|
|
@ -639,7 +639,7 @@ class Util
|
|||
# Returns true if given platform has 64bit architecture
|
||||
# expects client.platform
|
||||
def is_64bit_platform?(platform)
|
||||
platform =~ /win64/
|
||||
platform =~ /x64/
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
require 'rex/post/meterpreter/packet_response_waiter'
|
||||
require 'rex/logging'
|
||||
require 'rex/exceptions'
|
||||
require 'msf/core/payload/uuid'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
|
@ -244,6 +245,13 @@ module PacketDispatcher
|
|||
# removed. This happens if the waiter timed out above.
|
||||
remove_response_waiter(waiter)
|
||||
|
||||
# wire in the UUID for this, as it should be part of every response
|
||||
# packet
|
||||
if response && !self.payload_uuid
|
||||
uuid = response.get_tlv_value(TLV_TYPE_UUID)
|
||||
self.payload_uuid = Msf::Payload::UUID.new({:raw => uuid}) if uuid
|
||||
end
|
||||
|
||||
# Return the response packet, if any
|
||||
return response
|
||||
end
|
||||
|
|
|
@ -41,7 +41,7 @@ class MetasploitModule < Msf::Post
|
|||
when /osx/
|
||||
@platform = :osx
|
||||
paths = enum_users_unix
|
||||
when /win/
|
||||
when /windows/
|
||||
@platform = :windows
|
||||
drive = session.sys.config.getenv('SystemDrive')
|
||||
os = session.sys.config.sysinfo['OS']
|
||||
|
|
|
@ -47,7 +47,7 @@ class MetasploitModule < Msf::Post
|
|||
user_base = "/home/#{user}/"
|
||||
end
|
||||
dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"
|
||||
when /win/
|
||||
when /windows/
|
||||
if session.type =~ /meterpreter/
|
||||
user_profile = session.sys.config.getenv('USERPROFILE')
|
||||
else
|
||||
|
@ -63,7 +63,7 @@ class MetasploitModule < Msf::Post
|
|||
case session.platform
|
||||
when /linux/
|
||||
dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
|
||||
when /win/
|
||||
when /windows/
|
||||
dbvis_file = user_profile + "\\.dbvis\\config\\dbvis.xml"
|
||||
end
|
||||
unless file?(dbvis_file)
|
||||
|
|
|
@ -46,7 +46,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
case session.platform
|
||||
when /win/i
|
||||
when /windows/i
|
||||
cmd = "nslookup"
|
||||
when /solaris/i
|
||||
cmd = "/usr/sbin/host "
|
||||
|
@ -62,7 +62,7 @@ class MetasploitModule < Msf::Post
|
|||
r = cmd_exec(cmd, "#{n.strip}.#{domain}")
|
||||
|
||||
case session.platform
|
||||
when /win/
|
||||
when /windows/
|
||||
proccess_win(r, "#{n.strip}.#{domain}")
|
||||
else
|
||||
process_nix(r, "#{n.strip}.#{domain}")
|
||||
|
|
|
@ -45,7 +45,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
case session.platform
|
||||
when /win/i
|
||||
when /windows/i
|
||||
cmd = "nslookup"
|
||||
when /solaris/i
|
||||
cmd = "/usr/sbin/host"
|
||||
|
@ -59,7 +59,7 @@ class MetasploitModule < Msf::Post
|
|||
next if ip_add.nil?
|
||||
r = cmd_exec(cmd, " #{ip_add}")
|
||||
case session.platform
|
||||
when /win/
|
||||
when /windows/
|
||||
if r =~ /(Name)/
|
||||
r.scan(/Name:\s*\S*\s/) do |n|
|
||||
hostname = n.split(": ")
|
||||
|
|
|
@ -56,7 +56,7 @@ class MetasploitModule < Msf::Post
|
|||
a = []
|
||||
|
||||
case session.platform
|
||||
when /win/i
|
||||
when /windows/i
|
||||
ns_opt = " -query=srv "
|
||||
cmd = "nslookup"
|
||||
when /solaris/i
|
||||
|
@ -74,7 +74,7 @@ class MetasploitModule < Msf::Post
|
|||
r = cmd_exec(cmd, ns_opt + "#{srv}#{domain}")
|
||||
|
||||
case session.platform
|
||||
when /win/
|
||||
when /windows/
|
||||
if r =~ /\s*internet\saddress\s\=\s/
|
||||
nslookup_srv_consume("#{srv}#{domain}", r).each do |f|
|
||||
print_good("\t#{f[:srv]} #{f[:target]} #{f[:port]} #{f[:ip]}")
|
||||
|
|
|
@ -28,7 +28,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
def run
|
||||
if session.platform =~ /win/
|
||||
if session.platform =~ /windows/
|
||||
if session.type == 'meterpreter'
|
||||
begin
|
||||
res = cmd_exec('c:\\Program Files\\Oracle\\VirtualBox\\vboxmanage', 'list -l vms')
|
||||
|
|
|
@ -35,7 +35,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
def get_env_shell
|
||||
print_line @output if @output
|
||||
if session.platform =~ /win/
|
||||
if session.platform =~ /windows/
|
||||
@ltype = "windows.environment"
|
||||
cmd = "set"
|
||||
else
|
||||
|
|
|
@ -36,7 +36,8 @@ class MetasploitModule < Msf::Post
|
|||
when /osx/
|
||||
@platform = :osx
|
||||
paths = enum_users_unix
|
||||
when /win/
|
||||
when /windows/
|
||||
@platform = :windows
|
||||
profiles = grab_user_profiles()
|
||||
profiles.each do |user|
|
||||
next if user['AppData'] == nil
|
||||
|
|
|
@ -77,7 +77,7 @@ class MetasploitModule < Msf::Post
|
|||
@platform = :unix
|
||||
when /osx/
|
||||
@platform = :osx
|
||||
when /win/
|
||||
when /windows/
|
||||
if session.type != "meterpreter"
|
||||
print_error "Only meterpreter sessions are supported on Windows hosts"
|
||||
return
|
||||
|
@ -367,7 +367,7 @@ class MetasploitModule < Msf::Post
|
|||
loot_file = Rex::Text::rand_text_alpha(6) + ".txt"
|
||||
|
||||
case @platform
|
||||
when /win/
|
||||
when /windows/
|
||||
unless got_root || session.sys.config.sysinfo['OS'] =~ /xp/i
|
||||
print_warning("You may need SYSTEM privileges on this platform for the DECRYPT option to work")
|
||||
end
|
||||
|
@ -560,7 +560,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
args.insert(0, "\"#{@paths['ff']}firefox --display=:0 ")
|
||||
args << "\""
|
||||
cmd = "su #{user} -c"
|
||||
elsif @platform =~ /win|osx/
|
||||
elsif @platform =~ /windows|osx/
|
||||
cmd = @paths['ff'] + "firefox"
|
||||
# On OSX, run in background
|
||||
args << "& sleep 5 && killall firefox" if @platform =~ /osx/
|
||||
|
|
|
@ -38,7 +38,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
def run
|
||||
if session.platform =~ /win/ && session.type == "shell" # No Windows shell support
|
||||
if session.platform =~ /windows/ && session.type == "shell" # No Windows shell support
|
||||
print_error "Shell sessions on Windows are not supported"
|
||||
return
|
||||
end
|
||||
|
@ -79,7 +79,7 @@ class MetasploitModule < Msf::Post
|
|||
cookies_path_map = {}
|
||||
|
||||
case platform
|
||||
when /win/
|
||||
when /windows/
|
||||
browser_path_map = {
|
||||
'Chrome' => "#{user_profile['LocalAppData']}\\Google\\Chrome\\User Data\\Default\\databases\\chrome-extension_hdokiejnpimakedhajhdlcegeplioahd_0",
|
||||
'Firefox' => "#{user_profile['AppData']}\\Mozilla\\Firefox\\Profiles",
|
||||
|
@ -194,7 +194,7 @@ class MetasploitModule < Msf::Post
|
|||
"LocalAppData" => "/Users/#{user_name}/Library/Application Support"
|
||||
)
|
||||
end
|
||||
when /win/
|
||||
when /windows/
|
||||
user_profiles |= grab_user_profiles
|
||||
else
|
||||
print_error "OS not recognized: #{os}"
|
||||
|
@ -807,6 +807,6 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
# Returns OS separator in a session type agnostic way
|
||||
def system_separator
|
||||
return session.platform =~ /win/ ? '\\' : '/'
|
||||
return session.platform =~ /windows/ ? '\\' : '/'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,7 @@ class MetasploitModule < Msf::Post
|
|||
case session.platform
|
||||
when /unix|linux|bsd|osx/
|
||||
files = enum_user_directories.map {|d| d + "/.pgpass"}.select { |f| file?(f) }
|
||||
when /win/
|
||||
when /windows/
|
||||
if session.type != "meterpreter"
|
||||
print_error("Only meterpreter sessions are supported on windows hosts")
|
||||
return
|
||||
|
|
|
@ -44,8 +44,8 @@ class MetasploitModule < Msf::Post
|
|||
when /osx/
|
||||
@platform = :osx
|
||||
paths = enum_users_unix
|
||||
when /win/
|
||||
@platform = :win
|
||||
when /windows/
|
||||
@platform = :windows
|
||||
profiles = grab_user_profiles()
|
||||
profiles.each do |user|
|
||||
next if user['AppData'] == nil
|
||||
|
@ -107,7 +107,7 @@ class MetasploitModule < Msf::Post
|
|||
print_status("Checking for Pidgin profile in: #{purpledir}")
|
||||
session.fs.dir.foreach(purpledir) do |dir|
|
||||
if dir =~ /\.purple/
|
||||
if @platform == :win
|
||||
if @platform == :windows
|
||||
print_status("Found #{purpledir}\\#{dir}")
|
||||
path = "#{purpledir}\\#{dir}"
|
||||
else
|
||||
|
|
|
@ -42,7 +42,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
case session.platform
|
||||
when /win/i
|
||||
when /windows/i
|
||||
count = " -n 1 "
|
||||
cmd = "ping"
|
||||
when /solaris/i
|
||||
|
|
|
@ -61,7 +61,7 @@ class MetasploitModule < Msf::Post
|
|||
process_db(db_in_loot,p['name'])
|
||||
end
|
||||
end
|
||||
elsif (session.platform =~ /win/ and session.type =~ /meter/)
|
||||
elsif (session.platform =~ /windows/ and session.type =~ /meter/)
|
||||
# Iterate thru each user profile in a Windows System using Meterpreter Post API
|
||||
grab_user_profiles().each do |p|
|
||||
if check_skype(p['AppData'],p['UserName'])
|
||||
|
|
|
@ -48,7 +48,7 @@ class MetasploitModule < Msf::Post
|
|||
when /osx/
|
||||
user = session.shell_command("whoami").chomp
|
||||
base = "/Users/#{user}/Library/Thunderbird/Profiles/"
|
||||
when /win/
|
||||
when /windows/
|
||||
if session.type =~ /meterpreter/
|
||||
user_profile = session.sys.config.getenv('APPDATA')
|
||||
else
|
||||
|
@ -65,7 +65,7 @@ class MetasploitModule < Msf::Post
|
|||
# Steal!
|
||||
profiles.each do |profile|
|
||||
next if profile =~ /^\./
|
||||
slash = (session.platform =~ /win/) ? "\\" : "/"
|
||||
slash = (session.platform =~ /windows/) ? "\\" : "/"
|
||||
p = base + profile + slash
|
||||
|
||||
# Download the database, and attempt to process the content
|
||||
|
@ -98,7 +98,7 @@ class MetasploitModule < Msf::Post
|
|||
f.close
|
||||
end
|
||||
elsif session.type =~ /shell/
|
||||
cmd_show = (session.platform =~ /win/) ? 'type' : 'cat'
|
||||
cmd_show = (session.platform =~ /windows/) ? 'type' : 'cat'
|
||||
# The type command will add a 0x0a character in the file? Pff.
|
||||
# Gotta lstrip that.
|
||||
loot = cmd_exec(cmd_show, "\"#{p+item}\"").lstrip
|
||||
|
@ -210,11 +210,11 @@ class MetasploitModule < Msf::Post
|
|||
tb_profiles << subdir
|
||||
end
|
||||
else
|
||||
cmd = (session.platform =~ /win/) ? "dir \"#{path}\"" : "ls -ld #{path}*/"
|
||||
cmd = (session.platform =~ /windows/) ? "dir \"#{path}\"" : "ls -ld #{path}*/"
|
||||
dir = cmd_exec(cmd)
|
||||
dir.each_line do |line|
|
||||
line = line.strip
|
||||
next if session.platform =~ /win/ and line !~ /<DIR>((.+)\.(\w+)$)/
|
||||
next if session.platform =~ /windows/ and line !~ /<DIR>((.+)\.(\w+)$)/
|
||||
next if session.platform =~ /linux|osx/ and line !~ /(\w+\.\w+)/
|
||||
tb_profiles << $1 if not $1.nil?
|
||||
end
|
||||
|
|
|
@ -109,7 +109,7 @@ class MetasploitModule < Msf::Post
|
|||
# Run Method for when run command is issued
|
||||
def run
|
||||
case session.platform
|
||||
when /win/i
|
||||
when /windows/i
|
||||
listing = cmd_exec('netsh wlan show networks mode=bssid')
|
||||
if listing.nil?
|
||||
print_error("Unable to generate wireless listing.")
|
||||
|
|
|
@ -67,7 +67,7 @@ class MetasploitModule < Msf::Post
|
|||
user_base = "/home/#{user}/"
|
||||
end
|
||||
dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"
|
||||
when /win/
|
||||
when /windows/
|
||||
user_profile = session.sys.config.getenv('USERPROFILE')
|
||||
dbvis_file = "#{user_profile}\\.dbvis\\config70\\dbvis.xml"
|
||||
end
|
||||
|
@ -76,16 +76,18 @@ class MetasploitModule < Msf::Post
|
|||
#File not found, we next try with the old config path
|
||||
print_status("File not found: #{dbvis_file}")
|
||||
print_status("This could be an older version of dbvis, trying old path")
|
||||
|
||||
case session.platform
|
||||
when /linux/
|
||||
dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
|
||||
when /win/
|
||||
when /windows/
|
||||
dbvis_file = "#{user_profile }\\.dbvis\\config\\dbvis.xml"
|
||||
end
|
||||
unless file?(dbvis_file)
|
||||
print_error("File not found: #{dbvis_file}")
|
||||
return
|
||||
end
|
||||
|
||||
old_version = true
|
||||
end
|
||||
|
||||
|
@ -162,7 +164,7 @@ class MetasploitModule < Msf::Post
|
|||
else
|
||||
print_good("Dbviscmd found : #{dbvis}")
|
||||
end
|
||||
when /win/
|
||||
when /windows/
|
||||
# Find program files
|
||||
progfiles_env = session.sys.config.getenvs('ProgramFiles(X86)', 'ProgramFiles')
|
||||
progfiles_x86 = progfiles_env['ProgramFiles(X86)']
|
||||
|
|
|
@ -56,13 +56,15 @@ class MetasploitModule < Msf::Post
|
|||
when /linux/
|
||||
user = session.shell_command("whoami")
|
||||
print_status("Current user is #{user}")
|
||||
|
||||
if (user =~ /root/)
|
||||
user_base = "/root/"
|
||||
else
|
||||
user_base = "/home/#{user}/"
|
||||
end
|
||||
|
||||
dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"
|
||||
when /win/
|
||||
when /windows/
|
||||
user_profile = session.sys.config.getenv('USERPROFILE')
|
||||
dbvis_file = "#{user_profile}\\.dbvis\\config70\\dbvis.xml"
|
||||
end
|
||||
|
@ -71,16 +73,19 @@ class MetasploitModule < Msf::Post
|
|||
#File not found, we next try with the old config path
|
||||
print_status("File not found: #{dbvis_file}")
|
||||
print_status("This could be an older version of dbvis, trying old path")
|
||||
|
||||
case session.platform
|
||||
when /linux/
|
||||
dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
|
||||
when /win/
|
||||
when /windows/
|
||||
dbvis_file = "#{user_profile }\\.dbvis\\config\\dbvis.xml"
|
||||
end
|
||||
|
||||
unless file?(dbvis_file)
|
||||
print_error("File not found: #{dbvis_file}")
|
||||
return
|
||||
end
|
||||
|
||||
old_version = true
|
||||
end
|
||||
|
||||
|
@ -150,7 +155,7 @@ class MetasploitModule < Msf::Post
|
|||
else
|
||||
print_good("Dbviscmd found : #{dbvis}")
|
||||
end
|
||||
when /win/
|
||||
when /windows/
|
||||
# Find program files
|
||||
progfiles_env = session.sys.config.getenvs('ProgramFiles(X86)', 'ProgramFiles')
|
||||
progfiles_x86 = progfiles_env['ProgramFiles(X86)']
|
||||
|
|
|
@ -111,7 +111,7 @@ class MetasploitModule < Msf::Post
|
|||
case session.platform
|
||||
when /osx/
|
||||
osx_start_video(id)
|
||||
when /win/
|
||||
when /windows/
|
||||
win_start_video(id)
|
||||
when /linux/
|
||||
linux_start_video(id)
|
||||
|
|
|
@ -74,7 +74,7 @@ class MetasploitModule < Msf::Post
|
|||
case session.platform
|
||||
when /osx/
|
||||
osx_set_wallpaper(file)
|
||||
when /win/
|
||||
when /windows/
|
||||
win_set_wallpaper(file)
|
||||
when /android/
|
||||
android_set_wallpaper(file)
|
||||
|
|
|
@ -80,8 +80,8 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
# Handle platform specific variables and settings
|
||||
case session.platform
|
||||
when /win/i
|
||||
platform = 'win'
|
||||
when /windows/i
|
||||
platform = 'windows'
|
||||
payload_name = 'windows/meterpreter/reverse_tcp'
|
||||
lplat = [Msf::Platform::Windows]
|
||||
larch = [ARCH_X86]
|
||||
|
@ -139,7 +139,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
case platform
|
||||
when 'win'
|
||||
when 'windows'
|
||||
if session.type == 'powershell'
|
||||
template_path = File.join(Msf::Config.data_directory, 'templates', 'scripts')
|
||||
psh_payload = case datastore['Powershell::method']
|
||||
|
|
|
@ -92,7 +92,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
# If we want WINAPI egress, make sure winsock is loaded
|
||||
if type == 'WINAPI'
|
||||
unless client.railgun.ws2_32 && client.platform =~ /win/
|
||||
unless client.railgun.ws2_32 && client.platform =~ /windows/
|
||||
print_error("The WINAPI method requires Windows, railgun and support for winsock APIs. Try using the NATIVE method instead.")
|
||||
return
|
||||
end
|
||||
|
|
|
@ -153,8 +153,8 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
|
||||
#Make sure we are on a Windows host
|
||||
if client.platform !~ /win32|win64/
|
||||
print_status "This module does not support this meterpreter type"
|
||||
if client.platform !~ /windows/
|
||||
print_error('This module does not support this platform.')
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
def unsupported
|
||||
print_error("This version of Meterpreter is not supported with this script!")
|
||||
print_error("This platform is not supported with this script!")
|
||||
raise Rex::Script::Completed
|
||||
end
|
||||
|
||||
|
@ -41,7 +41,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
technique = datastore['TECHNIQUE'].to_i
|
||||
|
||||
unsupported if client.platform !~ /win32|win64/i
|
||||
unsupported if client.platform !~ /windows/i
|
||||
|
||||
if is_system?
|
||||
print_good("This session already has SYSTEM privileges")
|
||||
|
|
|
@ -37,7 +37,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
def unsupported
|
||||
print_error("This version of Meterpreter is not supported with this Script!")
|
||||
print_error("This platform is not supported with this Script!")
|
||||
raise Rex::Script::Completed
|
||||
end
|
||||
|
||||
|
@ -55,7 +55,7 @@ class MetasploitModule < Msf::Post
|
|||
{ :sig => "8bff558bec83ec50a1", :sigoffset => 0x97d3, :orig_code => "32c0", :patch => "b001", :patchoffset => 0x9878, :os => /Windows XP.*Service Pack 3 - spanish/ }
|
||||
]
|
||||
|
||||
unsupported if client.platform !~ /win32|win64/i
|
||||
unsupported if client.platform !~ /windows/i
|
||||
os = client.sys.config.sysinfo['OS']
|
||||
|
||||
targets.each do |t|
|
||||
|
|
|
@ -54,8 +54,8 @@ class MetasploitModule < Msf::Post
|
|||
screenshot = Msf::Config.get_config_root + "/logs/" + host + ".jpg"
|
||||
|
||||
migrate_explorer
|
||||
if session.platform !~ /win32|win64/i
|
||||
print_error("Unsupported Platform")
|
||||
if session.platform !~ /windows/i
|
||||
print_error('Unsupported Platform')
|
||||
return
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue