Sandboxie/SandboxiePlus/QSbieAPI/Sandboxie/SbieTemplates.cpp

535 lines
13 KiB
C++

/*
*
* Copyright (c) 2020, David Xanatos
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser 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 <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
#include "SbieTemplates.h"
#include "../SbieAPI.h"
#include "../SbieUtils.h"
#include <ntstatus.h>
#define WIN32_NO_STATUS
typedef long NTSTATUS;
#include <windows.h>
#include "SbieDefs.h"
#include "..\..\Sandboxie\common\win32_ntddk.h"
#include "..\..\Sandboxie\core\drv\api_flags.h"
CSbieTemplates::CSbieTemplates(CSbieAPI* pAPI, QObject* paretn)
: QObject(paretn)
{
m_pAPI = pAPI;
InitExpandPaths(false);
InitExpandPaths(true);
}
void CSbieTemplates::RunCheck()
{
CollectObjects();
CollectClasses();
CollectServices();
CollectProducts();
CollectTemplates();
QStringList Used = m_pAPI->GetGlobalSettings()->GetTextList("Template", false);
QStringList Rejected = m_pAPI->GetGlobalSettings()->GetTextList("TemplateReject", false);
for(QMap<QString, int>::iterator I = m_Templates.begin(); I != m_Templates.end(); ++I)
{
int Value = eNone;
if (Used.contains(I.key(), Qt::CaseInsensitive))
Value |= eEnabled;
if (CheckTemplate(I.key()))
Value |= eRequired;
if (Rejected.contains(I.key() , Qt::CaseInsensitive))
Value |= eDisabled;
I.value() = Value;
}
}
void CSbieTemplates::CollectTemplates()
{
m_Templates.clear();
QStringList Templates;
Templates.append(GetTemplateNames("EmailReader"));
Templates.append(GetTemplateNames("Print"));
Templates.append(GetTemplateNames("Security"));
Templates.append(GetTemplateNames("Desktop"));
Templates.append(GetTemplateNames("Download"));
Templates.append(GetTemplateNames("Misc"));
Templates.append(GetTemplateNames("WebBrowser"));
Templates.append(GetTemplateNames("MediaPlayer"));
Templates.append(GetTemplateNames("TorrentClient"));
foreach(const QString& Template, Templates)
m_Templates.insert(Template, 0);
}
void CSbieTemplates::SetCheckResult(const QStringList& Result)
{
CollectTemplates();
QStringList Used = m_pAPI->GetGlobalSettings()->GetTextList("Template", false);
QStringList Rejected = m_pAPI->GetGlobalSettings()->GetTextList("TemplateReject", false);
for(QMap<QString, int>::iterator I = m_Templates.begin(); I != m_Templates.end(); ++I)
{
int Value = eNone;
if (Used.contains(I.key(), Qt::CaseInsensitive))
Value |= eEnabled;
if (Result.contains(I.key()))
Value |= eRequired;
if (Rejected.contains(I.key() , Qt::CaseInsensitive))
Value |= eDisabled;
I.value() = Value;
}
}
bool CSbieTemplates::GetCheckState()
{
for (QMap<QString, int>::iterator I = m_Templates.begin(); I != m_Templates.end(); ++I)
{
if ((I.value() & eRequired) != 0 && (I.value() & eConfigured) == 0)
return true;
}
return false;
}
void CSbieTemplates::Reset()
{
m_Objects.clear();
m_Classes.clear();
m_Services.clear();
m_Products.clear();
}
QStringList CSbieTemplates::GetObjects()
{
if (m_Objects.isEmpty())
CollectObjects();
return m_Objects;
}
QStringList CSbieTemplates::GetClasses()
{
if (m_Classes.isEmpty())
CollectClasses();
return m_Classes;
}
QStringList CSbieTemplates::GetServices()
{
if (m_Services.isEmpty())
CollectServices();
return m_Services;
}
QStringList CSbieTemplates::GetProducts()
{
if (m_Products.isEmpty())
CollectProducts();
return m_Products;
}
void CSbieTemplates::CollectObjects()
{
m_Objects.clear();
QStringList objdirs;
objdirs.append("\\BaseNamedObjects");
objdirs.append("\\Sessions");
objdirs.append("\\RPC Control");
objdirs.append("\\Device");
static const WCHAR *WantedTypes[] = {
L"Directory",
L"Event", L"Mutant", L"Section", L"Semaphore",
L"Port", L"ALPC Port",
L"Device",
NULL
};
ULONG info_len = 0x8000;
OBJECT_DIRECTORY_INFORMATION *info = (OBJECT_DIRECTORY_INFORMATION *)malloc(info_len);
foreach(const QString objdir, objdirs)
{
std::wstring wobjdir = objdir.toStdWString();
if (wobjdir.substr(0,10) == L"\\Sessions\\" && wobjdir.length() <= 13)
wobjdir += L"\\BaseNamedObjects";
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING objname;
InitializeObjectAttributes(&objattrs, &objname, OBJ_CASE_INSENSITIVE, NULL, NULL);
RtlInitUnicodeString(&objname, wobjdir.c_str());
HANDLE handle;
NTSTATUS status = NtOpenDirectoryObject(&handle, DIRECTORY_QUERY, &objattrs);
if (!NT_SUCCESS(status))
continue;
for (int i = 0; i < 10; i++)
{
ULONG context;
ULONG len;
status = NtQueryDirectoryObject(handle, info, info_len, FALSE, TRUE, &context, &len);
if (status == STATUS_MORE_ENTRIES || status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_BUFFER_TOO_SMALL)
{
free(info);
info_len *= 2;
info = (OBJECT_DIRECTORY_INFORMATION *)malloc(info_len);
continue;
}
break;
}
NtClose(handle);
if (!NT_SUCCESS(status))
continue;
for (OBJECT_DIRECTORY_INFORMATION* info_ptr = info; info_ptr->Name.Buffer; info_ptr++)
{
int i;
for (i = 0; WantedTypes[i]; ++i) {
if (_wcsicmp(info_ptr->TypeName.Buffer, WantedTypes[i]) == 0)
break;
}
if (!WantedTypes[i])
continue;
QString objpath = objdir + "\\" + QString::fromWCharArray(info_ptr->Name.Buffer);
if (i == 0)
objdirs.append(objpath);
else
m_Objects.append(objpath.toLower());
}
}
free(info);
}
void CSbieTemplates::CollectClasses()
{
m_Classes.clear();
EnumWindows([](HWND hwnd, LPARAM lparam)
{
WCHAR clsnm[256];
GetClassName(hwnd, clsnm, 250);
clsnm[250] = L'\0';
if (clsnm[0] && wcsncmp(clsnm, L"Sandbox:", 8) != 0)
{
_wcslwr(clsnm);
((CSbieTemplates*)lparam)->m_Classes.append(QString::fromWCharArray(clsnm));
}
return TRUE;
}, (LPARAM)this);
}
void CSbieTemplates::CollectServices()
{
m_Services.clear();
SC_HANDLE hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (!hManager)
return;
ULONG info_len = 10240;
ENUM_SERVICE_STATUS* info = (ENUM_SERVICE_STATUS *)malloc(info_len);
ULONG ResumeHandle = 0;
for(;;)
{
ULONG len;
ULONG num;
BOOL ret = EnumServicesStatus(hManager, SERVICE_TYPE_ALL, SERVICE_STATE_ALL, info, info_len, &len, &num, &ResumeHandle);
if (!ret && GetLastError() != ERROR_MORE_DATA)
break;
for (ULONG i = 0; i < num; ++i)
{
_wcslwr(info[i].lpServiceName);
m_Services.append(QString::fromWCharArray(info[i].lpServiceName));
}
if (ret)
break;
}
free(info);
CloseServiceHandle(hManager);
}
void CSbieTemplates::CollectProducts()
{
BOOL is64BitOperatingSystem;
#ifdef _WIN64
is64BitOperatingSystem = TRUE;
#else // ! _WIN64
is64BitOperatingSystem = CSbieAPI::IsWow64();
#endif _WIN64
m_Products.clear();
ULONG DesiredAccess = KEY_READ;
for(;;)
{
HKEY hkey;
LONG rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall", 0, DesiredAccess, &hkey);
if (rc != 0)
continue;
WCHAR name[128];
for(ULONG index = 0; rc != ERROR_NO_MORE_ITEMS; index++)
{
ULONG name_len = 120;
rc = RegEnumKeyEx(hkey, index, name, &name_len, NULL, NULL, NULL, NULL);
if (rc == 0) {
_wcslwr(name);
m_Products.append(QString::fromWCharArray(name));
}
}
RegCloseKey(hkey);
#ifdef _WIN64
if (DesiredAccess & KEY_WOW64_32KEY)
break;
DesiredAccess |= KEY_WOW64_32KEY;
#else // ! _WIN64
if (!is64BitOperatingSystem || (DesiredAccess & KEY_WOW64_64KEY))
break;
DesiredAccess |= KEY_WOW64_64KEY;
#endif _WIN64
}
}
QStringList CSbieTemplates::GetTemplateNames(const QString& forClass)
{
QStringList list;
ULONG buf_len = sizeof(WCHAR) * CONF_LINE_LEN;
WCHAR *buf = (WCHAR*)malloc(buf_len);
BOOL all_classes = (forClass.compare("*") == 0);
for(int index = 0;; index++)
{
QString section = m_pAPI->SbieIniGet(QString(), QString(), index);
if (section.isEmpty())
break;
if (section.left(9).compare("Template_", Qt::CaseInsensitive) != 0)
continue;
QString value = m_pAPI->SbieIniGet(section, "Tmpl.Class", CONF_GET_NO_GLOBAL);
if (!value.isEmpty() && (all_classes || forClass.compare(value, Qt::CaseInsensitive) == 0))
list.append(section.mid(9));
}
free(buf);
return list;
}
bool CSbieTemplates::CheckTemplate(const QString& Name)
{
QSharedPointer<CSbieIni> pTemplate = QSharedPointer<CSbieIni>(new CSbieIni("Template_" + Name, m_pAPI));
QString scan = pTemplate->GetText("Tmpl.Scan", QString(), false, false, true);
BOOL scanIpc = (scan.indexOf(L'i') != -1);
BOOL scanWin = (scan.indexOf(L'w') != -1);
BOOL scanSvc = (scan.indexOf(L's') != -1);
if (!(scanIpc || scanWin || scanSvc))
return false;
QList<QPair<QString, QString>> settings = pTemplate->GetIniSection(0, true);
for(QList<QPair<QString, QString>>::iterator I = settings.begin(); I != settings.end(); ++I)
{
QString setting = I->first;
QString value = I->second;
if (scanIpc && ((setting.compare("OpenIpcPath", Qt::CaseInsensitive) == 0) || setting.compare("Tmpl.ScanIpc", Qt::CaseInsensitive) == 0))
{
if (value.compare("\\RPC Control\\epmapper") == 0)
continue;
if (value.compare("\\RPC Control\\OLE*") == 0)
continue;
if (value.compare("\\RPC Control\\LRPC*") == 0)
continue;
if (value.compare("*\\BaseNamedObjects*\\NamedBuffer*mAH*Process*API*") == 0)
continue;
if (CheckObjects(value))
return true;
}
else if (scanWin && ((setting.compare("OpenWinClass", Qt::CaseInsensitive) == 0 || setting.compare("Tmpl.ScanWinClass", Qt::CaseInsensitive) == 0)))
{
// skip to unspecific entries
if(value.left(2).compare("*:") == 0)
continue;
if (CheckClasses(value))
return true;
}
else if (scanSvc && setting.compare("Tmpl.ScanService", Qt::CaseInsensitive) == 0)
{
if (CheckServices(value))
return true;
}
else if (scanSvc && setting.compare("Tmpl.ScanProduct", Qt::CaseInsensitive) == 0)
{
if (CheckProducts(value))
return true;
}
else if (scanSvc && setting.compare("Tmpl.ScanKey", Qt::CaseInsensitive) == 0)
{
if (CheckRegistryKey(value))
return true;
}
else if (scanSvc && setting.compare("Tmpl.ScanFile", Qt::CaseInsensitive) == 0)
{
if (CheckFile(ExpandPath(value)))
return true;
}
}
return false;
}
bool CSbieTemplates::CheckRegistryKey(const QString& Value)
{
std::wstring keypath = Value.toStdWString();
OBJECT_ATTRIBUTES objattrs;
UNICODE_STRING objname;
RtlInitUnicodeString(&objname, keypath.c_str());
InitializeObjectAttributes(&objattrs, &objname, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE handle;
NTSTATUS status = NtOpenKey(&handle, KEY_QUERY_VALUE, &objattrs);
if (NT_SUCCESS(status))
{
NtClose(handle);
return true;
}
return false;
}
bool CSbieTemplates::CheckFile(const QString& Value)
{
std::wstring path = Value.toStdWString();
if (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES)
return true;
return false;
}
bool CSbieTemplates::CheckClasses(const QString& value)
{
QString Value = value.toLower();
for (auto I = m_Classes.begin(); I != m_Classes.end(); ++I)
{
if (CSbieUtils::WildCompare(Value, *I))
return true;
}
return false;
}
bool CSbieTemplates::CheckServices(const QString& value)
{
QString Value = value.toLower();
for (auto I = m_Services.begin(); I != m_Services.end(); ++I)
{
if (CSbieUtils::WildCompare(Value, *I))
return true;
}
return false;
}
bool CSbieTemplates::CheckProducts(const QString& value)
{
QString Value = value.toLower();
for (auto I = m_Products.begin(); I != m_Products.end(); ++I)
{
if (CSbieUtils::WildCompare(Value, *I))
return true;
}
return false;
}
bool CSbieTemplates::CheckObjects(const QString& value)
{
QString Value = value.toLower();
for (auto I = m_Objects.begin(); I != m_Objects.end(); ++I)
{
if (CSbieUtils::WildCompare(Value, *I))
return true;
}
return false;
}
void CSbieTemplates::InitExpandPaths(bool WithUser)
{
std::wstring keyPath(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\");
if (WithUser)
keyPath += L"User ";
keyPath += L"Shell Folders";
HKEY hkey;
LONG rc = RegOpenKey(HKEY_CURRENT_USER, keyPath.c_str(), &hkey);
for (ULONG index = 0; rc == 0; index++)
{
WCHAR name[64];
WCHAR value[MAX_PATH + 8];
ULONG name_len;
ULONG value_len;
ULONG type;
name_len = 60;
value_len = MAX_PATH + 4;
rc = RegEnumValue(hkey, index, name, &name_len, NULL, &type, (BYTE *)value, &value_len);
if (rc == 0 && (type == REG_SZ || type == REG_EXPAND_SZ))
{
WCHAR expand[MAX_PATH + 8];
ULONG len = ExpandEnvironmentStrings(value, expand, MAX_PATH + 4);
if (len > 0 && len <= MAX_PATH)
{
QString value = QString::fromWCharArray(expand);
if (!value.trimmed().isEmpty())
m_Expands[QString::fromWCharArray(name)] = value;
}
}
}
RegCloseKey(hkey);
}
QString CSbieTemplates::ExpandPath(QString path)
{
foreach(const QString& key, m_Expands.keys())
path.replace("%" + key + "%", m_Expands.value(key));
return path;
}