/* * Copyright 2022-2023 David Xanatos, xanasoft.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "framework.h" #include #include #include #include #include #include "../Common/helpers.h" #include "../Common/WebUtils.h" #include "../Common/json/JSON.h" #include "UpdUtil.h" bool GetDriverInfo(DWORD InfoClass, void* pBuffer, size_t Size); extern "C" { NTSTATUS CreateKeyPair(_In_ PCWSTR PrivFile, _In_ PCWSTR PubFile); NTSTATUS SignHash(_In_ PVOID Hash, _In_ ULONG HashSize, _In_ PVOID PrivKey, _In_ ULONG PrivKeySize, _Out_ PVOID* Signature, _Out_ ULONG* SignatureSize); NTSTATUS VerifyHashSignature(PVOID Hash, ULONG HashSize, PVOID Signature, ULONG SignatureSize); NTSTATUS VerifyFileSignature(const wchar_t* FilePath); NTSTATUS MyHashBuffer(_In_ PVOID pData, _In_ SIZE_T uSize, _Out_ PVOID* Hash, _Out_ PULONG HashSize); NTSTATUS MyHashFile(_In_ PCWSTR FileName, _Out_ PVOID* Hash, _Out_ PULONG HashSize); NTSTATUS MyReadFile(_In_ PWSTR FileName, _In_ ULONG FileSizeLimit, _Out_ PVOID* Buffer, _Out_ PULONG FileSize); NTSTATUS MyWriteFile(_In_ PWSTR FileName, _In_ PVOID Buffer, _In_ ULONG BufferSize); size_t b64_encoded_size(size_t inlen); wchar_t* b64_encode(const unsigned char* in, size_t inlen); size_t b64_decoded_size(const wchar_t* in); int b64_decode(const wchar_t* in, unsigned char* out, size_t outlen); } struct SFile { SFile() : State(eNone) {} std::wstring Path; std::wstring Url; std::wstring Hash; enum EState { eNone = 0, eChanged, ePending // downloaded } State; }; struct SFiles { std::map> Map; std::wstring Sign; }; struct SRelease: SFiles { //SRelease() : Status(eLoaded) {} std::wstring Version; int iUpdate; std::wstring CI; //enum EStatus //{ // eLoaded = 0, // eScanned, // ePrepared //} Status; std::wstring AgentArch; std::wstring Framework; }; typedef std::list TScope; struct SAddon : SFiles { std::wstring Id; /*std::wstring Name; std::wstring Icon; std::wstring Description; std::wstring Version; std::wstring InfoUrl;*/ JSONObject Data; bool IsDefault; std::wstring InstallPath; std::wstring Installer; std::wstring UninstallKey; }; typedef std::map> TAddonMap; std::wstring Arch2Str(ULONG architecture) { switch (architecture) { case IMAGE_FILE_MACHINE_ARM64: return L"a64"; case IMAGE_FILE_MACHINE_AMD64: return L"x64"; case IMAGE_FILE_MACHINE_I386: return L"x86"; default: return L""; } } extern "C" { NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformationEx( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, _In_ ULONG InputBufferLength, _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); } ULONG GetSysArch() { USHORT architecture = 0; NTSTATUS status; HANDLE ProcessHandle = GetCurrentProcess(); ULONG bufferLength; SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION* buffer; ULONG returnLength; bufferLength = sizeof(SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION[5]); buffer = (SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION*)malloc(bufferLength); const ULONG SystemSupportedProcessorArchitectures = 181; status = NtQuerySystemInformationEx((SYSTEM_INFORMATION_CLASS)SystemSupportedProcessorArchitectures, &ProcessHandle, sizeof(ProcessHandle), buffer, bufferLength, &returnLength); if (NT_SUCCESS(status)) { for (ULONG i = 0; i < returnLength / sizeof(SYSTEM_SUPPORTED_PROCESSOR_ARCHITECTURES_INFORMATION); i++) { if (buffer[i].Native) { architecture = (USHORT)buffer[i].Machine; break; } } } else // windows 7 fallback { SYSTEM_INFO SystemInfo = {0}; GetNativeSystemInfo(&SystemInfo); switch (SystemInfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: architecture = IMAGE_FILE_MACHINE_AMD64; break; //case PROCESSOR_ARCHITECTURE_ARM: architecture = ; break; case PROCESSOR_ARCHITECTURE_ARM64: architecture = IMAGE_FILE_MACHINE_ARM64; break; //case PROCESSOR_ARCHITECTURE_IA64: architecture = IMAGE_FILE_MACHINE_IA64; break; // itanium case PROCESSOR_ARCHITECTURE_INTEL: architecture = IMAGE_FILE_MACHINE_I386; break; } } free(buffer); return architecture; } std::wstring ReadRegistryStringValue(std::wstring key, const std::wstring& valueName) { auto RootPath = Split2(key, L"\\"); HKEY hKey; if (_wcsicmp(RootPath.first.c_str(), L"HKEY_LOCAL_MACHINE") == 0) hKey = HKEY_LOCAL_MACHINE; else if (_wcsicmp(RootPath.first.c_str(), L"HKEY_CURRENT_USER") == 0) hKey = HKEY_CURRENT_USER; else return L""; if (RegOpenKeyEx(hKey, RootPath.second.c_str(), 0, KEY_READ, &hKey) == ERROR_SUCCESS) { wchar_t valueData[0x1000] = L""; DWORD dataSize = sizeof(valueData); DWORD valueType; RegQueryValueExW(hKey, valueName.c_str(), NULL, &valueType, (LPBYTE)valueData, &dataSize); RegCloseKey(hKey); return valueData; } return L""; } ULONG GetBinaryArch(const std::wstring& file) { ULONG arch = 0; HANDLE hFile = CreateFile(file.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) goto finish; IMAGE_DOS_HEADER dosHeader; DWORD bytesRead; if (!ReadFile(hFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_DOS_HEADER)) goto finish; if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) goto finish; SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN); IMAGE_NT_HEADERS ntHeader; if (!ReadFile(hFile, &ntHeader, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_NT_HEADERS)) goto finish; if (ntHeader.Signature != IMAGE_NT_SIGNATURE) goto finish; arch = ntHeader.FileHeader.Machine; finish: if(hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); return arch; } std::wstring GetFileVersion(const std::wstring& file) { LPVOID versionInfo = NULL; VS_FIXEDFILEINFO *fixedFileInfo = NULL; UINT fixedFileInfoSize; DWORD versionHandle; DWORD versionSize = GetFileVersionInfoSizeW(file.c_str(), &versionHandle); if (versionSize == 0) goto finish; versionInfo = malloc(versionSize); if (!GetFileVersionInfo(file.c_str(), versionHandle, versionSize, versionInfo)) goto finish; if (!VerQueryValue(versionInfo, L"\\", (LPVOID *)&fixedFileInfo, &fixedFileInfoSize)) goto finish; finish: std::wstring version; if (fixedFileInfo) { DWORD fileVersionMS = fixedFileInfo->dwFileVersionMS; DWORD fileVersionLS = fixedFileInfo->dwFileVersionLS; WORD majorVersion = HIWORD(fileVersionMS); WORD minorVersion = LOWORD(fileVersionMS); WORD buildNumber = HIWORD(fileVersionLS); WORD revisionNumber = LOWORD(fileVersionLS); version = std::to_wstring(majorVersion) + L"." + std::to_wstring(minorVersion) + L"." + std::to_wstring(buildNumber); if(revisionNumber) version += L"." + std::to_wstring(revisionNumber); } if (versionInfo)free(versionInfo); return version; } void CreateDirectoryTree(const std::wstring& root, const std::wstring& path) { wchar_t* pathCopy = _wcsdup(path.c_str()); wchar_t* token = _wcstok(pathCopy, L"\\"); wchar_t currentPath[MAX_PATH] = L""; wcscpy(currentPath, root.c_str()); for (; token != NULL;) { wcscat(currentPath, L"\\"); wcscat(currentPath, token); if (CreateDirectoryW(currentPath, NULL) == 0) { if (GetLastError() != ERROR_ALREADY_EXISTS) { printf("Error creating directory %S\n", currentPath); } } token = _wcstok(NULL, L"\\"); } free(pathCopy); } std::pair SplitName(std::wstring path) { std::wstring name; size_t pos = path.find_last_of(L"\\"); if (pos == -1) { name = path; path.clear(); } else { name = path.substr(pos + 1); path.erase(pos); path = L"\\" + path; } return std::make_pair(path, name); } bool DeleteDirectoryRecursively(const std::wstring& root, const std::wstring& path, bool bWithFiles) { std::vector Entries; ListDir(root + path, Entries); for (int i = 0; i < Entries.size(); i++) { auto path_name = SplitName(Entries[i]); if (path_name.second.empty()) { if (!DeleteDirectoryRecursively(Entries[i], L"", bWithFiles)) return false; } else if (bWithFiles) { if (!DeleteFileW(Entries[i].c_str())) return false; } else return false; } return !!RemoveDirectoryW((root + path).c_str()); } std::shared_ptr ScanDir(std::wstring Path) { if (Path.back() != L'\\') Path.push_back(L'\\'); std::shared_ptr pFiles = std::make_shared(); std::vector Entries; Entries.push_back(Path); for (int i = 0; i < Entries.size(); i++) { if (Entries[i].back() == '\\') { ListDir(Entries[i], Entries); continue; } ULONG hashSize; PVOID hash = NULL; if (NT_SUCCESS(MyHashFile(Entries[i].c_str(), &hash, &hashSize))) { std::shared_ptr pFile = std::make_shared(); pFile->Path = Entries[i].substr(Path.length()); pFile->Hash = hexStr((unsigned char*)hash, hashSize); pFiles->Map[pFile->Path] = pFile; free(hash); } } return pFiles; } std::wstring GetJSONStringSafe(const JSONObject& root, const std::wstring& key, const std::wstring& def = L"") { auto I = root.find(key); if (I == root.end() || !I->second->IsString()) return def; return I->second->AsString(); } int GetJSONIntSafe(const JSONObject& root, const std::wstring& key, int def = 0) { auto I = root.find(key); if (I == root.end() || !I->second->IsNumber()) return def; return (int)I->second->AsNumber(); } int GetJSONBoolSafe(const JSONObject& root, const std::wstring& key, bool def = false) { auto I = root.find(key); if (I == root.end() || !I->second->IsBool()) return def; return I->second->AsBool(); } JSONObject GetJSONObjectSafe(const JSONObject& root, const std::wstring& key) { auto I = root.find(key); if (I == root.end() || !I->second->IsObject()) return JSONObject(); return I->second->AsObject(); } JSONArray GetJSONArraySafe(const JSONObject& root, const std::wstring& key) { auto I = root.find(key); if (I == root.end() || !I->second->IsArray()) return JSONArray(); return I->second->AsArray(); } std::string WriteUpdate(std::shared_ptr pFiles) { JSONObject root; JSONArray files; for (auto I = pFiles->Map.begin(); I != pFiles->Map.end(); ++I) { JSONObject file; file[L"path"] = new JSONValue(I->second->Path); file[L"hash"] = new JSONValue(I->second->Hash); file[L"url"] = new JSONValue(I->second->Url); //if(I->second->State == SFile::eChanged) // file[L"state"] = new JSONValue(L"changed"); //else if(I->second->State == SFile::ePending) // file[L"state"] = new JSONValue(L"pending"); files.push_back(new JSONValue(file)); } root[L"files"] = new JSONValue(files); root[L"signature"] = new JSONValue(pFiles->Sign); root[L"version"] = new JSONValue(pFiles->Version); root[L"update"] = new JSONValue(pFiles->iUpdate); root[L"ci"] = new JSONValue(pFiles->CI); //if(pFiles->Status == SRelease::eScanned) // root[L"status"] = new JSONValue(L"scanned"); //else if(pFiles->Status == SRelease::ePrepared) // root[L"status"] = new JSONValue(L"prepared"); root[L"framework"] = new JSONValue(pFiles->Framework); root[L"agent_arch"] = new JSONValue(pFiles->AgentArch); JSONValue *value = new JSONValue(root); auto wJson = value->Stringify(); delete value; return g_str_conv.to_bytes(wJson); } void ReadFiles(const JSONArray& jsonFiles, std::shared_ptr pFiles) { for (auto I = jsonFiles.begin(); I != jsonFiles.end(); ++I) { if ((*I)->IsObject()) { JSONObject jsonFile = (*I)->AsObject(); std::shared_ptr pFile = std::make_shared(); pFile->Path = GetJSONStringSafe(jsonFile, L"path"); pFile->Hash = GetJSONStringSafe(jsonFile, L"hash"); pFile->Url = GetJSONStringSafe(jsonFile, L"url"); //std::wstring state = GetJSONStringSafe(jsonObject, L"state"); //if(state == L"changed") // pFile->State = SFile::eChanged; //else if(state == L"pending") // pFile->State = SFile::ePending; pFiles->Map[pFile->Path] = pFile; } } } std::shared_ptr ReadUpdate(const JSONObject& jsonObject) { if (jsonObject.find(L"files") == jsonObject.end()) return std::shared_ptr(); std::shared_ptr pFiles = std::make_shared(); JSONArray jsonFiles = GetJSONArraySafe(jsonObject, L"files"); ReadFiles(jsonFiles, pFiles); pFiles->Sign = GetJSONStringSafe(jsonObject, L"signature"); pFiles->Version = GetJSONStringSafe(jsonObject, L"version"); pFiles->iUpdate = GetJSONIntSafe(jsonObject, L"update", -1); pFiles->CI = GetJSONStringSafe(jsonObject, L"ci"); std::wstring status = GetJSONStringSafe(jsonObject, L"status"); //if(status == L"scanned") // pFiles->Status = SRelease::eScanned; //else if(status == L"prepared") // pFiles->Status = SRelease::ePrepared; pFiles->Framework = GetJSONStringSafe(jsonObject, L"framework"); pFiles->AgentArch = GetJSONStringSafe(jsonObject, L"agent_arch"); return pFiles; } bool VerifyUpdate(std::shared_ptr pFiles) { bool pass = false; std::set hash_set; for (auto I = pFiles->Map.begin(); I != pFiles->Map.end(); ++I) hash_set.insert(I->second->Path + L":" + I->second->Hash); std::string hashes; for (auto I = hash_set.begin(); I != hash_set.end(); ++I) hashes += g_str_conv.to_bytes(*I) + "\n"; ULONG hashSize; PVOID hash = NULL; if (NT_SUCCESS(MyHashBuffer((void*)hashes.c_str(), hashes.length(), &hash, &hashSize))) { ULONG signatureSize = b64_decoded_size(pFiles->Sign.c_str()); if (signatureSize) { PUCHAR signature = (PUCHAR)malloc(signatureSize); b64_decode(pFiles->Sign.c_str(), signature, signatureSize); if (NT_SUCCESS(VerifyHashSignature((PUCHAR)hash, hashSize, (PUCHAR)signature, signatureSize))) { pass = true; } free(signature); } free(hash); } return pass; } void AddFilesToScope(std::shared_ptr pScope, const WCHAR* pFiles) { for (const WCHAR* pFile = pFiles; *pFile; pFile += wcslen(pFile) + 1) pScope->push_back(pFile); } // full|core|meta|lang|tmpl std::shared_ptr GetScope(std::wstring scope) { std::shared_ptr pScope = std::make_shared(); //if (scope == L"full") // everything if (scope == L"core" || scope == L"full") // core AddFilesToScope(pScope, SCOPE_CORE_FILES); if (scope == L"lang" || scope == L"meta" || scope == L"core" || scope == L"full") AddFilesToScope(pScope, SCOPE_LANG_FILES); if (scope == L"tmpl" || scope == L"meta" || scope == L"core" || scope == L"full") AddFilesToScope(pScope, SCOPE_TMPL_FILES); // when there are no entries it means everything if (pScope->empty()) pScope.reset(); return pScope; } bool InScope(std::shared_ptr pScope, std::wstring name) { if (!pScope) return true; auto len = name.length(); if (len > 4 && name.substr(len - 4) == L".sig") name = name.substr(0, len - 4); for (auto I = pScope->begin(); I != pScope->end(); ++I) { if (wildstrcmp(I->c_str(), name.c_str()) != NULL) return true; } return false; } int FindChanges(std::shared_ptr pNewFiles, std::wstring base_dir, std::wstring temp_dir, std::shared_ptr pScope) { std::shared_ptr pOldFiles = ScanDir(base_dir); if (!pOldFiles) return ERROR_SCAN; int Count = 0; for (auto I = pNewFiles->Map.begin(); I != pNewFiles->Map.end(); ++I) { I->second->State = SFile::eNone; if (!InScope(pScope, I->second->Path)) continue; auto J = pOldFiles->Map.find(I->first); if (J == pOldFiles->Map.end() || J->second->Hash != I->second->Hash) { I->second->State = SFile::eChanged; Count++; } } return Count; } BOOLEAN WebDownload(std::wstring url, PSTR* pData, ULONG* pDataLength) { size_t pos = url.find_first_of(L'/', 8); if (pos == std::wstring::npos) return FALSE; std::wstring path = url.substr(pos); std::wstring domain = url.substr(8, pos-8); return WebDownload(domain.c_str(), path.c_str(), pData, pDataLength); } int DownloadUpdate(std::wstring temp_dir, std::shared_ptr pNewFiles) { std::wcout << L"Downloading" << std::endl; int Count = 0; for (auto I = pNewFiles->Map.begin(); I != pNewFiles->Map.end(); ++I) { if (I->second->State != SFile::eChanged) continue; Count++; auto path_name = SplitName(I->second->Path); if (!path_name.first.empty()) CreateDirectoryTree(temp_dir, path_name.first); ULONG hashSize; PVOID hash = NULL; if (NT_SUCCESS(MyHashFile((wchar_t*)(temp_dir + L"\\" + I->second->Path).c_str(), &hash, &hashSize))) { std::wstring Hash = hexStr((unsigned char*)hash, hashSize); free(hash); if (I->second->Hash == Hash) { I->second->State = SFile::ePending; continue; // already downloaded and up to date } } std::wcout << L"\tDownloading: " << I->second->Path << L" ..."; char* pData = NULL; ULONG uDataLen = 0; if (WebDownload(I->second->Url, &pData, &uDataLen)) { ULONG hashSize; PVOID hash = NULL; if(NT_SUCCESS(MyHashBuffer(pData, uDataLen, &hash, &hashSize))) { std::wstring Hash = hexStr((unsigned char*)hash, hashSize); free(hash); if (I->second->Hash != Hash) { free(pData); std::wcout << L" BAD!!!" << std::endl; return ERROR_HASH; } MyWriteFile((wchar_t*)(temp_dir + L"\\" + I->second->Path).c_str(), pData, uDataLen); I->second->State = SFile::ePending; } free(pData); std::wcout << L" done" << std::endl; } else { std::wcout << L" FAILED" << std::endl; return ERROR_DOWNLOAD; } } return Count; } int ApplyUpdate(std::wstring base_dir, std::wstring temp_dir, std::shared_ptr pNewFiles) { std::wcout << L"Applying updates" << std::endl; int Count = 0; for (auto I = pNewFiles->Map.begin(); I != pNewFiles->Map.end(); ++I) { if (I->second->State != SFile::ePending) continue; if(_wcsicmp(I->second->Path.c_str(), L"UpdUtil.exe") == 0) continue; // don't try overwriting ourselves Count++; auto path_name = SplitName(I->second->Path); std::wcout << L"\tInstalling: " << I->second->Path << L" ..."; std::wstring src = temp_dir + L"\\" + I->second->Path; std::wstring dest = base_dir + L"\\" + I->second->Path; if (!path_name.first.empty()) CreateDirectoryTree(base_dir, path_name.first); if (MoveFileExW(src.c_str(), dest.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) { // inherit parent folder permissions ACL g_null_acl = { 0 }; InitializeAcl(&g_null_acl, sizeof(g_null_acl), ACL_REVISION); DWORD error = SetNamedSecurityInfoW((wchar_t*)dest.c_str(), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, (PACL)&g_null_acl, NULL); std::wcout << L" done" << std::endl; } else std::wcout << L" FAILED" << std::endl; } //std::wstring src = temp_dir + L"\\" _T(UPDATE_FILE); //std::wstring dest = base_dir + L"\\" _T(UPDATE_FILE); //MoveFileExW(src.c_str(), dest.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED); return Count; } void Execute(std::wstring wFile, std::wstring wParams) { SHELLEXECUTEINFO si = { sizeof(SHELLEXECUTEINFO) }; si.fMask = SEE_MASK_NOCLOSEPROCESS; si.lpVerb = L"runas"; si.lpFile = wFile.c_str(); si.lpParameters = wParams.c_str(); si.nShow = SW_HIDE; std::wcout << L"KmdUtil.exe " << si.lpParameters << std::endl; if (ShellExecuteEx(&si)) { WaitForSingleObject(si.hProcess, INFINITE); CloseHandle(si.hProcess); } } int ProcessUpdate(std::shared_ptr& pFiles, const std::wstring& step, const std::wstring& temp_dir, const std::wstring& base_dir, const std::wstring& scope) { int ret = 0; if (!pFiles || pFiles->Map.empty()) return ERROR_INTERNAL; //if (step.empty() || step == L"scan" || pFiles->Status < SRelease::eScanned) if (step.empty() || step == L"scan" || step == L"prepare" || step == L"apply") { std::shared_ptr pScope; if (!scope.empty()) pScope = GetScope(scope); ret = FindChanges(pFiles, base_dir, temp_dir, pScope); //pFiles->Status = SRelease::eScanned; if (ret <= 0) return ret; // error or nothing todo if (step == L"scan") return ret; } //if (step.empty() || step == L"prepare" || pFiles->Status < SRelease::ePrepared) if (step.empty() || step == L"prepare" || step == L"apply") { ret = DownloadUpdate(temp_dir, pFiles); //pFiles->Status = SRelease::ePrepared; if (ret <= 0) return ret; // error or nothing todo if (step == L"prepare") return ret; } return ret; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // addon stuff // std::wstring GetSpecificEntry(const JSONObject& root, const std::wstring& name, const std::wstring& core_arch, const std::wstring& agent_arch, const std::wstring& framework) { if (!framework.empty() && root.find(name + L"-" + framework) != root.end()) return name + L"-" + framework; std::wstring match = GetJSONStringSafe(root, L"matchArch"); std::wstring arch = (match != L"agent") ? core_arch : agent_arch; if (!arch.empty() && root.find(name + L"-" + arch) != root.end()) return name + L"-" + arch; return name; } std::wstring GetSpecificEntryValue(const JSONObject& root, const std::wstring& name, const std::wstring& core_arch, const std::wstring& agent_arch, const std::wstring& framework) { std::wstring entry = GetSpecificEntry(root, name, core_arch, agent_arch, framework); return GetJSONStringSafe(root, entry); } std::shared_ptr ReadAddon(const JSONObject& addon, const std::wstring& core_arch, const std::wstring& agent_arch, const std::wstring& framework) { std::shared_ptr pAddon = std::make_shared(); pAddon->Id = GetJSONStringSafe(addon, L"id"); std::wstring entry = GetSpecificEntry(addon, L"files", core_arch, agent_arch, framework); JSONArray jsonFiles = GetJSONArraySafe(addon, entry); ReadFiles(jsonFiles, pAddon); pAddon->Sign = GetJSONStringSafe(addon, entry + L"-sig"); /*pAddon->Name = GetJSONStringSafe(addon, L"name"); pAddon->Icon = GetJSONStringSafe(addon, L"icon"); pAddon->Description = GetJSONStringSafe(addon, L"description"); pAddon->Version = GetJSONStringSafe(addon, L"version"); pAddon->InfoUrl = GetJSONStringSafe(addon, L"info_url");*/ pAddon->IsDefault = GetJSONBoolSafe(addon, L"default"); pAddon->InstallPath = GetSpecificEntryValue(addon, L"installPath", core_arch, agent_arch, framework); pAddon->Installer = GetSpecificEntryValue(addon, L"installer", core_arch, agent_arch, framework); pAddon->UninstallKey = GetSpecificEntryValue(addon, L"uninstallKey", core_arch, agent_arch, framework); for (auto I = addon.begin(); I != addon.end(); ++I) { if (I->first.find(L'-') != std::wstring::npos) continue; // skip all entries containing "-" pAddon->Data.insert(*I); } return pAddon; } JSONObject WriteAddon(std::shared_ptr pAddon) { JSONObject addon; addon[L"id"] = new JSONValue(pAddon->Id); JSONArray files; for (auto J = pAddon->Map.begin(); J != pAddon->Map.end(); ++J) { JSONObject file; file[L"path"] = new JSONValue(J->second->Path); file[L"hash"] = new JSONValue(J->second->Hash); file[L"url"] = new JSONValue(J->second->Url); files.push_back(new JSONValue(file)); } if (!files.empty()) { addon[L"files"] = new JSONValue(files); addon[L"files-sig"] = new JSONValue(pAddon->Sign); } /*addon[L"name"] = new JSONValue(pAddon->Name); addon[L"icon"] = new JSONValue(pAddon->Icon); addon[L"description"] = new JSONValue(pAddon->Description); addon[L"version"] = new JSONValue(pAddon->Version); addon[L"info_url"] = new JSONValue(pAddon->InfoUrl);*/ addon[L"default"] = new JSONValue(pAddon->IsDefault); if (!pAddon->InstallPath.empty()) addon[L"installPath"] = new JSONValue(pAddon->InstallPath); if (!pAddon->Installer.empty()) addon[L"installer"] = new JSONValue(pAddon->Installer); if (!pAddon->UninstallKey.empty()) addon[L"uninstallKey"] = new JSONValue(pAddon->UninstallKey); for (auto I = pAddon->Data.begin(); I != pAddon->Data.end(); ++I) { addon.insert(*I); } return addon; } std::shared_ptr ReadAddons(const JSONObject& jsonObject, const std::wstring& core_arch, const std::wstring& agent_arch, const std::wstring& framework) { if (jsonObject.find(L"list") == jsonObject.end()) return std::shared_ptr(); std::shared_ptr pAddons = std::make_shared(); JSONArray list = GetJSONArraySafe(jsonObject, L"list"); for (auto I = list.begin(); I != list.end(); ++I) { if ((*I)->IsObject()) { JSONObject addon = (*I)->AsObject(); std::shared_ptr pAddon = ReadAddon(addon, core_arch, agent_arch, framework); (*pAddons)[MkLower(pAddon->Id)] = pAddon; } } return pAddons; } std::string WriteAddons(std::shared_ptr pAddons) { JSONObject root; JSONArray list; for (auto I = pAddons->begin(); I != pAddons->end(); ++I) { list.push_back(new JSONValue(WriteAddon(I->second))); } root[L"list"] = new JSONValue(list); JSONValue *value = new JSONValue(root); auto wJson = value->Stringify(); delete value; return g_str_conv.to_bytes(wJson); } bool VerifyAddons(std::shared_ptr pAddons) { for (auto I = pAddons->begin(); I != pAddons->end(); ++I) { if (I->second->Map.empty()) continue; if (!VerifyUpdate(I->second)) return false; } return true; } int DownloadAddon(std::shared_ptr pAddon, const std::wstring& step, const std::wstring& temp_dir, const std::wstring& base_dir) { int ret = 0; if (!pAddon || pAddon->Map.empty()) { std::wcout << L"Addon is not available for this platform" << std::endl; return ERROR_NO_ADDON2; } // always mark all files for download for (auto I = pAddon->Map.begin(); I != pAddon->Map.end(); ++I) I->second->State = SFile::eChanged; //if (step.empty() || step == L"prepare" || pFiles->Status < SRelease::ePrepared) if (step.empty() || step == L"prepare" || step == L"apply") { CreateDirectoryTree(temp_dir, pAddon->Id); ret = DownloadUpdate(temp_dir + L"\\" + pAddon->Id, pAddon); //pFiles->Status = SRelease::ePrepared; if (ret <= 0) return ret; // error or nothing todo if (step == L"prepare") return ret; } return ret; } int InstallAddon(std::shared_ptr pAddon, const std::wstring& temp_dir, const std::wstring& base_dir) { int ret = 0; if (!pAddon->Installer.empty() && FileExists((temp_dir + L"\\" + pAddon->Id + pAddon->Installer).c_str())) { LPWCH environmentStrings = GetEnvironmentStrings(); DWORD environmentLen = 0; for (LPWCH current = environmentStrings; *current; current += wcslen(current) + 1) environmentLen += wcslen(current) + 1; LPWCH modifiedEnvironment = (LPWCH)LocalAlloc(0, (environmentLen + 32 + base_dir.length() + 1 + 1) * sizeof(wchar_t)); memcpy(modifiedEnvironment, environmentStrings, (environmentLen + 1) * sizeof(wchar_t)); FreeEnvironmentStrings(environmentStrings); LPWCH modifiedEnvironmentEnd = modifiedEnvironment + environmentLen; wcscpy(modifiedEnvironmentEnd, L"SBIEHOME="); wcscat(modifiedEnvironmentEnd, base_dir.c_str()); modifiedEnvironmentEnd += wcslen(modifiedEnvironmentEnd) + 1; *modifiedEnvironmentEnd = 0; STARTUPINFO si = { sizeof(si), 0 }; PROCESS_INFORMATION pi = { 0 }; std::wstring cmdLine = temp_dir + L"\\" + pAddon->Id + pAddon->Installer; if (CreateProcessW(NULL, (wchar_t*)cmdLine.c_str(), NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, modifiedEnvironment, NULL, &si, &pi)) { while (WaitForSingleObject(pi.hProcess, 1000) == WAIT_TIMEOUT); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else ret = ERROR_BAD_ADDON2; LocalFree(modifiedEnvironment); if (ret >= 0 && !pAddon->UninstallKey.empty()) { std::wstring cmd = ReadRegistryStringValue(pAddon->UninstallKey, L"UninstallString"); if (cmd.empty()) // when the expected uninstall key is not present, ret = ERROR_BAD_ADDON2; // it means the installation failed } return ret; } if (pAddon->InstallPath.empty()) return ERROR_BAD_ADDON; // install addon CreateDirectoryTree(base_dir, pAddon->InstallPath); ret = ApplyUpdate(base_dir + pAddon->InstallPath, temp_dir + L"\\" + pAddon->Id, pAddon); return ret; } std::shared_ptr LoadAddon(const std::wstring& base_dir, const std::wstring& id) { std::shared_ptr pAddon; char* aJson = NULL; std::wstring file_path = base_dir + _T(ADDONS_PATH) + id + L".json"; if (NT_SUCCESS(MyReadFile((wchar_t*)file_path.c_str(), 1024 * 1024, (PVOID*)&aJson, NULL)) && aJson != NULL) { JSONValue* jsonObject = JSON::Parse(aJson); if (jsonObject) { if (jsonObject->IsObject()) pAddon = ReadAddon(jsonObject->AsObject(), L"", L"", L""); delete jsonObject; } free(aJson); } return pAddon; } int RemoveAddon(std::shared_ptr pAddon, const std::wstring& base_dir) { int ret = 0; if (!pAddon->UninstallKey.empty()) { std::wstring cmdLine = ReadRegistryStringValue(pAddon->UninstallKey, L"UninstallString"); if(cmdLine.empty()) // when the expected uninstall key is not present, return ret; // then it seems the addon was already uninstalled STARTUPINFO si = { sizeof(si), 0 }; PROCESS_INFORMATION pi = { 0 }; if (CreateProcessW(NULL, (wchar_t*)cmdLine.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { while (WaitForSingleObject(pi.hProcess, 1000) == WAIT_TIMEOUT); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else ret = ERROR_BAD_ADDON2; if (ret >= 0) { std::wstring cmd = ReadRegistryStringValue(pAddon->UninstallKey, L"UninstallString"); if (!cmd.empty()) // when the expected uninstall key is still present, ret = ERROR_BAD_ADDON2; // it means the uninstallation failed } return ret; } if (pAddon->InstallPath.empty()) return ERROR_BAD_ADDON; for (auto I = pAddon->Map.begin(); I != pAddon->Map.end(); ++I) { std::wstring file = base_dir + pAddon->InstallPath + I->second->Path; if (!DeleteFileW(file.c_str()) && FileExists(file.c_str())) { ret = ERROR_DELETE; break; } } if (ret >= 0 && pAddon->InstallPath != L"\\") DeleteDirectoryRecursively(base_dir, pAddon->InstallPath, false); return ret; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // other stuff // int DownloadFile(std::wstring url, std::wstring file_path) { char* pData = NULL; ULONG uDataLen = 0; if (WebDownload(url, &pData, &uDataLen)) { MyWriteFile((wchar_t*)file_path.c_str(), pData, uDataLen); free(pData); std::wcout << L" done" << std::endl; return 0; } std::wcout << L" FAILED" << std::endl; return ERROR_DOWNLOAD; } int PrintFile(std::wstring url) { char* pData = NULL; ULONG uDataLen = 0; if (WebDownload(url, &pData, &uDataLen)) { std::wcout << pData; free(pData); return 0; } return ERROR_DOWNLOAD; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // void PrintUsage() { std::wcout << L"Sandboxie Update Utility - Usage" << std::endl; std::wcout << L"================================" << std::endl; std::wcout << std::endl; std::wcout << L"Update to the latest version in a given release channel" << std::endl; std::wcout << L"\tUpdUtil.exe [update|upgrade] software (/channel:[stable|preview|live]) {Options}" << std::endl; std::wcout << std::endl; std::wcout << L"Update to the latest update of a specific version" << std::endl; std::wcout << L"\tUpdUtil.exe [update|upgrade] software (/version:[a.bb.c]) (/update:[d]) {Options}" << std::endl; std::wcout << std::endl; std::wcout << L"Options:" << std::endl; //std::wcout << L"\t/arch:[ARM64|a64|x86_64|x64|i386|x86]" << std::endl; std::wcout << L"\t/scope:[full|core|meta|lang|tmpl]" << std::endl; std::wcout << L"\t\tfull - update all components" << std::endl; std::wcout << L"\t\tcore - core components (for classic, same as full)" << std::endl; std::wcout << L"\t\tmeta - update metadata (lang and tmpl)" << std::endl; std::wcout << L"\t\tlang - update language files" << std::endl; std::wcout << L"\t\ttmpl - update Templates.ini" << std::endl; std::wcout << L"\t/step:[get|scan|prepare|apply]" << std::endl; std::wcout << L"\t\tget - download updated information to " _T(UPDATE_FILE) << std::endl; std::wcout << L"\t\tscan - check for updates, use existing " _T(UPDATE_FILE) " if present" << std::endl; std::wcout << L"\t\tprepare - download updates, but don't install" << std::endl; std::wcout << L"\t\tapply - install updates" << std::endl; std::wcout << L"" << std::endl; } bool HasFlag(const std::vector& arguments, std::wstring name) { return std::find(arguments.begin(), arguments.end(), L"/" + name) != arguments.end(); } std::wstring GetArgument(const std::vector& arguments, std::wstring name, std::wstring mod = L"/") { std::wstring prefix = mod + name + L":"; for (size_t i = 0; i < arguments.size(); i++) { if (_wcsicmp(arguments[i].substr(0, prefix.length()).c_str(), prefix.c_str()) == 0) { return arguments[i].substr(prefix.length()); } } return L""; } int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { InitOsVersionInfo(); wchar_t szPath[MAX_PATH]; GetModuleFileNameW(NULL, szPath, ARRAYSIZE(szPath)); *wcsrchr(szPath, L'\\') = L'\0'; std::wstring wPath = szPath; // install sandboxie-plus /version:1.6.1 /path:C:\Projects\Sandboxie\SandboxieLive\x64\Debug\Sandboxie_test int nArgs = 0; LPWSTR* szArglist = CommandLineToArgvW(lpCmdLine, &nArgs); // GetCommandLineW() std::vector arguments; for (int i = 0; i < nArgs; i++) arguments.push_back(szArglist[i]); LocalFree(szArglist); if (!HasFlag(arguments, L"embedded")) { if (AttachConsole(ATTACH_PARENT_PROCESS) == FALSE) AllocConsole(); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); if (HasFlag(arguments, L"pause")) { std::cout << "Sandboxie Update Utility" << std::endl; std::wcout << lpCmdLine << std::endl; std::cout << std::endl << "Press enter to continue..." << std::endl; std::cin.get(); } } std::wstring temp_dir = GetArgument(arguments, L"temp"); if (temp_dir.empty()) { wchar_t szTemp[MAX_PATH]; GetTempPath(MAX_PATH, (LPWSTR)&szTemp); temp_dir = std::wstring(szTemp) + L"sandboxie-updater"; } else if (temp_dir.back() == L'\\') temp_dir.pop_back(); std::wstring base_dir = GetArgument(arguments, L"path"); if (base_dir.empty()) base_dir = wPath; std::wstring arch = GetArgument(arguments, L"arch"); if (!arch.empty()) { // normalize architecture if (arch == L"x86_64") arch = L"x64"; else if (arch == L"i386") arch = L"x86"; else if (arch == L"ARM64" || arch == L"A64" || arch == L"arm64") arch = L"a64"; } else arch = Arch2Str(GetSysArch()); if (arguments.size() >= 2 && arguments[0] == L"download") // download file to disk { std::wstring url = arguments[1]; std::wstring name = GetArgument(arguments, L"name"); if (name.empty()) { size_t end = url.find_last_of(L'?'); size_t pos = url.find_last_of(L'/', end); if (pos == std::wstring::npos) return ERROR_INVALID; name = url.substr(++pos, end - pos); } return DownloadFile(url, temp_dir + L"\\" + name); } else if (arguments.size() >= 2 && arguments[0] == L"print") // download file and print to stdout { std::wstring url = arguments[1]; return PrintFile(url); } else if (arguments.size() >= 2 && arguments[0] == L"run_setup") // run a signed setup file { std::wstring wFile = arguments[1]; if(!NT_SUCCESS(VerifyFileSignature(wFile.c_str()))) return ERROR_SIGN; std::wstring wParams; wParams = L"/open_agent"; if (HasFlag(arguments, L"embedded")) wParams = L" /SILENT"; SHELLEXECUTEINFO si = { sizeof(SHELLEXECUTEINFO) }; //si.lpVerb = L"runas"; si.lpFile = wFile.c_str(); si.lpParameters = wParams.c_str(); si.nShow = SW_SHOW; if (!ShellExecuteEx(&si)) { //DWORD dwError = GetLastError(); //if (dwError == ERROR_CANCELLED) // return ERROR_CANCELED; return ERROR_EXEC; } return 0; } else if ((arguments.size() >= 2 && (arguments[0] == L"update" || arguments[0] == L"upgrade" || arguments[0] == L"install")) || (arguments.size() >= 1 && arguments[0] == L"modify")) { int ret = 0; bool bModify = arguments[0] == L"modify"; bool bDoAddons = (bModify || arguments[0] == L"install"); std::wstring software = bModify ? L"sandboxie-addons" : arguments[1]; std::shared_ptr pFiles; JSONValue* jsonAddons = NULL; auto add_addons = SplitStr(GetArgument(arguments, L"add", L""), L",", false); auto remove_addons = SplitStr(GetArgument(arguments, L"remove", L""), L",", false); std::wstring step = GetArgument(arguments, L"step"); // // Prepare update data, load from file or download // bool bSave = false; if (!step.empty() && step != L"get") // load from file { if (!bModify) { char* aJson = NULL; std::wstring file_path = temp_dir + L"\\" _T(UPDATE_FILE); if (NT_SUCCESS(MyReadFile((wchar_t*)file_path.c_str(), 1024 * 1024, (PVOID*)&aJson, NULL)) && aJson != NULL) { JSONValue* jsonObject = JSON::Parse(aJson); if (jsonObject) { if (jsonObject->IsObject()) pFiles = ReadUpdate(jsonObject->AsObject()); delete jsonObject; } free(aJson); } if (!pFiles) { std::wcout << L"No pending update found!" << std::endl; return ERROR_LOAD; } } if ((bModify && !add_addons.empty()) || arguments[0] == L"install") { char* aJson = NULL; std::wstring file_path = temp_dir + L"\\" _T(ADDONS_FILE); if (NT_SUCCESS(MyReadFile((wchar_t*)file_path.c_str(), 1024 * 1024, (PVOID*)&aJson, NULL)) && aJson != NULL) { JSONValue* jsonObject = JSON::Parse(aJson); if (jsonObject) { if (jsonObject->IsObject()) jsonAddons = jsonObject; } free(aJson); } if (!jsonAddons && arguments[0] != L"install") { // only fail here when we want to add addons std::wcout << L"No addons found!" << std::endl; return ERROR_LOAD; } } } else // load from server { std::wstringstream params; params << L"&action=" << arguments[0]; std::wstring channel; std::wstring version; if (!bModify) { channel = GetArgument(arguments, L"channel"); if (channel.empty()) channel = GetArgument(arguments, L"release"); if (!channel.empty()) params << L"&channel=" << channel; version = GetArgument(arguments, L"version"); if (!version.empty()) { params << L"&version=" << version; std::wstring update = GetArgument(arguments, L"update"); if (!update.empty()) params << L"&update=" << update; } // version or channel must be specified if (version.empty() && channel.empty()) return ERROR_INVALID; } params << L"&system=windows-" << g_osvi.dwMajorVersion << L"." << g_osvi.dwMinorVersion << L"." << g_osvi.dwBuildNumber << "-" << arch; wchar_t StrLang[16]; LCIDToLocaleName(GetUserDefaultLCID(), StrLang, ARRAYSIZE(StrLang), 0); if (StrLang[2] == L'-') StrLang[2] = '_'; params << L"&language=" << StrLang; std::wstring update_key = GetArgument(arguments, L"update_key"); if (!update_key.empty()) params << L"&update_key=" + update_key; CreateDirectoryW(temp_dir.c_str(), NULL); char* aJson = NULL; std::wstring path = L"/update.php?software=" + software + params.str(); if (WebDownload(_T(UPDATE_DOMAIN), path.c_str(), &aJson, NULL) && aJson != NULL) { JSONValue* jsonObject = JSON::Parse(aJson); if (jsonObject) { if (jsonObject->IsObject()) { JSONObject root = jsonObject->AsObject(); if (!bModify) { JSONObject update; if (!version.empty() && channel.empty()) update = GetJSONObjectSafe(root, L"update"); else update = GetJSONObjectSafe(root, L"release"); pFiles = ReadUpdate(update); } if (bDoAddons) { auto I = root.find(L"addons"); if (I != root.end() && I->second->IsObject()) { jsonAddons = I->second; root.erase(I); } } } delete jsonObject; } free(aJson); if (!bModify && !pFiles) { std::wcout << L"No update found !!!" << std::endl; return ERROR_GET; } } ret = 0; bSave = true; } if (pFiles && !VerifyUpdate(pFiles)) { std::wcout << L"INVALID Update SIGNATURE in " _T(UPDATE_FILE) "!!!" << std::endl; return ERROR_SIGN; } // // when needed shutdown the software // bool bRestart = false; if (ret >= 0 && (step.empty() || step == L"apply")) bRestart = HasFlag(arguments, L"restart"); if (bRestart) { Execute(base_dir + L"\\KmdUtil.exe", L"scandll_silent"); Execute(base_dir + L"\\KmdUtil.exe", L"stop SbieSvc"); Execute(base_dir + L"\\KmdUtil.exe", L"stop SbieDrv"); Sleep(3000); Execute(base_dir + L"\\KmdUtil.exe", L"stop SbieDrv"); } // // apply update // if (step != L"get" && !bModify) { std::wstring scope = GetArgument(arguments, L"scope"); //if(software == L"sandboxie" && scope.empty()) // scope = L"core"; ret = ProcessUpdate(pFiles, step, temp_dir, base_dir, scope); if (ret >= 0 && (step.empty() || step == L"apply")) { ret = ApplyUpdate(base_dir, temp_dir, pFiles); if (ret <= 0) return ret; // error or nothing todo } } // // load addons appropriate for the current installation // std::shared_ptr pAddons; if (jsonAddons) { std::wstring core_arch = Arch2Str(GetBinaryArch(base_dir + L"\\sbiesvc.exe")); std::wstring agent_arch = GetArgument(arguments, L"agent_arch"); if (agent_arch.empty()) { agent_arch = Arch2Str(GetBinaryArch(base_dir + L"\\SandMan.exe")); if (agent_arch.empty()) agent_arch = Arch2Str(GetBinaryArch(base_dir + L"\\SbieCtrl.exe")); } std::wstring framework = GetArgument(arguments, L"framework"); if (framework.empty()) { framework = GetFileVersion(base_dir + L"\\Qt5Core.dll"); if (framework.empty()) framework = GetFileVersion(base_dir + L"\\Qt6Core.dll"); if (!framework.empty()) framework = L"qt" + framework + L"_" + agent_arch; } if (pFiles) { if (agent_arch.empty()) agent_arch = pFiles->AgentArch; if (framework.empty()) agent_arch = pFiles->Framework; } pAddons = ReadAddons(jsonAddons->AsObject(), core_arch, agent_arch, framework); if(!pAddons){ std::wcout << L"No addons found!" << std::endl; if(bModify) return ERROR_LOAD; } else if (!VerifyAddons(pAddons)) { pAddons.reset(); // clear untrusted addon data std::wcout << L"INVALID " _T(ADDONS_FILE) L" SIGNATURE !!!" << std::endl; if(bModify) return ERROR_SIGN; } } // // install addons // if (step != L"get" && pAddons) { if (!bModify) { // install case for (auto I = pAddons->begin(); I != pAddons->end(); ++I) { if (!I->second->IsDefault) continue; // don't add default addons marked to be removed auto F = std::find(remove_addons.begin(), remove_addons.end(), I->first); if (F != remove_addons.end()) continue; // don't add already added addons F = std::find(add_addons.begin(), add_addons.end(), I->first); if (F != add_addons.end()) continue; add_addons.push_back(I->first); } } for (auto I = add_addons.begin(); I != add_addons.end(); ++I) { auto F = pAddons->find(MkLower(*I)); if (F != pAddons->end()) { std::wcout << L"Downloading addon " << *I << std::endl; ret = DownloadAddon(F->second, step, temp_dir, base_dir); } else { std::wcout << L"Addon Not Found" << std::endl; ret = ERROR_NO_ADDON; } if (ret >= 0 && (step.empty() || step == L"apply")) { std::shared_ptr pAddon = LoadAddon(base_dir, *I); if (pAddon && !pAddon->InstallPath.empty()) { std::wcout << L"Updating addon " << *I << std::endl; RemoveAddon(pAddon, base_dir); } else std::wcout << L"Installing addon " << *I << std::endl; ret = InstallAddon(F->second, temp_dir, base_dir); // register addon if (ret >= 0) { JSONValue* value = new JSONValue(WriteAddon(F->second)); auto wJson = value->Stringify(); delete value; std::string aJson = g_str_conv.to_bytes(wJson); MyWriteFile((wchar_t*)(base_dir + _T(ADDONS_PATH) + F->second->Id + L".json").c_str(), (char*)aJson.c_str(), aJson.length()); if (ret >= 0) DeleteDirectoryRecursively(temp_dir + L"\\", F->second->Id + L"\\", true); } } if (ret < 0 && !bModify) // install case ret = 0; // ignore addon errors } } // // remove addons // if(step != L"get" && !remove_addons.empty()) { for (auto I = remove_addons.begin(); I != remove_addons.end(); ++I) { std::shared_ptr pAddon = LoadAddon(base_dir, *I); if (!pAddon) ret = ERROR_NO_ADDON; if (ret >= 0 && (step.empty() || step == L"apply")) { std::wcout << L"Removing addon " << *I << std::endl; ret = RemoveAddon(pAddon, base_dir); // unregister addon if (ret >= 0) DeleteFileW((base_dir + _T(ADDONS_PATH) + pAddon->Id + L".json").c_str()); } if (ret < 0 && !bModify) // install case ret = 0; // ignore addon errors } } // // restart software // if (bRestart) { Sleep(1000); Execute(base_dir + L"\\KmdUtil.exe", L"start SbieSvc"); } std::wstring wOpen = GetArgument(arguments, L"open"); if (!wOpen.empty()) { Execute(base_dir + L"\\start.exe", L"open_agent:" + wOpen); } // // save data to file if needed // if (ret >= 0) { if (!bModify) { std::wstring file_path = temp_dir + L"\\" _T(UPDATE_FILE); if (step.empty() || step == L"apply" || (ret == 0 && step != L"get")) DeleteFileW(file_path.c_str()); // cleanup after apply or if there are no updates //else { // store partial state to file else if (bSave) { std::string Json = WriteUpdate(pFiles); MyWriteFile((wchar_t*)file_path.c_str(), (char*)Json.c_str(), Json.length()); } } if (bDoAddons && pAddons) { std::wstring file_path = temp_dir + L"\\" _T(ADDONS_FILE); if (step.empty() || step == L"apply" /*|| (ret == 0 && step != L"get")*/) DeleteFileW(file_path.c_str()); else if (bSave) { std::string Json = WriteAddons(pAddons); MyWriteFile((wchar_t*)file_path.c_str(), (char*)Json.c_str(), Json.length()); } } } return ret; } else if (arguments.size() >= 2 && arguments[0] == L"get_cert") { int ret = 0; std::wstring serial = arguments[1]; std::wstring path = L"/get_cert.php?SN=" + serial; if (serial.length() > 5 && toupper(serial[4]) == 'N') { // node locked business use wchar_t uuid_str[40]; GetDriverInfo(-2, uuid_str, sizeof(uuid_str)); path += L"&HwId=" + std::wstring(uuid_str); } std::wstring file_path = base_dir + L"\\Certificate.dat"; char* aCert = NULL; if (NT_SUCCESS(MyReadFile((wchar_t*)file_path.c_str(), 1024 * 1024, (PVOID*)&aCert, NULL)) && aCert != NULL) { std::string sCert = aCert; free(aCert); auto Cert = GetArguments(std::wstring(sCert.begin(), sCert.end()), L'\n', L':'); auto F = Cert.find(L"UPDATEKEY"); if (F != Cert.end()) path += L"&UpdateKey=" + F->second; } ULONG lCert = 0; if (WebDownload(_T(UPDATE_DOMAIN), path.c_str(), &aCert, &lCert) && aCert != NULL && *aCert) { if (aCert[0] == L'{') { JSONValue* jsonObject = JSON::Parse(aCert); if (jsonObject) { if (jsonObject->IsObject() && GetJSONBoolSafe(jsonObject->AsObject(), L"error")) { std::wcout << GetJSONStringSafe(jsonObject->AsObject(), L"errorMsg") << std::endl; ret = ERROR_GET_CERT; } delete jsonObject; } free(aCert); } } else { std::wcout << L"FAILED to call get_cert.php" << std::endl; ret = ERROR_GET_CERT; } if (ret == 0 && !NT_SUCCESS(MyWriteFile((wchar_t*)file_path.c_str(), aCert, lCert))) ret = ERROR_INTERNAL; return ret; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PrintUsage(); return ERROR_INVALID; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Driver API #define SANDBOXIE L"Sandboxie" #define SBIESVC_PORT L"\\RPC Control\\SbieSvcPort" #define MAX_REQUEST_LENGTH (2048 * 1024) typedef struct _UNICODE_STRING64 { USHORT Length; USHORT MaximumLength; __declspec(align(8)) unsigned __int64 Buffer; } UNICODE_STRING64; #include "..\..\Sandboxie\common\defines.h" #include "..\..\Sandboxie\core\drv\api_defs.h" #include "..\..\Sandboxie\core\drv\api_flags.h" HANDLE SbieApi_DeviceHandle = INVALID_HANDLE_VALUE; NTSTATUS SbieApi_Ioctl(ULONG64 *parms) { NTSTATUS status; OBJECT_ATTRIBUTES objattrs; UNICODE_STRING uni; IO_STATUS_BLOCK MyIoStatusBlock; if (parms == NULL) { if (SbieApi_DeviceHandle != INVALID_HANDLE_VALUE) NtClose(SbieApi_DeviceHandle); SbieApi_DeviceHandle = INVALID_HANDLE_VALUE; return STATUS_SUCCESS; } if (SbieApi_DeviceHandle == INVALID_HANDLE_VALUE) { RtlInitUnicodeString(&uni, API_DEVICE_NAME); InitializeObjectAttributes(&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtOpenFile( &SbieApi_DeviceHandle, FILE_GENERIC_READ, &objattrs, &MyIoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0); if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NO_SUCH_DEVICE) status = STATUS_SERVER_DISABLED; } else status = STATUS_SUCCESS; if (status != STATUS_SUCCESS) { SbieApi_DeviceHandle = INVALID_HANDLE_VALUE; } else { status = NtDeviceIoControlFile( SbieApi_DeviceHandle, NULL, NULL, NULL, &MyIoStatusBlock, API_SBIEDRV_CTLCODE, parms, sizeof(ULONG64) * 8, NULL, 0); } return status; } //LONG SbieApi_Call(ULONG api_code, LONG arg_num, ...) //{ // va_list valist; // NTSTATUS status; // __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; // // memzero(parms, sizeof(parms)); // parms[0] = api_code; // // if (arg_num >= (API_NUM_ARGS - 1)) // return STATUS_INVALID_PARAMETER; // // va_start(valist, arg_num); // for (LONG i = 1; i <= arg_num; i++) // parms[i] = (ULONG64)va_arg(valist, ULONG_PTR); // va_end(valist); // // status = SbieApi_Ioctl(parms); // // return status; //} bool GetDriverInfo(DWORD InfoClass, void* pBuffer, size_t Size) { __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; API_QUERY_DRIVER_INFO_ARGS *args = (API_QUERY_DRIVER_INFO_ARGS*)parms; memset(parms, 0, sizeof(parms)); args->func_code = API_QUERY_DRIVER_INFO; args->info_class.val = InfoClass; args->info_data.val = pBuffer; args->info_len.val = Size; NTSTATUS status = SbieApi_Ioctl(parms); if (!NT_SUCCESS(status)) { memset(pBuffer, 0, Size); return false; } return true; }