TDL/Source/Furutaka/sup.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;
}