1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-04-06 01:16:37 +02:00
OJ 3b40f1abd0 Command refactor for clean shutdown + inline calls
This work contains a bunch of changes around command dispatching. The
goals for this bit of work were to:

* Provide the ability for commands to be executed on the same thread as
  the server rather than always creating new threads and executing them on
  those threads.
* Have the means for _special_ commands, such as `exit` and `migrate` to
  shut down the server cleanly without having to rely on signalling across
  threads or by doing brutal thread termination via shared global handles.
  This should not only fix the dirty shutdown problem on Windows which
  leaves tasks dangling (or based on the prior attempt at fixing, crashing
  stuff as well), it should also help clean up the shutdown process in
  POSIX.

These changes hit a very important part of Meterpreter and so should be
reviewed with intense scrutnity. I expect this PR to garner a log of
critique and most likely a number of changes before being included in the
main line.

The `PacketDispatcher` was modified to include a new function pointer
called an `inline_handler`. This new member indicates that there's a
handler which should be invoked inline. While this sits alongside the
existing `handler`, they are actually mutually exclusive. If an
`inline_handler` is specified then the `handler` is ignored and it is
assumed that the command is intended to be handled inline. The signature
of the inline handler is different to the standard handler, and this is
why a new function pointer was added rather than a simple flag. Addition of
this parameter meant that the basic command structure changed, and that
obviously affects all of the extensions and their respective commands.
This changeset therefore updates each of those command declarations so
that they use the new macros that hide this detail.

Other things to be mindful of:

* This version of the code reads the command's `method` prior to invoking
  any other function, and after that the command itself is passed around to
  the threaded or non-threaded routes for invocation. An extra thread
  parameter was included as as result, and an overload for the
  `thread_create` function was added which supported this new parameter.
  This was named `thread_create3` because
  `thread_create_with_another_paramter` sounded a bit crap.
* The migration code, which originally had a `thread_kill` and an event
  wait once the new meterpreter session had been created, has been modified
  to not do any waiting at all. Instead it finishes execution as fast as
  possible and returns control to the server which should respond by
  shutting down in a clean way.
* Originally the code always attempted to call a command handler in the
  base command list and then, if found, would also call an "overload" in
  the extension commands list. From the investigation that I did, it
  appears that the overloaded methods did nothing in the base (they'd
  early out during invocation). As a result, the new way of doing things
  acts like a 'true' overload in that the extension commands are searched
  first, and if one is found this is the command that is executed. Any
  base commands with the same method name will not get executed. In the
  case where there is no extension command found, the base command list is
  then queried. If a command is found that command is instead invoked.
* The POSIX version still compiles cleanly, but I've never been able to
  build a version that runs on my machines. I'm not sure if there's a
  trick to getting POSIX builds to run, and if there is I don't know it.
  Whoever scrutinises this build should make sure that the POSIX version
  that they build can still run and (hopefully) exit cleanly.

I've added lots of documentation, but there's always room for improvement.

Hopefully this will fix the `*_tcp` side of Redmine 8438.

Bring on the feedback!
2013-10-17 22:36:49 +10:00

208 lines
4.9 KiB
C

