﻿/*
	Author: Axt Müller
	Description: Windows-Batch-Deployment DLL function test.
	Note: This file can be modified at will, but improper operation(s) will cause BSOD on the client(s).
*/
#include <locale.h>
#include <stdio.h>
#include <Windows.h>
#include "template.h"
#include "test.h"

#pragma warning (disable: 4533)

//If this macro is enabled, the test code will execute continuously on each client.
//#define CLIENT_STABILITY_TEST

//If this macro is enabled, the program will NOT check for updates of binary files.
//#define DO_NOT_CHECK_UPDATE

//If this macro is enabled, the program will update fake binary files to each client for testing.
//#define TEST_UPDATE_CLIENT

ULONG __stdcall SendCallback(ULONG count, ULONG current, ULONG all, ULONG id)
{
	printf("[%ld]Send: {%ld} | (%ld / %ld)\n", id, count, current, all);
	return 0;
}

ULONG __stdcall RecvCallback(ULONG count, ULONG current, ULONG all, ULONG id)
{
	printf("[%ld]Recv: {%ld} | (%ld / %ld)\n", id, count, current, all);
	return 0;
}

void __stdcall FunctionTest(PCLIENT_INFO p, char *szClientVersion)
{
	//goto __new_test;
	//======================================================================
	//Client configuration (ClientConfig)
	//======================================================================
	WCHAR file[MAX_PATH] = {0};
	WCHAR path[MAX_PATH] = {0};
	GetModuleFileNameW(0, path, MAX_PATH);
	for(SIZE_T i=wcslen(path)-1; i>=0; i--)
	{
		if(path[i]=='\\')
		{
			path[i+1]='\0';
			break;
		}
	}
	wcscpy(file, path);
	wcscat(file, L"test.cfg");
	if(_ClientConfig(p->id, CLIENT_CONFIG_SERVER, file))
	{
		printf("[%04ld]Configure client successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Configure client unsuccessfully.\n", p->id);
		goto __exit;
	}
	//======================================================================
	//Client update (ClientUpdate)
	//======================================================================
	wcscpy(file, path);
#ifndef DO_NOT_CHECK_UPDATE
	//Update the client binary file if new client binary is available.
	if(g_szLatestClientVersion[0]!=0)
	{
		if(strstr(p->szDescription, "(WIN32-"))
		{
			wcscat(file, L"LatestClient32.sys");
		}
		else
		{
			wcscat(file, L"LatestClient64.sys");
		}
		if(stricmp(g_szLatestClientVersion, szClientVersion))
		{
			printf("[%04ld]THE CLIENT NEED TO BE UPDATED.\n", p->id);
			if(_ClientUpdate(p->id, file))
			{
				printf("[%04ld]Update client successfully.\n", p->id);
				strcpy(szClientVersion, g_szLatestClientVersion);
			}
			else
			{
				printf("[%04ld]Update client unsuccessfully.\n", p->id);
				goto __exit;
			}
		}
		else
		{
			printf("[%04ld]THE CLIENT DOES NOT NEED TO BE UPDATED.\n", p->id);
		}
	}
	else
	{
		//Update the client binary file anyway, for test purposes only.
		//The update test drivers don't have any function, after rebooting, the effective WBD drivers will no longer exist.
#ifdef TEST_UPDATE_CLIENT
		if(strstr(p->szDescription, "(WIN32-"))
		{
			wcscat(file, L"UpdateTest32.sys");
		}
		else
		{
			wcscat(file, L"UpdateTest64.sys");
		}
		if(_ClientUpdate(p->id, file))
		{
			printf("[%04ld]Update client (test) successfully.\n", p->id);
			strcpy(szClientVersion, g_szLatestClientVersion);
		}
		else
		{
			printf("[%04ld]Update client (test) unsuccessfully.\n", p->id);
			goto __exit;
		}
#endif
	}
#endif
	//======================================================================
	//File transmission (CmdQueryDirectory + CmdUploadFileTo + CmdDownloadFileFrom)
	//======================================================================
	BOOLEAN bNeedExit = 0;
	ULONG dwFiles = 0;
	WCHAR wsDir[MAX_PATH] = L"C:\\windows\\";
	WCHAR wsAppPath[MAX_PATH] = {0};
	//Query program path.
	if(GetModuleFileNameW(0, wsAppPath, MAX_PATH))
	{
		size_t i, c = wcslen(wsAppPath);
		for(i=c-1; i>=0; i--)
		{
			if(wsAppPath[i]==L'\\')
			{
				wsAppPath[i+1]=L'\0';
				break;
			}
		}
	}
	else
	{
		printf("[%04ld]Query program path unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Query file of WINDOWS directory.
	if(_CmdQueryDirectory(p->id, wsDir, NULL, &dwFiles))
	{
		printf("[%04ld]Enumerate directory successfully, number of files: %ld.\n", p->id, dwFiles);
		//Allocate file buffer
		PBDP_FILE_INFO pFiles = (PBDP_FILE_INFO)MALLOC(sizeof(BDP_FILE_INFO) * dwFiles);
		if(pFiles)
		{
			RtlZeroMemory(pFiles, sizeof(BDP_FILE_INFO) * dwFiles);
			if(_CmdQueryDirectory(p->id, wsDir, pFiles, &dwFiles))
			{
				for(ULONG i=0; i<dwFiles; i++)
				{
					if(pFiles[i].FileSize==0xFFFFFFFFFFFFFFFF)
					{
						printf("[%04ld][D](%ld): %S.\n", p->id, i, pFiles[i].FileName);
					}
					else
					{
						printf("[%04ld][F](%ld): %S.\n", p->id, i, pFiles[i].FileName);
					}
				}
			}
			else
			{
				printf("[%04ld]Enumerate directory unsuccessfully.\n", p->id);
				bNeedExit = 1;
			}
			FREE(pFiles);
		}
		if(bNeedExit)
		{
			goto __exit;
		}
	}
	else
	{
		printf("[%04ld]Query directory file count unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Download NTOSKRNL file.
	WCHAR wsRF[MAX_PATH] = L"c:\\windows\\system32\\ntoskrnl.exe";
	WCHAR wsLF[MAX_PATH] = {0};
	WCHAR wsLocalSaveDir[MAX_PATH] = {0};
	wcscpy(wsLocalSaveDir, wsAppPath);
	wcscat(wsLocalSaveDir, L"ntoskrnl");
	CreateDirectoryW(wsLocalSaveDir, NULL);
	swprintf(wsLF, MAX_PATH, L"%s\\[%S]ntoskrnl.exe", wsLocalSaveDir, p->szDescription);
	SENDRECV_CALLBACK rCallback = {0};
	rCallback.id = p->id;
	rCallback.callback = RecvCallback;
	if(_CmdDownloadFileFrom(p->id, wsRF, wsLF, &rCallback))
	{
		printf("[%04ld]Download file successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Download file unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Upload test DLL file.
	wcscpy(wsRF, L"c:\\test.dll");
	wcscpy(wsLF, wsAppPath);
	if(strstr(p->szDescription, "(WIN32-"))
	{
		wcscat(wsLF, L"UserDemo32.dll");
	}
	else
	{
		wcscat(wsLF, L"UserDemo64.dll");
	}
	SENDRECV_CALLBACK sCallback = {0};
	sCallback.id = p->id;
	sCallback.callback = SendCallback;
	if(_CmdUploadFileTo(p->id, wsLF, wsRF, &sCallback))
	{
		printf("[%04ld]Upload file successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Upload file unsuccessfully.\n", p->id);
		goto __exit;
	}
	//======================================================================
	//File operation (CmdFileOperation)
	//======================================================================
	ULONG AttrVal;
	//Clean up last test
	_CmdFileOperation(p->id, CLIENT_FSO_DELETE, L"C:\\22222222", NULL, NULL);
	//Create directory.
	if(_CmdFileOperation(p->id, CLIENT_FSO_CREATE_DIRECTORY, L"c:\\00000000", NULL, NULL))
	{
		printf("[%04ld]Create directory successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Create directory unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Create file (file content can be NULL).
	if(_CmdFileOperation(p->id, CLIENT_FSO_CREATE_FILE, L"c:\\00000000\\test.TXT", L"Hello,World!", NULL))
	{
		printf("[%04ld]Create file successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Create file unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Copy file or directory.
	if(_CmdFileOperation(p->id, CLIENT_FSO_COPY, L"C:\\WINDOWS\\WEB", L"C:\\11111111", NULL))
	{
		printf("[%04ld]Copy directory successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Copy directory unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Rename file or directory.
	if(_CmdFileOperation(p->id, CLIENT_FSO_RENAME, L"C:\\11111111", L"C:\\22222222", NULL))
	{
		printf("[%04ld]Rename directory successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Rename directory unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Delete file or directory.
	if(_CmdFileOperation(p->id, CLIENT_FSO_DELETE, L"C:\\00000000", NULL, NULL))
	{
		printf("[%04ld]Delete directory successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Delete directory unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Set attribute of file or directory (you need to convert the file attribute value to a string).
	WCHAR wsAttr[10] = {0};
	_itow(FILE_ATTRIBUTE_HIDDEN, wsAttr, 10);
	if(_CmdFileOperation(p->id, CLIENT_FSO_SET_ATTRIB, L"C:\\22222222", wsAttr, NULL))
	{
		printf("[%04ld]Set file attribute successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Set file attribute unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Query attribute of file or directory.
	if(_CmdFileOperation(p->id, CLIENT_FSO_GET_ATTRIB, L"C:\\22222222", NULL, &AttrVal))
	{
		printf("[%04ld]Query file attribute successfully: 0x%X.\n", p->id, AttrVal);
	}
	else
	{
		printf("[%04ld]Query file attribute unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Download file via HTTP link.
	if(_CmdFileOperation(p->id, CLIENT_FSO_HTTP_DOWNLOAD, L"http://www.w3.org/Icons/w3c_home", L"C:\\22222222\\logo.png", &AttrVal))
	{
		printf("[%04ld]Download file successfully: 0x%X.\n", p->id, AttrVal);
	}
	else
	{
		printf("[%04ld]Download file unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Get DLL file hash
	if(_CmdFileOperation(p->id, CLIENT_FSO_GET_CRC32, L"C:\\22222222\\logo.png", NULL, &AttrVal))
	{
		printf("[%04ld]Get file CRC32 successfully: 0x%X\n", p->id, AttrVal);
	}
	else
	{
		printf("[%04ld]Get file CRC32 unsuccessfully.\n", p->id);
	}
	//Move file / folder after reboot
	if(_CmdFileOperation(p->id, CLIENT_FSO_DELAY_MOV_DEL, L"C:\\22222222", L"C:\\33333333", NULL))
	{
		printf("[%04ld]Delay to delete file successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Delay to delete file unsuccessfully.\n", p->id);
	}
	//======================================================================
	//Binary execution (CmdExecuteBinary)
	//======================================================================
	ULONG64 RetVal;
	//You must use the full path name of the driver file.
	if(_CmdExecuteBinary(p->id, L"c:\\windows\\system32\\drivers\\usb8023.sys", CLIENT_RUN_SYS_P1, CLIENT_RUN_SYS_P2, &RetVal))
	{
		printf("[%04ld]Load SYS successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Load SYS unsuccessfully.\n", p->id);
	}
	//You can add command line arguments after the program path name.
	if(_CmdExecuteBinary(p->id, L"c:\\windows\\system32\\taskmgr.exe", SW_SHOWMINIMIZED, 0, &RetVal))
	{
		//NOTE1: If UAC is enabled, TaskMgr will not run successfully, because it requires administrative privileges.
		//NOTE2: If you add CLIENT_BYPASS_UAC_UNSAFE to the third parameter,
		//       it can create a process that requires administrative privileges,
		//       but it may cause some systems to crash after multiple (>10) uses.
		//       DEMO: _CmdExecuteBinary(p->id, L"reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /v EnableLUA /t REG_DWORD /d 0 /f", SW_HIDE+CLIENT_BYPASS_UAC_UNSAFE, 0, &RetVal);
		//NOTE3: You can use the code of the UACME project to bypass UAC in your program, it will not cause the system to crash.
		printf("[%04ld]Run EXE successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Run EXE unsuccessfully.\n", p->id);
	}
	//You must write an exported function name after the DLL path name (separated by comma), otherwise the DLL will not be loaded.
	if(_CmdExecuteBinary(p->id, L"c:\\test.dll,ExportedFunction 123", CLIENT_RUN_DLL_P1, CLIENT_RUN_DLL_P2, &RetVal))
	{
		printf("[%04ld]Load DLL successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Load DLL unsuccessfully.\n", p->id);
	}
	//======================================================================
	//System shell (CmdSystemShell)
	//======================================================================
	char cmdtxt[1024] = {0};
	//If the command does not complete within a certain time, the function will return with empty string.
	if(_CmdSystemShell(p->id, L"type c:\\windows\\win.ini", cmdtxt, 1024))
	{
		printf("[%04ld]Run CMD successfully.\n%s\n\n", p->id, cmdtxt);
	}
	else
	{
		printf("[%04ld]Run CMD unsuccessfully.\n", p->id);
	}
	//======================================================================
	//Auto-Run operation (CmdAddAutoRunBin + CmdDelAutoRunBin + CmdQueryAutoRunBin + CmdClearAutoRunBin)
	//======================================================================
	//Add binaries to Auto-Run list.
	PWCHAR names = NULL;
	if(_CmdAddAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS, L"USB8023", L"c:\\windows\\system32\\drivers\\USB8023.SYS", NULL))
	{
		printf("[%04ld]Add auto-run successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Add auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	if(_CmdAddAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS, L"BRIDGE", L"c:\\windows\\system32\\drivers\\BRIDGE.SYS", NULL))
	{
		printf("[%04ld]Add auto-run successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Add auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	if(_CmdAddAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_EXE, L"WRITE", L"c:\\windows\\system32\\WRITE.EXE", L"c:\\windows\\win.ini"))
	{
		printf("[%04ld]Add auto-run successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Add auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	if(_CmdAddAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_DLL, L"SHELL32", L"c:\\windows\\system32\\SHELL32.DLL", L"Control_RunDLL intl.cpl,,1"))
	{
		printf("[%04ld]Add auto-run successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Add auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Enumerate Auto-Run list.
	if(_CmdQueryAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS, &names))
	{
		PWCHAR ptr = names;
		while(1)
		{
			if(wcslen(ptr)==0)break;
			printf("[%04ld][EnumAutoRun1]%S.\n", p->id, ptr);
			ptr = ptr + wcslen(ptr)+1;
		}
		FREE(names);
	}
	else
	{
		printf("[%04ld]Query auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Delete USB8023 from Auto-Run list.
	if(_CmdDelAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS, L"USB8023"))
	{
		printf("[%04ld]Delete auto-run successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Delete auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Enumerate Auto-Run list again to show the effect.
	if(_CmdQueryAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS, &names))
	{
		PWCHAR ptr = names;
		while(1)
		{
			if(wcslen(ptr)==0)break;
			printf("[%04ld][EnumAutoRun2]%S.\n", p->id, ptr);
			ptr = ptr + wcslen(ptr)+1;
		}
		FREE(names);
	}
	else
	{
		printf("[%04ld]Query auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Delete everything from Auto-Run list.
	if(_CmdClearAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS))
	{
		printf("[%04ld]Clear auto-run successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Clear auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Enumerate Auto-Run list again to show the effect.
	if(_CmdQueryAutoRunBin(p->id, CLIENT_AUTORUN_TYPE_SYS, &names))
	{
		PWCHAR ptr = names;
		while(1)
		{
			if(wcslen(ptr)==0)break;
			printf("[%04ld][EnumAutoRun3]%S.\n", p->id, ptr);
			ptr = ptr + wcslen(ptr)+1;
		}
		FREE(names);
	}
	else
	{
		printf("[%04ld]Query auto-run unsuccessfully.\n", p->id);
		goto __exit;
	}
	//======================================================================
	//Registry operation (CmdQueryRegistry + CmdRegistryOperation)
	//======================================================================
	ULONG dwItems = 0;
	WCHAR wsKey[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\AfD";
	//Query registry items of "AfD" key
	if(_CmdQueryRegistry(p->id, wsKey, NULL, &dwItems))
	{
		printf("[%04ld]Enumerate registry key successfully, number of items: %ld.\n", p->id, dwItems);
		//Allocate file buffer
		PBDP_REG_INFO pItems = (PBDP_REG_INFO)MALLOC(sizeof(BDP_REG_INFO) * dwItems);
		if(pItems)
		{
			RtlZeroMemory(pItems, sizeof(BDP_REG_INFO) * dwItems);
			if(_CmdQueryRegistry(p->id, wsKey, pItems, &dwItems))
			{
				for(ULONG i=0; i<dwItems; i++)
				{
					if(pItems[i].Type==0xFF)
					{
						printf("[%04ld][REG_KEY      ]%S\n", p->id, pItems[i].Name);
					}
					else
					{
						char szTypeName[32] = {0};
						//get type name
						if (pItems[i].Type==REG_NONE){strcpy(szTypeName, "REG_NONE     ");}
						else if (pItems[i].Type==REG_SZ){strcpy(szTypeName, "REG_SZ       ");}
						else if (pItems[i].Type==REG_EXPAND_SZ){strcpy(szTypeName, "REG_EXPAND_SZ");}
						else if (pItems[i].Type==REG_BINARY){strcpy(szTypeName, "REG_BINARY   ");}
						else if (pItems[i].Type==REG_DWORD){strcpy(szTypeName, "REG_DWORD    ");}
						else if (pItems[i].Type==REG_DWORD_BIG_ENDIAN){strcpy(szTypeName, "REG_DWORD_BIG_ENDIAN");}
						else if (pItems[i].Type==REG_LINK){strcpy(szTypeName, "REG_LINK     ");}
						else if (pItems[i].Type==REG_MULTI_SZ){strcpy(szTypeName, "REG_MULTI_SZ ");}
						else if (pItems[i].Type==REG_RESOURCE_LIST){strcpy(szTypeName, "REG_RESOURCE_LIST");}
						else if (pItems[i].Type==REG_FULL_RESOURCE_DESCRIPTOR){strcpy(szTypeName, "REG_FULL_RESOURCE_DESCRIPTOR");}
						else if (pItems[i].Type==REG_RESOURCE_REQUIREMENTS_LIST){strcpy(szTypeName, "REG_RESOURCE_REQUIREMENTS_LIST");}
						else if (pItems[i].Type==REG_QWORD){strcpy(szTypeName, "REG_QWORD    ");}
						//show name
						if(pItems[i].NameTooLong)
						{
							printf("[%04ld][%s]!%S\t\t", p->id, szTypeName, pItems[i].Name);
						}
						else
						{
							printf("[%04ld][%s]%S\t\t", p->id, szTypeName, pItems[i].Name);
						}
						//show data of some values
						if(pItems[i].Type==REG_SZ || pItems[i].Type==REG_EXPAND_SZ)
						{
							printf("%S",(PWCHAR)pItems[i].Data);
						}
						else if(pItems[i].Type==REG_DWORD)
						{
							printf("%ld",*(PDWORD)pItems[i].Data);
						}
						printf("\n");
					}
				}
			}
			else
			{
				printf("[%04ld]Enumerate registry key unsuccessfully.\n", p->id);
				bNeedExit = 1;
			}
			FREE(pItems);
		}
		if(bNeedExit)
		{
			goto __exit;
		}
	}
	else
	{
		printf("[%04ld]Query registry key item count unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Create registry key
	wcscpy(wsKey,L"\\REGISTRY\\MACHINE\\SYSTEM\\00000000");
	if(_CmdRegistryOperation(p->id, CLIENT_REG_CREATE_KEY, wsKey, NULL, NULL, NULL, NULL))
	{
		printf("[%04ld]Create registry key successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Create registry key unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Rename registry key
	if(_CmdRegistryOperation(p->id, CLIENT_REG_RENAME_KEY, wsKey, L"11111111", NULL, NULL, NULL))
	{
		printf("[%04ld]Rename registry key successfully.\n", p->id);
		wcscpy(wsKey,L"\\REGISTRY\\MACHINE\\SYSTEM\\11111111");
	}
	else
	{
		printf("[%04ld]Rename registry key unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Set registry value
	WCHAR wsValue[] = L"value";
	WCHAR wsValueData[] = L"Mit freundlichen Grüßen";//PWCHAR wsValue=NULL;
	DWORD dwValueType = REG_SZ;
	DWORD dwValueLength = (DWORD)wcslen(wsValueData)*2;
	if(_CmdRegistryOperation(p->id, CLIENT_REG_SET_VALUE, wsKey, wsValue, &dwValueType, wsValueData, &dwValueLength))
	{
		printf("[%04ld]Set registry value successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Set registry value unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Query registry value
	DWORD dwRetValueType, dwRetValueLength;
	PVOID wsRetValueData;
	if(_CmdRegistryOperation(p->id, CLIENT_REG_QUERY_VALUE, wsKey, wsValue, &dwRetValueType, NULL, &dwRetValueLength))
	{
		printf("[%04ld]Query registry value type and length successfully: type=%ld, length=%ld\n", p->id, dwRetValueType, dwRetValueLength);
		wsRetValueData = MALLOC(dwRetValueLength+2);
		if(wsRetValueData)
		{
			RtlZeroMemory(wsRetValueData, dwRetValueLength+2);
			if(_CmdRegistryOperation(p->id, CLIENT_REG_QUERY_VALUE, wsKey, wsValue, NULL, wsRetValueData, &dwRetValueLength))
			{
				printf("[%04ld]Query registry value data successfully: %S\n", p->id, wsRetValueData);
			}
			FREE(wsRetValueData);
		}
	}
	else
	{
		printf("[%04ld]Query registry value type and length unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Delete registry value
	if(_CmdRegistryOperation(p->id, CLIENT_REG_DELETE_VALUE, wsKey, wsValue, NULL, NULL, NULL))
	{
		printf("[%04ld]Delete registry value successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Delete registry value unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Delete registry key
	if(_CmdRegistryOperation(p->id, CLIENT_REG_DELETE_KEY, wsKey, NULL, NULL, NULL, NULL))
	{
		printf("[%04ld]Delete registry key successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Delete registry key unsuccessfully.\n", p->id);
		goto __exit;
	}
	//======================================================================
	//System information operation (CmdQuerySetSystemInformation)
	//======================================================================
	BDP_SI_SYSTEM_TIME TimeInfo = {0};
	ULONG TimeInfoLength = sizeof(TimeInfo), NoReturn = 0;
	//Set time
	printf("[%04ld]Set system time to 2002-12-12 12:12:12.\n", p->id);
	TimeInfo.LocalTime.wYear=2002;TimeInfo.LocalTime.wMonth = 12;TimeInfo.LocalTime.wDay = 12;
	TimeInfo.LocalTime.wHour=12;TimeInfo.LocalTime.wMinute = 12;TimeInfo.LocalTime.wSecond = 12;
	if(_CmdQuerySetSystemInformation(p->id, CLIENT_SSI_SET_LOCAL_TIME, &TimeInfo, TimeInfoLength, NULL, &NoReturn))
	{
		printf("[%04ld]Set time successfully.\n", p->id);
	}
	else
	{
		printf("[%04ld]Set time unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Get time
	if(_CmdQuerySetSystemInformation(p->id, CLIENT_QSI_QUERY_TICK_TIME, NULL, 0, &TimeInfo, &TimeInfoLength))
	{
		printf("[%04ld]Tick Count = %I64d\n", p->id, TimeInfo.TickCount);
		printf("[%04ld]Local Time = %d-%d-%d %d:%d:%d\n", p->id, TimeInfo.LocalTime.wYear, TimeInfo.LocalTime.wMonth, TimeInfo.LocalTime.wDay, 
															TimeInfo.LocalTime.wHour, TimeInfo.LocalTime.wMinute, TimeInfo.LocalTime.wSecond);
	}
	else
	{
		printf("[%04ld]Query time unsuccessfully.\n", p->id);
		goto __exit;
	}
	//Enum Process
	ULONG dwExplorerPID = 0;
	PBDP_SI_PROCESS_INFO ProcInfo = (PBDP_SI_PROCESS_INFO)MALLOC(sizeof(BDP_SI_PROCESS_INFO) * MAX_PATH);
	if(ProcInfo)
	{
		ULONG ProcInfoLength = sizeof(BDP_SI_PROCESS_INFO) * MAX_PATH;
		if(_CmdQuerySetSystemInformation(p->id,CLIENT_QSI_ENUM_PROCESS,NULL,0,ProcInfo,&ProcInfoLength))
		{
			ULONG i, dwStructCount = ProcInfoLength / sizeof(BDP_SI_PROCESS_INFO);
			for(i=0;i<dwStructCount;i++)
			{
#ifdef _M_X64
				if(ProcInfo[i].Object<(ULONG64)0xFFFFFFFF)
					printf("0x%X\t%ld\t%S\n", (ULONG)ProcInfo[i].Object, (ULONG)ProcInfo[i].ID, ProcInfo[i].Path);
				else
					printf("0x%p\t%ld\t%S\n", ProcInfo[i].Object, (ULONG)ProcInfo[i].ID, ProcInfo[i].Path);
#else
				if(ProcInfo[i].Object<(ULONG64)0xFFFFFFFF)
					printf("0x%p\t%ld\t%S\n", (ULONG)ProcInfo[i].Object, (ULONG)ProcInfo[i].ID, ProcInfo[i].Path);
				else
					printf("0x%I64X\t%ld\t%S\n", ProcInfo[i].Object, (ULONG)ProcInfo[i].ID, ProcInfo[i].Path);
#endif
				if(wcsstr(_wcslwr(ProcInfo[i].Path),L"\\explorer.exe"))
				{
					dwExplorerPID = (ULONG)ProcInfo[i].ID;
				}
			}
		}
		else
		{
			printf("[%ld]Get system information unsuccessfully!\n", p->id);
			goto __exit;
		}
		FREE(ProcInfo);
	}
	//Inject DLL
	if(dwExplorerPID)
	{
		BDP_SI_PROCESS_INFO DllInjectInfo = {0};
		DllInjectInfo.ID = dwExplorerPID;
		wcscpy(DllInjectInfo.Path, L"c:\\windows\\system32\\aclui.dll");
		if(_CmdQuerySetSystemInformation(p->id,CLIENT_SSI_INJECT_DLL,&DllInjectInfo,sizeof(DllInjectInfo), NULL, &NoReturn))
		{
			printf("[%ld]Inject DLL successfully!\n", p->id);
		}
		else
		{
			printf("[%ld]Inject DLL unsuccessfully!\n", p->id);
			goto __exit;
		}
	}
	else
	{
		printf("[%ld]PID of EXPLORER.EXE cannot be found!\n", p->id);
		goto __exit;
	}
	//Enum DLL
	if(dwExplorerPID)
	{
		BDP_SI_PROCESS_INFO DllQueryInfo = {0};
		ULONG ModInfoLen = sizeof(BDP_SI_PROCESS_MOD_INFO) * 1024;
		PBDP_SI_PROCESS_MOD_INFO ProcDllInfo = (PBDP_SI_PROCESS_MOD_INFO)MALLOC(ModInfoLen);
		if(ProcDllInfo)
		{
			DllQueryInfo.ID = dwExplorerPID;
			if(_CmdQuerySetSystemInformation(p->id,CLIENT_QSI_ENUM_PROC_MOD,&DllQueryInfo,sizeof(BDP_SI_PROCESS_INFO),ProcDllInfo,&ModInfoLen))
			{
				ULONG i, dwStructCount = ModInfoLen / sizeof(BDP_SI_PROCESS_MOD_INFO);
				for(i=0;i<dwStructCount;i++)
				{
#ifdef _M_X64
					if(ProcDllInfo[i].Base<(ULONG64)0xFFFFFFFF)
						printf("0x%X\t0x%-8X\t%S\n", (ULONG)ProcDllInfo[i].Base, (ULONG)ProcDllInfo[i].Size, ProcDllInfo[i].Path);
					else
						printf("0x%p\t0x%-8X\t%S\n", ProcDllInfo[i].Base, (ULONG)ProcDllInfo[i].Size, ProcDllInfo[i].Path);
#else
					if(ProcDllInfo[i].Base<(ULONG64)0xFFFFFFFF)
						printf("0x%p\t0x%-8X\t%S\n", (ULONG)ProcDllInfo[i].Base, (ULONG)ProcDllInfo[i].Size, ProcDllInfo[i].Path);
					else
						printf("0x%I64X\t0x%-8X\t%S\n", ProcDllInfo[i].Base, (ULONG)ProcDllInfo[i].Size, ProcDllInfo[i].Path);
#endif
				}
			}
			else
			{
				printf("[%ld]Get system information unsuccessfully!\n", p->id);
			}
			FREE(ProcDllInfo);
		}
	}
	else
	{
		printf("[%ld]PID of EXPLORER.EXE cannot be found!\n", p->id);
		goto __exit;
	}
	//Kill Process
	if(dwExplorerPID)
	{
		BDP_SI_PROCESS_INFO KillProcInfo = {0};
		KillProcInfo.ID = dwExplorerPID;
		if(_CmdQuerySetSystemInformation(p->id,CLIENT_SSI_KILL_PROCESS,&KillProcInfo,sizeof(KillProcInfo), NULL, &NoReturn))
		{
			printf("[%ld]Kill process successfully!\n", p->id);
		}
		else
		{
			printf("[%ld]Kill process unsuccessfully!\n", p->id);
			goto __exit;
		}
	}
	else
	{
		printf("[%ld]PID of EXPLORER.EXE cannot be found!\n", p->id);
		goto __exit;
	}
	//======================================================================
	//Disk operation (CmdReadWriteDisk)
	//======================================================================
//__new_test:
	BDP_DISK_QUERY_READ_WRITE dqrw = {0};
	PBDP_PARTITION_INFO retPI = (PBDP_PARTITION_INFO)MALLOC(sizeof(BDP_PARTITION_INFO));
	if(retPI)
	{
		RtlZeroMemory(retPI, sizeof(BDP_PARTITION_INFO));
		ULONG lret = sizeof(BDP_PARTITION_INFO), pid = 0, fc = 0, ok = 0;
		//Query partition information
		while(1)
		{
			pid++;
			dqrw.PartitionId = pid;
			if(_CmdReadWriteDisk(p->id,CLIENT_DISK_QUERY_PARTITION,&dqrw,retPI,&lret))
			{
				printf("[HarddiskVolume%ld] (If a partition has multiple segments, only the first one is displayed.)\n", pid);
				printf("|-DiskId:            %ld\n", retPI->DiskId);
				printf("|-SectorsPerCluster: %ld\n", retPI->SectorsPerCluster);
				printf("|-BytesPerSector:    %ld\n", retPI->BytesPerSector);
				retPI->DosDevice?printf("|-DosDevice:         %C\n", retPI->DosDevice):printf("|-DosDevice:         (NONE)\n");
				printf("|-StartOffset:       0x%I64X\n", retPI->StartOffset);
				printf("|-ExtentLength:      0x%I64X\n", retPI->ExtentLength);
				ok++;
			}
			else
			{
				if(fc>sizeof(int)){break;}else{fc++;}
			}
		}
		if(!ok)
		{
			printf("Failed to query partitions.\n");
		}
		FREE(retPI);
	}
	//Query file position
	RtlZeroMemory(&dqrw, sizeof(dqrw));
	PBDP_FILE_CLUSTERS retFC = (PBDP_FILE_CLUSTERS)MALLOC(USN_PAGE_SIZE);
	if(retFC)
	{
		RtlZeroMemory(retFC, USN_PAGE_SIZE);
		ULONG lret = USN_PAGE_SIZE, i = 0;
		wcscpy(dqrw.Path, L"c:\\windows\\");
		if(_CmdReadWriteDisk(p->id,CLIENT_DISK_QUERY_FILE_POS,&dqrw,retFC,&lret))
		{
			printf("Located on Disk: %ld\n", retFC->DiskId);
			printf("Located on Partition: \\Device\\HarddiskVolume%ld\n", retFC->PartitionId);
			printf("Sectors per Cluster: %ld\n", retFC->SectorsPerCluster);
			printf("Bytes per Sector:    %ld\n", retFC->BytesPerSector);
			printf("Partition Starting Offset: %ld\n", retFC->PartitionStartOffset);
			for(i=0;i<retFC->SectionCount;i++)
			{
				printf("ClusterOnPartition[%I64d]~ClusterOnPartition[%I64d]: OffsetOnDisk[0x%I64X]~OffsetOnDisk[0x%I64X]\n", 
						retFC->Sections[i].Begin, retFC->Sections[i].End,
						retFC->Sections[i].Begin * retFC->BytesPerSector * retFC->SectorsPerCluster + retFC->PartitionStartOffset, 
						retFC->Sections[i].End * retFC->BytesPerSector * retFC->SectorsPerCluster + retFC->PartitionStartOffset + retFC->BytesPerSector - 1);
			}
		}
		else
		{
			printf("Failed to query file position.\n");
		}
		FREE(retFC);
	}
	//Write disk
	PBDP_DISK_QUERY_READ_WRITE pdqrw = (PBDP_DISK_QUERY_READ_WRITE)MALLOC(sizeof(BDP_DISK_QUERY_READ_WRITE) + USN_PAGE_SIZE);
	if(pdqrw)
	{
		PUCHAR ret = NULL;
		ULONG lret = 0, i = 0;
		pdqrw->DiskId = 0;
		pdqrw->LBA = 1;
		pdqrw->SectorSize = 512;
		pdqrw->CountLBA = USN_PAGE_SIZE / pdqrw->SectorSize;
		for(i=0;i<USN_PAGE_SIZE;i++){pdqrw->Buffer[i]=(UCHAR)i;}//data for test
		if(_CmdReadWriteDisk(p->id,CLIENT_DISK_WRITE_SECTOR,pdqrw,(void**)&ret,&lret))
		{
			printf("Succeeded to write disk.\n");
			FREE(ret);
		}
		else
		{
			printf("Failed to write disk.\n");
		}
		FREE(pdqrw);
	}
	else
	{
		printf("Failed to allocate structure.\n");
	}
	//Read disk
	RtlZeroMemory(&dqrw, sizeof(dqrw));
	PUCHAR ret = (PUCHAR)MALLOC(1024);
	if(ret)
	{
		ULONG lret = 1024, i = 0;
		dqrw.DiskId = 0;
		dqrw.LBA = 0;
		dqrw.CountLBA = 2;
		dqrw.SectorSize = 512;
		if(_CmdReadWriteDisk(p->id,CLIENT_DISK_READ_SECTOR,&dqrw,ret,&lret))
		{
			printf("%012I64X\t", dqrw.LBA * dqrw.SectorSize);
			for(i=0;i<lret;i++)
			{
				printf("%02X ", ret[i]);
				if(((i+1)%16==0) && (i+1<lret))
				{
					printf("\n%012I64X\t", dqrw.LBA * dqrw.SectorSize + i + 1);
				}
			}
			printf("\n");
			FREE(ret);
		}
		else
		{
			printf("Failed to read disk.\n");
		}
		FREE(ret);
	}
__exit:;
}

void main()
{
	BOOLEAN bCheckUpdate;
	BOOLEAN bOnlyConnectOnce;
	//If update checking is needed, set bCheckUpdate to 1.
#ifdef DO_NOT_CHECK_UPDATE
	bCheckUpdate = FALSE;
#else
	bCheckUpdate = TRUE;
#endif
	//If client stability testing is needed, set bOnlyConnectOnce to 0.
#ifdef CLIENT_STABILITY_TEST
	bOnlyConnectOnce = FALSE;
#else
	bOnlyConnectOnce = TRUE;
#endif
	//Start the function test.
	bCheckUpdate = FALSE;
	CreateSimpleServer(9999, FunctionTest, bCheckUpdate, bOnlyConnectOnce, 0, USN_PAGE_SIZE);
	system("pause");
}