1858 lines
50 KiB
C
1858 lines
50 KiB
C
/*
|
|
* Copyright 2004-2020 Sandboxie Holdings, LLC
|
|
* Copyright 2020-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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Shell
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "dll.h"
|
|
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <shlobj.h>
|
|
#include <stdio.h>
|
|
#include "dll.h"
|
|
#include "taskbar.h"
|
|
#include "common/win32_ntddk.h"
|
|
#include "common/my_shlwapi.h"
|
|
#include "msgs/msgs.h"
|
|
#include "gui_p.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Functions
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static BOOL SH_OpenFolder(const WCHAR *PathW, WCHAR verb, HANDLE *hProcess);
|
|
|
|
static WCHAR *SH32_AdjustPath(WCHAR *src, WCHAR **pArgs);
|
|
|
|
static HKEY SbieDll_AssocQueryKeyWow64(const WCHAR *subj);
|
|
|
|
static BOOL SH32_ShellExecuteExW(SHELLEXECUTEINFOW *lpExecInfo);
|
|
|
|
static BOOL SH32_Shell_NotifyIconW(
|
|
DWORD dwMessage, PNOTIFYICONDATAW lpData);
|
|
|
|
static WCHAR *SbieDll_AssocQueryCommandInternal(
|
|
const WCHAR *subj, const WCHAR *verb);
|
|
|
|
static ULONG SH32_SHChangeNotifyRegister(
|
|
HWND hwnd, int fSources, LONG fEvents, UINT wMsg,
|
|
int cEntries, SHChangeNotifyEntry *pfsne);
|
|
|
|
static ULONG SH32_SHOpenFolderAndSelectItems(
|
|
LPCITEMIDLIST pidlFolder, UINT cidl, void *apidl, ULONG dwFlags);
|
|
|
|
static BOOLEAN SH32_SHOpenFolderAndSelectItems_2(
|
|
LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild, WCHAR *path);
|
|
|
|
static ULONG SH32_GetPrivateProfileStringW(
|
|
void *lpAppName, void *lpKeyName, void *lpDefault,
|
|
void *lpReturnedString, ULONG nSize, void *lpFileName);
|
|
|
|
static HRESULT SH32_SHGetFolderLocation(
|
|
HWND hwndOwner, int nFolder, HANDLE hToken,
|
|
DWORD dwReserved, void **ppidl);
|
|
|
|
static NTSTATUS SH32_LdrGetDllHandleEx(
|
|
ULONG_PTR One, ULONG_PTR Unknown1, ULONG_PTR Unknown2,
|
|
UNICODE_STRING *DllName, ULONG_PTR *hModule);
|
|
|
|
static ULONG SH_WindowMonitorThread(void *lpParameter);
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
typedef BOOL (*P_ShellExecuteEx)(
|
|
void *lpExecInfo);
|
|
|
|
typedef BOOL (*P_Shell_NotifyIconW)(
|
|
DWORD dwMessage, PNOTIFYICONDATAW lpData);
|
|
|
|
typedef ULONG (*P_SHChangeNotifyRegister)(
|
|
HWND hwnd, int fSources, LONG fEvents, UINT wMsg,
|
|
int cEntries, SHChangeNotifyEntry *pfsne);
|
|
|
|
typedef ULONG (*P_SHOpenFolderAndSelectItems)(
|
|
LPCITEMIDLIST pidlFolder, UINT cidl, void *apidl, ULONG dwFlags);
|
|
|
|
typedef BOOL (*P_SHGetPathFromIDList)(
|
|
LPCITEMIDLIST pidl, void *pszPath);
|
|
|
|
typedef ULONG (*P_GetPrivateProfileString)(
|
|
void *lpAppName, void *lpKeyName, void *lpDefault,
|
|
void *lpReturnedString, ULONG nSize, void *lpFileName);
|
|
|
|
typedef HRESULT (*P_SHGetFolderLocation)(
|
|
HWND hwndOwner, int nFolder, HANDLE hToken,
|
|
DWORD dwReserved, void **ppidl);
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static P_ShellExecuteEx __sys_ShellExecuteExW = NULL;
|
|
|
|
static P_Shell_NotifyIconW __sys_Shell_NotifyIconW = NULL;
|
|
|
|
static P_SHChangeNotifyRegister __sys_SHChangeNotifyRegister = NULL;
|
|
|
|
static P_SHOpenFolderAndSelectItems
|
|
__sys_SHOpenFolderAndSelectItems = NULL;
|
|
|
|
static P_SHGetPathFromIDList __sys_SHGetPathFromIDListW = NULL;
|
|
|
|
static P_GetPrivateProfileString __sys_GetPrivateProfileStringW = NULL;
|
|
|
|
static P_SHGetFolderLocation __sys_SHGetFolderLocation = NULL;
|
|
|
|
extern P_LdrGetDllHandleEx __sys_LdrGetDllHandleEx;
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Variables
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
extern const WCHAR *File_BQQB;
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_IsDirectory
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN SbieDll_IsDirectory(const WCHAR *PathW)
|
|
{
|
|
NTSTATUS status;
|
|
WCHAR *ntpath, *dummy;
|
|
ULONG len1, len2;
|
|
UNICODE_STRING uni;
|
|
OBJECT_ATTRIBUTES objattrs;
|
|
FILE_NETWORK_OPEN_INFORMATION openinfo;
|
|
BOOLEAN isdir;
|
|
|
|
//
|
|
// otherwise check if the path is a directory
|
|
//
|
|
|
|
len1 = 128;
|
|
ntpath = Dll_Alloc((len1 + 16) * sizeof(WCHAR));
|
|
len2 = GetFullPathName(PathW, len1, ntpath, &dummy);
|
|
if (len2 >= len1) {
|
|
Dll_Free(ntpath);
|
|
len1 = len2;
|
|
ntpath = Dll_Alloc((len1 + 16) * sizeof(WCHAR));
|
|
len2 = GetFullPathName(PathW, len1, ntpath, &dummy);
|
|
}
|
|
if (len2 == 0)
|
|
return FALSE;
|
|
|
|
if (len2 == 2 || len2 == 3) {
|
|
WCHAR ch0 = towlower(PathW[0]);
|
|
WCHAR ch1 = PathW[1];
|
|
WCHAR ch2 = PathW[2];
|
|
if (ch0 >= L'a' && ch0 <= L'z' && ch1 == L':' &&
|
|
(ch2 == L'\\' || ch2 == L'\0'))
|
|
return TRUE;
|
|
}
|
|
|
|
wmemmove(ntpath + 4, ntpath, wcslen(ntpath) + 1);
|
|
wmemcpy(ntpath, File_BQQB, 4);
|
|
|
|
RtlInitUnicodeString(&uni, ntpath);
|
|
InitializeObjectAttributes(
|
|
&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
status = NtQueryFullAttributesFile(&objattrs, &openinfo);
|
|
|
|
isdir = FALSE;
|
|
if (status == STATUS_NO_MEDIA_IN_DEVICE)
|
|
isdir = TRUE;
|
|
else if (status == STATUS_SUCCESS &&
|
|
(openinfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
isdir = TRUE;
|
|
|
|
Dll_Free(ntpath);
|
|
|
|
return isdir;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH_OpenFolder
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOL SH_OpenFolder(const WCHAR *PathW, WCHAR verb, HANDLE *hProcess)
|
|
{
|
|
const WCHAR *ErrorText = NULL;
|
|
WCHAR cmd[512];
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
*hProcess = NULL;
|
|
|
|
//
|
|
// set up command line to invoke Windows Explorer
|
|
//
|
|
|
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
si.dwFlags = STARTF_FORCEOFFFEEDBACK;
|
|
|
|
cmd[0] = L'\"';
|
|
GetSystemWindowsDirectory(&cmd[1], MAX_PATH);
|
|
wcscat(cmd, L"\\explorer.exe\" ");
|
|
if (verb == L'e')
|
|
wcscat(cmd, L"/e,");
|
|
else if (verb == L's')
|
|
wcscat(cmd, L"/n,/select,");
|
|
wcscat(cmd, L"\"");
|
|
wcscat(cmd, PathW);
|
|
if (cmd[wcslen(cmd) - 1] == L'\\')
|
|
cmd[wcslen(cmd) - 1] = L'\0';
|
|
wcscat(cmd, L"\"");
|
|
|
|
if (! CreateProcess(
|
|
NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
|
|
|
ULONG err = GetLastError();
|
|
SbieApi_Log(2210, L"%S (%d)", PathW, err);
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_AdjustPath
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX WCHAR *SH32_AdjustPath(WCHAR *src, WCHAR **pArgs)
|
|
{
|
|
WCHAR *ptr, *dst;
|
|
ULONG len;
|
|
|
|
//
|
|
// if src does not begin with a quote, we return it as-is,
|
|
// but we also check if there are any parameters
|
|
//
|
|
|
|
if (! src)
|
|
return src;
|
|
|
|
if (*src != L'\"') {
|
|
ptr = wcschr(src, L' ');
|
|
if (ptr)
|
|
*pArgs = ptr + 1;
|
|
return src;
|
|
}
|
|
|
|
//
|
|
// src is a quoted string so remove the quotes,
|
|
// and also check if there are any parameters
|
|
//
|
|
|
|
ptr = wcschr(src + 1, L'\"');
|
|
if (! ptr)
|
|
return src;
|
|
++ptr;
|
|
if (*ptr == L' ')
|
|
*pArgs = ptr + 1;
|
|
|
|
len = (ULONG)(ULONG_PTR)(ptr - src - 2);
|
|
dst = HeapAlloc(GetProcessHeap(), 0, (len + 4) * sizeof(WCHAR));
|
|
if (! dst)
|
|
return src;
|
|
wmemcpy(dst, src + 1, len);
|
|
dst[len] = L'\0';
|
|
|
|
return dst;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_ShellExecuteExW
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOL SH32_ShellExecuteExW(SHELLEXECUTEINFOW *lpExecInfo)
|
|
{
|
|
THREAD_DATA *TlsData = Dll_GetTlsData(NULL);
|
|
BOOL CallSystem = TRUE;
|
|
const WCHAR *verb;
|
|
WCHAR *path, *pArgs = NULL;
|
|
BOOL FreePath = FALSE;
|
|
BOOL b;
|
|
ULONG err;
|
|
BOOLEAN is_explore_verb;
|
|
|
|
//
|
|
// check if the request is to open a directory
|
|
//
|
|
|
|
++TlsData->sh32_shell_execute;
|
|
|
|
verb = lpExecInfo->lpVerb;
|
|
|
|
is_explore_verb = (verb && _wcsicmp(verb, L"explore") == 0);
|
|
if ((! verb) || (! verb[0]) || is_explore_verb ||
|
|
_wcsicmp(verb, L"open") == 0) {
|
|
|
|
if (lpExecInfo->fMask & SEE_MASK_IDLIST) {
|
|
path = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS,
|
|
(MAX_PATH + 8) * sizeof(WCHAR));
|
|
FreePath = TRUE;
|
|
if (! __sys_SHGetPathFromIDListW(lpExecInfo->lpIDList, path))
|
|
path[0] = L'\0';
|
|
|
|
} else {
|
|
|
|
path = SH32_AdjustPath((WCHAR *)lpExecInfo->lpFile, &pArgs);
|
|
if (path != lpExecInfo->lpFile)
|
|
FreePath = TRUE;
|
|
}
|
|
|
|
if (SbieDll_IsDirectory(path))
|
|
CallSystem = FALSE;
|
|
}
|
|
|
|
//
|
|
// call the system in case this is not a directory,
|
|
// or call explorer.exe if this is a directory
|
|
//
|
|
|
|
err = 0;
|
|
if (CallSystem) {
|
|
|
|
b = __sys_ShellExecuteExW(lpExecInfo);
|
|
if (! b) {
|
|
err = GetLastError();
|
|
|
|
//
|
|
// Adobe Flash Installer calls ShellExecuteEx with a quoted
|
|
// lpFile which also includes parameters:
|
|
// "C:\ProgramData\NOS\...\flash_install_ax.exe" /s
|
|
// for some reason, sandboxed ShellExecuteEx fails to locate
|
|
// the file in this case, so we try again with an unquoted
|
|
// lpFile, and split the parameters into lpParameters
|
|
//
|
|
|
|
if ((err == ERROR_FILE_NOT_FOUND) &&
|
|
FreePath && pArgs && (! lpExecInfo->lpParameters)) {
|
|
|
|
SHELLEXECUTEINFOW MyExecInfo;
|
|
|
|
memcpy(&MyExecInfo, lpExecInfo, sizeof(SHELLEXECUTEINFOW));
|
|
MyExecInfo.lpFile = path;
|
|
MyExecInfo.lpParameters = pArgs;
|
|
|
|
b = __sys_ShellExecuteExW(&MyExecInfo);
|
|
if (! b)
|
|
err = GetLastError();
|
|
|
|
lpExecInfo->hInstApp = MyExecInfo.hInstApp;
|
|
lpExecInfo->hProcess = MyExecInfo.hProcess;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
b = SH_OpenFolder(
|
|
path, (is_explore_verb ? L'e' : 0), &lpExecInfo->hProcess);
|
|
|
|
//
|
|
// set and return results like the real ShellExecuteExW
|
|
//
|
|
|
|
if (b)
|
|
lpExecInfo->hInstApp = (HINSTANCE)33;
|
|
else {
|
|
lpExecInfo->hInstApp = (HINSTANCE)SE_ERR_FNF;
|
|
err = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// finish
|
|
//
|
|
|
|
if (FreePath)
|
|
HeapFree(GetProcessHeap(), 0, path);
|
|
|
|
--TlsData->sh32_shell_execute;
|
|
|
|
SetLastError(err);
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_BornderToIcon
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
HICON SH32_BorderToIcon(HICON hIcon, COLORREF color)
|
|
{
|
|
typedef HDC(*P_GetDC)(HWND hWnd);
|
|
typedef int(*P_ReleaseDC)(HWND hWnd, HDC hDC);
|
|
typedef BOOL(*P_GetIconInfo)(HICON hIcon, PICONINFO piconinfo);
|
|
typedef HICON(*P_CreateIconIndirect)(PICONINFO piconinfo);
|
|
|
|
typedef HDC(*P_CreateCompatibleDC)(HDC hdc);
|
|
typedef HGDIOBJ(*P_SelectObject)(HDC hdc, HGDIOBJ h);
|
|
typedef COLORREF(*P_GetPixel)(HDC hdc, int x, int y);
|
|
typedef COLORREF(*P_SetPixel)(HDC hdc, int x, int y, COLORREF color);
|
|
typedef BOOL(*P_DeleteObject)(HGDIOBJ ho);
|
|
typedef BOOL(*P_DeleteDC)(HDC hdc);
|
|
|
|
GET_WIN_API(GetDC, DllName_user32);
|
|
GET_WIN_API(ReleaseDC, DllName_user32);
|
|
GET_WIN_API(GetIconInfo, DllName_user32);
|
|
GET_WIN_API(CreateIconIndirect, DllName_user32);
|
|
|
|
GET_WIN_API(CreateCompatibleDC, DllName_gdi32);
|
|
GET_WIN_API(SelectObject, DllName_gdi32);
|
|
GET_WIN_API(GetPixel, DllName_gdi32);
|
|
GET_WIN_API(SetPixel, DllName_gdi32);
|
|
GET_WIN_API(DeleteObject, DllName_gdi32);
|
|
GET_WIN_API(DeleteDC, DllName_gdi32);
|
|
|
|
|
|
HICON hNewIcon = NULL;
|
|
HDC hMainDC = NULL, hMemDC1 = NULL, hMemDC3 = NULL;
|
|
HBITMAP hOldBmp1 = NULL, hOldBmp3 = NULL;
|
|
ICONINFO csII;
|
|
|
|
if (!GetIconInfo(hIcon, &csII)) return NULL;
|
|
|
|
hMainDC = GetDC(NULL);
|
|
hMemDC1 = CreateCompatibleDC(hMainDC);
|
|
hMemDC3 = CreateCompatibleDC(hMainDC);
|
|
if (hMainDC == NULL || hMemDC1 == NULL || hMemDC3 == NULL) return NULL;
|
|
|
|
|
|
DWORD dwWidth = csII.xHotspot * 2;
|
|
DWORD dwHeight = csII.yHotspot * 2;
|
|
|
|
hOldBmp1 = (HBITMAP)SelectObject(hMemDC1, csII.hbmColor);
|
|
hOldBmp3 = (HBITMAP)SelectObject(hMemDC3, csII.hbmMask);
|
|
|
|
DWORD dwLoopY = 0, dwLoopX = 0;
|
|
COLORREF crPixel = 0;
|
|
|
|
for (dwLoopY = 0; dwLoopY < dwHeight; dwLoopY++)
|
|
{
|
|
for (dwLoopX = 0; dwLoopX < dwWidth; dwLoopX++)
|
|
{
|
|
crPixel = GetPixel(hMemDC1, dwLoopX, dwLoopY);
|
|
if (dwLoopY == 0 || dwLoopX == 0 || dwLoopY + 1 == dwHeight || dwLoopX + 1 == dwWidth)
|
|
crPixel = color; // RGB(255, 255, 0);
|
|
SetPixel(hMemDC1, dwLoopX, dwLoopY, crPixel);
|
|
|
|
crPixel = GetPixel(hMemDC3, dwLoopX, dwLoopY);
|
|
if (dwLoopY == 0 || dwLoopX == 0 || dwLoopY + 1 == dwHeight || dwLoopX + 1 == dwWidth)
|
|
crPixel = 0;
|
|
SetPixel(hMemDC3, dwLoopX, dwLoopY, crPixel);
|
|
}
|
|
}
|
|
|
|
SelectObject(hMemDC1, hOldBmp1);
|
|
SelectObject(hMemDC3, hOldBmp3);
|
|
|
|
hNewIcon = CreateIconIndirect(&csII);
|
|
|
|
|
|
DeleteObject(csII.hbmColor);
|
|
DeleteObject(csII.hbmMask);
|
|
DeleteDC(hMemDC1);
|
|
DeleteDC(hMemDC3);
|
|
ReleaseDC(NULL, hMainDC);
|
|
|
|
return hNewIcon;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_Shell_NotifyIconW
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOL SH32_Shell_NotifyIconW(
|
|
DWORD dwMessage, PNOTIFYICONDATAW lpData)
|
|
{
|
|
BOOL ret;
|
|
HICON icon = NULL;
|
|
|
|
if (dwMessage == NIM_ADD || dwMessage == NIM_MODIFY)
|
|
{
|
|
if (!Gui_DisableTitle && lpData && lpData->cbSize >= sizeof(PNOTIFYICONDATAW))
|
|
{
|
|
ULONG len = wcslen(lpData->szTip);
|
|
|
|
if (Gui_BoxNameTitleLen != 0 && (len + Gui_BoxNameTitleLen + 2) <= 127)
|
|
{
|
|
wmemmove(lpData->szTip + Gui_BoxNameTitleLen + 2, lpData->szTip, len + 1);
|
|
wmemcpy(lpData->szTip, Gui_BoxNameTitleW, Gui_BoxNameTitleLen);
|
|
wmemcpy(lpData->szTip + Gui_BoxNameTitleLen, L"\r\n", 2);
|
|
}
|
|
else
|
|
{
|
|
if (len + 8 > 127) {
|
|
lpData->szTip[127 - 8 - 3] = L'\0';
|
|
wcscat(lpData->szTip, L"...");
|
|
len = 127 - 8;
|
|
}
|
|
|
|
wmemmove(lpData->szTip + 4, lpData->szTip, len + 1);
|
|
wmemcpy(lpData->szTip, L"[#] ", 4);
|
|
wcscat(lpData->szTip, L" [#]");
|
|
}
|
|
}
|
|
|
|
COLORREF color;
|
|
if (SbieDll_GetBorderColor(NULL, &color, NULL, NULL))
|
|
{
|
|
HICON newIcon = SH32_BorderToIcon(lpData->hIcon, color);
|
|
if (newIcon) {
|
|
icon = lpData->hIcon;
|
|
lpData->hIcon = newIcon;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = __sys_Shell_NotifyIconW(dwMessage, lpData);
|
|
|
|
if (icon)
|
|
{
|
|
typedef BOOL(*P_DestroyIcon)(HICON hIcon);
|
|
P_DestroyIcon DestroyIcon = Ldr_GetProcAddrNew(DllName_user32, L"DestroyIcon", "DestroyIcon");
|
|
|
|
DestroyIcon(lpData->hIcon);
|
|
lpData->hIcon = icon;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_SHChangeNotifyRegister
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX ULONG SH32_SHChangeNotifyRegister(
|
|
HWND hwnd, int fSources, LONG fEvents, UINT wMsg,
|
|
int cEntries, SHChangeNotifyEntry *pfsne)
|
|
{
|
|
ULONG rc;
|
|
|
|
if (Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER && cEntries == 1) {
|
|
|
|
ULONG LastError = GetLastError();
|
|
|
|
WCHAR *path = Dll_AllocTemp((MAX_PATH + 16) * sizeof(WCHAR));
|
|
if (__sys_SHGetPathFromIDListW(pfsne->pidl, path + 4)) {
|
|
|
|
ULONG FileAttrs = GetFileAttributes(path + 4);
|
|
if ((FileAttrs != INVALID_FILE_ATTRIBUTES) &&
|
|
(FileAttrs & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
wmemcpy(path, File_BQQB, 4);
|
|
File_CreateBoxedPath(path);
|
|
}
|
|
}
|
|
|
|
Dll_Free(path);
|
|
SetLastError(LastError);
|
|
}
|
|
|
|
rc = __sys_SHChangeNotifyRegister(
|
|
hwnd, fSources, fEvents, wMsg, cEntries, pfsne);
|
|
|
|
if (! rc) {
|
|
|
|
//
|
|
// the File Open dialog box may get stuck if SHChangeNotifyRegister
|
|
// returns zero (i.e., error) due to not being able to send a
|
|
// message 0x401 (through SendCallbackMessageW) to a WorkerW window
|
|
// in Windows Explorer
|
|
//
|
|
|
|
rc = tzuk;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_SHOpenFolderAndSelectItems
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX ULONG SH32_SHOpenFolderAndSelectItems(
|
|
LPCITEMIDLIST pidlFolder, UINT cidl, void *apidl, ULONG dwFlags)
|
|
{
|
|
HRESULT hr = __sys_SHOpenFolderAndSelectItems(
|
|
pidlFolder, cidl, apidl, dwFlags);
|
|
if (hr == REGDB_E_CLASSNOTREG || hr == E_UNEXPECTED) {
|
|
|
|
//
|
|
// SHOpenFolderAndSelectItems uses ShellWindows/IShellWindows
|
|
// which is not available in the sandbox
|
|
//
|
|
|
|
ULONG LastError = GetLastError();
|
|
|
|
WCHAR *path = Dll_AllocTemp((MAX_PATH + 64) * sizeof(WCHAR));
|
|
|
|
WCHAR verb = 0;
|
|
|
|
if (cidl == 0) {
|
|
|
|
//
|
|
// if cidl is zero, pidlFolder should specify the full pidl path
|
|
// to an item to select in the parent folder window that opens
|
|
//
|
|
|
|
if (SH32_SHOpenFolderAndSelectItems_2(pidlFolder, NULL, path))
|
|
verb = L's';
|
|
|
|
} else if (cidl == 1) {
|
|
|
|
//
|
|
// if cidl is one, pidlFolder specifies the folder and the first
|
|
// (and only) entry in apidl specifies the child to select.
|
|
// MSDN does not say if the child pidl should be relative or
|
|
// absolute, and firefox uses absolute pidl. in any case, the
|
|
// ILCombine in SH32_SHOpenFolderAndSelectItems_2 seems to work
|
|
//
|
|
|
|
LPCITEMIDLIST pidl2 = ((LPCITEMIDLIST *)apidl)[0];
|
|
if (SH32_SHOpenFolderAndSelectItems_2(pidlFolder, pidl2, path))
|
|
verb = L's';
|
|
|
|
} else if (__sys_SHGetPathFromIDListW(pidlFolder, path)) {
|
|
|
|
//
|
|
// if we have more than one child entry, we just open the folder
|
|
//
|
|
|
|
ULONG FileAttrs = GetFileAttributes(path);
|
|
if ((FileAttrs != INVALID_FILE_ATTRIBUTES) &&
|
|
(FileAttrs & FILE_ATTRIBUTE_DIRECTORY))
|
|
verb = L'e';
|
|
}
|
|
|
|
if (verb) {
|
|
|
|
HANDLE hProcess;
|
|
BOOL b = SH_OpenFolder(path, verb, &hProcess);
|
|
if (hProcess)
|
|
CloseHandle(hProcess);
|
|
if (b) {
|
|
hr = S_OK;
|
|
LastError = 0;
|
|
}
|
|
}
|
|
|
|
Dll_Free(path);
|
|
SetLastError(LastError);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_SHOpenFolderAndSelectItems_2
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN SH32_SHOpenFolderAndSelectItems_2(
|
|
LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild, WCHAR *path)
|
|
{
|
|
typedef BOOL (*P_ILIsParent)(
|
|
LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fImmediate);
|
|
typedef LPITEMIDLIST (*P_ILCombine)(
|
|
LPCITEMIDLIST pidl1, LPCITEMIDLIST pid2);
|
|
typedef void (*P_SHFree)(void *pv);
|
|
|
|
BOOLEAN result = FALSE;
|
|
|
|
P_ILIsParent pILIsParent = (P_ILIsParent)
|
|
Ldr_GetProcAddrNew(DllName_shell32, L"ILIsParent","ILIsParent");
|
|
|
|
P_ILCombine pILCombine = (P_ILCombine)
|
|
Ldr_GetProcAddrNew(DllName_shell32, L"ILCombine","ILCombine");
|
|
|
|
P_SHFree pSHFree = (P_SHFree)
|
|
Ldr_GetProcAddrNew(DllName_shell32, L"SHFree","SHFree");
|
|
|
|
if (pILIsParent && pILCombine && pSHFree) {
|
|
|
|
LPCITEMIDLIST pidl;
|
|
if (pidlParent && pidlChild &&
|
|
pILIsParent(pidlParent, pidlChild, FALSE)) {
|
|
// if pidlChild is an absolute pidl with a full path
|
|
// then we don't really need to combine it with parent
|
|
pidlParent = NULL;
|
|
}
|
|
pidl = pILCombine(pidlParent, pidlChild);
|
|
|
|
if (pidl) {
|
|
|
|
if (__sys_SHGetPathFromIDListW(pidl, path)) {
|
|
|
|
ULONG FileAttrs = GetFileAttributes(path);
|
|
if (FileAttrs != INVALID_FILE_ATTRIBUTES) {
|
|
|
|
result = TRUE;
|
|
}
|
|
}
|
|
|
|
pSHFree((void *)pidl);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_DoRunAs
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOL SH32_DoRunAs(
|
|
const WCHAR *CmdLine, const WCHAR *WorkDir,
|
|
PROCESS_INFORMATION *pi, BOOL *cancelled)
|
|
{
|
|
THREAD_DATA *TlsData = Dll_GetTlsData(NULL);
|
|
WCHAR *arg;
|
|
SHELLEXECUTEINFO shex;
|
|
HMODULE shell32;
|
|
|
|
//
|
|
// implementation of the standard ElevateCreateProcess fix / shim
|
|
// initially created for Internet Explorer, however this seems to be
|
|
// needed in other programs as well.
|
|
//
|
|
// abort if running inside ShellExecuteEx which already does this
|
|
//
|
|
|
|
if (TlsData->sh32_shell_execute)
|
|
return FALSE;
|
|
|
|
//
|
|
// separate the first component of the path, i.e. the program name,
|
|
// and the rest of the path, i.e. the arguments to the program.
|
|
// remove any quotes around the program name.
|
|
//
|
|
|
|
if (CmdLine == NULL)
|
|
return FALSE;
|
|
|
|
if (CmdLine[0] == L'\"') {
|
|
++CmdLine;
|
|
arg = wcschr(CmdLine, L'\"');
|
|
if (! arg)
|
|
return FALSE;
|
|
} else
|
|
arg = wcschr(CmdLine, L' ');
|
|
if (arg) {
|
|
*arg = L'\0';
|
|
do { ++arg; } while (*arg == L' ');
|
|
}
|
|
|
|
//
|
|
// use ShellExecute to run the program with elevation
|
|
//
|
|
|
|
memzero(pi, sizeof(PROCESS_INFORMATION));
|
|
|
|
memzero(&shex, sizeof(SHELLEXECUTEINFO));
|
|
shex.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
shex.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
shex.hwnd = NULL;
|
|
shex.lpVerb = L"runas";
|
|
shex.lpFile = CmdLine;
|
|
shex.lpParameters = arg;
|
|
shex.lpDirectory = WorkDir;
|
|
shex.nShow = SW_SHOWNORMAL;
|
|
shex.hInstApp = NULL;
|
|
|
|
shell32 = LoadLibrary(DllName_shell32);
|
|
if (shell32) {
|
|
|
|
P_ShellExecuteEx pShellExecuteEx = (P_ShellExecuteEx)
|
|
GetProcAddress(shell32, "ShellExecuteExW");
|
|
|
|
if (pShellExecuteEx) {
|
|
|
|
if (pShellExecuteEx(&shex) &&
|
|
((int)(ULONG_PTR)shex.hInstApp > 32)) {
|
|
|
|
pi->hProcess = shex.hProcess;
|
|
return TRUE;
|
|
}
|
|
|
|
if (GetLastError() == ERROR_CANCELLED)
|
|
*cancelled = TRUE;
|
|
}
|
|
|
|
FreeLibrary(shell32);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_GetPrivateProfileStringW
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX ULONG SH32_GetPrivateProfileStringW(
|
|
void *lpAppName, void *lpKeyName, void *lpDefault,
|
|
void *lpReturnedString, ULONG nSize, void *lpFileName)
|
|
{
|
|
ULONG rv = __sys_GetPrivateProfileStringW(
|
|
lpAppName, lpKeyName, lpDefault,
|
|
lpReturnedString, nSize, lpFileName);
|
|
|
|
if (rv == 12) {
|
|
|
|
const WCHAR *AppNameW = (WCHAR *)lpAppName;
|
|
const WCHAR *KeyNameW = (WCHAR *)lpKeyName;
|
|
const WCHAR *FileNameW = (WCHAR* )lpFileName;
|
|
const WCHAR *RetStrW = (WCHAR *)lpReturnedString;
|
|
if (AppNameW && 0 == _wcsicmp(AppNameW, L"boot") &&
|
|
KeyNameW && 0 == _wcsicmp(KeyNameW, L"shell") &&
|
|
FileNameW && 0 == _wcsicmp(FileNameW, L"system.ini") &&
|
|
RetStrW && 0 == _wcsicmp(RetStrW, L"explorer.exe")) {
|
|
|
|
*(WCHAR *)(RetStrW + 0) = L'x';
|
|
*(WCHAR *)(RetStrW + 1) = L'\0';
|
|
rv = 1;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_SHGetFolderLocation
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX HRESULT SH32_SHGetFolderLocation(
|
|
HWND hwndOwner, int nFolder, HANDLE hToken,
|
|
DWORD dwReserved, void **ppidl)
|
|
{
|
|
HRESULT hr = __sys_SHGetFolderLocation(
|
|
hwndOwner, nFolder, hToken, dwReserved, ppidl);
|
|
|
|
if (hr == 0x80070002 && nFolder == CSIDL_PERSONAL) {
|
|
|
|
//
|
|
// if the Documents folder is missing, Windows Explorer hangs
|
|
// on startup on Windows 7. work around that by creating the
|
|
// Documents folder in the sandbox
|
|
//
|
|
|
|
hr = __sys_SHGetFolderLocation(
|
|
hwndOwner, nFolder | CSIDL_FLAG_CREATE, hToken,
|
|
dwReserved, ppidl);
|
|
}
|
|
|
|
if (hr != 0 && nFolder == CSIDL_PERSONAL) {
|
|
|
|
//
|
|
// if the Documents folder is missing outside the sandbox,
|
|
// and ClosedFilePath (or something else) prevents its creation
|
|
// in the sandbox, then issue an error message
|
|
//
|
|
|
|
SbieApi_Log(2306, L"Documents");
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_LdrGetDllHandleEx
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX NTSTATUS SH32_LdrGetDllHandleEx(
|
|
ULONG_PTR One, ULONG_PTR Unknown1, ULONG_PTR Unknown2,
|
|
UNICODE_STRING *DllName, ULONG_PTR *hModule)
|
|
{
|
|
//
|
|
// block GetModuleHandle calls for the explorer.exe module, which is
|
|
// done primarily by SHELL32!IsProcessAnExplorer. this is necessary
|
|
// because SHELL32!CMountPoint::GetDrivesMask uses IsProcessAnExplorer
|
|
// to decide if it will use GetLogicalDrives or rely on the
|
|
// Shell HW Detection service, and we want to force GetLogicalDrives.
|
|
//
|
|
|
|
if (DllName && DllName->Buffer) {
|
|
|
|
const WCHAR *_DllName = L"explorer.exe";
|
|
const ULONG len = wcslen(_DllName);
|
|
if (DllName->Length == len * sizeof(WCHAR) &&
|
|
_wcsnicmp(DllName->Buffer, _DllName, len) == 0) {
|
|
|
|
*hModule = 0;
|
|
return STATUS_DLL_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
return __sys_LdrGetDllHandleEx(
|
|
One, Unknown1, Unknown2, DllName, hModule);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH32_Init
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOLEAN SH32_Init(HMODULE module)
|
|
{
|
|
P_ShellExecuteEx ShellExecuteExW;
|
|
P_Shell_NotifyIconW Shell_NotifyIconW;
|
|
P_SHChangeNotifyRegister SHChangeNotifyRegister;
|
|
void *SHGetItemFromObject;
|
|
P_SHOpenFolderAndSelectItems SHOpenFolderAndSelectItems;
|
|
//
|
|
// find utility APIs
|
|
//
|
|
|
|
__sys_SHGetPathFromIDListW = (P_SHGetPathFromIDList)
|
|
GetProcAddress(module, "SHGetPathFromIDListW");
|
|
|
|
//
|
|
// intercept entry points
|
|
//
|
|
|
|
ShellExecuteExW = (P_ShellExecuteEx)
|
|
GetProcAddress(module, "ShellExecuteExW");
|
|
|
|
Shell_NotifyIconW = (P_Shell_NotifyIconW)
|
|
GetProcAddress(module, "Shell_NotifyIconW");
|
|
|
|
SHChangeNotifyRegister = (P_SHChangeNotifyRegister)
|
|
GetProcAddress(module, "SHChangeNotifyRegister");
|
|
|
|
SHGetItemFromObject = GetProcAddress(module, "SHGetItemFromObject");
|
|
|
|
SHOpenFolderAndSelectItems = (P_SHOpenFolderAndSelectItems)
|
|
GetProcAddress(module, "SHOpenFolderAndSelectItems");
|
|
|
|
SBIEDLL_HOOK(SH32_,ShellExecuteExW);
|
|
|
|
SBIEDLL_HOOK(SH32_,Shell_NotifyIconW);
|
|
|
|
if (SHChangeNotifyRegister && SHGetItemFromObject) {
|
|
|
|
//
|
|
// the test for Windows 7-only SHGetItemFromObject is needed
|
|
// to hook SHChangeNotifyRegister only on Windows 7
|
|
//
|
|
|
|
SBIEDLL_HOOK(SH32_,SHChangeNotifyRegister);
|
|
}
|
|
|
|
if (SHOpenFolderAndSelectItems) {
|
|
|
|
SBIEDLL_HOOK(SH32_,SHOpenFolderAndSelectItems);
|
|
}
|
|
|
|
//
|
|
// windows 7 taskbar management
|
|
//
|
|
|
|
if (! Taskbar_Init(module))
|
|
return FALSE;
|
|
|
|
//
|
|
// install window monitor thread to auto-close Explorer windows
|
|
//
|
|
|
|
if ( Dll_ImageType == DLL_IMAGE_SHELL_EXPLORER
|
|
|| Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER) {
|
|
|
|
WCHAR buf[8];
|
|
SbieApi_QueryConfAsIs(
|
|
NULL, L"NoAutoExitExplorer", 0, buf, sizeof(buf));
|
|
if (! buf[0]) {
|
|
|
|
HANDLE ThreadHandle = CreateThread(NULL, 0, SH_WindowMonitorThread, NULL, 0, NULL);
|
|
if (ThreadHandle)
|
|
CloseHandle(ThreadHandle);
|
|
}
|
|
}
|
|
|
|
//
|
|
// initialization for the Windows Shell Explorer process
|
|
//
|
|
|
|
if (Dll_ImageType == DLL_IMAGE_SHELL_EXPLORER) {
|
|
|
|
//
|
|
// hook ntdll!LdrGetDllHandleEx
|
|
//
|
|
|
|
if (! __sys_LdrGetDllHandleEx) {
|
|
|
|
__sys_LdrGetDllHandleEx = (P_LdrGetDllHandleEx)
|
|
GetProcAddress(Dll_Ntdll, "LdrGetDllHandleEx");
|
|
}
|
|
|
|
if (__sys_LdrGetDllHandleEx) {
|
|
|
|
*(ULONG_PTR *)&__sys_LdrGetDllHandleEx = (ULONG_PTR)
|
|
SbieDll_Hook("LdrGetDllHandleEx",
|
|
__sys_LdrGetDllHandleEx, SH32_LdrGetDllHandleEx, module);
|
|
}
|
|
|
|
//
|
|
// hook GetPrivateProfileString to prevent Windows Explorer shell
|
|
// process from taking over the desktop
|
|
//
|
|
|
|
if (1) {
|
|
|
|
P_GetPrivateProfileString GetPrivateProfileStringW;
|
|
|
|
GetPrivateProfileStringW = (P_GetPrivateProfileString)
|
|
GetProcAddress(Dll_Kernel32, "GetPrivateProfileStringW");
|
|
|
|
SBIEDLL_HOOK(SH32_,GetPrivateProfileStringW);
|
|
}
|
|
|
|
//
|
|
// on Windows Vista, the registry key
|
|
// CLSID\{ceff45ee-c862-41de-aee2-a022c81eda92}
|
|
// (for CLSID_SeparateExplorerFactory) is associated with an
|
|
// AppId key, which causes failure with status
|
|
// CO_E_WRONG_SERVER_IDENTITY.
|
|
// this association is undone to let Explorer start sandboxed.
|
|
//
|
|
|
|
if (Dll_OsBuild >= 6000) {
|
|
|
|
Key_DeleteValueFromCLSID(
|
|
L"clsid", L"ceff45ee-c862-41de-aee2-a022c81eda92", L"AppId");
|
|
}
|
|
|
|
//
|
|
// on Windows 7, hook SHGetFolderLocation in Windows Explorer
|
|
//
|
|
|
|
if (Dll_OsBuild >= 7600) {
|
|
|
|
P_SHGetFolderLocation SHGetFolderLocation;
|
|
|
|
SHGetFolderLocation = (P_SHGetFolderLocation)
|
|
GetProcAddress(module, "SHGetFolderLocation");
|
|
|
|
SBIEDLL_HOOK(SH32_,SHGetFolderLocation);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_AssocQueryKeyWow64
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX HKEY SbieDll_AssocQueryKeyWow64(const WCHAR *subj)
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objattrs;
|
|
UNICODE_STRING objname;
|
|
HKEY root;
|
|
WCHAR subkey[128], *subkey2;
|
|
HANDLE hkey;
|
|
|
|
InitializeObjectAttributes(
|
|
&objattrs, &objname, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
root = HKEY_CURRENT_USER;
|
|
|
|
retry:
|
|
|
|
//
|
|
// open HKEY_CLASSES_ROOT key for (subj)
|
|
//
|
|
|
|
wcscpy(subkey, L"\\REGISTRY\\");
|
|
if (root == HKEY_CURRENT_USER)
|
|
wcscat(subkey, L"USER\\CURRENT");
|
|
else if (root == HKEY_LOCAL_MACHINE)
|
|
wcscat(subkey, L"MACHINE");
|
|
wcscat(subkey, L"\\SOFTWARE\\CLASSES\\");
|
|
subkey2 = subkey + wcslen(subkey);
|
|
wcscpy(subkey2, subj);
|
|
RtlInitUnicodeString(&objname, subkey);
|
|
|
|
status = NtOpenKey(&hkey, KEY_READ, &objattrs);
|
|
if (NT_SUCCESS(status))
|
|
return hkey;
|
|
|
|
//
|
|
// if we are a Wow64 process and couldn't find in HKEY_CLASSES_ROOT,
|
|
// then look again in HKEY_CLASSES_ROOT\Wow64
|
|
//
|
|
|
|
#ifndef _WIN64
|
|
if (Dll_IsWow64) {
|
|
|
|
wcscpy(subkey2, L"Wow6432Node\\");
|
|
wcscat(subkey2, subj);
|
|
RtlInitUnicodeString(&objname, subkey);
|
|
|
|
status = NtOpenKey(&hkey, KEY_READ, &objattrs);
|
|
if (NT_SUCCESS(status))
|
|
return hkey;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if we looked at HKEY_CURRENT_USER, try again for HKEY_LOCAL_MACHINE
|
|
//
|
|
|
|
if (root == HKEY_CURRENT_USER) {
|
|
|
|
root = HKEY_LOCAL_MACHINE;
|
|
goto retry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_AssocQueryCommandInternal
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX WCHAR *SbieDll_AssocQueryCommandInternal(
|
|
const WCHAR *subj, const WCHAR *verb)
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES objattrs;
|
|
UNICODE_STRING objname;
|
|
HANDLE hkey, hkey2;
|
|
WCHAR subkey[64];
|
|
ULONG len;
|
|
KEY_VALUE_PARTIAL_INFORMATION *kvpi;
|
|
const ULONG kvpi_len = 1024;
|
|
ULONG retry_count = 0;
|
|
WCHAR *buf, *buf2;
|
|
|
|
//
|
|
// open the key for (subj), but if (subj) is really a dummy key pointing
|
|
// somewhere else, then open the pointed-to key
|
|
//
|
|
|
|
kvpi = Dll_Alloc(kvpi_len);
|
|
|
|
hkey = SbieDll_AssocQueryKeyWow64(subj);
|
|
|
|
wcscpy(subkey, L"shell\\");
|
|
wcscat(subkey, verb);
|
|
wcscat(subkey, L"\\command");
|
|
|
|
buf = NULL;
|
|
|
|
retry:
|
|
|
|
++retry_count;
|
|
if ((! hkey) || retry_count == 8)
|
|
goto finish;
|
|
|
|
InitializeObjectAttributes(
|
|
&objattrs, &objname, OBJ_CASE_INSENSITIVE, hkey, NULL);
|
|
|
|
RtlInitUnicodeString(&objname, subkey);
|
|
|
|
status = NtOpenKey(&hkey2, KEY_READ, &objattrs);
|
|
if (! NT_SUCCESS(status)) {
|
|
|
|
if (status != STATUS_OBJECT_NAME_NOT_FOUND &&
|
|
status != STATUS_OBJECT_PATH_NOT_FOUND)
|
|
{
|
|
goto finish;
|
|
}
|
|
|
|
RtlInitUnicodeString(&objname, L"");
|
|
ZeroMemory(kvpi, kvpi_len);
|
|
status = NtQueryValueKey(
|
|
hkey, &objname, KeyValuePartialInformation,
|
|
kvpi, kvpi_len - 32, &len);
|
|
|
|
if ((! NT_SUCCESS(status)) || kvpi->DataLength == 0)
|
|
goto finish;
|
|
|
|
NtClose(hkey);
|
|
|
|
hkey = SbieDll_AssocQueryKeyWow64((WCHAR *)kvpi->Data);
|
|
|
|
goto retry;
|
|
}
|
|
|
|
NtClose(hkey);
|
|
hkey = hkey2;
|
|
hkey2 = NULL;
|
|
|
|
//
|
|
// we have the key (subj)\shell\verb\command, get the default value
|
|
//
|
|
|
|
RtlInitUnicodeString(&objname, L"");
|
|
ZeroMemory(kvpi, kvpi_len);
|
|
status = NtQueryValueKey(
|
|
hkey, &objname, KeyValuePartialInformation,
|
|
kvpi, kvpi_len - 32, &len);
|
|
|
|
if ((! NT_SUCCESS(status)) || kvpi->DataLength == 0 ||
|
|
(kvpi->Type != REG_SZ && kvpi->Type != REG_EXPAND_SZ))
|
|
{
|
|
goto finish;
|
|
}
|
|
|
|
len = kvpi->DataLength;
|
|
buf = Dll_Alloc(len + sizeof(WCHAR) * 2);
|
|
ZeroMemory(buf, len + sizeof(WCHAR) * 2);
|
|
CopyMemory(buf, kvpi->Data, len);
|
|
|
|
//
|
|
// finally, expand any environment strings within
|
|
//
|
|
|
|
if (kvpi->Type == REG_EXPAND_SZ) {
|
|
|
|
len = ExpandEnvironmentStrings(buf, NULL, 0);
|
|
if (len == 0) {
|
|
Dll_Free(buf);
|
|
buf = NULL;
|
|
goto finish;
|
|
}
|
|
|
|
buf2 = Dll_Alloc((len + 8) * sizeof(WCHAR));
|
|
ExpandEnvironmentStrings(buf, buf2, len);
|
|
buf2[len] = L'\0';
|
|
|
|
Dll_Free(buf);
|
|
buf = buf2;
|
|
}
|
|
|
|
//
|
|
// finish
|
|
//
|
|
|
|
finish:
|
|
|
|
if (hkey)
|
|
NtClose(hkey);
|
|
Dll_Free(kvpi);
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_AssocQueryCommand
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX WCHAR *SbieDll_AssocQueryCommand(const WCHAR *subj)
|
|
{
|
|
WCHAR *cmd = SbieDll_AssocQueryCommandInternal(subj, L"open");
|
|
if (! cmd)
|
|
cmd = SbieDll_AssocQueryCommandInternal(subj, L"cplopen");
|
|
return cmd;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SbieDll_AssocQueryProgram
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX WCHAR *SbieDll_AssocQueryProgram(const WCHAR *subj)
|
|
{
|
|
WCHAR *cmd, *buf;
|
|
WCHAR *ptr, *ptr2;
|
|
WCHAR ch;
|
|
ULONG len;
|
|
|
|
cmd = SbieDll_AssocQueryCommand(subj);
|
|
if (! cmd)
|
|
return NULL;
|
|
|
|
//
|
|
// strip leading blanks and quotes, leave first word
|
|
//
|
|
|
|
ptr = cmd;
|
|
while (*ptr && *ptr == L' ')
|
|
++ptr;
|
|
if (*ptr == L'\"') {
|
|
ch = L'\"';
|
|
++ptr;
|
|
} else
|
|
ch = L' ';
|
|
ptr2 = wcschr(ptr, ch);
|
|
if (ptr2)
|
|
*ptr2 = L'\0';
|
|
|
|
//
|
|
// in case the path is in short form, get the long name
|
|
//
|
|
|
|
len = GetLongPathName(ptr, NULL, 0);
|
|
if (len == 0) {
|
|
Dll_Free(cmd);
|
|
return NULL;
|
|
}
|
|
|
|
buf = Dll_Alloc((len + 8) * sizeof(WCHAR));
|
|
GetLongPathName(ptr, buf, len);
|
|
buf[len] = L'\0';
|
|
|
|
Dll_Free(cmd);
|
|
return buf;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH_GetInternetExplorerVersion
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX ULONG SH_GetInternetExplorerVersion(void)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle;
|
|
OBJECT_ATTRIBUTES objattrs;
|
|
UNICODE_STRING objname;
|
|
ULONG version;
|
|
|
|
if (Dll_ImageType != DLL_IMAGE_INTERNET_EXPLORER)
|
|
return 0;
|
|
|
|
RtlInitUnicodeString(&objname,
|
|
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Internet Explorer");
|
|
InitializeObjectAttributes(
|
|
&objattrs, &objname, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
status = NtOpenKey(&handle, KEY_READ, &objattrs);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ULONG len;
|
|
ULONG kvpi_space[64];
|
|
KEY_VALUE_PARTIAL_INFORMATION *kvpi =
|
|
(KEY_VALUE_PARTIAL_INFORMATION *)&kvpi_space;
|
|
|
|
RtlInitUnicodeString(&objname, L"svcVersion"); // for IE 10
|
|
memzero(kvpi_space, sizeof(kvpi_space));
|
|
|
|
status = NtQueryValueKey(
|
|
handle, &objname, KeyValuePartialInformation,
|
|
kvpi, sizeof(kvpi_space) - 4, &len);
|
|
|
|
if (! NT_SUCCESS(status)) {
|
|
|
|
RtlInitUnicodeString(&objname, L"Version"); // IE 9 and earlier
|
|
memzero(kvpi_space, sizeof(kvpi_space));
|
|
|
|
status = NtQueryValueKey(
|
|
handle, &objname, KeyValuePartialInformation,
|
|
kvpi, sizeof(kvpi_space) - 4, &len);
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && kvpi->Type == REG_SZ) {
|
|
|
|
version = _wtoi((WCHAR *)kvpi->Data);
|
|
}
|
|
|
|
NtClose(handle);
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// SHSetInstanceExplorer support
|
|
//
|
|
// Code running in Explorer (both Windows and Internet) may AddRef() on the
|
|
// host process using SHGetInstanceExplorer and then forget to Release().
|
|
// This causes a sandboxed IE or Explorer to go on running indefinitely.
|
|
// To work around this, we have a thread that monitors the number of open
|
|
// windows, and forces Explorer to close when there are no more windows.
|
|
//
|
|
// Alternatively, in an IE 8 tab process, there are no open windows, so
|
|
// in this case we wait for the main IE process to terminate
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
static ULONG SH_WindowMonitorThread(void *lpParameter);
|
|
|
|
static void SH_WindowMonitorThread_2(void);
|
|
|
|
static ULONG SH_WindowMonitorCount(void);
|
|
|
|
static BOOL SH_WindowMonitorEnum(HWND hWnd, LPARAM lParam);
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH_WindowMonitorThread
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX ULONG SH_WindowMonitorThread(void *lpParameter)
|
|
{
|
|
ULONG win_count;
|
|
BOOLEAN any_windows_ever_seen = FALSE;
|
|
|
|
const ULONG timeout =
|
|
(Dll_ImageType == DLL_IMAGE_SHELL_EXPLORER) ? 2500 : 5000;
|
|
|
|
SH_WindowMonitorThread_2();
|
|
|
|
while (1) {
|
|
|
|
Sleep(any_windows_ever_seen ? timeout : 500);
|
|
|
|
win_count = SH_WindowMonitorCount();
|
|
|
|
if (win_count)
|
|
any_windows_ever_seen = TRUE;
|
|
|
|
else if (any_windows_ever_seen) {
|
|
|
|
Sleep(timeout);
|
|
|
|
win_count = SH_WindowMonitorCount();
|
|
|
|
if (win_count == 0) {
|
|
|
|
if (SH_GetInternetExplorerVersion() >= 8) {
|
|
|
|
//
|
|
// Internet Explorer version 8 needs a little more time
|
|
// to properly shut down after closing all windows.
|
|
// otherwise next run will try to recover last session.
|
|
//
|
|
|
|
Sleep(30000);
|
|
}
|
|
|
|
ExitProcess(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH_WindowMonitorThread_2
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX void SH_WindowMonitorThread_2(void)
|
|
{
|
|
extern BOOLEAN Secure_IsInternetExplorerTabProcess;
|
|
if (! Secure_IsInternetExplorerTabProcess)
|
|
return;
|
|
Proc_WaitForParentExit(0);
|
|
Sleep(8000);
|
|
ExitProcess(0);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH_WindowMonitorCount
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX ULONG SH_WindowMonitorCount(void)
|
|
{
|
|
SYSTEM_PROCESS_INFORMATION *info, *info_ptr;
|
|
ULONG_PTR args[2];
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
//
|
|
// get list of processes active in the system
|
|
//
|
|
|
|
info = SysInfo_QueryProcesses(NULL);
|
|
if (! info)
|
|
return 1234;
|
|
|
|
//
|
|
// for each process, set HandleCount = 1 if
|
|
// - this is the current process
|
|
// - this iexplore.exe process is a child of the current process
|
|
// otherwise set HandleCount = 0
|
|
//
|
|
|
|
info_ptr = info;
|
|
while (1) {
|
|
|
|
info_ptr->HandleCount = 0;
|
|
|
|
if (Dll_ProcessId == (ULONG_PTR)info_ptr->UniqueProcessId)
|
|
info_ptr->HandleCount = 1;
|
|
|
|
else if (Dll_ImageType == DLL_IMAGE_INTERNET_EXPLORER &&
|
|
Dll_ProcessId ==
|
|
(ULONG_PTR)info_ptr->InheritedFromProcessId) {
|
|
|
|
WCHAR image[112];
|
|
status = SbieApi_QueryProcess(info_ptr->UniqueProcessId,
|
|
NULL, image, NULL, NULL);
|
|
if (NT_SUCCESS(status) && _wcsicmp(image, L"iexplore.exe") == 0)
|
|
info_ptr->HandleCount = 1;
|
|
}
|
|
|
|
i = info_ptr->NextEntryOffset;
|
|
if (! i)
|
|
break;
|
|
info_ptr = (SYSTEM_PROCESS_INFORMATION *)((UCHAR *)info_ptr + i);
|
|
}
|
|
|
|
//
|
|
// count active windows in the processes with HandleCount = 1
|
|
//
|
|
|
|
args[0] = (ULONG_PTR)info;
|
|
args[1] = 0;
|
|
|
|
if(!Gui_UseProxyService && __sys_EnumWindows)
|
|
__sys_EnumWindows(SH_WindowMonitorEnum, (LPARAM)args);
|
|
else
|
|
Gui_EnumWindows(SH_WindowMonitorEnum, (LPARAM)args);
|
|
|
|
Dll_Free(info);
|
|
|
|
return (ULONG)args[1];
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// SH_WindowMonitorEnum
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX BOOL SH_WindowMonitorEnum(HWND hWnd, LPARAM lParam)
|
|
{
|
|
extern DWORD (*__sys_GetWindowThreadProcessId)(HWND hWnd, ULONG *ppid);
|
|
extern BOOL Gui_IsWindowVisible(HWND hWnd);
|
|
|
|
SYSTEM_PROCESS_INFORMATION *info_ptr;
|
|
ULONG pid, i;
|
|
|
|
ULONG_PTR *args = (ULONG_PTR *)lParam;
|
|
|
|
if (Gui_IsWindowVisible(hWnd)) {
|
|
|
|
__sys_GetWindowThreadProcessId(hWnd, &pid);
|
|
|
|
info_ptr = (SYSTEM_PROCESS_INFORMATION *)args[0];
|
|
while (1) {
|
|
|
|
if (info_ptr->HandleCount &&
|
|
(ULONG)(ULONG_PTR)info_ptr->UniqueProcessId == pid)
|
|
++args[1];
|
|
|
|
i = info_ptr->NextEntryOffset;
|
|
if (! i)
|
|
break;
|
|
info_ptr = (SYSTEM_PROCESS_INFORMATION *)((UCHAR *)info_ptr + i);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// ZipFldr support
|
|
//
|
|
// The standard ZipFldr.dll pops up an extraction dialog box when run
|
|
// sandboxed. We want to make sure that an Explorer window opens instead.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
typedef void (*P_RouteTheCall)(
|
|
void *Unknown1, HINSTANCE hInstance, const UCHAR *Path, ULONG Unknown2);
|
|
|
|
|
|
static ULONG_PTR __sys_RouteTheCall = 0;
|
|
|
|
|
|
_FX void SH32_RouteTheCall(
|
|
void *Unknown1, HINSTANCE hInstance, const UCHAR *Path, ULONG Unknown2)
|
|
{
|
|
ANSI_STRING ansi;
|
|
UNICODE_STRING uni;
|
|
WCHAR *iptr, *optr;
|
|
HANDLE hProcess;
|
|
|
|
RtlInitString(&ansi, Path);
|
|
uni.Buffer = NULL;
|
|
RtlAnsiStringToUnicodeString(&uni, &ansi, TRUE);
|
|
|
|
iptr = uni.Buffer;
|
|
optr = uni.Buffer;
|
|
while (*iptr) {
|
|
if (*iptr != L'\"') {
|
|
*optr = *iptr;
|
|
++optr;
|
|
}
|
|
++iptr;
|
|
}
|
|
*optr = L'\0';
|
|
|
|
SH_OpenFolder(uni.Buffer, 0, &hProcess);
|
|
|
|
ExitProcess(0);
|
|
}
|
|
|
|
|
|
_FX BOOLEAN SH32_Init_ZipFldr(HMODULE module)
|
|
{
|
|
P_RouteTheCall RouteTheCall;
|
|
|
|
if (Dll_ImageType != DLL_IMAGE_RUNDLL32)
|
|
return TRUE;
|
|
|
|
RouteTheCall = (P_RouteTheCall) GetProcAddress(module, "RouteTheCall");
|
|
if (RouteTheCall) {
|
|
|
|
SBIEDLL_HOOK(SH32_,RouteTheCall);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// UxTheme support
|
|
//
|
|
// Hook the Windows Vista API SetWindowThemeAttribute so we can prevent
|
|
// the Shell Explorer from hiding its caption bar.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
typedef HRESULT (*P_SetWindowThemeAttribute)(
|
|
HWND hwnd, ULONG eAttribute, void *pvAttribute, ULONG cbAttribute);
|
|
|
|
|
|
static ULONG_PTR __sys_SetWindowThemeAttribute = 0;
|
|
|
|
|
|
_FX HRESULT SH32_SetWindowThemeAttribute(
|
|
HWND hwnd, ULONG eAttribute, void *pvAttribute, ULONG cbAttribute)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
_FX BOOLEAN SH32_Init_UxTheme(HMODULE module)
|
|
{
|
|
if (Dll_ImageType == DLL_IMAGE_SHELL_EXPLORER) {
|
|
|
|
P_SetWindowThemeAttribute SetWindowThemeAttribute;
|
|
|
|
SetWindowThemeAttribute = (P_SetWindowThemeAttribute)
|
|
GetProcAddress(module, "SetWindowThemeAttribute");
|
|
|
|
if (SetWindowThemeAttribute) {
|
|
|
|
SBIEDLL_HOOK(SH32_,SetWindowThemeAttribute);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// IContextMenu support
|
|
//
|
|
// Make sure that Windows Explorer passes a full path (including the
|
|
// sandbox prefix) for selected right-click shell extension handlers
|
|
// which are known to pass this path to a component outside the sandbox
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Functions
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
_FX void SH32_IContextMenu_HookVtbl(void *lpVtbl, int index, void *func)
|
|
{
|
|
//
|
|
// hook a specified member in the specified vtbl. note that before
|
|
// hooking, we check if we already hooked the specified vtbl
|
|
//
|
|
|
|
ULONG_PTR *vtbl = *(ULONG_PTR **)lpVtbl;
|
|
void *xold = (void *)vtbl[index];
|
|
void *xnew;
|
|
ULONG_PTR *StubData = Dll_JumpStubDataForCode(xold);
|
|
if (StubData && StubData[2] == tzuk)
|
|
xnew = NULL;
|
|
else
|
|
xnew = Dll_JumpStub(xold, func, tzuk);
|
|
if (xnew) {
|
|
ULONG prot = PAGE_READWRITE;
|
|
if (VirtualProtect(&vtbl[index], sizeof(ULONG_PTR), prot, &prot)) {
|
|
vtbl[index] = (ULONG_PTR)xnew;
|
|
VirtualProtect(&vtbl[index], sizeof(ULONG_PTR), prot, &prot);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
_FX HRESULT SH32_IShellExtInit_Initialize(
|
|
void *pShellExtInit,
|
|
void *pidlFolder, IDataObject *pDataObject, HKEY hkeyProgID)
|
|
{
|
|
#if !defined(_M_ARM64) && !defined(_M_ARM64EC)
|
|
ULONG_PTR *StubData = Dll_JumpStubData();
|
|
#else
|
|
ULONG_PTR *StubData = (ULONG_PTR *)hkeyProgID;
|
|
hkeyProgID = (HKEY)StubData[3];
|
|
#endif
|
|
|
|
extern IDataObject *Ole_XDataObject_From_IDataObject(
|
|
IDataObject *pDataObject);
|
|
typedef HRESULT (*P_Initialize)(void *, void *, IDataObject *, HKEY);
|
|
return ((P_Initialize)StubData[1])(
|
|
pShellExtInit, pidlFolder,
|
|
Ole_XDataObject_From_IDataObject(pDataObject),
|
|
hkeyProgID);
|
|
}
|
|
|
|
|
|
_FX HRESULT SH32_IContextMenuHook_QueryInterface(
|
|
void *pContextMenu, REFIID riid, void **ppv
|
|
#if !defined(_M_ARM64) && !defined(_M_ARM64EC)
|
|
) {
|
|
ULONG_PTR *StubData = Dll_JumpStubData();
|
|
#else
|
|
, ULONG_PTR *StubData) {
|
|
#endif
|
|
|
|
EXTERN_C const IID IID_IShellExtInit;
|
|
typedef HRESULT (*P_QueryInterface)(void *, REFIID, void **);
|
|
HRESULT hr = ((P_QueryInterface)StubData[1])(pContextMenu, riid, ppv);
|
|
if (SUCCEEDED(hr) &&
|
|
memcmp(riid, &IID_IShellExtInit, sizeof(GUID)) == 0) {
|
|
|
|
void *pShellExtInit = (*ppv);
|
|
SH32_IContextMenu_HookVtbl(
|
|
pShellExtInit, 3, SH32_IShellExtInit_Initialize);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
_FX void SH32_IContextMenu_Hook(REFCLSID rclsid, void *pContextMenu)
|
|
{
|
|
//
|
|
// this function is called by Com_CoCreateInstance when the
|
|
// interface is IContextMenu. check if the associated CLSID
|
|
// is listed in the IContextMenuClsid setting for the box
|
|
//
|
|
|
|
typedef HRESULT (*P_IIDFromString)(LPCOLESTR lpsz, IID *lpiid);
|
|
|
|
WCHAR buf[96];
|
|
GUID guid;
|
|
ULONG index = 0;
|
|
BOOLEAN match = FALSE;
|
|
|
|
P_IIDFromString pIIDFromString = (P_IIDFromString)
|
|
Ldr_GetProcAddrNew(DllName_ole32_or_combase, L"IIDFromString","IIDFromString");
|
|
if (! pIIDFromString)
|
|
return;
|
|
|
|
while (1) {
|
|
NTSTATUS status = SbieApi_QueryConfAsIs(
|
|
NULL, L"IContextMenuClsid", index, buf, 90 * sizeof(WCHAR));
|
|
if (! NT_SUCCESS(status))
|
|
break;
|
|
++index;
|
|
if (pIIDFromString(buf, &guid) == 0 &&
|
|
memcmp(rclsid, &guid, sizeof(GUID)) == 0) {
|
|
match = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// given an IContextMenu interface, hook its QueryInterface function
|
|
// so we can intercept requests for the IShellExtInit interface
|
|
//
|
|
|
|
if (match) {
|
|
|
|
SH32_IContextMenu_HookVtbl(
|
|
pContextMenu, 0, SH32_IContextMenuHook_QueryInterface);
|
|
}
|
|
}
|
|
|