/*!
* @file common.h
* @brief Declarations for various common components used across the Meterpreter suite.
*/
#ifndef _METERPRETER_SOURCE_COMMON_COMMON_H
#define _METERPRETER_SOURCE_COMMON_COMMON_H
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
typedef DWORD __u32;
typedef struct ___u128 {
__u32 a1;
__u32 a2;
__u32 a3;
__u32 a4;
}__u128;
#endif
#include "openssl/ssl.h"
#ifdef _UNIX
#include "compat_types.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/endian.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/atomics.h>
#define FLAGS_LEN 100
typedef struct ___u128 {
__u32 a1;
__u32 a2;
__u32 a3;
__u32 a4;
}__u128;
struct iface_address {
int family;
union {
__u32 addr;
__u128 addr6;
} ip;
union {
__u32 netmask;
__u128 netmask6;
} nm;
};
struct iface_entry {
unsigned char name[IFNAMSIZ+1];
unsigned char hwaddr[6];
uint32_t mtu;
uint32_t index;
unsigned char flags[FLAGS_LEN+1];
int addr_count;
struct iface_address *addr_list;
};
struct ifaces_list {
int entries;
struct iface_entry ifaces[0];
};
struct ipv4_route_entry {
__u32 dest;
__u32 netmask;
__u32 nexthop;
unsigned char interface[IFNAMSIZ+1];
__u32 metric;
};
struct ipv6_route_entry {
__u128 dest6;
__u128 netmask6;
__u128 nexthop6;
unsigned char interface[IFNAMSIZ+1];
__u32 metric;
};
struct ipv4_routing_table {
int entries;
struct ipv4_route_entry routes[0];
};
struct ipv6_routing_table {
int entries;
struct ipv6_route_entry routes[0];
};
struct routing_table {
struct ipv4_routing_table ** table_ipv4;
struct ipv6_routing_table ** table_ipv6;
};
struct arp_entry {
__u32 ipaddr;
unsigned char hwaddr[6];
unsigned char name[IFNAMSIZ+1];
};
struct arp_table {
int entries;
struct arp_entry table[0];
};
int netlink_get_routing_table(struct ipv4_routing_table **table_ipv4, struct ipv6_routing_table **table_ipv6);
int netlink_get_interfaces(struct ifaces_list **iface_list);
extern int debugging_enabled;
#define dprintf(...) if(debugging_enabled) { real_dprintf(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); }
void real_dprintf(char *filename, int line, const char *function, char *format, ...);
#endif
#include "linkage.h"
#include "args.h"
#include "buffer.h"
#include "base.h"
#include "core.h"
#include "remote.h"
#include "channel.h"
#include "scheduler.h"
#include "thread.h"
#include "list.h"
#include "zlib/zlib.h"
/*! @brief Indication that the Meterpreter transport is using SSL. */
#define METERPRETER_TRANSPORT_SSL 0
/*! @brief Indication that the Meterpreter transport is using HTTP. */
#define METERPRETER_TRANSPORT_HTTP 1
/*! @brief Indication that the Meterpreter transport is using HTTPS. */
#define METERPRETER_TRANSPORT_HTTPS 2
#ifdef _WIN32
#include <wininet.h>
/*! @brief When defined, debug output is enabled. */
#define DEBUGTRACE 1
#ifdef DEBUGTRACE
#define dprintf(...) real_dprintf(__VA_ARGS__)
#else
#define dprintf(...) do{}while(0);
#endif
/*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `break;` */
#define BREAK_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d", str, dwResult ); break; }
/*! @brief Sets `dwResult` to `error`, prints debug output, then `break;` */
#define BREAK_WITH_ERROR( str, err ) { dwResult = err; dprintf( "%s. error=%d", str, dwResult ); break; }
/*! @brief Sets `dwResult` to the return value of `WASGetLastError()`, prints debug output, then does `break;` */
#define BREAK_ON_WSAERROR( str ) { dwResult = WSAGetLastError(); dprintf( "%s. error=%d", str, dwResult ); break; }
/*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `continue;` */
#define CONTINUE_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d", str, dwResult ); continue; }
/*! @brief Close a service handle if not already closed and set the handle to NULL. */
#define CLOSE_SERVICE_HANDLE( h ) if( h ) { CloseServiceHandle( h ); h = NULL; }
/*! @brief Close a handle if not already closed and set the handle to NULL. */
#define CLOSE_HANDLE( h ) if( h ) { DWORD dwHandleFlags; if(GetHandleInformation( h , &dwHandleFlags)) CloseHandle( h ); h = NULL; }
#ifdef DEBUGTRACE
/*!
* @brief Output a debug string to the debug console.
* @details The function emits debug strings via `OutputDebugStringA`, hence all messages can be viewed
* using Visual Studio's _Output_ window, _DebugView_ from _SysInternals_, or _Windbg_.
*/
static void real_dprintf(char *format, ...) {
va_list args;
char buffer[1024];
va_start(args,format);
vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer)-3, format,args);
strcat_s(buffer, sizeof(buffer), "\r\n");
OutputDebugStringA(buffer);
}
#endif
#endif
#endif
int current_unix_timestamp(void);