Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/post/meterpreter/extensions/stdapi/railgun/library.rb
Views: 11792
# -*- coding: binary -*-1# Copyright (c) 2010, [email protected]2# All rights reserved.3#4# Redistribution and use in source and binary forms, with or without5# modification, are permitted provided that the following conditions are met:6# * Redistributions of source code must retain the above copyright7# notice, this list of conditions and the following disclaimer.8# * Redistributions in binary form must reproduce the above copyright9# notice, this list of conditions and the following disclaimer in the10# documentation and/or other materials provided with the distribution.11# * The names of the author may not be used to endorse or promote products12# derived from this software without specific prior written permission.13#14# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED16# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE17# DISCLAIMED. IN NO EVENT SHALL [email protected] BE LIABLE FOR ANY18# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES19# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;20# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND21# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS23# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2425require 'rex/post/meterpreter/extensions/stdapi/railgun/library_function'26require 'rex/post/meterpreter/extensions/stdapi/railgun/library_helper'27require 'rex/post/meterpreter/extensions/stdapi/railgun/buffer_item'28require 'rex/post/meterpreter/extensions/stdapi/railgun/tlv'29require 'rex/post/meterpreter/packet'3031module Rex32module Post33module Meterpreter34module Extensions35module Stdapi36module Railgun3738#39# Represents a library, e.g. kernel32.dll40#41class Library4243include LibraryHelper4445@@datatype_map = {46'HANDLE' => 'LPVOID',47# really should be PVOID* but LPVOID is handled specially with the 'L' prefix to *not* treat it as a pointer, and48# for railgun's purposes LPVOID == ULONG_PTR49'PHANDLE' => 'PULONG_PTR',50'SIZE_T' => 'ULONG_PTR',51'PSIZE_T' => 'PULONG_PTR',52'PLPVOID' => 'PULONG_PTR',53'ULONG' => 'DWORD',54'PULONG' => 'PDWORD',55'NTSTATUS' => 'DWORD'56}.freeze5758attr_accessor :functions59attr_reader :library_path6061def initialize(library_path, consts_mgr)62@library_path = library_path6364# needed by LibraryHelper65@consts_mgr = consts_mgr6667self.functions = {}68end6970def known_function_names71return functions.keys72end7374def get_function(name)75return functions[name]76end7778#79# Perform a function call in this library on the remote system.80#81# Returns a Hash containing the return value, the result of GetLastError(),82# and any +inout+ parameters.83#84# Raises an exception if +function+ is not a known function in this library,85# i.e., it hasn't been defined in a Def.86#87def call_function(function, args, client)88unless function.instance_of? LibraryFunction89func_name = function.to_s9091unless known_function_names.include? func_name92raise "Library-function #{func_name} not found. Known functions: #{PP.pp(known_function_names, '')}"93end9495function = get_function(func_name)96end9798return process_function_call(function, args, client)99end100101#102# Define a function for this library.103#104# Every function argument is described by a tuple (type,name,direction)105#106# Example:107# add_function("MessageBoxW", # name108# "DWORD", # return value109# [ # params110# ["DWORD","hWnd","in"],111# ["PWCHAR","lpText","in"],112# ["PWCHAR","lpCaption","in"],113# ["DWORD","uType","in"],114# ])115#116# Use +remote_name+ when the actual library name is different from the117# ruby variable. You might need to do this for example when the actual118# func name is myFunc@4 or when you want to create an alternative version119# of an existing function.120#121# When the new function is called it will return a list containing the122# return value and all inout params. See #call_function.123#124def add_function(name, return_type, params, remote_name=nil, calling_conv='stdcall')125return_type = reduce_type(return_type)126params = reduce_parameter_types(params)127if remote_name == nil128remote_name = name129end130@functions[name] = LibraryFunction.new(return_type, params, remote_name, calling_conv)131end132133def build_packet_and_layouts(packet, function, args, arch)134case arch135when ARCH_X64136native = 'Q<'137when ARCH_X86138native = 'V'139else140raise NotImplementedError, 'Unsupported architecture (must be ARCH_X86 or ARCH_X64)'141end142143# We transmit the immediate stack and three heap-buffers:144# in, inout and out. The reason behind the separation is bandwidth.145# We don't want to transmit uninitialized data in or no-longer-needed data out.146147# out-only-buffers that are ONLY transmitted on the way BACK148out_only_layout = {} # paramName => BufferItem149out_only_size_bytes = 0150#puts " assembling out-only buffer"151function.params.each_with_index do |param_desc, param_idx|152#puts " processing #{param_desc[1]}"153154# Special case:155# The user can choose to supply a Null pointer instead of a buffer156# in this case we don't need space in any heap buffer157if param_desc[0][0,1] == 'P' # type is a pointer (except LPVOID where the L negates this)158if args[param_idx] == nil159next160end161end162163# we care only about out-only buffers164if param_desc[2] == 'out'165if !args[param_idx].kind_of? Integer166raise "error in param #{param_desc[1]}: Out-only buffers must be described by a number indicating their size in bytes"167end168buffer_size = args[param_idx]169if param_desc[0] == 'PULONG_PTR'170# bump up the size for an x64 pointer171if arch == ARCH_X64 && buffer_size == 4172buffer_size = args[param_idx] = 8173end174175if arch == ARCH_X64176if buffer_size != 8177raise "Please pass 8 for 'out' PULONG_PTR, since they require a buffer of size 8"178end179elsif arch == ARCH_X86180if buffer_size != 4181raise "Please pass 4 for 'out' PULONG_PTR, since they require a buffer of size 4"182end183end184end185186out_only_layout[param_desc[1]] = BufferItem.new(param_idx, out_only_size_bytes, buffer_size, param_desc[0])187out_only_size_bytes += buffer_size188end189end190191in_only_layout, in_only_buffer = assemble_buffer('in', function, args, arch)192inout_layout, inout_buffer = assemble_buffer('inout', function, args, arch)193194# now we build the stack195# every stack dword will be described by two dwords:196# first dword describes second dword:197# 0 - literal,198# 1 = relative to in-only buffer199# 2 = relative to out-only buffer200# 3 = relative to inout buffer201202# (literal numbers and pointers to buffers we have created)203literal_pairs_blob = ""204#puts " assembling literal stack"205function.params.each_with_index do |param_desc, param_idx|206#puts " processing (#{param_desc[0]}, #{param_desc[1]}, #{param_desc[2]})"207buffer = nil208# is it a pointer to a buffer on our stack209if ['PULONG_PTR', 'PDWORD', 'PWCHAR', 'PCHAR', 'PBLOB'].include?(param_desc[0])210if ['PWCHAR', 'PCHAR', 'PBLOB'].include?(param_desc[0]) && param_desc[2] == 'in' && args[param_idx].is_a?(Integer)211# allow PWCHAR, PCHAR and PBLOB to also be passed as a pointer instead of a buffer212buffer = [0].pack(native)213num = param_to_number(args[param_idx])214buffer += [num].pack(native)215elsif args[param_idx] == nil # null pointer?216buffer = [0].pack(native) # type: LPVOID (so the library does not rebase it)217buffer += [0].pack(native) # value: 0218elsif param_desc[2] == 'in'219buffer = [1].pack(native)220buffer += [in_only_layout[param_desc[1]].addr].pack(native)221elsif param_desc[2] == 'out'222buffer = [2].pack(native)223buffer += [out_only_layout[param_desc[1]].addr].pack(native)224elsif param_desc[2] == 'inout'225buffer = [3].pack(native)226buffer += [inout_layout[param_desc[1]].addr].pack(native)227else228raise 'unexpected direction'229end230else231#puts " not a pointer"232# it's not a pointer (LPVOID is a pointer but is not backed by railgun memory, ala PBLOB)233buffer = [0].pack(native)234case param_desc[0]235when 'LPVOID', 'ULONG_PTR'236num = param_to_number(args[param_idx])237buffer += [num].pack(native)238when 'DWORD'239num = param_to_number(args[param_idx])240buffer += [num & 0xffffffff].pack(native)241when 'WORD'242num = param_to_number(args[param_idx])243buffer += [num & 0xffff].pack(native)244when 'BYTE'245num = param_to_number(args[param_idx])246buffer += [num & 0xff].pack(native)247when 'BOOL'248case args[param_idx]249when true250buffer += [1].pack(native)251when false252buffer += [0].pack(native)253else254raise "param #{param_desc[1]}: true or false expected"255end256else257raise "unexpected type for param #{param_desc[1]}"258end259end260261#puts " adding pair to blob"262literal_pairs_blob += buffer263#puts " buffer size %X" % buffer.length264#puts " blob size so far: %X" % literal_pairs_blob.length265end266267layouts = {in: in_only_layout, inout: inout_layout, out: out_only_layout}268269packet.add_tlv(TLV_TYPE_RAILGUN_SIZE_OUT, out_only_size_bytes)270packet.add_tlv(TLV_TYPE_RAILGUN_STACKBLOB, literal_pairs_blob)271packet.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_IN, in_only_buffer)272packet.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT, inout_buffer)273274packet.add_tlv(TLV_TYPE_RAILGUN_LIBNAME, @library_path)275packet.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.remote_name)276packet.add_tlv(TLV_TYPE_RAILGUN_CALLCONV, function.calling_conv)277[packet, layouts]278end279280def build_response(packet, function, layouts, client)281case client.native_arch282when ARCH_X64283native = 'Q<'284when ARCH_X86285native = 'V'286else287raise NotImplementedError, 'Unsupported architecture (must be ARCH_X86 or ARCH_X64)'288end289290rec_inout_buffers = packet.get_tlv_value(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT)291rec_out_only_buffers = packet.get_tlv_value(TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_OUT)292rec_return_value = packet.get_tlv_value(TLV_TYPE_RAILGUN_BACK_RET)293rec_last_error = packet.get_tlv_value(TLV_TYPE_RAILGUN_BACK_ERR)294rec_err_msg = packet.get_tlv_value(TLV_TYPE_RAILGUN_BACK_MSG)295296# Error messages come back with trailing CRLF, so strip it out if we do get a message.297rec_err_msg.strip! unless rec_err_msg.nil?298299# the hash the function returns300return_hash = {301'GetLastError' => rec_last_error,302'ErrorMessage' => rec_err_msg303}304305# process return value306case function.return_type307when 'LPVOID', 'ULONG_PTR'308if client.native_arch == ARCH_X64309return_hash['return'] = rec_return_value310else311return_hash['return'] = rec_return_value & 0xffffffff312end313when 'DWORD'314return_hash['return'] = rec_return_value & 0xffffffff315when 'WORD'316return_hash['return'] = rec_return_value & 0xffff317when 'BYTE'318return_hash['return'] = rec_return_value & 0xff319when 'BOOL'320return_hash['return'] = (rec_return_value != 0)321when 'VOID'322return_hash['return'] = nil323when 'PCHAR'324return_hash['return'] = rec_return_value == 0 ? nil : client.railgun.util.read_string(rec_return_value)325return_hash['&return'] = rec_return_value326when 'PWCHAR'327return_hash['return'] = rec_return_value == 0 ? nil : client.railgun.util.read_wstring(rec_return_value)328return_hash['&return'] = rec_return_value329when 'PULONG_PTR'330if client.native_arch == ARCH_X64331return_hash['return'] = rec_return_value == 0 ? nil : client.railgun.util.memread(rec_return_value, 8)&.unpack1('Q<')332return_hash['&return'] = rec_return_value333else334return_hash['return'] = rec_return_value == 0 ? nil : client.railgun.util.memread(rec_return_value, 4)&.unpack1('V')335return_hash['&return'] = rec_return_value336end337else338raise "unexpected return type: #{function.return_type}"339end340341# process out-only buffers342layouts[:out].each_pair do |param_name, buffer_item|343buffer = rec_out_only_buffers[buffer_item.addr, buffer_item.length_in_bytes]344case buffer_item.datatype345when 'PULONG_PTR'346return_hash[param_name] = buffer.unpack(native).first347when 'PDWORD'348return_hash[param_name] = buffer.unpack('V').first349when 'PCHAR'350return_hash[param_name] = asciiz_to_str(buffer)351when 'PWCHAR'352return_hash[param_name] = uniz_to_str(buffer)353when 'PBLOB'354return_hash[param_name] = buffer355else356raise "unexpected type in out-only buffer of #{param_name}: #{buffer_item.datatype}"357end358end359360# process in-out buffers361layouts[:inout].each_pair do |param_name, buffer_item|362buffer = rec_inout_buffers[buffer_item.addr, buffer_item.length_in_bytes]363case buffer_item.datatype364when 'PULONG_PTR'365return_hash[param_name] = buffer.unpack(native).first366when 'PDWORD'367return_hash[param_name] = buffer.unpack('V').first368when 'PCHAR'369return_hash[param_name] = asciiz_to_str(buffer)370when 'PWCHAR'371return_hash[param_name] = uniz_to_str(buffer)372when 'PBLOB'373return_hash[param_name] = buffer374else375raise "unexpected type in in-out-buffer of #{param_name}: #{buffer_item.datatype}"376end377end378379return_hash380end381382private383384def process_function_call(function, args, client)385raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length386387request, layouts = build_packet_and_layouts(388Packet.create_request(COMMAND_ID_STDAPI_RAILGUN_API),389function,390args,391client.native_arch392)393394response = client.send_request(request)395396build_response(response, function, layouts, client)397end398399# perform type conversions as necessary to reduce the datatypes to their primitives400def reduce_parameter_types(params)401params.each_with_index do |param, idx|402type, name, direction = param403params[idx] = [reduce_type(type), name, direction]404end405406params407end408409def reduce_type(datatype)410while @@datatype_map.key?(datatype)411datatype = @@datatype_map[datatype]412end413414datatype415end416end417418end; end; end; end; end; end;419420421