Add doxygen, begin documenting, kitrap0d tidy

* Added the doxygen binaries and configuration.
* Added a `make docs` which generates documentation to the `docs` folder.
* Added some documentation to various areas of the source that I was
  working with. Over time I'll be adding more and more rather than trying
  to do it all in one hit.
* Refactored the kitrap0d code a bit to try to reduce code size.
This commit is contained in:
OJ 2013-09-26 08:05:05 +10:00
parent 2d96468969
commit 08793782d1
18 changed files with 2699 additions and 347 deletions

View File

@ -40,3 +40,6 @@ source/jpeg-8/Backup/*
# other VS garbage
*.*proj.filters
# Doxygen output
docs/*

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

2299
c/meterpreter/doxygen.cnf Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,25 @@
@ECHO OFF
IF "%1"=="clean" GOTO CLEAN
IF "%VCINSTALLDIR%" == "" GOTO NEED_VS
IF "%1"=="docs" GOTO DOCS
IF "%VCINSTALLDIR%" == "" (
ECHO "VC++ Environment not found, attempting to locate..."
REM Attempt to load up the dev env variables if they're not
REM set, saves people doing it manually
SET SETUP="Microsoft Visual Studio 11.0\Common7\Tools\vsvars32.bat"
IF EXIST "%ProgramFiles%\%SETUP%" (
ECHO "Found at '%ProgramFiles%\%SETUP%'"
"%ProgramFiles%\%SETUP%"
)
IF EXIST "%ProgramFiles(x86)%\%SETUP%" (
ECHO "Found at '%ProgramFiles(x86)%\%SETUP%'"
"%ProgramFiles(x86)%\%SETUP%"
)
REM If we still don't have what we need, then throw an error
IF "%VCINSTALLDIR%" == "" GOTO NEED_VS
)
SET PREF=
IF EXIST "..\pssdk\" SET PREF=r7_
@ -37,6 +56,10 @@ IF EXIST "output\x64\" (
)
GOTO :END
:DOCS
tools\doxygen\doxygen.exe doxygen.cnf
GOTO :END
:NEED_VS
ECHO "This command must be executed from within a Visual Studio Command prompt."
ECHO "This can be found under Microsoft Visual Studio 2012 -> Visual Studio Tools"

View File

@ -35,15 +35,14 @@ DWORD remote_response_core_console_write(Remote *remote, Packet *packet)
}
/*
* Base RPC dispatch table
/*!
* @brief Base RPC dispatch table.
*/
Command commands[] =
{
/*
* Core commands
*/
* Core commands
*/
// Console commands
{ "core_console_write",
@ -52,74 +51,53 @@ Command commands[] =
},
// Native Channel commands
{ "core_channel_open",
{ remote_request_core_channel_open, { 0 }, 0 },
{ remote_response_core_channel_open, { 0 }, 0 },
},
{ "core_channel_write",
{ remote_request_core_channel_write, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "core_channel_close",
{ remote_request_core_channel_close, { 0 }, 0 },
{ remote_response_core_channel_close, { 0 }, 0 },
},
COMMAND_REQ_REP( "core_channel_open", remote_request_core_channel_open, remote_response_core_channel_open ),
COMMAND_REQ( "core_channel_write", remote_request_core_channel_write ),
COMMAND_REQ_REP( "core_channel_close", remote_request_core_channel_close, remote_response_core_channel_close ),
// Buffered/Pool channel commands
{ "core_channel_read",
{ remote_request_core_channel_read, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "core_channel_read", remote_request_core_channel_read ),
// Pool channel commands
{ "core_channel_seek",
{ remote_request_core_channel_seek, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "core_channel_eof",
{ remote_request_core_channel_eof, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "core_channel_tell",
{ remote_request_core_channel_tell, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "core_channel_seek", remote_request_core_channel_seek ),
COMMAND_REQ( "core_channel_eof", remote_request_core_channel_eof ),
COMMAND_REQ( "core_channel_tell", remote_request_core_channel_tell ),
// Soon to be deprecated
{ "core_channel_interact",
{ remote_request_core_channel_interact, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "core_channel_interact", remote_request_core_channel_interact ),
// Crypto
{ "core_crypto_negotiate",
{ remote_request_core_crypto_negotiate, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "core_crypto_negotiate", remote_request_core_crypto_negotiate ),
// Migration
{ "core_migrate",
{ remote_request_core_migrate, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "core_migrate", remote_request_core_migrate ),
// Shutdown
{ "core_shutdown",
{ remote_request_core_shutdown, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "core_shutdown", remote_request_core_shutdown ),
// Terminator
{ NULL,
{ NULL, { 0 }, 0 },
{ NULL, { 0 }, 0 },
},
COMMAND_TERMINATOR
};
// Dynamically registered command extensions
Command *extension_commands = NULL;
/*
* Dynamically register a custom command handler
/*!
* @brief Register a full list of commands with meterpreter.
* @param commands The array of commands that are to be registered for the module/extension.
* @return `ERROR_SUCCESS` when all commands register successfully, otherwise return the error.
*/
DWORD command_register_all(Command commands[])
{
DWORD index;
DWORD result;
for (index = 0; commands[index].method; index++)
{
if( result = command_register(&commands[index]) != ERROR_SUCCESS )
break;
}
return result;
}
/*!
* @brief Dynamically register a custom command handler
* @param command Pointer to the command that should be registered.
* @return `ERROR_SUCCESS` when command registers successfully, otherwise returns the error.
*/
DWORD command_register(Command *command)
{
@ -139,24 +117,45 @@ DWORD command_register(Command *command)
dprintf("Fixing next/prev...");
newCommand->next = extension_commands;
newCommand->prev = NULL;
extension_commands = newCommand;
extension_commands = newCommand;
dprintf("Done...");
return ERROR_SUCCESS;
}
/*
* Dynamically deregister a custom command handler
/*!
* @brief Deregister a full list of commands from meterpreter.
* @param commands The array of commands that are to be deregistered from the module/extension.
* @return `ERROR_SUCCESS` when all commands deregister successfully, otherwise return the error.
*/
DWORD command_deregister_all(Command commands[])
{
DWORD index;
DWORD result;
for (index = 0; commands[index].method; index++)
{
if( result = command_deregister(&commands[index]) != ERROR_SUCCESS )
break;
}
return result;
}
/*!
* @brief Dynamically deregister a custom command handler
* @param command Pointer to the command that should be deregistered.
* @return `ERROR_SUCCESS` when command deregisters successfully, otherwise returns the error.
*/
DWORD command_deregister(Command *command)
{
Command *current, *prev;
DWORD res = ERROR_NOT_FOUND;
// Search the extension list for the command
for (current = extension_commands, prev = NULL;
current;
prev = current, current = current->next)
current;
prev = current, current = current->next)
{
if (strcmp(command->method, current->method))
continue;
@ -171,7 +170,7 @@ DWORD command_deregister(Command *command)
// Deallocate it
free(current);
res = ERROR_SUCCESS;
break;
@ -181,13 +180,13 @@ DWORD command_deregister(Command *command)
}
/*
* A list of all command threads currenlty executing.
*/
* A list of all command threads currenlty executing.
*/
LIST * commandThreadList = NULL;
/*
* Block untill all running command threads have finished.
*/
* Block untill all running command threads have finished.
*/
VOID command_join_threads( VOID )
{
while( list_count( commandThreadList ) > 0 )
@ -199,25 +198,25 @@ VOID command_join_threads( VOID )
}
/*
* Crude method of throttling the ammount of concurrent command
* threads we allow in the system at a given time.
*/
* Crude method of throttling the ammount of concurrent command
* threads we allow in the system at a given time.
*/
/*
VOID command_throtle( int maxthreads )
{
while( list_count( commandThreadList ) >= maxthreads )
{
Sleep( 250 );
}
while( list_count( commandThreadList ) >= maxthreads )
{
Sleep( 250 );
}
}
*/
#ifndef _WIN32
/*
* Reap child zombie threads on linux 2.4 (before NPTL)
* each thread appears as a process and pthread_join don't necessarily reap it
* threads are created using the clone syscall, so use special __WCLONE flag in waitpid
*/
* Reap child zombie threads on linux 2.4 (before NPTL)
* each thread appears as a process and pthread_join don't necessarily reap it
* threads are created using the clone syscall, so use special __WCLONE flag in waitpid
*/
VOID reap_zombie_thread(void * param)
{
@ -230,8 +229,8 @@ VOID reap_zombie_thread(void * param)
#endif
/*
* Process a single command in a seperate thread of execution.
*/
* Process a single command in a seperate thread of execution.
*/
DWORD THREADCALL command_process_thread( THREAD * thread )
{
DWORD index = 0;
@ -243,14 +242,14 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
Command * current = NULL;
Remote * remote = NULL;
Packet * packet = NULL;
if( thread == NULL )
return ERROR_INVALID_HANDLE;
remote = (Remote *)thread->parameter1;
if( remote == NULL )
return ERROR_INVALID_HANDLE;
packet = (Packet *)thread->parameter2;
if( packet == NULL )
return ERROR_INVALID_DATA;
@ -289,7 +288,7 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
}
}
#endif
// Get the request identifier if the packet has one.
result = packet_get_tlv_string( packet, TLV_TYPE_REQUEST_ID, &requestIdTlv );
if( result == ERROR_SUCCESS )
@ -311,11 +310,11 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
// Regardless of error code, try to see if someone has overriden a base handler
for( current = extension_commands, result = ERROR_NOT_FOUND ;
result == ERROR_NOT_FOUND && current && current->method ; current = current->next )
result == ERROR_NOT_FOUND && current && current->method ; current = current->next )
{
if( strcmp( current->method, method ) )
continue;
// Call the custom handler
result = command_call_dispatch( current, remote, packet );
}
@ -327,7 +326,7 @@ DWORD THREADCALL command_process_thread( THREAD * thread )
// If we get here, we're successful.
result = ERROR_SUCCESS;
} while( 0 );
}
__except( EXCEPTION_EXECUTE_HANDLER )
@ -452,7 +451,7 @@ DWORD command_process_remote_loop(Remote *remote)
/*
* Call the dispatch routine for a given command
*/
DWORD command_call_dispatch(Command *command, Remote *remote, Packet *packet)
DWORD command_call_dispatch(Command *command, Remote *remote, Packet *packet)
{
DWORD res;
@ -463,27 +462,27 @@ DWORD command_call_dispatch(Command *command, Remote *remote, Packet *packet)
switch (packet_get_type(packet))
{
case PACKET_TLV_TYPE_REQUEST:
case PACKET_TLV_TYPE_PLAIN_REQUEST:
if (command->request.handler)
res = command->request.handler(remote, packet);
break;
case PACKET_TLV_TYPE_RESPONSE:
case PACKET_TLV_TYPE_PLAIN_RESPONSE:
if (command->response.handler)
res = command->response.handler(remote, packet);
break;
default:
res = ERROR_NOT_FOUND;
break;
case PACKET_TLV_TYPE_REQUEST:
case PACKET_TLV_TYPE_PLAIN_REQUEST:
if (command->request.handler)
res = command->request.handler(remote, packet);
break;
case PACKET_TLV_TYPE_RESPONSE:
case PACKET_TLV_TYPE_PLAIN_RESPONSE:
if (command->response.handler)
res = command->response.handler(remote, packet);
break;
default:
res = ERROR_NOT_FOUND;
break;
}
return res;
}
/*
* Validate command arguments
*/
* Validate command arguments
*/
DWORD command_validate_arguments(Command *command, Packet *packet)
{
PacketDispatcher *dispatcher = NULL;
@ -494,23 +493,23 @@ DWORD command_validate_arguments(Command *command, Packet *packet)
// Select the dispatcher table
if ((type == PACKET_TLV_TYPE_RESPONSE) ||
(type == PACKET_TLV_TYPE_PLAIN_RESPONSE))
(type == PACKET_TLV_TYPE_PLAIN_RESPONSE))
dispatcher = &command->response;
else
dispatcher = &command->request;
// Enumerate the arguments, validating the meta types of each
for (commandIndex = 0, packetIndex = 0;
((packet_enum_tlv(packet, packetIndex, TLV_TYPE_ANY, &current)
== ERROR_SUCCESS) &&
(res == ERROR_SUCCESS));
commandIndex++, packetIndex++)
((packet_enum_tlv(packet, packetIndex, TLV_TYPE_ANY, &current)
== ERROR_SUCCESS) &&
(res == ERROR_SUCCESS));
commandIndex++, packetIndex++)
{
TlvMetaType tlvMetaType;
// Check to see if we've reached the end of the command arguments
if ((dispatcher->numArgumentTypes) &&
(commandIndex == (dispatcher->numArgumentTypes & ARGUMENT_FLAG_MASK)))
(commandIndex == (dispatcher->numArgumentTypes & ARGUMENT_FLAG_MASK)))
{
// If the repeat flag is set, reset the index
if (commandIndex & ARGUMENT_FLAG_REPEAT)
@ -518,23 +517,23 @@ DWORD command_validate_arguments(Command *command, Packet *packet)
else
break;
}
// Make sure the argument is at least one of the meta types
tlvMetaType = packet_get_tlv_meta(packet, &current);
// Validate argument meta types
switch (tlvMetaType)
{
case TLV_META_TYPE_STRING:
if (packet_is_tlv_null_terminated(packet, &current) != ERROR_SUCCESS)
res = ERROR_INVALID_PARAMETER;
break;
default:
break;
case TLV_META_TYPE_STRING:
if (packet_is_tlv_null_terminated(packet, &current) != ERROR_SUCCESS)
res = ERROR_INVALID_PARAMETER;
break;
default:
break;
}
if ((res != ERROR_SUCCESS) &&
(commandIndex < dispatcher->numArgumentTypes))
(commandIndex < dispatcher->numArgumentTypes))
break;
}

View File

@ -14,8 +14,11 @@ typedef DWORD (*DISPATCH_ROUTINE)(Remote *remote, Packet *packet);
#define ARGUMENT_FLAG_REPEAT (1 << 28)
#define ARGUMENT_FLAG_MASK 0x0fffffff
// Blank dispatch handler
#define EMPTY_DISPATCH_HANDLER NULL, { 0 }, 0
#define COMMAND_TERMINATOR { NULL, { EMPTY_DISPATCH_HANDLER }, { EMPTY_DISPATCH_HANDLER } }
#define COMMAND_REQ(name, reqHandler) { name, { reqHandler, { 0 }, 0 }, { EMPTY_DISPATCH_HANDLER } }
#define COMMAND_REP(name, repHandler) { name, { EMPTY_DISPATCH_HANDLER }, { repHandler, { 0 }, 0 } }
#define COMMAND_REQ_REP(name, reqHandler, repHandler) { name, { reqHandler, { 0 }, 0 }, { repHandler, { 0 }, 0 } }
// Place holders
#define EXPORT_TABLE_BEGIN()
@ -40,6 +43,8 @@ typedef struct command
struct command *prev;
} Command;
LINKAGE DWORD command_register_all(Command commands[]);
LINKAGE DWORD command_deregister_all(Command commands[]);
LINKAGE DWORD command_register(Command *command);
LINKAGE DWORD command_deregister(Command *command);

View File

@ -1,8 +1,15 @@
/*!
* @file common.c
* @brief Definitions for various common components used across the Meterpreter suite.
*/
#include "common.h"
#ifdef _WIN32
// This function returns a unix timestamp in UTC
/*!
* @brief Returns a unix timestamp in UTC.
* @return Integer value representing the UTC Unix timestamp of the current time.
*/
int current_unix_timestamp(void) {
SYSTEMTIME system_time;
FILETIME file_time;
@ -19,7 +26,10 @@ int current_unix_timestamp(void) {
#include <sys/time.h>
// This function returns a unix timestamp in UTC
/*!
* @brief Returns a unix timestamp in UTC.
* @return Integer value representing the UTC Unix timestamp of the current time.
*/
int current_unix_timestamp(void) {
struct timeval tv;
struct timezone tz;
@ -37,10 +47,15 @@ int current_unix_timestamp(void) {
int debugging_enabled;
/*
* If we supply real_dprintf in the common.h, each .o file will have a private copy of that symbol.
* This leads to bloat. Defining it here means that there will only be a single implementation of it.
*/
/*!
* @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_.
* @remark If we supply real_dprintf in the common.h, each .o file will have a private copy of that symbol.
* This leads to bloat. Defining it here means that there will only be a single implementation of it.
*/
void real_dprintf(char *filename, int line, const char *function, char *format, ...)
{
va_list args;

View File

@ -1,3 +1,7 @@
/*!
* @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
@ -145,17 +149,16 @@ void real_dprintf(char *filename, int line, const char *function, char *format,
#include "zlib/zlib.h"
#define METERPRETER_TRANSPORT_SSL 0
#define METERPRETER_TRANSPORT_HTTP 1
#define METERPRETER_TRANSPORT_HTTPS 2
#define METERPRETER_TRANSPORT_SSL 0 ///< Indication that the Meterpreter transport is using SSL.
#define METERPRETER_TRANSPORT_HTTP 1 ///< Indication that the Meterpreter transport is using HTTP.
#define METERPRETER_TRANSPORT_HTTPS 2 ///< Indication that the Meterpreter transport is using HTTPS.
#ifdef _WIN32
#include <wininet.h>
// Enable debugging
//#define DEBUGTRACE 1
#define DEBUGTRACE 1 ///< When defined, debug output is enabled.
#ifdef DEBUGTRACE
#define dprintf(...) real_dprintf(__VA_ARGS__)
@ -163,15 +166,26 @@ void real_dprintf(char *filename, int line, const char *function, char *format,
#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; }
// Simple macros to close a handle and set the handle to NULL.
/*! @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];
@ -180,6 +194,7 @@ static void real_dprintf(char *format, ...) {
strcat_s(buffer, sizeof(buffer), "\r\n");
OutputDebugStringA(buffer);
}
#endif
#endif

View File

@ -1,3 +1,10 @@
/*!
* @file core.h
* @brief Declarations of core components of the Meterpreter suite.
* @details Much of what exists in the core files is used in almost every area
* of the Meterpreter code base, and hence it's very important. Don't
* change this stuff unless you know what you're doing!
*/
#ifndef _METERPRETER_CORE_H
#define _METERPRETER_CORE_H
@ -5,30 +12,45 @@
#include "remote.h"
#include "list.h"
/*
* Enumerations for TLVs and packets
/*!
* @brief Creates a new TLV value based on `actual` and `meta` values.
*/
#define TLV_VALUE(meta, actual) actual | meta
/*!
* @brief Creates a new custom TVL type.
*/
#define MAKE_TLV(name, meta, actual) TLV_TYPE_ ## name = actual | meta
#define MAKE_CUSTOM_TLV(meta, base, actual) (TlvType)((base + actual) | meta)
/*!
* @brief Enumeration of allowed Packet TLV types.
*/
typedef enum
{
PACKET_TLV_TYPE_REQUEST = 0,
PACKET_TLV_TYPE_RESPONSE = 1,
PACKET_TLV_TYPE_PLAIN_REQUEST = 10,
PACKET_TLV_TYPE_PLAIN_RESPONSE = 11,
PACKET_TLV_TYPE_REQUEST = 0, ///< Indicates a request packet.
PACKET_TLV_TYPE_RESPONSE = 1, ///< Indicates a response packet.
PACKET_TLV_TYPE_PLAIN_REQUEST = 10, ///< Indicates a plain request packet.
PACKET_TLV_TYPE_PLAIN_RESPONSE = 11, ///< Indicates a plain response packet.
} PacketTlvType;
// Meta argument types, used for validation
/*! @brief Meta TLV argument type representing a null value. */
#define TLV_META_TYPE_NONE (0 << 0)
/*! @brief Meta TLV argument type representing a string value. */
#define TLV_META_TYPE_STRING (1 << 16)
/*! @brief Meta TLV argument type representing a unsigned integer value. */
#define TLV_META_TYPE_UINT (1 << 17)
/*! @brief Meta TLV argument type representing a raw data value. */
#define TLV_META_TYPE_RAW (1 << 18)
/*! @brief Meta TLV argument type representing a boolean value. */
#define TLV_META_TYPE_BOOL (1 << 19)
/*! @brief Meta TLV argument type representing a quad-word value. */
#define TLV_META_TYPE_QWORD (1 << 20)
/*! @brief Meta TLV argument type representing a compressed data value. */
#define TLV_META_TYPE_COMPRESSED (1 << 29)
/*! @brief Meta TLV argument type representing a group value. */
#define TLV_META_TYPE_GROUP (1 << 30)
/*! @brief Meta TLV argument type representing a nested/complex value. */
#define TLV_META_TYPE_COMPLEX (1 << 31)
/*! @brief Meta TLV argument type representing a flag set/mask value. */
#define TLV_META_TYPE_MASK(x) ((x) & 0xffff0000)
#define TLV_RESERVED 0
@ -43,59 +65,63 @@ typedef enum
#define CHANNEL_FLAG_SYNCHRONOUS (1 << 0)
#define CHANNEL_FLAG_COMPRESS (1 << 1)
/*! @brief Type definition with defines `TlvMetaType` as an double-word. */
typedef DWORD TlvMetaType;
/*!
* @brief Full list of recognised TLV types.
*/
typedef enum
{
MAKE_TLV(ANY, TLV_META_TYPE_NONE, 0),
MAKE_TLV(METHOD, TLV_META_TYPE_STRING, 1),
MAKE_TLV(REQUEST_ID, TLV_META_TYPE_STRING, 2),
MAKE_TLV(EXCEPTION, TLV_META_TYPE_GROUP, 3),
MAKE_TLV(RESULT, TLV_META_TYPE_UINT, 4),
TLV_TYPE_ANY = TLV_VALUE(TLV_META_TYPE_NONE, 0),
TLV_TYPE_METHOD = TLV_VALUE(TLV_META_TYPE_STRING, 1),
TLV_TYPE_REQUEST_ID = TLV_VALUE(TLV_META_TYPE_STRING, 2),
TLV_TYPE_EXCEPTION = TLV_VALUE(TLV_META_TYPE_GROUP, 3),
TLV_TYPE_RESULT = TLV_VALUE(TLV_META_TYPE_UINT, 4),
// Argument basic types
MAKE_TLV(STRING, TLV_META_TYPE_STRING, 10),
MAKE_TLV(UINT, TLV_META_TYPE_UINT, 11),
MAKE_TLV(BOOL, TLV_META_TYPE_BOOL, 12),
TLV_TYPE_STRING = TLV_VALUE(TLV_META_TYPE_STRING, 10), ///< Represents a string value.
TLV_TYPE_UINT = TLV_VALUE(TLV_META_TYPE_UINT, 11), ///< Represents an unsigned integer value.
TLV_TYPE_BOOL = TLV_VALUE(TLV_META_TYPE_BOOL, 12), ///< Represents a boolean value.
// Extended types
MAKE_TLV(LENGTH, TLV_META_TYPE_UINT, 25),
MAKE_TLV(DATA, TLV_META_TYPE_RAW, 26),
MAKE_TLV(FLAGS, TLV_META_TYPE_UINT, 27),
TLV_TYPE_LENGTH = TLV_VALUE(TLV_META_TYPE_UINT, 25), ///< Represents a length (unsigned integer).
TLV_TYPE_DATA = TLV_VALUE(TLV_META_TYPE_RAW, 26), ///< Represents arbitrary data (raw).
TLV_TYPE_FLAGS = TLV_VALUE(TLV_META_TYPE_UINT, 27), ///< Represents a set of flags (unsigned integer).
// Channel types
MAKE_TLV(CHANNEL_ID, TLV_META_TYPE_UINT, 50),
MAKE_TLV(CHANNEL_TYPE, TLV_META_TYPE_STRING, 51),
MAKE_TLV(CHANNEL_DATA, TLV_META_TYPE_RAW, 52),
MAKE_TLV(CHANNEL_DATA_GROUP, TLV_META_TYPE_GROUP, 53),
MAKE_TLV(CHANNEL_CLASS, TLV_META_TYPE_UINT, 54),
MAKE_TLV(CHANNEL_PARENTID, TLV_META_TYPE_UINT, 55),
TLV_TYPE_CHANNEL_ID = TLV_VALUE(TLV_META_TYPE_UINT, 50), ///< Represents a channel identifier (unsigned integer).
TLV_TYPE_CHANNEL_TYPE = TLV_VALUE(TLV_META_TYPE_STRING, 51), ///< Represents a channel type (string).
TLV_TYPE_CHANNEL_DATA = TLV_VALUE(TLV_META_TYPE_RAW, 52), ///< Represents channel data (raw).
TLV_TYPE_CHANNEL_DATA_GROUP = TLV_VALUE(TLV_META_TYPE_GROUP, 53), ///< Represents a channel data group (group).
TLV_TYPE_CHANNEL_CLASS = TLV_VALUE(TLV_META_TYPE_UINT, 54), ///< Represents a channel class (unsigned integer).
TLV_TYPE_CHANNEL_PARENTID = TLV_VALUE(TLV_META_TYPE_UINT, 55), ///< Represents a channel parent identifier (unsigned integer).
// Channel extended types
MAKE_TLV(SEEK_WHENCE, TLV_META_TYPE_UINT, 70),
MAKE_TLV(SEEK_OFFSET, TLV_META_TYPE_UINT, 71),
MAKE_TLV(SEEK_POS, TLV_META_TYPE_UINT, 72),
TLV_TYPE_SEEK_WHENCE = TLV_VALUE(TLV_META_TYPE_UINT, 70),
TLV_TYPE_SEEK_OFFSET = TLV_VALUE(TLV_META_TYPE_UINT, 71),
TLV_TYPE_SEEK_POS = TLV_VALUE(TLV_META_TYPE_UINT, 72),
// Grouped identifiers
MAKE_TLV(EXCEPTION_CODE, TLV_META_TYPE_UINT, 300),
MAKE_TLV(EXCEPTION_STRING, TLV_META_TYPE_STRING, 301),
TLV_TYPE_EXCEPTION_CODE = TLV_VALUE(TLV_META_TYPE_UINT, 300),
TLV_TYPE_EXCEPTION_STRING = TLV_VALUE(TLV_META_TYPE_STRING, 301),
// Library loading
MAKE_TLV(LIBRARY_PATH, TLV_META_TYPE_STRING, 400),
MAKE_TLV(TARGET_PATH, TLV_META_TYPE_STRING, 401),
MAKE_TLV(MIGRATE_PID, TLV_META_TYPE_UINT, 402),
MAKE_TLV(MIGRATE_LEN, TLV_META_TYPE_UINT, 403),
MAKE_TLV(MIGRATE_PAYLOAD, TLV_META_TYPE_STRING, 404),
MAKE_TLV(MIGRATE_ARCH, TLV_META_TYPE_UINT, 405),
MAKE_TLV(MIGRATE_TECHNIQUE, TLV_META_TYPE_UINT, 406),
TLV_TYPE_LIBRARY_PATH = TLV_VALUE(TLV_META_TYPE_STRING, 400), ///< Represents a path to the library to be loaded (string).
TLV_TYPE_TARGET_PATH = TLV_VALUE(TLV_META_TYPE_STRING, 401), ///< Represents a target path (string).
TLV_TYPE_MIGRATE_PID = TLV_VALUE(TLV_META_TYPE_UINT, 402), ///< Represents a process identifier of the migration target (unsigned integer).
TLV_TYPE_MIGRATE_LEN = TLV_VALUE(TLV_META_TYPE_UINT, 403), ///< Represents a migration payload size/length in bytes (unsigned integer).
TLV_TYPE_MIGRATE_PAYLOAD = TLV_VALUE(TLV_META_TYPE_STRING, 404), ///< Represents a migration payload (string).
TLV_TYPE_MIGRATE_ARCH = TLV_VALUE(TLV_META_TYPE_UINT, 405), ///< Represents a migration target architecture.
TLV_TYPE_MIGRATE_TECHNIQUE = TLV_VALUE(TLV_META_TYPE_UINT, 406), ///< Represents a migration technique (unsigned int).
// Cryptography
MAKE_TLV(CIPHER_NAME, TLV_META_TYPE_STRING, 500),
MAKE_TLV(CIPHER_PARAMETERS, TLV_META_TYPE_GROUP, 501),
TLV_TYPE_CIPHER_NAME = TLV_VALUE(TLV_META_TYPE_STRING, 500),
TLV_TYPE_CIPHER_PARAMETERS = TLV_VALUE(TLV_META_TYPE_GROUP, 501),
MAKE_TLV(EXTENSIONS, TLV_META_TYPE_COMPLEX, 20000),
MAKE_TLV(USER, TLV_META_TYPE_COMPLEX, 40000),
MAKE_TLV(TEMP, TLV_META_TYPE_COMPLEX, 60000),
TLV_TYPE_EXTENSIONS = TLV_VALUE(TLV_META_TYPE_COMPLEX, 20000),
TLV_TYPE_USER = TLV_VALUE(TLV_META_TYPE_COMPLEX, 40000),
TLV_TYPE_TEMP = TLV_VALUE(TLV_META_TYPE_COMPLEX, 60000),
} TlvType;
#ifdef _WIN32
@ -198,6 +224,12 @@ LINKAGE DWORD packet_receive_via_http_wininet(Remote *remote, Packet **packet);
LINKAGE DWORD packet_transmit_via_http_wininet(Remote *remote, Packet *packet, PacketRequestCompletion *completion);
#endif
/*!
* @brief Transmit a `TLV_TYPE_RESULT` response if `response` is present.
* @param result The result to be sent.
* @param remote Reference to the remote connection to send the response to.
* @param response the Response to add the `result` to.
*/
#define packet_transmit_response(result, remote, response) \
if (response) { \
packet_add_tlv_uint(response, TLV_TYPE_RESULT, result); \

View File

@ -1,14 +1,13 @@
#ifndef _METERPRETER_LIB_REMOTE_H
#define _METERPRETER_LIB_REMOTE_H
#include "crypto.h"
#include "thread.h"
/*
* Remote context allocation
/*!
* @brief Remote context allocation.
*
* Wraps the initialized file descriptor for extension purposes
* Wraps the initialized file descriptor for extension purposes.
*/
typedef struct _Remote
{

View File

@ -0,0 +1,17 @@
/*!
* @mainpage Meterpreter Project Documentation
*
* This is the auto-generated documentation for the
* [Meterpreter](https://github.com/rapid7/meterpreter) project; the
* native payload for Windows and Linux in [Metasploit](https://github.com/rapid7/metasploit-framework)
*
* This documentation comes straight from the source and is created with doxygen.
*
* The source has only just started being annotated by the developers, hence the
* documentation found here is scant. Over time, more and more work will be put
* into filling the holes so that the source becomes more understandable and
* easier to contribute to.
*
* For more information on Metasploit and Meterpreter, contact the fine folks
* at [Rapid7](http://www.rapid7.com/).
*/// Stub that contains the main page documentation for the project.

View File

@ -1,14 +1,22 @@
/*!
* @file elevate.c
* @brief Definitions for SYSTEM privilege escalation.
*/
#include "precomp.h"
#include "namedpipe.h"
#include "tokendup.h"
#include "kitrap0d.h"
/*
* Get the native architecture of the system we are running on. (ripped from the stdapi's ps.c)
/*!
* @brief Get the native architecture of the system we are running on. (ripped from the stdapi's ps.c)
* @return A flag indicating the architecture of the system.
* @retval PROCESS_ARCH_X64 The architecture is AMD64.
* @retval PROCESS_ARCH_IA64 The architecture is IA64.
* @retval PROCESS_ARCH_X86 The architecture is X86.
*/
DWORD elevate_getnativearch( VOID )
{
HANDLE hKernel = NULL;
HMODULE hKernel = NULL;
GETNATIVESYSTEMINFO pGetNativeSystemInfo = NULL;
DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
SYSTEM_INFO SystemInfo = {0};
@ -51,8 +59,13 @@ DWORD elevate_getnativearch( VOID )
return dwNativeArch;
}
/*
* Attempt to elevate the current meterpreter to local system using a variety of techniques.
/*!
* @brief Attempt to elevate the current meterpreter to local system using a variety of techniques.
* @details This function attempts to get system level privileges using a number of techniques.
* If the caller hasn't specified a particular technique, then all of the known techniques are
* attempted in order until one succeeds.
* @return Indication of success or failure.
* @retval ERROR_SUCCESS Elevation to `SYSTEM` was successful.
*/
DWORD elevate_getsystem( Remote * remote, Packet * packet )
{
@ -67,71 +80,44 @@ DWORD elevate_getsystem( Remote * remote, Packet * packet )
BREAK_WITH_ERROR( "[ELEVATE] get_system. packet_create_response failed", ERROR_INVALID_HANDLE );
dwTechnique = packet_get_tlv_value_uint( packet, TLV_TYPE_ELEVATE_TECHNIQUE );
dprintf( "[ELEVATE] Technique requested (%u)", dwTechnique );
// if we are to to use ELEVATE_TECHNIQUE_ANY, we try everything at our disposal...
if( dwTechnique == ELEVATE_TECHNIQUE_ANY )
{
do
{
// firstly, try to use the in-memory named pipe impersonation technique (Requires Local Admin rights)
if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE ) {
dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE (%u)", ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE );
if ( (dwResult = elevate_via_service_namedpipe( remote, packet )) == ERROR_SUCCESS ) {
dwTechnique = ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE;
dwResult = elevate_via_service_namedpipe( remote, packet );
if( dwResult == ERROR_SUCCESS )
break;
// secondly, try to use the in-memory KiTrap0D exploit (CVE-2010-0232) (Requires Local User rights and vulnerable system)
// Note: If successfully, we end up replacing our processes primary token and as such cant rev3self at a later stage.
dwTechnique = ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D;
dwResult = elevate_via_exploit_kitrap0d( remote, packet );
if( dwResult == ERROR_SUCCESS )
break;
// thirdly, try to use the in-memory service token duplication technique (Requires Local Admin rights and SeDebugPrivilege)
dwTechnique = ELEVATE_TECHNIQUE_SERVICE_TOKENDUP;
dwResult = elevate_via_service_tokendup( remote, packet );
if( dwResult == ERROR_SUCCESS )
break;
// fourthly, try to use the touching disk named pipe impersonation technique (Requires Local Admin rights)
dwTechnique = ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2;
dwResult = elevate_via_service_namedpipe2( remote, packet );
if( dwResult == ERROR_SUCCESS )
break;
} while( 0 );
}
else
{
// if we are to only use a specific technique, try the specified one and return the success...
switch( dwTechnique )
{
case ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE:
dwResult = elevate_via_service_namedpipe( remote, packet );
break;
case ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2:
dwResult = elevate_via_service_namedpipe2( remote, packet );
break;
case ELEVATE_TECHNIQUE_SERVICE_TOKENDUP:
dwResult = elevate_via_service_tokendup( remote, packet );
break;
case ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D:
dwResult = elevate_via_exploit_kitrap0d( remote, packet );
break;
default:
dwResult = ERROR_CALL_NOT_IMPLEMENTED;
break;
break;
}
}
if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 ) {
dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 (%u)", ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 );
if ( (dwResult = elevate_via_service_namedpipe2( remote, packet )) == ERROR_SUCCESS ) {
dwTechnique = ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2;
break;
}
}
if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_SERVICE_TOKENDUP ) {
dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_SERVICE_TOKENDUP (%u)", ELEVATE_TECHNIQUE_SERVICE_TOKENDUP );
if ( (dwResult = elevate_via_service_tokendup( remote, packet )) == ERROR_SUCCESS ) {
dwTechnique = ELEVATE_TECHNIQUE_SERVICE_TOKENDUP;
break;
}
}
if( dwTechnique == ELEVATE_TECHNIQUE_ANY || dwTechnique == ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D ) {
dprintf( "[ELEVATE] Attempting ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D (%u)", ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D );
if ( (dwResult = elevate_via_exploit_kitrap0d( remote, packet )) == ERROR_SUCCESS ) {
dwTechnique = ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D;
break;
}
}
} while( 0 );
if( response )
{
if( dwResult == ERROR_SUCCESS )
packet_add_tlv_uint( response, TLV_TYPE_ELEVATE_TECHNIQUE, dwTechnique );
else
packet_add_tlv_uint( response, TLV_TYPE_ELEVATE_TECHNIQUE, ELEVATE_TECHNIQUE_NONE );
packet_add_tlv_uint( response, TLV_TYPE_ELEVATE_TECHNIQUE, dwResult == ERROR_SUCCESS ? dwTechnique : ELEVATE_TECHNIQUE_NONE );
packet_transmit_response( dwResult, remote, response );
}

View File

@ -1,20 +1,23 @@
/*!
* @file elevate.h
* @brief Declarations for SYSTEM privilege escalation.
*/
#ifndef _METERPRETER_SOURCE_EXTENSION_PRIV_PRIV_SERVER_ELEVATE_ELEVATE_H
#define _METERPRETER_SOURCE_EXTENSION_PRIV_PRIV_SERVER_ELEVATE_ELEVATE_H
#define ELEVATE_TECHNIQUE_NONE -1
#define ELEVATE_TECHNIQUE_ANY 0
#define ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE 1
#define ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 2
#define ELEVATE_TECHNIQUE_SERVICE_TOKENDUP 3
#define ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D 4
#define ELEVATE_TECHNIQUE_NONE -1 ///< Identifier that indicates no technique was successful
#define ELEVATE_TECHNIQUE_ANY 0 ///< Identifier that indicates that all techniques should be attempted.
#define ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE 1 ///< Identifier for the Named Pipe service tecnique (#1)
#define ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 2 ///< Identifier for the Named Pipe service tecnique (#2)
#define ELEVATE_TECHNIQUE_SERVICE_TOKENDUP 3 ///< Identifier for the Token Duplication service technique.
#define ELEVATE_TECHNIQUE_EXPLOIT_KITRAP0D 4 ///< Identifier for the Kitrap0d exploit technique.
// from ps.h
typedef void (WINAPI * GETNATIVESYSTEMINFO)( LPSYSTEM_INFO lpSystemInfo );
typedef void (WINAPI * GETNATIVESYSTEMINFO)( LPSYSTEM_INFO lpSystemInfo ); ///< Stolen from ps.h
#define PROCESS_ARCH_UNKNOWN 0
#define PROCESS_ARCH_X86 1
#define PROCESS_ARCH_X64 2
#define PROCESS_ARCH_IA64 3
#define PROCESS_ARCH_UNKNOWN 0 ///< Indicates that the architecture is not known.
#define PROCESS_ARCH_X86 1 ///< Indicates that the architecture is X86.
#define PROCESS_ARCH_X64 2 ///< Indicates that the architecture is AMDX64.
#define PROCESS_ARCH_IA64 3 ///< Indicates that the architecture is IA64.
DWORD elevate_getnativearch( VOID );

View File

@ -1,34 +1,34 @@
#ifndef _METERPRETER_SOURCE_EXTENSION_PRIV_PRIV_SERVER_ELEVATE_TECHNIQUES_KITRAP0D_H
#define _METERPRETER_SOURCE_EXTENSION_PRIV_PRIV_SERVER_ELEVATE_TECHNIQUES_KITRAP0D_H
#define PAGE_SIZE 0x1000
enum { SystemModuleInformation = 11 };
typedef struct
{
ULONG Unknown1;
ULONG Unknown2;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT NameLength;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, * PSYSTEM_MODULE_INFORMATION_ENTRY;
typedef struct
{
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
#define PAGE_SIZE 0x1000
typedef struct CodeSignature
{
UCHAR Signature[16];
DWORD Version;
enum { SystemModuleInformation = 11 };
typedef struct
{
ULONG Unknown1;
ULONG Unknown2;
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT NameLength;
USHORT LoadCount;
USHORT PathLength;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION_ENTRY, * PSYSTEM_MODULE_INFORMATION_ENTRY;
typedef struct
{
ULONG Count;
SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
typedef struct CodeSignature
{
UCHAR Signature[16];
DWORD Version;
};
DWORD elevate_via_exploit_kitrap0d( Remote * remote, Packet * packet );

View File

@ -1,5 +1,5 @@
/*
* This module implements privilege escalation features.
/*!
* @brief This module implements privilege escalation features.
*/
#include "precomp.h"
@ -8,86 +8,42 @@
// second stage reflective dll inject payload and not the metsrv itself when it loads extensions.
#include "../../../ReflectiveDLLInjection/ReflectiveLoader.c"
// NOTE: _CRT_SECURE_NO_WARNINGS has been added to Configuration->C/C++->Preprocessor->Preprocessor
// this sets the delay load hook function, see DelayLoadMetSrv.h
EnableDelayLoadMetSrv();
/*!
* @brief `priv` extension dispatch table.
*/
Command customCommands[] =
{
// Elevate
{ "priv_elevate_getsystem",
{ elevate_getsystem, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
// Priv
{ "priv_passwd_get_sam_hashes",
{ request_passwd_get_sam_hashes, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
// Fs
{ "priv_fs_get_file_mace",
{ request_fs_get_file_mace, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "priv_fs_set_file_mace",
{ request_fs_set_file_mace, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "priv_fs_set_file_mace_from_file",
{ request_fs_set_file_mace_from_file, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "priv_fs_blank_file_mace",
{ request_fs_blank_file_mace, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
{ "priv_fs_blank_directory_mace",
{ request_fs_blank_directory_mace, { 0 }, 0 },
{ EMPTY_DISPATCH_HANDLER },
},
// Terminator
{ NULL,
{ EMPTY_DISPATCH_HANDLER },
{ EMPTY_DISPATCH_HANDLER },
},
COMMAND_REQ( "priv_elevate_getsystem", elevate_getsystem ),
COMMAND_REQ( "priv_passwd_get_sam_hashes", request_passwd_get_sam_hashes ),
COMMAND_REQ( "priv_fs_get_file_mace", request_fs_get_file_mace ),
COMMAND_REQ( "priv_fs_set_file_mace", request_fs_set_file_mace ),
COMMAND_REQ( "priv_fs_set_file_mace_from_file", request_fs_set_file_mace_from_file ),
COMMAND_REQ( "priv_fs_blank_file_mace", request_fs_blank_file_mace ),
COMMAND_REQ( "priv_fs_blank_directory_mace", request_fs_blank_directory_mace ),
COMMAND_TERMINATOR
};
/*
* Initialize the server extension
*/
/*!
* @brief Initialize the `priv` server extension.
*/
DWORD __declspec(dllexport) InitServerExtension(Remote *remote)
{
DWORD index;
hMetSrv = remote->hMetSrv;
for (index = 0; customCommands[index].method; index++)
{
dprintf("Registering command index %d", index);
dprintf(" Command: %s", customCommands[index].method);
dprintf(" Register: 0x%.8x", command_register);
command_register(&customCommands[index]);
}
command_register_all(customCommands);
return ERROR_SUCCESS;
}
/*
* Deinitialize the server extension
*/
/*!
* @brief Deinitialize the `priv` server extension.
*/
DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote)
{
DWORD index;
for (index = 0;
customCommands[index].method;
index++)
command_deregister(&customCommands[index]);
command_deregister_all(customCommands);
return ERROR_SUCCESS;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.