313 lines
8.6 KiB
C++
313 lines
8.6 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 "BoxedProcess.h"
|
|
#include "SandBox.h"
|
|
#include "../SbieAPI.h"
|
|
|
|
#include <ntstatus.h>
|
|
#define WIN32_NO_STATUS
|
|
typedef long NTSTATUS;
|
|
|
|
#include <windows.h>
|
|
#include "..\..\Sandboxie\common\win32_ntddk.h"
|
|
#include <psapi.h> // For access to GetModuleFileNameEx
|
|
|
|
#include <winnt.h>
|
|
|
|
//struct SBoxedProcess
|
|
//{
|
|
//};
|
|
|
|
CBoxedProcess::CBoxedProcess(quint32 ProcessId, class CSandBox* pBox)
|
|
{
|
|
m_pBox = pBox;
|
|
|
|
//m = new SBoxedProcess;
|
|
|
|
m_ProcessId = ProcessId;
|
|
|
|
m_ParendPID = 0;
|
|
m_SessionId = 0;
|
|
|
|
m_ProcessFlags = 0;
|
|
m_ImageType = -1;
|
|
|
|
m_uTerminated = 0;
|
|
//m_bSuspended = IsSuspended();
|
|
|
|
m_bIsWoW64 = false;
|
|
}
|
|
|
|
CBoxedProcess::~CBoxedProcess()
|
|
{
|
|
//delete m;
|
|
}
|
|
|
|
typedef enum _PEB_OFFSET
|
|
{
|
|
PhpoCurrentDirectory,
|
|
PhpoDllPath,
|
|
PhpoImagePathName,
|
|
PhpoCommandLine,
|
|
PhpoWindowTitle,
|
|
PhpoDesktopInfo,
|
|
PhpoShellInfo,
|
|
PhpoRuntimeData,
|
|
PhpoTypeMask = 0xffff,
|
|
PhpoWow64 = 0x10000
|
|
} PEB_OFFSET;
|
|
|
|
typedef struct _STRING32
|
|
{
|
|
USHORT Length;
|
|
USHORT MaximumLength;
|
|
ULONG Buffer;
|
|
} UNICODE_STRING32, * PUNICODE_STRING32;
|
|
|
|
QString CBoxedProcess__GetPebString(HANDLE ProcessHandle, PEB_OFFSET Offset)
|
|
{
|
|
BOOL is64BitOperatingSystem = FALSE;
|
|
BOOL isWow64Process = FALSE;
|
|
#ifdef _WIN64
|
|
is64BitOperatingSystem = TRUE;
|
|
#else // ! _WIN64
|
|
IsWow64Process(GetCurrentProcess(), &isWow64Process);
|
|
is64BitOperatingSystem = isWow64Process;
|
|
#endif _WIN64
|
|
|
|
BOOL isTargetWow64Process = FALSE;
|
|
IsWow64Process(ProcessHandle, &isTargetWow64Process);
|
|
BOOL isTarget64BitProcess = is64BitOperatingSystem && !isTargetWow64Process;
|
|
|
|
|
|
ULONG processParametersOffset = isTarget64BitProcess ? 0x20 : 0x10;
|
|
|
|
ULONG offset = 0;
|
|
switch (Offset)
|
|
{
|
|
case PhpoCurrentDirectory: offset = isTarget64BitProcess ? 0x38 : 0x24; break;
|
|
case PhpoCommandLine: offset = isTarget64BitProcess ? 0x70 : 0x40; break;
|
|
default:
|
|
return QString();
|
|
}
|
|
|
|
wstring s;
|
|
if (isTargetWow64Process) // OS : 64Bit, Cur : 32 or 64, Tar: 32bit
|
|
{
|
|
PVOID peb32;
|
|
if (!NT_SUCCESS(NtQueryInformationProcess(ProcessHandle, ProcessWow64Information, &peb32, sizeof(PVOID), NULL)))
|
|
return QString();
|
|
|
|
ULONG procParams;
|
|
if (!NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, (PVOID)((ULONG64)peb32 + processParametersOffset), &procParams, sizeof(ULONG), NULL)))
|
|
return QString();
|
|
|
|
UNICODE_STRING32 us;
|
|
if (!NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, (PVOID)(procParams + offset), &us, sizeof(UNICODE_STRING32), NULL)))
|
|
return QString();
|
|
|
|
if ((us.Buffer == 0) || (us.Length == 0))
|
|
return QString();
|
|
|
|
s.resize(us.Length / 2);
|
|
if (!NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, (PVOID)us.Buffer, (PVOID)s.c_str(), s.length() * 2, NULL)))
|
|
return QString();
|
|
}
|
|
else if (isWow64Process) //Os : 64Bit, Cur 32, Tar 64
|
|
{
|
|
return QString(); // not supported
|
|
}
|
|
else // Os,Cur,Tar : 64 or 32
|
|
{
|
|
PROCESS_BASIC_INFORMATION pbi;
|
|
if (!NT_SUCCESS(NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL)))
|
|
return QString();
|
|
|
|
ULONG_PTR procParams;
|
|
if (!NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, (PVOID)((ULONG64)pbi.PebBaseAddress + processParametersOffset), &procParams, sizeof(ULONG_PTR), NULL)))
|
|
return QString();
|
|
|
|
UNICODE_STRING us;
|
|
if (!NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, (PVOID)(procParams + offset), &us, sizeof(UNICODE_STRING), NULL)))
|
|
return QString();
|
|
|
|
if ((us.Buffer == 0) || (us.Length == 0))
|
|
return QString();
|
|
|
|
s.resize(us.Length / 2);
|
|
if (!NT_SUCCESS(NtReadVirtualMemory(ProcessHandle, (PVOID)us.Buffer, (PVOID)s.c_str(), s.length() * 2, NULL)))
|
|
return QString();
|
|
}
|
|
|
|
return QString::fromWCharArray(s.c_str());
|
|
}
|
|
|
|
bool CBoxedProcess::InitProcessInfo()
|
|
{
|
|
HANDLE ProcessHandle;
|
|
ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, (DWORD)m_ProcessId);
|
|
if (ProcessHandle == INVALID_HANDLE_VALUE) // try with less rights
|
|
ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, (DWORD)m_ProcessId);
|
|
if (ProcessHandle == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
|
|
PROCESS_BASIC_INFORMATION BasicInformation;
|
|
NTSTATUS status = NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &BasicInformation, sizeof(PROCESS_BASIC_INFORMATION), NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
m_ParendPID = (quint32)BasicInformation.InheritedFromUniqueProcessId;
|
|
}
|
|
|
|
TCHAR filename[MAX_PATH];
|
|
DWORD dwSize = MAX_PATH;
|
|
if(QueryFullProcessImageNameW(ProcessHandle, 0, filename, &dwSize))
|
|
m_ImagePath = QString::fromWCharArray(filename);
|
|
|
|
BOOL isTargetWow64Process = FALSE;
|
|
IsWow64Process(ProcessHandle, &isTargetWow64Process);
|
|
m_bIsWoW64 = isTargetWow64Process;
|
|
|
|
if (1) // windows 8.1 and later // todo add os version check
|
|
{
|
|
#define ProcessCommandLineInformation ((PROCESSINFOCLASS)60)
|
|
ULONG returnLength = 0;
|
|
NTSTATUS status = NtQueryInformationProcess(ProcessHandle, ProcessCommandLineInformation, NULL, 0, &returnLength);
|
|
if (!(status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL && status != STATUS_INFO_LENGTH_MISMATCH))
|
|
{
|
|
PUNICODE_STRING commandLine = (PUNICODE_STRING)malloc(returnLength);
|
|
status = NtQueryInformationProcess(ProcessHandle, ProcessCommandLineInformation, commandLine, returnLength, &returnLength);
|
|
if (NT_SUCCESS(status) && commandLine->Buffer != NULL)
|
|
m_CommandLine = QString::fromWCharArray(commandLine->Buffer);
|
|
free(commandLine);
|
|
}
|
|
#undef ProcessCommandLineInformation
|
|
}
|
|
|
|
if (m_CommandLine.isEmpty()) // fall back to teh win 7 method - requirers PROCESS_VM_READ
|
|
{
|
|
m_CommandLine = CBoxedProcess__GetPebString(ProcessHandle, PhpoCommandLine);
|
|
}
|
|
|
|
NtClose(ProcessHandle);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CBoxedProcess::InitProcessInfoEx()
|
|
{
|
|
if (m_ProcessFlags == 0 && m_pBox) {
|
|
m_ProcessFlags = m_pBox->Api()->QueryProcessInfo(m_ProcessId);
|
|
m_ImageType = m_pBox->Api()->QueryProcessInfo(m_ProcessId, 'gpit');
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#include <TlHelp32.h>
|
|
|
|
SB_STATUS CBoxedProcess::Terminate()
|
|
{
|
|
SB_STATUS Status = m_pBox->Api()->Terminate(m_ProcessId);
|
|
if (!Status.IsError())
|
|
SetTerminated();
|
|
return Status;
|
|
}
|
|
|
|
void CBoxedProcess::SetTerminated()
|
|
{
|
|
m_uTerminated = ::GetTickCount64();
|
|
}
|
|
|
|
bool CBoxedProcess::IsTerminated(quint64 forMs) const
|
|
{
|
|
if (m_uTerminated == 0)
|
|
return false;
|
|
if (forMs == 0)
|
|
return true;
|
|
return ::GetTickCount64() - m_uTerminated > forMs;
|
|
}
|
|
|
|
/*SB_STATUS CBoxedProcess::SetSuspend(bool bSet)
|
|
{
|
|
HANDLE ProcessHandle = OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, (DWORD)m_ProcessId);
|
|
if (ProcessHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
NTSTATUS status;
|
|
if(bSet)
|
|
status = NtSuspendProcess(ProcessHandle);
|
|
else
|
|
status = NtResumeProcess(ProcessHandle);
|
|
NtClose(ProcessHandle);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return SB_ERR(status);
|
|
m_bSuspended = IsSuspended();
|
|
return SB_OK;
|
|
}
|
|
return SB_ERR();
|
|
}
|
|
|
|
bool CBoxedProcess::IsSuspended() const
|
|
{
|
|
bool isSuspended = true;
|
|
|
|
// todo: do that globaly 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))
|
|
{
|
|
do
|
|
{
|
|
if (te32.th32OwnerProcessID != m_ProcessId)
|
|
continue;
|
|
|
|
HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID);
|
|
|
|
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);
|
|
|
|
return isSuspended;
|
|
}
|
|
*/
|