2025-05-13 19:49:49 +03:00

594 lines
20 KiB
C

/*
* Process Hacker Extra Plugins -
* Plugin Manager
*
* Copyright (C) 2016 dmex
*
* This file is part of Process Hacker.
*
* Process Hacker 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.
*
* Process Hacker 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 Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include "main.h"
#include "miniz\miniz.h"
ULONGLONG ParseVersionString(
_In_ PPH_STRING Version
)
{
PH_STRINGREF remainingPart, majorPart, minorPart, revisionPart, reservedPart;
ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0, reservedInteger = 0;
PhInitializeStringRef(&remainingPart, PhGetString(Version));
PhSplitStringRefAtChar(&remainingPart, '.', &majorPart, &remainingPart);
PhSplitStringRefAtChar(&remainingPart, '.', &minorPart, &remainingPart);
PhSplitStringRefAtChar(&remainingPart, '.', &revisionPart, &remainingPart);
PhSplitStringRefAtChar(&remainingPart, '.', &reservedPart, &remainingPart);
PhStringToInteger64(&majorPart, 10, &majorInteger);
PhStringToInteger64(&minorPart, 10, &minorInteger);
PhStringToInteger64(&revisionPart, 10, &revisionInteger);
PhStringToInteger64(&reservedPart, 10, &reservedInteger);
return MAKE_VERSION_ULONGLONG(majorInteger, minorInteger, reservedInteger, revisionInteger);
}
json_object_ptr json_get_object(
_In_ json_object_ptr Object,
_In_ PCSTR Key
)
{
json_object_ptr returnObj;
if (json_object_object_get_ex(Object, Key, &returnObj))
{
return returnObj;
}
return NULL;
}
static BOOLEAN ReadRequestString(
_In_ HINTERNET Handle,
_Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data,
_Out_ ULONG *DataLength
)
{
BYTE buffer[PAGE_SIZE];
PSTR data;
ULONG allocatedLength;
ULONG dataLength;
ULONG returnLength;
allocatedLength = sizeof(buffer);
data = (PSTR)PhAllocate(allocatedLength);
dataLength = 0;
// Zero the buffer
memset(buffer, 0, PAGE_SIZE);
memset(data, 0, allocatedLength);
while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength))
{
if (returnLength == 0)
break;
if (allocatedLength < dataLength + returnLength)
{
allocatedLength *= 2;
data = (PSTR)PhReAllocate(data, allocatedLength);
}
// Copy the returned buffer into our pointer
memcpy(data + dataLength, buffer, returnLength);
// Zero the returned buffer for the next loop
//memset(buffer, 0, returnLength);
dataLength += returnLength;
}
if (allocatedLength < dataLength + 1)
{
allocatedLength++;
data = (PSTR)PhReAllocate(data, allocatedLength);
}
// Ensure that the buffer is null-terminated.
data[dataLength] = 0;
*DataLength = dataLength;
*Data = data;
return TRUE;
}
VOID EnumerateLoadedPlugins(
_In_ PWCT_CONTEXT Context
)
{
PPH_AVL_LINKS root;
PPH_AVL_LINKS links;
root = PluginInstance->Links.Parent;
while (root)
{
if (!root->Parent)
break;
root = root->Parent;
}
for (
links = PhMinimumElementAvlTree((PPH_AVL_TREE)root);
links;
links = PhSuccessorElementAvlTree(links)
)
{
HANDLE handle;
IO_STATUS_BLOCK isb;
FILE_BASIC_INFORMATION basic;
PPHAPP_PLUGIN pluginInstance;
PH_STRINGREF pluginBaseName;
pluginInstance = CONTAINING_RECORD(links, PHAPP_PLUGIN, Links);
PhInitializeStringRefLongHint(&pluginBaseName, PhGetPluginBaseName(pluginInstance));
if (PhIsPluginDisabled(&pluginBaseName))
{
continue;
}
memset(&basic, 0, sizeof(FILE_BASIC_INFORMATION));
if (NT_SUCCESS(PhCreateFileWin32(
&handle,
PhGetString(pluginInstance->FileName),
FILE_GENERIC_READ,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
)))
{
NtQueryInformationFile(
handle,
&isb,
&basic,
sizeof(FILE_BASIC_INFORMATION),
FileBasicInformation
);
NtClose(handle);
}
ULONG versionSize;
PVOID versionInfo;
PUSHORT languageInfo;
UINT language;
UINT bufferSize = 0;
PWSTR buffer = NULL;
PPH_STRING internalName = NULL;
PPH_STRING version = NULL;
versionSize = GetFileVersionInfoSize(PhGetString(pluginInstance->FileName), NULL);
versionInfo = PhAllocate(versionSize);
memset(versionInfo, 0, versionSize);
if (GetFileVersionInfo(PhGetString(pluginInstance->FileName), 0, versionSize, versionInfo))
{
if (VerQueryValue(versionInfo, L"\\", &buffer, &bufferSize))
{
VS_FIXEDFILEINFO* info = (VS_FIXEDFILEINFO*)buffer;
if (info->dwSignature == 0xfeef04bd)
{
version = PhFormatString(
L"%lu.%lu.%lu.%lu",
(info->dwFileVersionMS >> 16) & 0xffff,
(info->dwFileVersionMS >> 0) & 0xffff,
(info->dwFileVersionLS >> 16) & 0xffff,
(info->dwFileVersionLS >> 0) & 0xffff
);
}
}
if (VerQueryValue(versionInfo, L"\\VarFileInfo\\Translation", &languageInfo, &language))
{
PPH_STRING internalNameString = PhFormatString(
L"\\StringFileInfo\\%04x%04x\\InternalName",
languageInfo[0],
languageInfo[1]
);
if (VerQueryValue(versionInfo, PhGetStringOrEmpty(internalNameString), &buffer, &bufferSize))
{
internalName = PhCreateStringEx(buffer, bufferSize * sizeof(WCHAR));
}
PhDereferenceObject(internalNameString);
}
}
PPLUGIN_NODE entry = PhCreateAlloc(sizeof(PLUGIN_NODE));
memset(entry, 0, sizeof(PLUGIN_NODE));
entry->State = PLUGIN_STATE_LOCAL;
entry->PluginInstance = pluginInstance;
entry->InternalName = internalName;
entry->Version = PhSubstring(version, 0, version->Length / sizeof(WCHAR) - 4);
entry->Name = PhCreateString(pluginInstance->Information.DisplayName);
entry->Author = PhCreateString(pluginInstance->Information.Author);
entry->Description = PhCreateString(pluginInstance->Information.Description);
entry->FilePath = PhCreateString2(&pluginInstance->FileName->sr);
entry->FileName = PhGetBaseName(entry->FilePath);
SYSTEMTIME utcTime, localTime;
PhLargeIntegerToSystemTime(&utcTime, &basic.LastWriteTime);
SystemTimeToTzSpecificLocalTime(NULL, &utcTime, &localTime);
entry->UpdatedTime = PhFormatDateTime(&localTime);
PhLargeIntegerToSystemTime(&utcTime, &basic.CreationTime);
SystemTimeToTzSpecificLocalTime(NULL, &utcTime, &localTime);
entry->AddedTime = PhFormatDateTime(&localTime);
entry->PluginOptions = pluginInstance->Information.HasOptions;
PostMessage(Context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry);
}
}
NTSTATUS QueryPluginsCallbackThread(
_In_ PVOID Parameter
)
{
HINTERNET httpSessionHandle = NULL;
HINTERNET httpConnectionHandle = NULL;
HINTERNET httpRequestHandle = NULL;
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 };
ULONG xmlStringBufferLength = 0;
PSTR xmlStringBuffer = NULL;
json_object_ptr rootJsonObject;
PWCT_CONTEXT context = Parameter;
WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
if (!(httpSessionHandle = WinHttpOpen(
L"ExtraPlugins_1.0",
proxyConfig.lpszProxy ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
proxyConfig.lpszProxy,
proxyConfig.lpszProxyBypass,
0
)))
{
goto CleanupExit;
}
if (!(httpConnectionHandle = WinHttpConnect(
httpSessionHandle,
L"wj32.org",
INTERNET_DEFAULT_HTTP_PORT,
0
)))
{
goto CleanupExit;
}
if (!(httpRequestHandle = WinHttpOpenRequest(
httpConnectionHandle,
NULL,
L"/processhacker/plugins/list.php",
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH
)))
{
goto CleanupExit;
}
if (!WinHttpSendRequest(
httpRequestHandle,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
0
))
{
goto CleanupExit;
}
if (!WinHttpReceiveResponse(httpRequestHandle, NULL))
goto CleanupExit;
if (!ReadRequestString(httpRequestHandle, &xmlStringBuffer, &xmlStringBufferLength))
goto CleanupExit;
if (!(rootJsonObject = json_tokener_parse(xmlStringBuffer)))
goto CleanupExit;
for (INT i = 0; i < json_object_array_length(rootJsonObject); i++)
{
json_object_ptr jvalue;
PPLUGIN_NODE entry;
SYSTEMTIME time = { 0 };
SYSTEMTIME localTime = { 0 };
PH_STRINGREF pluginBaseName;
PPH_STRING pluginDllPath;
entry = PhCreateAlloc(sizeof(PLUGIN_NODE));
memset(entry, 0, sizeof(PLUGIN_NODE));
jvalue = json_object_array_get_idx(rootJsonObject, i);
entry->Id = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_id")));
entry->Visible = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_visible")));
entry->InternalName = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_internal_name")));
entry->Name = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_name")));
entry->Version = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_version")));
entry->Author = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_author")));
entry->Description = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_description")));
entry->IconUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_icon")));
entry->Requirements = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_requirements")));
entry->FeedbackUrl = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_feedback")));
entry->Screenshots = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_screenshots")));
entry->AddedTime = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_datetime_added")));
entry->UpdatedTime = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_datetime_updated")));
entry->Download_count = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_download_count")));
entry->Download_link_32 = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_download_link_32")));
entry->Download_link_64 = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_download_link_64")));
entry->SHA2_32 = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_hash_32")));
entry->SHA2_64 = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_hash_64")));
entry->HASH_32 = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_signed_32")));
entry->HASH_64 = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_signed_64")));
entry->FileName = PhConvertUtf8ToUtf16(json_object_get_string(json_get_object(jvalue, "plugin_filename")));
swscanf(
PhGetString(entry->UpdatedTime),
L"%hu-%hu-%hu %hu:%hu:%hu",
&time.wYear,
&time.wMonth,
&time.wDay,
&time.wHour,
&time.wMinute,
&time.wSecond
);
if (SystemTimeToTzSpecificLocalTime(NULL, &time, &localTime))
{
entry->UpdatedTime = PhFormatDateTime(&localTime);
}
pluginDllPath = PhConcatStrings(3, PhGetString(PhGetApplicationDirectory()), L"Plugins\\", PhGetString(entry->FileName));
PhInitializeStringRefLongHint(&pluginBaseName, PhGetString(entry->FileName));
if (PhIsPluginDisabled(&pluginBaseName))
goto CleanupExit;
if (RtlDoesFileExists_U(PhGetString(pluginDllPath)))
{
ULONG versionSize;
PVOID versionInfo;
PUSHORT languageInfo;
UINT language;
UINT bufferSize = 0;
PWSTR buffer = NULL;
PPH_STRING internalName = NULL;
PPH_STRING version = NULL;
entry->FilePath = PhCreateString2(&pluginDllPath->sr);//entry->PluginInstance->FileName->sr);
versionSize = GetFileVersionInfoSize(PhGetString(entry->FilePath), NULL);
versionInfo = PhAllocate(versionSize);
memset(versionInfo, 0, versionSize);
if (GetFileVersionInfo(PhGetString(entry->FilePath), 0, versionSize, versionInfo))
{
if (VerQueryValue(versionInfo, L"\\", &buffer, &bufferSize))
{
VS_FIXEDFILEINFO* info = (VS_FIXEDFILEINFO*)buffer;
if (info->dwSignature == 0xfeef04bd)
{
version = PhFormatString(
L"%lu.%lu.%lu.%lu",
(info->dwFileVersionMS >> 16) & 0xffff,
(info->dwFileVersionMS >> 0) & 0xffff,
(info->dwFileVersionLS >> 16) & 0xffff,
(info->dwFileVersionLS >> 0) & 0xffff
);
}
}
if (VerQueryValue(versionInfo, L"\\VarFileInfo\\Translation", &languageInfo, &language))
{
PPH_STRING internalNameString = PhFormatString(
L"\\StringFileInfo\\%04x%04x\\InternalName",
languageInfo[0],
languageInfo[1]
);
if (VerQueryValue(versionInfo, PhGetStringOrEmpty(internalNameString), &buffer, &bufferSize))
{
internalName = PhCreateStringEx(buffer, bufferSize * sizeof(WCHAR));
}
PhDereferenceObject(internalNameString);
}
}
PhFree(versionInfo);
if (entry->PluginInstance = (PPHAPP_PLUGIN)PhFindPlugin(PhGetString(entry->InternalName)))
{
ULONGLONG currentVersion = ParseVersionString(version);
ULONGLONG latestVersion = ParseVersionString(entry->Version);
entry->PluginOptions = entry->PluginInstance->Information.HasOptions;
if (currentVersion < latestVersion)
{
// User is running an older version
entry->State = PLUGIN_STATE_UPDATE;
PostMessage(context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry);
}
}
else
{
// Plugin waiting to load?
entry->State = PLUGIN_STATE_RESTART;
PostMessage(context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry);
}
}
else
{
// New plugin available for download
entry->State = PLUGIN_STATE_REMOTE;
PostMessage(context->DialogHandle, ID_UPDATE_ADD, 0, (LPARAM)entry);
}
}
CleanupExit:
if (httpRequestHandle)
WinHttpCloseHandle(httpRequestHandle);
if (httpConnectionHandle)
WinHttpCloseHandle(httpConnectionHandle);
if (httpSessionHandle)
WinHttpCloseHandle(httpSessionHandle);
if (xmlStringBuffer)
PhFree(xmlStringBuffer);
PostMessage(context->DialogHandle, ID_UPDATE_COUNT, 0, 0);
return STATUS_SUCCESS;
}
NTSTATUS SetupExtractBuild(
_In_ PVOID Parameter
)
{
static PH_STRINGREF pluginsDirectory = PH_STRINGREF_INIT(L"plugins\\");
mz_uint64 totalLength = 0;
mz_uint64 total_size = 0;
mz_uint64 downloadedBytes = 0;
PPH_STRING totalSizeStr = NULL;
mz_bool status = MZ_FALSE;
mz_zip_archive zip_archive;
PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter;
memset(&zip_archive, 0, sizeof(zip_archive));
// TODO: Move existing folder items.
if (!(status = mz_zip_reader_init_file(&zip_archive, PhGetStringOrEmpty(context->SetupFilePath), 0)))
{
goto error;
}
for (mz_uint i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++)
{
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat))
{
//mz_zip_reader_end(&zip_archive);
break;
}
total_size += file_stat.m_uncomp_size;
}
totalSizeStr = PhFormatSize(total_size, -1);
for (ULONG i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++)
{
mz_zip_archive_file_stat stat;
if (!mz_zip_reader_file_stat(&zip_archive, i, &stat))
{
continue;
}
if (mz_zip_reader_is_file_a_directory(&zip_archive, i))
{
PPH_STRING directory;
PPH_STRING fileName;
PPH_STRING fullSetupPath;
PPH_STRING extractPath;
ULONG indexOfFileName = -1;
fileName = PhConvertUtf8ToUtf16(stat.m_filename);
directory = PhGetApplicationDirectory();
extractPath = PhConcatStringRef3(&directory->sr, &pluginsDirectory, &fileName->sr);
fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(extractPath), &indexOfFileName);
SHCreateDirectoryEx(NULL, PhGetStringOrEmpty(fullSetupPath), NULL);
}
else
{
PPH_STRING directory;
PPH_STRING fileName;
PPH_STRING fullSetupPath;
PPH_STRING extractPath;
PPH_STRING directoryPath;
//PPH_STRING baseNameString;
PPH_STRING fileNameString;
ULONG indexOfFileName = -1;
fileName = PhConvertUtf8ToUtf16(stat.m_filename);
directory = PhGetApplicationDirectory();
extractPath = PhConcatStringRef3(&directory->sr, &pluginsDirectory, &fileName->sr);
fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(extractPath), &indexOfFileName);
//baseNameString = PhGetBaseName(fullSetupPath);
//fullSetupPath = PhGetFullPath(PhGetStringOrEmpty(fileName), &indexOfFileName);
fileNameString = PhConcatStrings(2, fullSetupPath->Buffer, L".bak");
if (indexOfFileName != -1)
{
if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName))
{
SHCreateDirectoryEx(NULL, PhGetStringOrEmpty(directoryPath), NULL);
PhDereferenceObject(directoryPath);
}
}
if (RtlDoesFileExists_U(PhGetStringOrEmpty(fullSetupPath)))
{
MoveFileEx(PhGetString(fullSetupPath), PhGetString(fileNameString), MOVEFILE_REPLACE_EXISTING);
}
if (!mz_zip_reader_extract_to_file(&zip_archive, i, PhGetString(fullSetupPath), 0))
{
goto error;
}
PhDereferenceObject(fullSetupPath);
PhDereferenceObject(extractPath);
}
}
mz_zip_reader_end(&zip_archive);
return STATUS_SUCCESS;
error:
mz_zip_reader_end(&zip_archive);
return STATUS_FAIL_CHECK;
}