This commit is contained in:
DavidXanatos 2023-07-30 14:28:35 +02:00
parent c638c63c36
commit 3912dbfd53
20 changed files with 1158 additions and 527 deletions

View File

@ -6,6 +6,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [1.10.2 / 5.65.2] - 2023-07-??
### Added
- re-added option to suspend sandboxed processes [#3126](https://github.com/sandboxie-plus/Sandboxie/issues/3126)
### Changed
- changed format of the addon data [#3135](https://github.com/sandboxie-plus/Sandboxie/issues/3135)
- all users coming from versions 1.10.0 and 1.10.1 will need to reinstall the components in the addon manager

View File

@ -54,7 +54,7 @@ CBoxedProcess::CBoxedProcess(quint32 ProcessId, class CSandBox* pBox)
m_ReturnCode = STATUS_PENDING;
m_uTerminated = 0;
//m_bSuspended = IsSuspended();
m_bSuspended = false;
m_bIsWoW64 = false;
}
@ -280,6 +280,8 @@ void CBoxedProcess::InitProcessInfoImpl(void* ProcessHandle)
{
m_WorkingDir = CBoxedProcess__GetPebString(ProcessHandle, PhpoCurrentDirectory);
}
m_bSuspended = IsSuspended();
}
bool CBoxedProcess::InitProcessInfoEx()
@ -291,14 +293,20 @@ bool CBoxedProcess::InitProcessInfoEx()
return true;
}
//extern "C"
//{
// NTSYSCALLAPI NTSTATUS NTAPI NtTerminateProcess(_In_opt_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus);
// NTSYSCALLAPI NTSTATUS NTAPI NtSuspendProcess(_In_ HANDLE ProcessHandle);
// NTSYSCALLAPI NTSTATUS NTAPI NtResumeProcess(_In_ HANDLE ProcessHandle);
//}
extern "C"
{
NTSYSCALLAPI NTSTATUS NTAPI NtTerminateProcess(_In_opt_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus);
NTSYSCALLAPI NTSTATUS NTAPI NtSuspendProcess(_In_ HANDLE ProcessHandle);
NTSYSCALLAPI NTSTATUS NTAPI NtResumeProcess(_In_ HANDLE ProcessHandle);
#include <TlHelp32.h>
NTSYSCALLAPI NTSTATUS NTAPI NtGetNextThread(HANDLE ProcessHandle, HANDLE ThreadHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, ULONG Flags, PHANDLE NewThreadHandle);
#define OBJ_KERNEL_EXCLUSIVE 0x00010000L
#define OBJ_VALID_PRIVATE_ATTRIBUTES 0x00010000L
#define OBJ_ALL_VALID_ATTRIBUTES (OBJ_VALID_PRIVATE_ATTRIBUTES | OBJ_VALID_ATTRIBUTES)
}
//#include <TlHelp32.h>
SB_STATUS CBoxedProcess::Terminate()
{
@ -327,7 +335,7 @@ bool CBoxedProcess::IsTerminated(quint64 forMs) const
return ::GetTickCount64() - m_uTerminated > forMs;
}
/*SB_STATUS CBoxedProcess::SetSuspend(bool bSet)
SB_STATUS CBoxedProcess::SetSuspend(bool bSet)
{
HANDLE ProcessHandle = OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, (DWORD)m_ProcessId);
if (ProcessHandle != INVALID_HANDLE_VALUE)
@ -351,43 +359,26 @@ bool CBoxedProcess::IsSuspended() const
{
bool isSuspended = true;
// todo: do that globally once per sec for all boxed processes
// Note: If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return false;
THREADENTRY32 te32 = { 0 };
te32.dwSize = sizeof(THREADENTRY32);
if (Thread32First(hThreadSnap, &te32))
for(HANDLE hThread = NULL;;)
{
do
{
if (te32.th32OwnerProcessID != m_ProcessId)
continue;
HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID);
HANDLE nNextThread = NULL;
NTSTATUS status = NtGetNextThread(m->Handle, hThread, THREAD_QUERY_INFORMATION, 0, 0, &nNextThread);
if(hThread) NtClose(hThread);
if (!NT_SUCCESS(status))
break;
hThread = nNextThread;
ULONG SuspendCount = 0;
NTSTATUS status = NtQueryInformationThread(hThread, (THREADINFOCLASS)35/ThreadSuspendCount/, &SuspendCount, sizeof(ULONG), NULL);
CloseHandle(hThread);
if (SuspendCount == 0)
{
isSuspended = false;
break;
}
} while (Thread32Next(hThreadSnap, &te32));
}
CloseHandle(hThreadSnap);
ULONG SuspendCount = 0;
status = NtQueryInformationThread(hThread, (THREADINFOCLASS)35/*ThreadSuspendCount*/, &SuspendCount, sizeof(ULONG), NULL);
if (SuspendCount == 0) {
isSuspended = false;
NtClose(hThread);
break;
}
}
return isSuspended;
}
*/
void CBoxedProcess::ResolveSymbols(const QVector<quint64>& Addresses)
{

View File

@ -47,8 +47,8 @@ public:
virtual bool IsTerminated(quint64 forMs = 0) const;
virtual void SetTerminated();
//virtual SB_STATUS SetSuspend(bool bSet);
//virtual bool IsSuspended() const;
virtual SB_STATUS SetSuspend(bool bSet);
virtual bool IsSuspended() const;
virtual bool IsWoW64() const { return m_bIsWoW64; }
@ -79,7 +79,7 @@ protected:
QDateTime m_StartTime;
quint32 m_ReturnCode;
quint64 m_uTerminated;
//bool m_bSuspended;
bool m_bSuspended;
bool m_bIsWoW64;
class CSandBox* m_pBox;

View File

@ -9,7 +9,7 @@
#include <QJsonObject>
#include "../QSbieAPI/Sandboxie/SbieTemplates.h"
#include <QtConcurrent>
#include "../MiscHelpers/Archive/Archive.h"
#include "../../SandboxieTools/UpdUtil/UpdUtil.h"
#include <Windows.h>
@ -27,8 +27,8 @@ void CAddonManager::UpdateAddonsWhenNotCached()
OnUpdateData(Data, QVariantMap());
return;
}
else if (!m_Addons.isEmpty()) {
QFileInfo info(theConf->GetConfigDir() + "/addons.json");
else if (!m_KnownAddons.isEmpty()) {
QFileInfo info(theConf->GetConfigDir() + "/" ADDONS_FILE);
if (info.birthTime() > QDateTime::currentDateTime().addDays(-1))
return;
}
@ -49,77 +49,111 @@ void CAddonManager::OnUpdateData(const QVariantMap& Data, const QVariantMap& Par
QVariantMap Addons = Data["addons"].toMap();
QJsonDocument doc(QJsonValue::fromVariant(Addons).toObject());
QFile::remove(theConf->GetConfigDir() + "/addons.json");
WriteStringToFile(theConf->GetConfigDir() + "/addons.json", doc.toJson());
QFile::remove(theConf->GetConfigDir() + "/" ADDONS_FILE);
WriteStringToFile(theConf->GetConfigDir() + "/" ADDONS_FILE, doc.toJson());
LoadAddons();
emit DataUpdated();
}
QList<CAddonPtr> CAddonManager::GetAddons()
QList<CAddonInfoPtr> CAddonManager::GetAddons()
{
if (m_Addons.isEmpty()) {
if (m_KnownAddons.isEmpty()) {
if (!LoadAddons())
UpdateAddons();
}
else {
foreach(const CAddonPtr& pAddon, m_Addons)
pAddon->Installed = CheckAddon(pAddon);
QMap<QString, CAddonInfoPtr> Addons;
m_Installed.clear();
QDir Dir(theAPI->GetSbiePath() + ADDONS_PATH);
foreach(const QFileInfo & Info, Dir.entryInfoList(QStringList() << "*.json", QDir::Files | QDir::Hidden | QDir::System)) {
QString AddonPath = theAPI->GetSbiePath() + ADDONS_PATH + Info.fileName();
QString AddonStr = ReadFileAsString(AddonPath);
QVariantMap Data = QJsonDocument::fromJson(AddonStr.toUtf8()).toVariant().toMap();
m_Installed.append(CAddonPtr(new CAddon(Data)));
Addons.insert(Data["id"].toString().toLower(), CAddonInfoPtr(new CAddonInfo(Data, true)));
}
return m_Addons;
foreach(const CAddonPtr& pAddon, m_KnownAddons) {
CAddonInfoPtr& pInfo = Addons[pAddon->Id.toLower()];
if (pInfo.isNull()) {
bool Installed = false;
QString Key = pAddon->GetSpecificEntry("uninstallKey").toString();
if (!Key.isEmpty()) {
QSettings settings(Key, QSettings::NativeFormat);
QString Uninstall = settings.value("UninstallString").toString();
if (!Uninstall.isEmpty()) {
Installed = true;
m_Installed.append(CAddonPtr(new CAddon(pAddon->Data)));
}
}
pInfo = CAddonInfoPtr(new CAddonInfo(pAddon->Data, Installed));
}
else if (pInfo->Data["version"] != pAddon->Data["version"])
pInfo->UpdateVersion = pAddon->Data["version"].toString();
}
return Addons.values();
}
bool CAddonManager::LoadAddons()
{
m_Addons.clear();
m_KnownAddons.clear();
QString AddonPath = theConf->GetConfigDir() + "/addons.json";
QVariantMap Data = QJsonDocument::fromJson(ReadFileAsString(AddonPath).toUtf8()).toVariant().toMap();
foreach(const QVariant vAddon, Data["list"].toList()) {
CAddonPtr pAddon = CAddonPtr(new CAddon(vAddon.toMap()));
pAddon->Installed = CheckAddon(pAddon);
m_Addons.append(pAddon);
}
QString AddonPath = theConf->GetConfigDir() + "/" ADDONS_FILE;
QString AddonStr = ReadFileAsString(AddonPath);
QVariantMap Data = QJsonDocument::fromJson(AddonStr.toUtf8()).toVariant().toMap();
foreach(const QVariant vAddon, Data["list"].toList())
m_KnownAddons.append(CAddonPtr(new CAddon(vAddon.toMap())));
return !m_Addons.isEmpty();
return !m_KnownAddons.isEmpty();
}
CAddonPtr CAddonManager::GetAddon(const QString& Id)
CAddonPtr CAddonManager::GetAddon(const QString& Id, EState State)
{
if (m_Addons.isEmpty())
LoadAddons();
if (State != eNotINstalled)
{
foreach(const CAddonPtr & pAddon, m_Installed) {
if (pAddon->Id.compare(Id, Qt::CaseInsensitive) == 0)
return pAddon;
}
}
foreach(const CAddonPtr& pAddon, m_Addons) {
if (pAddon->Id.compare(Id, Qt::CaseInsensitive) == 0) {
pAddon->Installed = CheckAddon(pAddon);
return pAddon;
if (State != eInstalled)
{
if (m_KnownAddons.isEmpty())
LoadAddons();
foreach(const CAddonPtr & pAddon, m_KnownAddons) {
if (pAddon->Id.compare(Id, Qt::CaseInsensitive) == 0)
return pAddon;
}
}
return CAddonPtr();
}
bool CAddonManager::HasAddon(const QString& Id)
/*bool CAddonManager::CheckAddon(const CAddonPtr& pAddon)
{
CAddonPtr pAddon = GetAddon(Id);
return pAddon && pAddon->Installed;
}
bool CAddonManager::CheckAddon(const CAddonPtr& pAddon)
{
QString Key = pAddon->GetSpecificEntry("uninstall_key").toString();
QString Key = pAddon->GetSpecificEntry("uninstallKey").toString();
if (!Key.isEmpty()) {
QSettings settings(Key, QSettings::NativeFormat);
QString Uninstall = settings.value("UninstallString").toString();
return !Uninstall.isEmpty();
}
QStringList Files = pAddon->GetSpecificEntry("files").toStringList();
/ *QStringList Files = pAddon->GetSpecificEntry("files").toStringList();
foreach(const QString & File, Files) {
if (theGUI->GetCompat()->CheckFile(ExpandPath(File)))
return true;
}
return false;
}
return false;* /
QString AddonFile = theAPI->GetSbiePath() + ADDONS_PATH + pAddon->Id + ".json";
return QFile::exists(AddonFile);
}*/
SB_PROGRESS CAddonManager::TryInstallAddon(const QString& Id, QWidget* pParent, const QString& Prompt)
{
@ -135,151 +169,6 @@ SB_PROGRESS CAddonManager::TryInstallAddon(const QString& Id, QWidget* pParent,
return Status;
}
SB_PROGRESS CAddonManager::InstallAddon(const QString& Id)
{
CAddonPtr pAddon = GetAddon(Id);
if (!pAddon)
return SB_ERR(SB_OtherError, QVariantList() << tr("Addon not found, please try updating the addon list in the global settings!"));
if (pAddon->Installed)
return SB_ERR(SB_OtherError, QVariantList() << tr("Addon already installed!"));
QString Entry;
QString Url = pAddon->GetSpecificEntry("download", &Entry).toString();
if (Url.isEmpty())
return SB_ERR(SB_OtherError, QVariantList() << tr("Addon has no download url, addon may not be available for your platform."));
QVariantMap Params;
Params["name"] = Id;
Params["path"] = theGUI->m_pUpdater->GetUpdateDir(true) + "/" + QUrl(Url).fileName();
Params["signature"] = pAddon->Data.value(Entry + "_sig");
theGUI->m_pUpdater->DownloadFile(Url, this, SLOT(OnAddonDownloaded(const QString&, const QVariantMap&)), Params);
pAddon->pProgress = CSbieProgressPtr(new CSbieProgress());
connect(pAddon->pProgress.data(), SIGNAL(Finished()), this, SIGNAL(AddonInstalled()));
pAddon->pProgress->ShowMessage(tr("Downloading Addon %1").arg(pAddon->Id));
return SB_PROGRESS(OP_ASYNC, pAddon->pProgress);
}
extern "C" NTSTATUS VerifyFileSignatureImpl(const wchar_t* FilePath, PVOID Signature, ULONG SignatureSize);
void CAddonManager::OnAddonDownloaded(const QString& Path, const QVariantMap& Params)
{
CAddonPtr pAddon = GetAddon(Params["name"].toString());
QByteArray Signature = QByteArray::fromBase64(Params["signature"].toByteArray());
if (VerifyFileSignatureImpl(QString(Path).replace("/","\\").toStdWString().c_str(), Signature.data(), Signature.size()) < 0) { // !NT_SUCCESS
pAddon->pProgress->Finish(SB_ERR(SB_OtherError, QVariantList() << tr("Download signature is not valid!")));
pAddon->pProgress.create();
return;
}
pAddon->pProgress->ShowMessage(tr("Installing Addon %1").arg(pAddon->Id));
QtConcurrent::run(CAddonManager::InstallAddonAsync, Path, pAddon);
}
void CAddonManager::InstallAddonAsync(const QString& FilePath, CAddonPtr pAddon)
{
SB_STATUS Status = SB_OK;
CArchive Archive(FilePath);
if (Archive.Open() == 1)
{
QString FileDir = Split2(FilePath, ".", true).first.replace("/", "\\");
if (Archive.Extract(FileDir)) {
QString Cmd = pAddon->GetSpecificEntry("installer").toString();
QString Path = ExpandPath(pAddon->GetSpecificEntry("install_path").toString());
if (!Cmd.isEmpty() && QFile::exists(FileDir + Cmd))
{
pAddon->pProgress->ShowMessage(tr("Running Installer for %1").arg(pAddon->Id));
std::wstring sbiehome = theAPI->GetSbiePath().toStdWString();
std::wstring plusdata = theConf->GetConfigDir().toStdWString();
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 + sbiehome.length() + 1 + plusdata.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, sbiehome.c_str());
modifiedEnvironmentEnd += wcslen(modifiedEnvironmentEnd) + 1;
wcscpy(modifiedEnvironmentEnd, L"PLUSDATA=");
wcscat(modifiedEnvironmentEnd, plusdata.c_str());
modifiedEnvironmentEnd += wcslen(modifiedEnvironmentEnd) + 1;
*modifiedEnvironmentEnd = 0;
STARTUPINFO si = { sizeof(si), 0 };
PROCESS_INFORMATION pi = { 0 };
if (CreateProcessW(NULL, (wchar_t*)(FileDir + Cmd).toStdWString().c_str(), NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, modifiedEnvironment, NULL, &si, &pi))
{
while (WaitForSingleObject(pi.hProcess, 1000) == WAIT_TIMEOUT && !pAddon->pProgress->IsCanceled());
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
Status = SB_ERR(SB_OtherError, QVariantList() << tr("Failed to start installer (%1)!").arg(GetLastError()));
LocalFree(modifiedEnvironment);
}
else if (!Path.isEmpty())
{
pAddon->pProgress->ShowMessage(tr("Copying Files for %1").arg(pAddon->Id));
std::wstring from;
foreach(const QString & file, ListDir(FileDir)) {
QString File = QString(file).replace("/", "\\");
from.append((FileDir + "\\" + File).toStdWString());
from.append(L"\0", 1);
}
from.append(L"\0", 1);
std::wstring to;
to.append(Path.toStdWString());
to.append(L"\0", 1);
SHFILEOPSTRUCT SHFileOp;
memset(&SHFileOp, 0, sizeof(SHFILEOPSTRUCT));
SHFileOp.hwnd = NULL;
SHFileOp.wFunc = FO_MOVE;
SHFileOp.pFrom = from.c_str();
SHFileOp.pTo = to.c_str();
SHFileOp.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
SHFileOperation(&SHFileOp);
}
QDir(FileDir).removeRecursively();
}
else
Status = SB_ERR(SB_OtherError, QVariantList() << tr("Failed to unpack addon!"));
Archive.Close();
}
QFile::remove(FilePath);
if (!Status.IsError()) {
pAddon->Installed = CheckAddon(pAddon);
if (!pAddon->Installed)
Status = SB_ERR(SB_OtherError, QVariantList() << tr("Addon Installation Failed!"));
}
pAddon->pProgress->Finish(Status);
pAddon->pProgress.create();
}
SB_PROGRESS CAddonManager::TryRemoveAddon(const QString& Id, QWidget* pParent)
{
if (QMessageBox("Sandboxie-Plus", tr("Do you want to remove %1?").arg(Id),
@ -294,100 +183,92 @@ SB_PROGRESS CAddonManager::TryRemoveAddon(const QString& Id, QWidget* pParent)
return Status;
}
SB_PROGRESS CAddonManager::RemoveAddon(const QString& Id)
SB_PROGRESS CAddonManager::InstallAddon(const QString& Id)
{
CAddonPtr pAddon = GetAddon(Id);
CAddonPtr pAddon = GetAddon(Id, eNotINstalled);
if (!pAddon)
return SB_ERR(SB_OtherError, QVariantList() << tr("Addon not found!"));
return SB_ERR(SB_OtherError, QVariantList() << tr("Addon not found, please try updating the addon list in the global settings!"));
QFile::remove(theGUI->m_pUpdater->GetUpdateDir(true) + "/" ADDONS_FILE);
QFile::copy(theConf->GetConfigDir() + "/" ADDONS_FILE, theGUI->m_pUpdater->GetUpdateDir(true) + "/" ADDONS_FILE);
QStringList Params;
Params.append("modify");
Params.append("add:" + pAddon->Id);
Params.append("/agent_arch:" + GetAppArch());
Params.append("/framework:" + GetFramework());
Params.append("/step:apply");
Params.append("/embedded");
Params.append("/temp:" + theGUI->m_pUpdater->GetUpdateDir().replace("/", "\\"));
pAddon->pProgress = CSbieProgressPtr(new CSbieProgress());
QtConcurrent::run(CAddonManager::RemoveAddonAsync, pAddon);
QtConcurrent::run(CAddonManager::RunUpdaterAsync, pAddon, Params);
//QTimer::singleShot(10, this, [=]() { CAddonManager::RunUpdaterAsync(pAddon, Params); });
return SB_PROGRESS(OP_ASYNC, pAddon->pProgress);
}
void CAddonManager::CleanupPath(const QString& Path)
SB_PROGRESS CAddonManager::RemoveAddon(const QString& Id)
{
StrPair PathName = Split2(Path, "\\", true);
if (ListDir(PathName.first).isEmpty())
CAddonPtr pAddon = GetAddon(Id, eInstalled);
if (!pAddon)
return SB_ERR(SB_OtherError, QVariantList() << tr("Addon not found!"));
QStringList Params;
Params.append("modify");
Params.append("remove:" + pAddon->Id);
Params.append("/step:apply");
Params.append("/embedded");
pAddon->pProgress = CSbieProgressPtr(new CSbieProgress());
QtConcurrent::run(CAddonManager::RunUpdaterAsync, pAddon, Params);
//QTimer::singleShot(10, this, [=]() { CAddonManager::RunUpdaterAsync(pAddon, Params); });
return SB_PROGRESS(OP_ASYNC, pAddon->pProgress);
}
QString GetUpdErrorStr(int exitCode);
QString GetUpdErrorStr2(int exitCode)
{
switch (exitCode)
{
QDir().rmdir(PathName.first);
//qDebug() << "delete dir" << PathName.first;
CleanupPath(PathName.first);
case ERROR_NO_ADDON: return CAddonManager::tr("Addon Not Found");
case ERROR_NO_ADDON2: return CAddonManager::tr("Addon is not available for this paltform");
case ERROR_BAD_ADDON: return CAddonManager::tr("Missing instalation instructions");
case ERROR_BAD_ADDON2: return CAddonManager::tr("Executing addon setup failed");
case ERROR_DELETE: return CAddonManager::tr("Failed to delete a file during addon removal");
default: return GetUpdErrorStr(exitCode);
}
}
void CAddonManager::RemoveAddonAsync(CAddonPtr pAddon)
void CAddonManager::RunUpdaterAsync(CAddonPtr pAddon, const QStringList& Params)
{
SB_STATUS Status = SB_OK;
#ifdef _DEBUG
CSbieResult<int> Status = COnlineUpdater::RunUpdater(Params, false, true);
#else
CSbieResult<int> Status = COnlineUpdater::RunUpdater(Params, true, true);
#endif
QString Key = pAddon->GetSpecificEntry("uninstall_key").toString();
if (!Key.isEmpty())
{
QSettings settings(Key, QSettings::NativeFormat);
QString Cmd = settings.value("UninstallString").toString();
pAddon->pProgress->ShowMessage(tr("Running Uninstaller for %1").arg(pAddon->Id));
STARTUPINFO si = { sizeof(si), 0 };
PROCESS_INFORMATION pi = { 0 };
if (CreateProcessW(NULL, (wchar_t*)Cmd.toStdWString().c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
while (WaitForSingleObject(pi.hProcess, 1000) == WAIT_TIMEOUT && !pAddon->pProgress->IsCanceled());
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
Status = SB_ERR(SB_OtherError, QVariantList() << tr("Failed to start uninstaller!"));
}
if(Status.IsError())
pAddon->pProgress->Finish(SB_ERR(SB_OtherError, QVariantList() << tr("Updater failed to to perform plugin operation")));
else if(Status.GetValue() < 0)
pAddon->pProgress->Finish(SB_ERR(SB_OtherError, QVariantList() << tr("Updater failed to to perform plugin operation, error: %1").arg(GetUpdErrorStr2(Status.GetValue()))));
else
{
QStringList Files = pAddon->GetSpecificEntry("files").toStringList();
//foreach(const QString & File, Files) {
// pAddon->pProgress->ShowMessage(tr("Removing %1").arg(File));
// QString FilePath = ExpandPath(File);
// QFile::remove(FilePath);
// CleanupPath(FilePath);
//}
std::wstring from;
foreach(const QString & File, Files) {
QString FilePath = ExpandPath(File);
if (QFile::exists(FilePath)) {
from.append(FilePath.toStdWString());
from.append(L"\0", 1);
}
}
from.append(L"\0", 1);
SHFILEOPSTRUCT SHFileOp;
memset(&SHFileOp, 0, sizeof(SHFILEOPSTRUCT));
SHFileOp.hwnd = NULL;
SHFileOp.wFunc = FO_DELETE;
SHFileOp.pFrom = from.c_str();
SHFileOp.pTo = NULL;
SHFileOp.fFlags = FOF_NOCONFIRMATION;
SHFileOperation(&SHFileOp);
}
if (!Status.IsError()) {
pAddon->Installed = CheckAddon(pAddon);
if (pAddon->Installed)
Status = SB_ERR(SB_OtherError, QVariantList() << tr("Addon Removal Failed!"));
}
pAddon->pProgress->Finish(Status);
pAddon->pProgress.create();
pAddon->pProgress->Finish(SB_OK);
pAddon->pProgress.clear();
}
QString CAddonManager::ExpandPath(QString Path)
QString CAddonManager::GetAppArch()
{
Path.replace("%SbieHome%", theAPI->GetSbiePath(), Qt::CaseInsensitive);
Path.replace("%PlusData%", theConf->GetConfigDir(), Qt::CaseInsensitive);
return theGUI->GetCompat()->ExpandPath(Path);
#ifdef _M_ARM64
return "a64";
#elif _WIN64
return "x64";
#else
return "x86";
#endif
}
QString GetArch()
QString CAddonManager::GetSysArch()
{
SYSTEM_INFO systemInfo;
GetSystemInfo(&systemInfo);
@ -397,42 +278,43 @@ QString GetArch()
case PROCESSOR_ARCHITECTURE_AMD64: return "x64";
case PROCESSOR_ARCHITECTURE_ARM64: return "a64";
}
return "???";
return GetAppArch(); // fallback
}
QString CAddonManager::GetFramework()
{
QString qt = QString("qt%1.%2.%3").arg(QT_VERSION_MAJOR).arg(QT_VERSION_MINOR).arg(QT_VERSION_PATCH);
#ifdef _DEBUG
qt.append("d");
#endif // _DEBUG
qt.append("_" + GetAppArch());
return qt;
}
QVariant CAddon::GetSpecificEntry(const QString& Name, QString* pName)
{
#ifdef _M_ARM64
QString arch = "a64";
#elif _WIN64
QString arch = "x64";
#else
QString arch = "x86";
#endif
//
// First we check the qt cpecific entry for our version of qt and platform
//
QString qt = QString("qt%1_%2_%3_%4").arg(QT_VERSION_MAJOR).arg(QT_VERSION_MINOR).arg(QT_VERSION_PATCH).arg(arch);
QString qt = CAddonManager::GetFramework();
#ifdef _DEBUG
qt.append("d");
#endif // _DEBUG
if (Data.contains(Name + "_" + qt)) {
if (pName) *pName = Name + "_" + qt;
return Data[Name + "_" + qt];
if (Data.contains(Name + "-" + qt)) {
if (pName) *pName = Name + "-" + qt;
return Data[Name + "-" + qt];
}
//
// Second we check the actual architecture
//
QString match = Data["match_arch"].toString();
if (match != "agent")
arch = GetArch();
if (Data.contains(Name + "_" + arch)) {
if (pName) *pName = Name + "_" + arch;
return Data[Name + "_" + arch];
QString match = Data["matchArch"].toString();
QString arch = match != "agent" ? CAddonManager::GetSysArch() : CAddonManager::GetAppArch();
if (Data.contains(Name + "-" + arch)) {
if (pName) *pName = Name + "-" + arch;
return Data[Name + "-" + arch];
}
//
@ -447,7 +329,7 @@ QVariant CAddon::GetSpecificEntry(const QString& Name, QString* pName)
return QString();
}
QString CAddon::GetLocalizedEntry(const QString& Name)
QString CAddonInfo::GetLocalizedEntry(const QString& Name)
{
if (Data.contains(Name + "_" + theGUI->m_Language))
return Data[Name + "_" + theGUI->m_Language].toString();
@ -458,4 +340,4 @@ QString CAddon::GetLocalizedEntry(const QString& Name)
return Data[Name + "_" + LangAux].toString();
return Data[Name].toString();
}
}

View File

@ -6,21 +6,37 @@
class CAddon : public QObject
{
public:
CAddon(const QVariantMap& Data) : Installed(false), Data(Data)
CAddon(const QVariantMap& Data) : Data(Data)
{
Id = Data["id"].toString();
}
QString Id;
QVariantMap Data;
bool Installed;
CSbieProgressPtr pProgress;
QVariant GetSpecificEntry(const QString& Name, QString* pName = NULL);
QString GetLocalizedEntry(const QString& Name);
};
typedef QSharedPointer<CAddon> CAddonPtr;
class CAddonInfo
{
public:
CAddonInfo(const QVariantMap& data, bool installed) : Data(data), Installed(installed) {
Id = Data["id"].toString();
};
QString Id;
QVariantMap Data;
bool Installed;
QString UpdateVersion;
QString GetLocalizedEntry(const QString& Name);
};
typedef QSharedPointer<CAddonInfo> CAddonInfoPtr;
class CAddonManager : public QObject
{
Q_OBJECT
@ -32,15 +48,24 @@ public:
void UpdateAddonsWhenNotCached();
void UpdateAddons();
QList<CAddonPtr> GetAddons();
QList<CAddonInfoPtr> GetAddons();
CAddonPtr GetAddon(const QString& Id);
bool HasAddon(const QString& Id);
enum EState {
eAny = 0,
eInstalled,
eNotINstalled
};
CAddonPtr GetAddon(const QString& Id, EState State = eAny);
SB_PROGRESS TryInstallAddon(const QString& Id, QWidget* pParent, const QString& Prompt = QString());
SB_PROGRESS InstallAddon(const QString& Id);
SB_PROGRESS TryRemoveAddon(const QString& Id, QWidget* pParent);
SB_PROGRESS RemoveAddon(const QString& Id);
static QString GetAppArch();
static QString GetSysArch();
static QString GetFramework();
signals:
void DataUpdated();
@ -48,18 +73,12 @@ signals:
private slots:
void OnUpdateData(const QVariantMap& Data, const QVariantMap& Params);
void OnAddonDownloaded(const QString& Path, const QVariantMap& Params);
protected:
static bool CheckAddon(const CAddonPtr& pAddon);
static void CleanupPath(const QString& Path);
static void RunUpdaterAsync(CAddonPtr pAddon, const QStringList& Params);
static void InstallAddonAsync(const QString& FilePath, CAddonPtr pAddon);
static void RemoveAddonAsync(CAddonPtr pAddon);
static QString ExpandPath(QString Path);
QList<CAddonPtr> m_Addons;
QList<CAddonPtr> m_Installed;
QList<CAddonPtr> m_KnownAddons;
};

View File

@ -1227,6 +1227,11 @@
<string>Status</string>
</property>
</column>
<column>
<property name="text">
<string>Version</string>
</property>
</column>
<column>
<property name="text">
<string>Description</string>

View File

@ -676,7 +676,8 @@ bool COnlineUpdater::ApplyUpdate(bool bSilent)
if (Scope == eFull)
Params.append("/open:sandman.exe");
if (RunUpdater(Params, bSilent, Scope != eFull)) {
SB_RESULT(int) status = RunUpdater(Params, bSilent, Scope != eFull);
if (!status.IsError()) {
if(bSilent)
theConf->DelValue("Updater/UpdateVersion");
if (Scope == eMeta)
@ -690,15 +691,15 @@ bool COnlineUpdater::ApplyUpdate(bool bSilent)
return false;
}
bool COnlineUpdater::RunUpdater(const QStringList& Params, bool bSilent, bool Wait)
SB_RESULT(int) COnlineUpdater::RunUpdater(const QStringList& Params, bool bSilent, bool Wait)
{
if (bSilent) {
SB_RESULT(int) Result = theAPI->RunUpdateUtility(Params, 2, Wait);
if (!Result.IsError())
return true;
return Result;
// else fallback to ShellExecuteEx
if (theConf->GetBool("Options/UpdateNoFallback", false))
return false;
return Result;
}
std::wstring wFile = QString(QApplication::applicationDirPath() + "/UpdUtil.exe").replace("/", "\\").toStdWString();
@ -708,7 +709,10 @@ bool COnlineUpdater::RunUpdater(const QStringList& Params, bool bSilent, bool Wa
wParams += L"\"" + Param.toStdWString() + L"\"";
}
return RunElevated(wFile, wParams, Wait ? INFINITE : 0) == 0;
int ExitCode = RunElevated(wFile, wParams, Wait ? INFINITE : 0);
if (ExitCode == STATUS_PENDING && !Wait)
ExitCode = 0;
return CSbieResult<int>(ExitCode);
}
void COnlineUpdater::DownloadFile(const QString& Url, QObject* receiver, const char* member, const QVariantMap& Params)

View File

@ -92,8 +92,10 @@ protected:
bool AskDownload(const QVariantMap& Update);
bool RunUpdater(const QStringList& Params, bool bSilent, bool Wait = false);
bool RunInstaller2(const QString& FilePath, bool bSilent);
friend class CAddonManager;
static SB_RESULT(int) RunUpdater(const QStringList& Params, bool bSilent, bool Wait = false);
static bool RunInstaller2(const QString& FilePath, bool bSilent);
CNetworkAccessManager* m_RequestManager;
CSbieProgressPtr m_pUpdateProgress;

View File

@ -76,8 +76,8 @@ QStringList CSandMan::GetFileCheckers(const CSandBoxPtr& pBox)
{
QStringList Checkers;
if (theGUI->GetAddonManager()->HasAddon("FileChecker"))
Checkers.append(pBox->Expand("powershell -exec bypass -nop -File \"%SbieHome%\\bin\\CheckFile.ps1\" -bin"));
if (!theGUI->GetAddonManager()->GetAddon("FileChecker", CAddonManager::eInstalled).isNull())
Checkers.append(pBox->Expand("powershell -exec bypass -nop -File \"%SbieHome%\\addons\\FileChecker\\CheckFile.ps1\" -bin"));
if (!pBox.isNull()) {
foreach(const QString & Value, pBox->GetTextList("OnFileRecovery", true, false, true)) {

View File

@ -272,8 +272,8 @@ void CSbieView::CreateMenu()
m_pMenuMarkLinger->setCheckable(true);
m_pMenuMarkLeader = m_pMenuPreset->addAction(tr("Set Leader Process"), this, SLOT(OnProcessAction()));
m_pMenuMarkLeader->setCheckable(true);
//m_pMenuSuspend = m_pMenuProcess->addAction(tr("Suspend"), this, SLOT(OnProcessAction()));
//m_pMenuResume = m_pMenuProcess->addAction(tr("Resume"), this, SLOT(OnProcessAction()));
m_pMenuSuspend = m_pMenuProcess->addAction(tr("Suspend"), this, SLOT(OnProcessAction()));
m_pMenuResume = m_pMenuProcess->addAction(tr("Resume"), this, SLOT(OnProcessAction()));
}
void CSbieView::CreateOldMenu()
@ -607,7 +607,7 @@ bool CSbieView::UpdateMenu(bool bAdvanced, const CSandBoxPtr &pBox, int iSandBox
return bBoxBusy == false;
}
void CSbieView::UpdateProcMenu(const CBoxedProcessPtr& pProcess, int iProcessCount)
void CSbieView::UpdateProcMenu(const CBoxedProcessPtr& pProcess, int iProcessCount, int iSuspendedCount)
{
m_pMenuLinkTo->setEnabled(iProcessCount == 1);
@ -640,8 +640,8 @@ void CSbieView::UpdateProcMenu(const CBoxedProcessPtr& pProcess, int iProcessCou
m_pMenuMarkLeader->setChecked(pProcess.objectCast<CSbieProcess>()->IsLeaderProgram());
}
//m_pMenuSuspend->setEnabled(iProcessCount > iSuspendedCount);
//m_pMenuResume->setEnabled(iSuspendedCount > 0);
m_pMenuSuspend->setEnabled(iProcessCount > iSuspendedCount);
m_pMenuResume->setEnabled(iSuspendedCount > 0);
}
bool CSbieView::UpdateMenu()
@ -655,7 +655,7 @@ bool CSbieView::UpdateMenu()
int iProcessCount = 0;
int iSandBoxeCount = 0;
int iGroupe = 0;
//int iSuspendedCount = 0;
int iSuspendedCount = 0;
QModelIndexList Rows = m_pSbieTree->selectedRows();
foreach(const QModelIndex& Index, Rows)
{
@ -666,8 +666,8 @@ bool CSbieView::UpdateMenu()
{
m_CurProcesses.append(pProcess);
iProcessCount++;
//if (pProcess->IsSuspended())
// iSuspendedCount++;
if (pProcess->IsSuspended())
iSuspendedCount++;
}
else
{
@ -699,7 +699,7 @@ bool CSbieView::UpdateMenu()
m_pDelGroupe->setVisible(iGroupe > 0 && iSandBoxeCount == 0 && iProcessCount == 0);
if (!pProcess.isNull())
UpdateProcMenu(pProcess, iProcessCount);
UpdateProcMenu(pProcess, iProcessCount, iSuspendedCount);
return UpdateMenu(bAdvanced, pBox, iSandBoxeCount, bBoxBusy);
}
@ -1596,10 +1596,10 @@ void CSbieView::OnProcessAction(QAction* Action, const QList<CBoxedProcessPtr>&
pProcess.objectCast<CSbieProcess>()->SetLingeringProgram(m_pMenuMarkLinger->isChecked());
else if (Action == m_pMenuMarkLeader)
pProcess.objectCast<CSbieProcess>()->SetLeaderProgram(m_pMenuMarkLeader->isChecked());
/*else if (Action == m_pMenuSuspend)
else if (Action == m_pMenuSuspend)
Results.append(pProcess->SetSuspend(true));
else if (Action == m_pMenuResume)
Results.append(pProcess->SetSuspend(false));*/
Results.append(pProcess->SetSuspend(false));
}
theGUI->CheckResults(Results, this);

View File

@ -124,7 +124,7 @@ private:
void CreateTrayMenu();
bool UpdateMenu(bool bAdvanced, const CSandBoxPtr &pBox, int iSandBoxeCount = 1, bool bBoxBusy = false);
void UpdateProcMenu(const CBoxedProcessPtr &pProcess = CBoxedProcessPtr(), int iProcessCount = 0);
void UpdateProcMenu(const CBoxedProcessPtr &pProcess = CBoxedProcessPtr(), int iProcessCount = 0, int iSuspendedCount = 0);
bool UpdateMenu();
void UpdateMoveMenu();
void RenameGroup(const QString OldName, const QString NewName);
@ -212,8 +212,8 @@ private:
QAction* m_pMenuMarkLinger;
QAction* m_pMenuMarkLeader;
QAction* m_pMenuPinToRun;
//QAction* m_pMenuSuspend;
//QAction* m_pMenuResume;
QAction* m_pMenuSuspend;
QAction* m_pMenuResume;
QAction* m_pRemove;

View File

@ -378,7 +378,7 @@ void CTraceView::SetEnabled(bool bSet)
void CTraceView::OnShowStack()
{
if (!theGUI->GetAddonManager()->HasAddon("DbgHelp"))
if (!theGUI->GetAddonManager()->GetAddon("DbgHelp", CAddonManager::eInstalled).isNull())
theGUI->GetAddonManager()->TryInstallAddon("DbgHelp", this, tr("To use the stack traces feature the DbgHelp.dll and SymSrv.dll are required, do you want to download and install them?"));
theAPI->GetGlobalSettings()->SetBool("MonitorStackTrace", m_pShowStack->isChecked());
m_pTrace->m_pStackView->setVisible(m_pShowStack->isChecked());

View File

@ -1536,15 +1536,21 @@ void CSettingsWindow::OnOptChanged()
void CSettingsWindow::OnLoadAddon()
{
ui.treeAddons->clear();
foreach(const CAddonPtr pAddon, theGUI->GetAddonManager()->GetAddons()) {
foreach(const CAddonInfoPtr pAddon, theGUI->GetAddonManager()->GetAddons()) {
QTreeWidgetItem* pItem = new QTreeWidgetItem;
pItem->setText(0, pAddon->GetLocalizedEntry("name"));
if(!pAddon->Data["mandatory"].toBool())
pItem->setData(0, Qt::UserRole, pAddon->Id);
pItem->setIcon(0, pAddon->Data.contains("icon") ? CSandMan::GetIcon(pAddon->Data["icon"].toString()) : CSandMan::GetIcon("Addon"));
pItem->setText(1, pAddon->Installed ? tr("Installed") : "");
pItem->setText(2, pAddon->GetLocalizedEntry("description"));
if (pAddon->Installed) {
if(!pAddon->UpdateVersion.isEmpty())
pItem->setText(1, tr("Update Available"));
else
pItem->setText(1, tr("Installed"));
}
pItem->setText(2, pAddon->Data["version"].toString());
pItem->setText(3, pAddon->GetLocalizedEntry("description"));
ui.treeAddons->addTopLevelItem(pItem);
}

View File

@ -96,7 +96,7 @@ void CBoxAssistant::OnToggleDebugger()
{
m_bUseDebugger = !m_bUseDebugger;
if (m_bUseDebugger && !theGUI->GetAddonManager()->HasAddon("V4dbg"))
if (m_bUseDebugger && theGUI->GetAddonManager()->GetAddon("V4dbg", CAddonManager::eInstalled).isNull())
theGUI->GetAddonManager()->TryInstallAddon("V4dbg", this, tr("To debug troubleshooting scripts you need the V4 Script Debugger addon, do you want to download and install it?"));
QString title = windowTitle();

View File

@ -2,8 +2,8 @@
#define VERSION_MJR 1
#define VERSION_MIN 10
#define VERSION_REV 1
#define VERSION_UPD 1
#define VERSION_REV 2
#define VERSION_UPD 0
#ifndef STR
#define STR2(X) #X

View File

@ -876,7 +876,7 @@ std::wstring JSONValue::StringifyString(const std::wstring &str)
{
wchar_t chr = *iter;
if (chr == L'"' || chr == L'\\' || chr == L'/')
if (chr == L'"' || chr == L'\\' /*|| chr == L'/'*/)
{
str_out += L'\\';
str_out += chr;

View File

@ -279,7 +279,6 @@ NTSTATUS MyHashBuffer(
)
{
NTSTATUS status;
IO_STATUS_BLOCK iosb;
MY_HASH_OBJ hashObj;
if (!NT_SUCCESS(status = MyInitHash(&hashObj)))

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
#define UPDATE_DOMAIN "sandboxie-plus.com"
#define UPDATE_FILE "update.json"
#define ADDONS_FILE "addons.json"
#define ADDONS_PATH "\\addons\\"
#define SCOPE_CORE_FILES L"32\\SbieDll.dll\0"\
@ -33,5 +35,10 @@
#define ERROR_EXEC (-8)
#define ERROR_CANCELED (-9)
#define ERROR_INTERNAL (-10) // internal error, should not happen
#define ERROR_NO_ADDON (-11) // addon not found
#define ERROR_NO_ADDON2 (-12) // no addon for this paltform or framework
#define ERROR_BAD_ADDON (-13)
#define ERROR_BAD_ADDON2 (-14)
#define ERROR_DELETE (-15)

View File

@ -126,11 +126,12 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies);Version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -147,7 +148,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies);Version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -156,11 +157,12 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies);Version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
@ -169,11 +171,12 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies);Version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -190,7 +193,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies);Version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
@ -207,7 +210,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>noenv.obj;ntdll.lib;bcrypt.lib;wininet.lib;winhttp.lib;%(AdditionalDependencies);Version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>