/* * 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 . */ #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; }