607 lines
15 KiB
C
607 lines
15 KiB
C
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT AUTHORS, 2016 - 2019
|
|
*
|
|
* TITLE: SUP.C
|
|
*
|
|
* VERSION: 1.15
|
|
*
|
|
* DATE: 19 Apr 2019
|
|
*
|
|
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
* PARTICULAR PURPOSE.
|
|
*
|
|
*******************************************************************************/
|
|
#include "global.h"
|
|
|
|
/*
|
|
* supGetSystemInfo
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Wrapper for NtQuerySystemInformation.
|
|
*
|
|
*/
|
|
PVOID supGetSystemInfo(
|
|
_In_ SYSTEM_INFORMATION_CLASS InfoClass
|
|
)
|
|
{
|
|
INT c = 0;
|
|
PVOID Buffer = NULL;
|
|
ULONG Size = 0x1000;
|
|
NTSTATUS status;
|
|
ULONG memIO;
|
|
PVOID hHeap = NtCurrentPeb()->ProcessHeap;
|
|
|
|
do {
|
|
Buffer = RtlAllocateHeap(hHeap, HEAP_ZERO_MEMORY, (SIZE_T)Size);
|
|
if (Buffer != NULL) {
|
|
status = NtQuerySystemInformation(InfoClass, Buffer, Size, &memIO);
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
if (status == STATUS_INFO_LENGTH_MISMATCH) {
|
|
RtlFreeHeap(hHeap, 0, Buffer);
|
|
Buffer = NULL;
|
|
Size *= 2;
|
|
c++;
|
|
if (c > 100) {
|
|
status = STATUS_SECRET_TOO_LONG;
|
|
break;
|
|
}
|
|
}
|
|
} while (status == STATUS_INFO_LENGTH_MISMATCH);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
return Buffer;
|
|
}
|
|
|
|
if (Buffer) {
|
|
RtlFreeHeap(hHeap, 0, Buffer);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* supGetNtOsBase
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Return ntoskrnl base address.
|
|
*
|
|
*/
|
|
ULONG_PTR supGetNtOsBase(
|
|
VOID
|
|
)
|
|
{
|
|
PRTL_PROCESS_MODULES miSpace;
|
|
ULONG_PTR NtOsBase = 0;
|
|
|
|
miSpace = (PRTL_PROCESS_MODULES)supGetSystemInfo(SystemModuleInformation);
|
|
if (miSpace) {
|
|
NtOsBase = (ULONG_PTR)miSpace->Modules[0].ImageBase;
|
|
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, miSpace);
|
|
}
|
|
return NtOsBase;
|
|
}
|
|
|
|
/*
|
|
* supQueryResourceData
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Load resource by given id (win32 FindResource, SizeofResource, LockResource).
|
|
*
|
|
*/
|
|
PBYTE supQueryResourceData(
|
|
_In_ ULONG_PTR ResourceId,
|
|
_In_ PVOID DllHandle,
|
|
_In_ PULONG DataSize
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG_PTR IdPath[3];
|
|
IMAGE_RESOURCE_DATA_ENTRY *DataEntry;
|
|
PBYTE Data = NULL;
|
|
ULONG SizeOfData = 0;
|
|
|
|
if (DllHandle != NULL) {
|
|
|
|
IdPath[0] = (ULONG_PTR)RT_RCDATA; //type
|
|
IdPath[1] = ResourceId; //id
|
|
IdPath[2] = 0; //lang
|
|
|
|
status = LdrFindResource_U(DllHandle, (ULONG_PTR*)&IdPath, 3, &DataEntry);
|
|
if (NT_SUCCESS(status)) {
|
|
status = LdrAccessResource(DllHandle, DataEntry, (PVOID*)&Data, &SizeOfData);
|
|
if (NT_SUCCESS(status)) {
|
|
if (DataSize) {
|
|
*DataSize = SizeOfData;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Data;
|
|
}
|
|
|
|
/*
|
|
* supBackupVBoxDrv
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Backup virtualbox driver file if it already installed.
|
|
*
|
|
*/
|
|
BOOL supBackupVBoxDrv(
|
|
_In_ BOOL bRestore
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
WCHAR szOldDriverName[MAX_PATH * 2];
|
|
WCHAR szNewDriverName[MAX_PATH * 2];
|
|
WCHAR szDriverDirName[MAX_PATH * 2];
|
|
|
|
if (!GetSystemDirectory(szDriverDirName, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
_strcat(szDriverDirName, TEXT("\\drivers\\"));
|
|
|
|
if (bRestore) {
|
|
_strcpy(szOldDriverName, szDriverDirName);
|
|
_strcat(szOldDriverName, TEXT("VBoxDrv.backup"));
|
|
if (PathFileExists(szOldDriverName)) {
|
|
_strcpy(szNewDriverName, szDriverDirName);
|
|
_strcat(szNewDriverName, TEXT("VBoxDrv.sys"));
|
|
bResult = MoveFileEx(szOldDriverName, szNewDriverName,
|
|
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
|
}
|
|
}
|
|
else {
|
|
_strcpy(szOldDriverName, szDriverDirName);
|
|
_strcat(szOldDriverName, TEXT("VBoxDrv.sys"));
|
|
_strcpy(szNewDriverName, szDriverDirName);
|
|
_strcat(szNewDriverName, TEXT("VBoxDrv.backup"));
|
|
bResult = MoveFileEx(szOldDriverName, szNewDriverName,
|
|
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
* supWriteBufferToFile
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Create new file (or open existing) and write (append) buffer to it.
|
|
*
|
|
*/
|
|
SIZE_T supWriteBufferToFile(
|
|
_In_ PWSTR lpFileName,
|
|
_In_ PVOID Buffer,
|
|
_In_ SIZE_T Size,
|
|
_In_ BOOL Flush,
|
|
_In_ BOOL Append
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD dwFlag;
|
|
HANDLE hFile = NULL;
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING NtFileName;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
LARGE_INTEGER Position;
|
|
ACCESS_MASK DesiredAccess;
|
|
PLARGE_INTEGER pPosition = NULL;
|
|
ULONG_PTR nBlocks, BlockIndex;
|
|
ULONG BlockSize, RemainingSize;
|
|
PBYTE ptr = (PBYTE)Buffer;
|
|
SIZE_T BytesWritten = 0;
|
|
|
|
if (RtlDosPathNameToNtPathName_U(lpFileName, &NtFileName, NULL, NULL) == FALSE)
|
|
return 0;
|
|
|
|
DesiredAccess = FILE_WRITE_ACCESS | SYNCHRONIZE;
|
|
dwFlag = FILE_OVERWRITE_IF;
|
|
|
|
if (Append != FALSE) {
|
|
DesiredAccess |= FILE_READ_ACCESS;
|
|
dwFlag = FILE_OPEN_IF;
|
|
}
|
|
|
|
InitializeObjectAttributes(&attr, &NtFileName, OBJ_CASE_INSENSITIVE, 0, NULL);
|
|
|
|
__try {
|
|
Status = NtCreateFile(&hFile, DesiredAccess, &attr,
|
|
&IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, dwFlag,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, NULL, 0);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
__leave;
|
|
|
|
pPosition = NULL;
|
|
|
|
if (Append != FALSE) {
|
|
Position.LowPart = FILE_WRITE_TO_END_OF_FILE;
|
|
Position.HighPart = -1;
|
|
pPosition = &Position;
|
|
}
|
|
|
|
if (Size < 0x80000000) {
|
|
BlockSize = (ULONG)Size;
|
|
Status = NtWriteFile(hFile, 0, NULL, NULL, &IoStatus, ptr, BlockSize, pPosition, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
__leave;
|
|
|
|
BytesWritten += IoStatus.Information;
|
|
}
|
|
else {
|
|
BlockSize = 0x7FFFFFFF;
|
|
nBlocks = (Size / BlockSize);
|
|
for (BlockIndex = 0; BlockIndex < nBlocks; BlockIndex++) {
|
|
|
|
Status = NtWriteFile(hFile, 0, NULL, NULL, &IoStatus, ptr, BlockSize, pPosition, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
__leave;
|
|
|
|
ptr += BlockSize;
|
|
BytesWritten += IoStatus.Information;
|
|
}
|
|
RemainingSize = (ULONG)(Size % BlockSize);
|
|
if (RemainingSize != 0) {
|
|
Status = NtWriteFile(hFile, 0, NULL, NULL, &IoStatus, ptr, RemainingSize, pPosition, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
__leave;
|
|
BytesWritten += IoStatus.Information;
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
if (hFile != NULL) {
|
|
if (Flush != FALSE) NtFlushBuffersFile(hFile, &IoStatus);
|
|
NtClose(hFile);
|
|
}
|
|
RtlFreeUnicodeString(&NtFileName);
|
|
}
|
|
return BytesWritten;
|
|
}
|
|
|
|
/*
|
|
* supDetectObjectCallback
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Comparer callback routine used in objects enumeration.
|
|
*
|
|
*/
|
|
NTSTATUS NTAPI supDetectObjectCallback(
|
|
_In_ POBJECT_DIRECTORY_INFORMATION Entry,
|
|
_In_ PVOID CallbackParam
|
|
)
|
|
{
|
|
POBJSCANPARAM Param = (POBJSCANPARAM)CallbackParam;
|
|
|
|
if (Entry == NULL) {
|
|
return STATUS_INVALID_PARAMETER_1;
|
|
}
|
|
|
|
if (CallbackParam == NULL) {
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
|
|
if (Param->Buffer == NULL || Param->BufferSize == 0) {
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
}
|
|
|
|
if (Entry->Name.Buffer) {
|
|
if (_strcmpi_w(Entry->Name.Buffer, Param->Buffer) == 0) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* supEnumSystemObjects
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Lookup object by name in given directory.
|
|
*
|
|
*/
|
|
NTSTATUS NTAPI supEnumSystemObjects(
|
|
_In_opt_ LPWSTR pwszRootDirectory,
|
|
_In_opt_ HANDLE hRootDirectory,
|
|
_In_ PENUMOBJECTSCALLBACK CallbackProc,
|
|
_In_opt_ PVOID CallbackParam
|
|
)
|
|
{
|
|
BOOL cond = TRUE;
|
|
ULONG ctx, rlen;
|
|
HANDLE hDirectory = NULL;
|
|
NTSTATUS status;
|
|
NTSTATUS CallbackStatus;
|
|
OBJECT_ATTRIBUTES attr;
|
|
UNICODE_STRING sname;
|
|
|
|
POBJECT_DIRECTORY_INFORMATION objinf;
|
|
|
|
if (CallbackProc == NULL) {
|
|
return STATUS_INVALID_PARAMETER_4;
|
|
}
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
__try {
|
|
|
|
// We can use root directory.
|
|
if (pwszRootDirectory != NULL) {
|
|
RtlSecureZeroMemory(&sname, sizeof(sname));
|
|
RtlInitUnicodeString(&sname, pwszRootDirectory);
|
|
InitializeObjectAttributes(&attr, &sname, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
status = NtOpenDirectoryObject(&hDirectory, DIRECTORY_QUERY, &attr);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
else {
|
|
if (hRootDirectory == NULL) {
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
hDirectory = hRootDirectory;
|
|
}
|
|
|
|
// Enumerate objects in directory.
|
|
ctx = 0;
|
|
do {
|
|
|
|
rlen = 0;
|
|
status = NtQueryDirectoryObject(hDirectory, NULL, 0, TRUE, FALSE, &ctx, &rlen);
|
|
if (status != STATUS_BUFFER_TOO_SMALL)
|
|
break;
|
|
|
|
objinf = (POBJECT_DIRECTORY_INFORMATION)RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, HEAP_ZERO_MEMORY, rlen);
|
|
if (objinf == NULL)
|
|
break;
|
|
|
|
status = NtQueryDirectoryObject(hDirectory, objinf, rlen, TRUE, FALSE, &ctx, &rlen);
|
|
if (!NT_SUCCESS(status)) {
|
|
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, objinf);
|
|
break;
|
|
}
|
|
|
|
CallbackStatus = CallbackProc(objinf, CallbackParam);
|
|
|
|
RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, objinf);
|
|
|
|
if (NT_SUCCESS(CallbackStatus)) {
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
} while (cond);
|
|
|
|
if (hDirectory != NULL) {
|
|
NtClose(hDirectory);
|
|
}
|
|
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* supIsObjectExists
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Return TRUE if the given object exists, FALSE otherwise.
|
|
*
|
|
*/
|
|
BOOLEAN supIsObjectExists(
|
|
_In_ LPWSTR RootDirectory,
|
|
_In_ LPWSTR ObjectName
|
|
)
|
|
{
|
|
OBJSCANPARAM Param;
|
|
|
|
if (ObjectName == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Param.Buffer = ObjectName;
|
|
Param.BufferSize = (ULONG)_strlen(ObjectName);
|
|
|
|
return NT_SUCCESS(supEnumSystemObjects(RootDirectory, NULL, supDetectObjectCallback, &Param));
|
|
}
|
|
|
|
/*
|
|
* supxStopServiceShowError
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Display Function + LastError message for SCM part, Function limited to MAX_PATH.
|
|
*
|
|
*/
|
|
VOID supxStopServiceShowError(
|
|
_In_ LPWSTR Function,
|
|
_In_ DWORD ErrorCode)
|
|
{
|
|
WCHAR szMessage[300];
|
|
|
|
_strcpy(szMessage, TEXT("SCM: "));
|
|
_strcat(szMessage, Function);
|
|
_strcat(szMessage, TEXT(" failed ("));
|
|
ultostr(ErrorCode, _strend(szMessage));
|
|
_strcat(szMessage, TEXT(")"));
|
|
cuiPrintText(szMessage, TRUE);
|
|
}
|
|
|
|
/*
|
|
* supStopVBoxService
|
|
*
|
|
* Purpose:
|
|
*
|
|
* Stop given VirtualBox service (unload kernel driver).
|
|
*
|
|
*/
|
|
BOOLEAN supStopVBoxService(
|
|
_In_ SC_HANDLE schSCManager,
|
|
_In_ LPWSTR szSvcName //MAX_PATH limit
|
|
)
|
|
{
|
|
BOOLEAN bResult = FALSE;
|
|
|
|
SC_HANDLE schService;
|
|
SERVICE_STATUS_PROCESS ssp;
|
|
DWORD dwStartTime = GetTickCount();
|
|
DWORD dwBytesNeeded;
|
|
DWORD dwTimeout = 30000; //30 seconds timeout for this proc.
|
|
DWORD dwWaitTime;
|
|
DWORD dwLastError;
|
|
|
|
WCHAR szMessage[MAX_PATH * 2];
|
|
|
|
_strcpy(szMessage, TEXT("SCM: Attempt to stop "));
|
|
_strcat(szMessage, szSvcName);
|
|
cuiPrintText(szMessage, TRUE);
|
|
|
|
//
|
|
// Open service, if service does not exist consider this as success and leave.
|
|
//
|
|
schService = OpenService(
|
|
schSCManager,
|
|
szSvcName,
|
|
SERVICE_STOP |
|
|
SERVICE_QUERY_STATUS);
|
|
|
|
if (schService == NULL) {
|
|
dwLastError = GetLastError();
|
|
if (dwLastError == ERROR_SERVICE_DOES_NOT_EXIST) {
|
|
cuiPrintText(TEXT("SCM: Service does not exist, skip"), TRUE);
|
|
return TRUE;
|
|
}
|
|
else {
|
|
supxStopServiceShowError(TEXT("OpenService"), GetLastError());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query service status.
|
|
//
|
|
if (!QueryServiceStatusEx(
|
|
schService,
|
|
SC_STATUS_PROCESS_INFO,
|
|
(LPBYTE)&ssp,
|
|
sizeof(SERVICE_STATUS_PROCESS),
|
|
&dwBytesNeeded))
|
|
{
|
|
supxStopServiceShowError(TEXT("QueryServiceStatusEx"), GetLastError());
|
|
goto stop_cleanup;
|
|
}
|
|
|
|
if (ssp.dwCurrentState == SERVICE_STOPPED) {
|
|
cuiPrintText(TEXT("SCM: Service is already stopped"), TRUE);
|
|
bResult = TRUE;
|
|
goto stop_cleanup;
|
|
}
|
|
|
|
//
|
|
// If service already in stop pending state, wait a little.
|
|
//
|
|
while (ssp.dwCurrentState == SERVICE_STOP_PENDING)
|
|
{
|
|
cuiPrintText(TEXT("SCM: Service stop pending..."), TRUE);
|
|
|
|
dwWaitTime = ssp.dwWaitHint / 10;
|
|
|
|
if (dwWaitTime < 1000)
|
|
dwWaitTime = 1000;
|
|
else if (dwWaitTime > 10000)
|
|
dwWaitTime = 10000;
|
|
|
|
Sleep(dwWaitTime);
|
|
|
|
if (!QueryServiceStatusEx(
|
|
schService,
|
|
SC_STATUS_PROCESS_INFO,
|
|
(LPBYTE)&ssp,
|
|
sizeof(SERVICE_STATUS_PROCESS),
|
|
&dwBytesNeeded))
|
|
{
|
|
supxStopServiceShowError(TEXT("QueryServiceStatusEx"), GetLastError());
|
|
goto stop_cleanup;
|
|
}
|
|
|
|
if (ssp.dwCurrentState == SERVICE_STOPPED)
|
|
{
|
|
cuiPrintText(TEXT("SCM: Service stopped successfully"), TRUE);
|
|
bResult = TRUE;
|
|
goto stop_cleanup;
|
|
}
|
|
|
|
//
|
|
// 30 seconds execution timeout reached.
|
|
//
|
|
if (GetTickCount() - dwStartTime > dwTimeout) {
|
|
cuiPrintText(TEXT("SCM: Service stop timed out.\n"), TRUE);
|
|
goto stop_cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stop service.
|
|
//
|
|
if (!ControlService(
|
|
schService,
|
|
SERVICE_CONTROL_STOP,
|
|
(LPSERVICE_STATUS)&ssp))
|
|
{
|
|
supxStopServiceShowError(TEXT("ControlService"), GetLastError());
|
|
goto stop_cleanup;
|
|
}
|
|
|
|
//
|
|
// Check whatever we need to wait for service stop.
|
|
//
|
|
while (ssp.dwCurrentState != SERVICE_STOPPED)
|
|
{
|
|
Sleep(ssp.dwWaitHint);
|
|
if (!QueryServiceStatusEx(
|
|
schService,
|
|
SC_STATUS_PROCESS_INFO,
|
|
(LPBYTE)&ssp,
|
|
sizeof(SERVICE_STATUS_PROCESS),
|
|
&dwBytesNeeded))
|
|
{
|
|
supxStopServiceShowError(TEXT("QueryServiceStatusEx"), GetLastError());
|
|
goto stop_cleanup;
|
|
}
|
|
|
|
if (ssp.dwCurrentState == SERVICE_STOPPED)
|
|
break;
|
|
|
|
//
|
|
// 30 seconds execution timeout reached.
|
|
//
|
|
if (GetTickCount() - dwStartTime > dwTimeout) {
|
|
cuiPrintText(TEXT("SCM: Wait timed out"), TRUE);
|
|
goto stop_cleanup;
|
|
}
|
|
}
|
|
cuiPrintText(TEXT("SCM: Service stopped successfully"), TRUE);
|
|
bResult = TRUE;
|
|
|
|
stop_cleanup:
|
|
CloseServiceHandle(schService);
|
|
|
|
return bResult;
|
|
}
|