Files
metasploit-gs/lib/rex/post/meterpreter/extensions/stdapi/sys/thread.rb
T
Brent Cook c250740a81 Fixup finalizers to not double-close Meterpreter objects
We add finalizers to an assortment of Meterpreter-managed objects in order to
clean things up in the event that a post module crashes and does not clean
things up. However, this also means that even a properly-written post module
can lead to an object getting double-closed on the Meterpreter session when the
garbage collector kicks in. This can lead to quite non-deterministic behavior
and crashes.

This change modifies the instance close methods to unregister the finalizer on
close, ensuring we cannot do a double-close automatically if one is requested
explicitly first. As an additional measure, we check an instance variable to
see if we called close directly twice as well. This is not sufficient in
itself, since we do not have a reference to 'self' in the finalizer proc to
check the close state.

This also removes a couple of references to 'self' in the finalizer proc
itself, which may cure some memory leaks as well due to circular references.
2016-03-02 21:43:51 -06:00

187 lines
3.9 KiB
Ruby

# -*- coding: binary -*-
require 'rex/post/thread'
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/extensions/stdapi/constants'
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Sys
##
#
# This class implements the Rex::Post::Thread interface which
# wrappers a logical thread for a given process.
#
##
class Thread < Rex::Post::Thread
include Rex::Post::Meterpreter::ObjectAliasesContainer
##
#
# Constructor
#
##
#
# Initialize the thread instance.
#
def initialize(process, handle, tid)
self.process = process
self.handle = handle
self.tid = tid
# Ensure the remote object is closed when all references are removed
ObjectSpace.define_finalizer(self, self.class.finalize(process.client, handle))
end
def self.finalize(client,handle)
proc { self.close(client,handle) }
end
##
#
# Execution
#
##
#
# Suspends the thread's execution.
#
def suspend
request = Packet.create_request('stdapi_sys_process_thread_suspend')
request.add_tlv(TLV_TYPE_THREAD_HANDLE, handle)
process.client.send_request(request)
return true
end
#
# Resumes the thread's execution.
#
def resume
request = Packet.create_request('stdapi_sys_process_thread_resume')
request.add_tlv(TLV_TYPE_THREAD_HANDLE, handle)
process.client.send_request(request)
return true
end
#
# Terminates the thread's execution.
#
def terminate(code)
request = Packet.create_request('stdapi_sys_process_thread_terminate')
request.add_tlv(TLV_TYPE_THREAD_HANDLE, handle)
request.add_tlv(TLV_TYPE_EXIT_CODE, code)
process.client.send_request(request)
return true
end
##
#
# Register manipulation
#
##
#
# Queries the register state of the thread.
#
def query_regs
request = Packet.create_request('stdapi_sys_process_thread_query_regs')
regs = {}
request.add_tlv(TLV_TYPE_THREAD_HANDLE, handle)
response = process.client.send_request(request)
response.each(TLV_TYPE_REGISTER) { |reg|
regs[reg.get_tlv_value(TLV_TYPE_REGISTER_NAME)] = reg.get_tlv_value(TLV_TYPE_REGISTER_VALUE_32)
}
return regs
end
#
# Sets the register state of the thread. The registers are supplied
# in the form of a hash.
#
def set_regs(regs_hash)
request = Packet.create_request('stdapi_sys_process_thread_set_regs')
request.add_tlv(TLV_TYPE_THREAD_HANDLE, handle)
# Add all of the register that we're setting
regs_hash.each_key { |name|
t = request.add_tlv(TLV_TYPE_REGISTER)
t.add_tlv(TLV_TYPE_REGISTER_NAME, name)
t.add_tlv(TLV_TYPE_REGISTER_VALUE_32, regs_hash[name])
}
process.client.send_request(request)
return true
end
#
# Formats the registers in a pretty way.
#
def pretty_regs
regs = query_regs
buf = sprintf("eax=%.8x ebx=%.8x ecx=%.8x edx=%.8x esi=%.8x edi=%.8x\n",
regs['eax'], regs['ebx'], regs['ecx'], regs['edx'], regs['esi'], regs['edi'])
buf += sprintf("eip=%.8x esp=%.8x ebp=%.8x\n",
regs['eip'], regs['esp'], regs['ebp'])
buf += sprintf("cs=%.4x ss=%.4x ds=%.4x es=%.4x fs=%.4x gs=%.4x\n",
regs['cs'], regs['ss'], regs['ds'], regs['es'], regs['fs'], regs['gs'])
return buf
end
##
#
# Closure
#
##
#
# Closes the thread handle.
#
def self.close(client, handle)
request = Packet.create_request('stdapi_sys_process_thread_close')
request.add_tlv(TLV_TYPE_THREAD_HANDLE, handle)
client.send_request(request, nil)
handle = nil
return true
end
# Instance method
def close
unless self.handle.nil?
ObjectSpace.undefine_finalizer(self)
self.class.close(self.process.client, self.handle)
self.handle = nil
end
end
attr_reader :process, :handle, :tid # :nodoc:
protected
attr_writer :process, :handle, :tid # :nodoc:
end
end; end; end; end; end; end