Sandboxie/SandboxiePlus/QSbieAPI/Sandboxie/SbieTemplates.cpp

479 lines
12 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 <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);
}
bool CSbieTemplates::RunCheck()
{
CollectObjects();
CollectClasses();
CollectServices();
CollectProducts();
CollectTemplates();
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::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)
{
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.push_back(objpath.toLower().toStdWString());
}
}
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.push_back(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.push_back(info[i].lpServiceName);
}
if (ret)
break;
}
free(info);
CloseServiceHandle(hManager);
}
void CSbieTemplates::CollectProducts()
{
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.push_back(name);
}
}
RegCloseKey(hkey);
#ifdef _WIN64
if (DesiredAccess & KEY_WOW64_32KEY)
break;
DesiredAccess |= KEY_WOW64_32KEY;
#else // ! _WIN64
break;
#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;
}
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);
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;
}
}
template <typename T>
const T* wildcmpex(const T* Wild, const T* Str)
{
const T *cp = NULL, *mp = NULL;
while ((*Str) && (*Wild != '*'))
{
if ((*Wild != *Str) && (*Wild != '?'))
return NULL;
Wild++;
Str++;
}
while (*Str)
{
if (*Wild == '*')
{
if (!*++Wild)
return Str;
mp = Wild;
cp = Str + 1;
}
else if ((*Wild == *Str) || (*Wild == '?'))
{
Wild++;
Str++;
}
else
{
Wild = mp;
Str = cp++;
}
}
while (*Wild == '*')
Wild++;
return *Wild ? NULL : Str;
}
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);
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;
list<wstring> Keys, Files;
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;
list<wstring> *List = NULL;
if (scanIpc && setting.compare("OpenIpcPath", Qt::CaseInsensitive) == 0)
List = &m_Objects;
else if (scanSvc && setting.compare("Tmpl.ScanIpc", Qt::CaseInsensitive) == 0)
List = &m_Objects;
else if (scanWin && setting.compare("OpenWinClass", Qt::CaseInsensitive) == 0)
List = &m_Classes;
else if (scanSvc && setting.compare("Tmpl.ScanWinClass", Qt::CaseInsensitive) == 0)
List = &m_Classes;
else if (scanSvc && setting.compare("Tmpl.ScanService", Qt::CaseInsensitive) == 0)
List = &m_Services;
else if (scanSvc && setting.compare("Tmpl.ScanProduct", Qt::CaseInsensitive) == 0)
List = &m_Products;
else if (scanSvc && setting.compare("Tmpl.ScanKey", Qt::CaseInsensitive) == 0)
List = &Keys;
else if (scanSvc && setting.compare("Tmpl.ScanFile", Qt::CaseInsensitive) == 0)
List = &Files;
else
continue;
QString value = I->second;
if(!value.isEmpty())
{
if (List == &Keys) {
if (CheckRegistryKey(value))
return true;
continue;
}
else if (List == &Files) {
if (CheckFile(value))
return true;
continue;
}
// skip to unspecific entries
if (List == &m_Classes)
{
if(value.left(2).compare("*:") == 0)
continue;
}
if (List == &m_Objects)
{
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;
}
//
wstring wild = value.toLower().toStdWString();
for (list<wstring>::iterator I = List->begin(); I != List->end(); ++I)
{
if (wildcmpex(wild.c_str(), I->c_str()) != NULL)
return true;
}
}
}
return false;
}
bool CSbieTemplates::CheckRegistryKey(const QString& Value)
{
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)
{
wstring path = ExpandPath(Value).toStdWString();
if (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES)
return true;
return false;
}
void CSbieTemplates::InitExpandPaths(bool WithUser)
{
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;
}