/* * Process Hacker Online Checks - * Uploader Window * * Copyright (C) 2010-2013 wj32 * Copyright (C) 2012-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 "onlnchk.h" #include "json-c/json.h" static SERVICE_INFO UploadServiceInfo[] = { { UPLOAD_SERVICE_VIRUSTOTAL, L"www.virustotal.com", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"???", L"file" }, { UPLOAD_SERVICE_JOTTI, L"virusscan.jotti.org", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"/en-US/submit-file?isAjax=true", L"sample-file[]" }, { UPLOAD_SERVICE_CIMA, L"camas.comodo.com", INTERNET_DEFAULT_HTTP_PORT, 0, L"/cgi-bin/submit", L"file" } }; json_object_ptr json_get_object(json_object_ptr rootObj, const char* key) { json_object_ptr returnObj; if (json_object_object_get_ex(rootObj, key, &returnObj)) { return returnObj; } return NULL; } HFONT InitializeFont( _In_ HWND hwnd ) { LOGFONT logFont; // Create the font handle if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0)) { HDC hdc; if (hdc = GetDC(hwnd)) { HFONT fontHandle = CreateFont( -MulDiv(-14, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY | ANTIALIASED_QUALITY, DEFAULT_PITCH, logFont.lfFaceName ); SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE); ReleaseDC(hwnd, hdc); return fontHandle; } } return NULL; } BOOL 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); 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 RaiseUploadError( _In_ PUPLOAD_CONTEXT Context, _In_ PWSTR Error, _In_ ULONG ErrorCode ) { if (Context->DialogHandle) { PostMessage( Context->DialogHandle, UM_ERROR, 0, (LPARAM)PhFormatString(L"Error: [%lu] %s", ErrorCode, Error) ); } } PSERVICE_INFO GetUploadServiceInfo( _In_ ULONG Id ) { ULONG i; for (i = 0; i < ARRAYSIZE(UploadServiceInfo); i++) { if (UploadServiceInfo[i].Id == Id) return &UploadServiceInfo[i]; } return NULL; } BOOLEAN PerformSubRequest( _In_ PUPLOAD_CONTEXT Context, _In_ PWSTR HostName, _In_ INTERNET_PORT HostPort, _In_ ULONG HostFlags, _In_ PWSTR ObjectName, _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data, _Out_opt_ PULONG DataLength ) { BOOLEAN result = FALSE; HINTERNET connectHandle = NULL; HINTERNET requestHandle = NULL; __try { // Connect to the online service. if (!(connectHandle = WinHttpConnect( Context->HttpHandle, HostName, HostPort, 0 ))) { RaiseUploadError(Context, L"Unable to connect to the service", GetLastError()); __leave; } // Create the request. if (!(requestHandle = WinHttpOpenRequest( connectHandle, NULL, ObjectName, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH | HostFlags ))) { RaiseUploadError(Context, L"Unable to create the request", GetLastError()); __leave; } // Send the request. if (!WinHttpSendRequest( requestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0 )) { RaiseUploadError(Context, L"Unable to send the request", GetLastError()); __leave; } // Wait for the send request to complete and receive the response. if (WinHttpReceiveResponse(requestHandle, NULL)) { BYTE buffer[PAGE_SIZE]; PSTR data; ULONG allocatedLength; ULONG dataLength; ULONG returnLength; allocatedLength = sizeof(buffer); data = PhAllocate(allocatedLength); dataLength = 0; while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength)) { if (returnLength == 0) break; if (allocatedLength < dataLength + returnLength) { allocatedLength *= 2; data = PhReAllocate(data, allocatedLength); } memcpy(data + dataLength, buffer, returnLength); dataLength += returnLength; } if (allocatedLength < dataLength + 1) { allocatedLength++; data = PhReAllocate(data, allocatedLength); } // Ensure that the buffer is null-terminated. data[dataLength] = 0; *Data = data; if (DataLength) { *DataLength = dataLength; } } else { RaiseUploadError(Context, L"Unable to receive the response", GetLastError()); __leave; } result = TRUE; } __finally { if (requestHandle) WinHttpCloseHandle(requestHandle); if (connectHandle) WinHttpCloseHandle(connectHandle); } return result; } NTSTATUS HashFileAndResetPosition( _In_ HANDLE FileHandle, _In_ PLARGE_INTEGER FileSize, _In_ ULONG Algorithm, _Out_ PVOID Hash ) { NTSTATUS status; IO_STATUS_BLOCK iosb; PH_HASH_CONTEXT hashContext; sha256_context sha256; ULONG64 bytesRemaining; FILE_POSITION_INFORMATION positionInfo; UCHAR buffer[PAGE_SIZE]; bytesRemaining = FileSize->QuadPart; switch (Algorithm) { case HASH_SHA1: PhInitializeHash(&hashContext, Sha1HashAlgorithm); break; case HASH_SHA256: sha256_starts(&sha256); break; } while (bytesRemaining) { status = NtReadFile( FileHandle, NULL, NULL, NULL, &iosb, buffer, sizeof(buffer), NULL, NULL ); if (!NT_SUCCESS(status)) break; switch (Algorithm) { case HASH_SHA1: PhUpdateHash(&hashContext, buffer, (ULONG)iosb.Information); break; case HASH_SHA256: sha256_update(&sha256, (PUCHAR)buffer, (ULONG)iosb.Information); break; } bytesRemaining -= (ULONG)iosb.Information; } if (status == STATUS_END_OF_FILE) status = STATUS_SUCCESS; if (NT_SUCCESS(status)) { switch (Algorithm) { case HASH_SHA1: PhFinalHash(&hashContext, Hash, 20, NULL); break; case HASH_SHA256: sha256_finish(&sha256, Hash); break; } positionInfo.CurrentByteOffset.QuadPart = 0; status = NtSetInformationFile( FileHandle, &iosb, &positionInfo, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation ); } return status; } NTSTATUS UploadFileThreadStart( _In_ PVOID Parameter ) { NTSTATUS status = STATUS_SUCCESS; ULONG httpStatus = 0; ULONG httpStatusLength = sizeof(ULONG); ULONG httpPostSeed = 0; ULONG totalUploadLength = 0; ULONG totalUploadedLength = 0; ULONG totalPostHeaderWritten = 0; ULONG totalPostFooterWritten = 0; ULONG totalWriteLength = 0; LARGE_INTEGER timeNow; LARGE_INTEGER timeStart; ULONG64 timeTicks = 0; ULONG64 timeBitsPerSecond = 0; HANDLE fileHandle = INVALID_HANDLE_VALUE; IO_STATUS_BLOCK isb; PSERVICE_INFO serviceInfo = NULL; HINTERNET connectHandle = NULL; HINTERNET requestHandle = NULL; PPH_STRING postBoundary = NULL; PPH_BYTES asciiPostData = NULL; PPH_BYTES asciiFooterData = NULL; PH_STRING_BUILDER httpRequestHeaders = { 0 }; PH_STRING_BUILDER httpPostHeader = { 0 }; PH_STRING_BUILDER httpPostFooter = { 0 }; BYTE buffer[PAGE_SIZE]; PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; serviceInfo = GetUploadServiceInfo(context->Service); __try { // Open the file and check its size. status = PhCreateFileWin32( &fileHandle, context->FileName->Buffer, FILE_GENERIC_READ, 0, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS(status)) { RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status)); __leave; } // Connect to the online service. if (!(connectHandle = WinHttpConnect( context->HttpHandle, serviceInfo->HostName, serviceInfo->HostPort, 0 ))) { RaiseUploadError(context, L"Unable to connect to the service", GetLastError()); __leave; } // Create the request. if (!(requestHandle = WinHttpOpenRequest( connectHandle, L"POST", context->ObjectName->Buffer, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH | serviceInfo->HostFlags ))) { RaiseUploadError(context, L"Unable to create the request", GetLastError()); __leave; } if (context->Service == UPLOAD_SERVICE_JOTTI) { PPH_STRING ajaxHeader; ajaxHeader = PhCreateString(L"X-Requested-With: XMLHttpRequest"); WinHttpAddRequestHeaders( requestHandle, ajaxHeader->Buffer, (ULONG)ajaxHeader->Length / sizeof(WCHAR), WINHTTP_ADDREQ_FLAG_ADD ); PhDereferenceObject(ajaxHeader); } // Create and POST data. PhInitializeStringBuilder(&httpRequestHeaders, MAX_PATH); PhInitializeStringBuilder(&httpPostHeader, MAX_PATH); PhInitializeStringBuilder(&httpPostFooter, MAX_PATH); // build request boundary string postBoundary = PhFormatString( L"------------------------%I64u", (ULONG64)RtlRandomEx(&httpPostSeed) | ((ULONG64)RtlRandomEx(&httpPostSeed) << 31) ); // build request header string PhAppendFormatStringBuilder( &httpRequestHeaders, L"Content-Type: multipart/form-data; boundary=%s\r\n", postBoundary->Buffer ); if (context->Service == UPLOAD_SERVICE_JOTTI) { // POST boundary header PhAppendFormatStringBuilder( &httpPostHeader, L"\r\n--%s\r\n", postBoundary->Buffer ); PhAppendFormatStringBuilder( &httpPostHeader, L"Content-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n268435456\r\n" ); PhAppendFormatStringBuilder( &httpPostHeader, L"--%s\r\n", postBoundary->Buffer ); PhAppendFormatStringBuilder( &httpPostHeader, L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", serviceInfo->FileNameFieldName, context->BaseFileName->Buffer ); PhAppendFormatStringBuilder( &httpPostHeader, L"Content-Type: application/x-msdownload\r\n\r\n" ); // POST boundary footer PhAppendFormatStringBuilder( &httpPostFooter, L"\r\n--%s--\r\n", postBoundary->Buffer ); } else { // POST boundary header PhAppendFormatStringBuilder( &httpPostHeader, L"--%s\r\n", postBoundary->Buffer ); PhAppendFormatStringBuilder( &httpPostHeader, L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", serviceInfo->FileNameFieldName, context->BaseFileName->Buffer ); PhAppendFormatStringBuilder( &httpPostHeader, L"Content-Type: application/octet-stream\r\n\r\n" ); // POST boundary footer PhAppendFormatStringBuilder( &httpPostFooter, L"\r\n--%s--\r\n\r\n", postBoundary->Buffer ); } // add headers if (!WinHttpAddRequestHeaders( requestHandle, httpRequestHeaders.String->Buffer, -1L, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD )) { RaiseUploadError(context, L"Unable to add request headers", GetLastError()); __leave; } // All until now has been just for this; Calculate the total request length. totalUploadLength = (ULONG)httpPostHeader.String->Length / sizeof(WCHAR) + context->TotalFileLength + (ULONG)httpPostFooter.String->Length / sizeof(WCHAR); // Send the request. if (!WinHttpSendRequest( requestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, totalUploadLength, 0 )) { RaiseUploadError(context, L"Unable to send the request", GetLastError()); __leave; } // Convert to ASCII asciiPostData = PhConvertUtf16ToAscii(httpPostHeader.String->Buffer, '-'); asciiFooterData = PhConvertUtf16ToAscii(httpPostFooter.String->Buffer, '-'); // Start the clock. PhQuerySystemTime(&timeStart); // Write the header if (!WinHttpWriteData( requestHandle, asciiPostData->Buffer, (ULONG)asciiPostData->Length, &totalPostHeaderWritten )) { RaiseUploadError(context, L"Unable to write the post header", GetLastError()); __leave; } // Upload the file... while (TRUE) { status = NtReadFile( fileHandle, NULL, NULL, NULL, &isb, buffer, PAGE_SIZE, NULL, NULL ); if (!NT_SUCCESS(status)) break; if (!WinHttpWriteData(requestHandle, buffer, (ULONG)isb.Information, &totalWriteLength)) { RaiseUploadError(context, L"Unable to upload the file data", GetLastError()); __leave; } totalUploadedLength += totalWriteLength; // Query the current time PhQuerySystemTime(&timeNow); // Calculate the number of ticks timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC; timeBitsPerSecond = totalUploadedLength / __max(timeTicks, 1); { FLOAT percent = ((FLOAT)totalUploadedLength / context->TotalFileLength * 100); PPH_STRING totalLength = PhFormatSize(context->TotalFileLength, -1); PPH_STRING totalUploaded = PhFormatSize(totalUploadedLength, -1); PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1); PPH_STRING statusMessage = PhFormatString( L"%s of %s @ %s/s", totalUploaded->Buffer, totalLength->Buffer, totalSpeed->Buffer ); Static_SetText(context->StatusHandle, statusMessage->Buffer); // Update the progress bar position PostMessage(context->ProgressHandle, PBM_SETPOS, (INT)percent, 0); PhDereferenceObject(statusMessage); PhDereferenceObject(totalSpeed); PhDereferenceObject(totalLength); PhDereferenceObject(totalUploaded); } } // Write the footer bytes if (!WinHttpWriteData( requestHandle, asciiFooterData->Buffer, (ULONG)asciiFooterData->Length, &totalPostFooterWritten )) { RaiseUploadError(context, L"Unable to write the post footer", GetLastError()); __leave; } // Wait for the send request to complete and receive the response. if (!WinHttpReceiveResponse(requestHandle, NULL)) { RaiseUploadError(context, L"Unable to receive the response", GetLastError()); __leave; } // Handle service-specific actions. WinHttpQueryHeaders( requestHandle, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &httpStatus, &httpStatusLength, NULL ); if (httpStatus == HTTP_STATUS_OK || httpStatus == HTTP_STATUS_REDIRECT_METHOD || httpStatus == HTTP_STATUS_REDIRECT) { switch (context->Service) { case UPLOAD_SERVICE_VIRUSTOTAL: { ULONG bufferLength = 0; // Use WinHttpQueryOption to obtain a buffer size. if (!WinHttpQueryOption(requestHandle, WINHTTP_OPTION_URL, NULL, &bufferLength)) { PPH_STRING buffer = PhCreateStringEx(NULL, bufferLength); // Use WinHttpQueryOption again, this time to retrieve the URL in the new buffer if (WinHttpQueryOption(requestHandle, WINHTTP_OPTION_URL, buffer->Buffer, &bufferLength)) { // Format the retrieved URL... context->LaunchCommand = PhDuplicateString(buffer); } PhDereferenceObject(buffer); } } break; case UPLOAD_SERVICE_JOTTI: { PSTR hrefEquals = NULL; PSTR quote = NULL; PSTR buffer = NULL; ULONG bufferLength = 0; json_object_ptr rootJsonObject; //This service returns some json that redirects the user to the new location. if (!ReadRequestString(requestHandle, &buffer, &bufferLength)) { RaiseUploadError(context, L"Unable to complete the request", GetLastError()); __leave; } if (rootJsonObject = json_tokener_parse(buffer)) { PSTR redirectUrl = json_object_get_string(json_get_object(rootJsonObject, "redirecturl")); context->LaunchCommand = PhFormatString( L"http://virusscan.jotti.org%hs", redirectUrl ); json_object_put(rootJsonObject); } } break; case UPLOAD_SERVICE_CIMA: { PSTR urlEquals = NULL; PSTR quote = NULL; PSTR buffer = NULL; ULONG bufferLength = 0; // This service returns some HTML that redirects the user to the new location. if (!ReadRequestString(requestHandle, &buffer, &bufferLength)) { RaiseUploadError(context, L"Unable to complete the CIMA request", GetLastError()); __leave; } // The HTML looks like this: // urlEquals = strstr(buffer, "url="); if (urlEquals) { urlEquals += 4; quote = strchr(urlEquals, '"'); if (quote) { context->LaunchCommand = PhFormatString( L"http://camas.comodo.com%.*S", quote - urlEquals, urlEquals ); } } } break; } } else { RaiseUploadError(context, L"Unable to complete the request", STATUS_FVE_PARTIAL_METADATA); __leave; } if (!PhIsNullOrEmptyString(context->LaunchCommand)) { PostMessage(context->DialogHandle, UM_LAUNCH, 0, 0); } else { RaiseUploadError(context, L"Unable to complete the Launch request (please try again after a few minutes)", ERROR_INVALID_DATA); __leave; } } __finally { if (postBoundary) { PhDereferenceObject(postBoundary); } if (asciiFooterData) { PhDereferenceObject(asciiFooterData); } if (asciiPostData) { PhDereferenceObject(asciiPostData); } if (httpPostFooter.String) { PhDeleteStringBuilder(&httpPostFooter); } if (httpPostHeader.String) { PhDeleteStringBuilder(&httpPostHeader); } if (httpRequestHeaders.String) { PhDeleteStringBuilder(&httpRequestHeaders); } if (fileHandle != INVALID_HANDLE_VALUE) { NtClose(fileHandle); } } return status; } NTSTATUS UploadCheckThreadStart( _In_ PVOID Parameter ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN fileExists = FALSE; LARGE_INTEGER fileSize64; PSTR subRequestBuffer = NULL; HINTERNET connectHandle = NULL; HINTERNET requestHandle = NULL; PSERVICE_INFO serviceInfo = NULL; PPH_STRING hashString = NULL; PPH_STRING subObjectName = NULL; HANDLE fileHandle = INVALID_HANDLE_VALUE; PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; serviceInfo = GetUploadServiceInfo(context->Service); __try { // Open the file and check its size. status = PhCreateFileWin32( &fileHandle, context->FileName->Buffer, FILE_GENERIC_READ, 0, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS(status)) { RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status)); __leave; } if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize64))) { if (context->Service == UPLOAD_SERVICE_VIRUSTOTAL) { if (fileSize64.QuadPart > 128 * 1024 * 1024) // 128 MB { RaiseUploadError(context, L"The file is too large (over 128 MB)", ERROR_FILE_TOO_LARGE); __leave; } } else { if (fileSize64.QuadPart > 20 * 1024 * 1024) // 20 MB { RaiseUploadError(context, L"The file is too large (over 20 MB)", ERROR_FILE_TOO_LARGE); __leave; } } context->TotalFileLength = fileSize64.LowPart; } // Get proxy configuration and create winhttp handle (used for all winhttp sessions + requests). { PPH_STRING phVersion = NULL; PPH_STRING userAgent = NULL; WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 }; // Create a user agent string. phVersion = PhGetPhVersion(); userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer); // Query the current system proxy WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig); // Open the HTTP session with the system proxy configuration if available context->HttpHandle = WinHttpOpen( userAgent->Buffer, proxyConfig.lpszProxy != NULL ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, proxyConfig.lpszProxy, proxyConfig.lpszProxyBypass, 0 ); PhClearReference(&phVersion); PhClearReference(&userAgent); if (!context->HttpHandle) __leave; if (WindowsVersion >= WINDOWS_8_1) { // Enable GZIP and DEFLATE support on Windows 8.1 and above using undocumented flags. ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE; WinHttpSetOption( context->HttpHandle, WINHTTP_OPTION_DECOMPRESSION, &httpFlags, sizeof(ULONG) ); } } switch (context->Service) { case UPLOAD_SERVICE_VIRUSTOTAL: { PSTR uploadUrl = NULL; PSTR quote = NULL; ULONG bufferLength = 0; UCHAR hash[32]; json_object_ptr rootJsonObject; if (!NT_SUCCESS(status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA256, hash))) { RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status)); __leave; } hashString = PhBufferToHexString(hash, 32); subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", hashString->Buffer); context->LaunchCommand = PhFormatString(L"http://www.virustotal.com/file/%s/analysis/", hashString->Buffer); if (!PerformSubRequest( context, serviceInfo->HostName, serviceInfo->HostPort, serviceInfo->HostFlags, subObjectName->Buffer, &subRequestBuffer, &bufferLength )) { __leave; } if (rootJsonObject = json_tokener_parse(subRequestBuffer)) { json_bool file_Exists; PSTR uploadUrl; file_Exists = json_object_get_boolean(json_get_object(rootJsonObject, "file_exists")); uploadUrl = json_object_get_string(json_get_object(rootJsonObject, "upload_url")); if (file_Exists) { fileExists = TRUE; } context->ObjectName = PhZeroExtendToUtf16(uploadUrl + strlen("https://www.virustotal.com")); json_object_put(rootJsonObject); } // Create the default upload URL if (!context->ObjectName) context->ObjectName = PhCreateString(serviceInfo->UploadObjectName); } break; case UPLOAD_SERVICE_JOTTI: { // Create the default upload URL if (!context->ObjectName) context->ObjectName = PhCreateString(serviceInfo->UploadObjectName); } break; case UPLOAD_SERVICE_CIMA: { PSTR quote = NULL; ULONG bufferLength = 0; UCHAR hash[32]; ULONG status = 0; ULONG statusLength = sizeof(statusLength); if (!NT_SUCCESS(status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA256, hash))) { RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status)); __leave; } hashString = PhBufferToHexString(hash, 32); subObjectName = PhConcatStrings2(L"/cgi-bin/submit?file=", hashString->Buffer); context->LaunchCommand = PhFormatString(L"http://camas.comodo.com/cgi-bin/submit?file=%s", hashString->Buffer); // Connect to the CIMA online service. if (!(connectHandle = WinHttpConnect( context->HttpHandle, serviceInfo->HostName, INTERNET_DEFAULT_HTTP_PORT, 0 ))) { RaiseUploadError(context, L"Unable to connect to the CIMA service", GetLastError()); __leave; } // Create the request. if (!(requestHandle = WinHttpOpenRequest( connectHandle, NULL, subObjectName->Buffer, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH ))) { RaiseUploadError(context, L"Unable to create the CIMA request", GetLastError()); __leave; } // Send the request. if (!WinHttpSendRequest( requestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0 )) { RaiseUploadError(context, L"Unable to send the CIMA request", GetLastError()); __leave; } // Wait for the send request to complete and receive the response. if (!WinHttpReceiveResponse(requestHandle, NULL)) { RaiseUploadError(context, L"Unable to receive the CIMA response", GetLastError()); __leave; } WinHttpQueryHeaders( requestHandle, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &statusLength, NULL ); if (status == HTTP_STATUS_OK) { fileExists = TRUE; } if (!context->ObjectName) { // Create the default upload URL context->ObjectName = PhCreateString(serviceInfo->UploadObjectName); } } break; } // Do we need to prompt the user? if (fileExists && !PhIsNullOrEmptyString(context->LaunchCommand)) { PostMessage(context->DialogHandle, UM_EXISTS, 0, 0); __leave; } // No existing file found... Start the upload. if (!NT_SUCCESS(UploadFileThreadStart(context))) __leave; } __finally { PhClearReference(&hashString); PhClearReference(&subObjectName); if (requestHandle) { WinHttpCloseHandle(requestHandle); } if (connectHandle) { WinHttpCloseHandle(connectHandle); } if (fileHandle != INVALID_HANDLE_VALUE) { NtClose(fileHandle); } } return status; } INT_PTR CALLBACK UploadDlgProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam ) { PUPLOAD_CONTEXT context = NULL; if (uMsg == WM_INITDIALOG) { context = (PUPLOAD_CONTEXT)lParam; SetProp(hwndDlg, L"Context", (HANDLE)context); } else { context = (PUPLOAD_CONTEXT)GetProp(hwndDlg, L"Context"); if (uMsg == WM_NCDESTROY) { PhClearReference(&context->FileName); PhClearReference(&context->BaseFileName); PhClearReference(&context->WindowFileName); PhClearReference(&context->LaunchCommand); PhClearReference(&context->ObjectName); if (context->MessageFont) DeleteObject(context->MessageFont); if (context->HttpHandle) WinHttpCloseHandle(context->HttpHandle); RemoveProp(hwndDlg, L"Context"); PhFree(context); PostQuitMessage(0); } } if (!context) return FALSE; switch (uMsg) { case WM_INITDIALOG: { HANDLE dialogThread = NULL; HWND parentWindow = GetParent(hwndDlg); PhCenterWindow(hwndDlg, (IsWindowVisible(parentWindow) && !IsMinimized(parentWindow)) ? parentWindow : NULL); context->DialogHandle = hwndDlg; context->StatusHandle = GetDlgItem(hwndDlg, IDC_STATUS); context->ProgressHandle = GetDlgItem(hwndDlg, IDC_PROGRESS1); context->MessageHandle = GetDlgItem(hwndDlg, IDC_MESSAGE); context->MessageFont = InitializeFont(context->MessageHandle); context->WindowFileName = PhFormatString(L"Uploading: %s", context->BaseFileName->Buffer); context->UploadServiceState = PhUploadServiceChecking; // Reset the window status... Static_SetText(context->MessageHandle, context->WindowFileName->Buffer); switch (context->Service) { case UPLOAD_SERVICE_VIRUSTOTAL: Static_SetText(hwndDlg, L"Uploading to VirusTotal..."); break; case UPLOAD_SERVICE_JOTTI: Static_SetText(hwndDlg, L"Uploading to Jotti..."); break; case UPLOAD_SERVICE_CIMA: Static_SetText(hwndDlg, L"Uploading to Comodo..."); break; } if (dialogThread = PhCreateThread(0, UploadCheckThreadStart, (PVOID)context)) NtClose(dialogThread); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDYES: { context->UploadServiceState = PhUploadServiceMaximum; if (!PhIsNullOrEmptyString(context->LaunchCommand)) { PhShellExecute(hwndDlg, context->LaunchCommand->Buffer, NULL); } DestroyWindow(hwndDlg); } break; case IDNO: { if (context->UploadServiceState == PhUploadServiceViewReport) { HANDLE dialogThread = NULL; // Set state to uploading... context->UploadServiceState = PhUploadServiceUploading; // Reset the window status... Static_SetText(context->MessageHandle, context->WindowFileName->Buffer); Static_SetText(GetDlgItem(hwndDlg, IDNO), L"Cancel"); Control_Visible(GetDlgItem(hwndDlg, IDYES), FALSE); // Start the upload thread... if (dialogThread = PhCreateThread(0, UploadFileThreadStart, (PVOID)context)) NtClose(dialogThread); } else { context->UploadServiceState = PhUploadServiceMaximum; DestroyWindow(hwndDlg); } } break; case IDCANCEL: { context->UploadServiceState = PhUploadServiceMaximum; DestroyWindow(hwndDlg); } } break; } break; case WM_CTLCOLORBTN: case WM_CTLCOLORDLG: case WM_CTLCOLORSTATIC: { HDC hDC = (HDC)wParam; HWND hwndChild = (HWND)lParam; // Check for our static label and change the color. if (GetDlgCtrlID(hwndChild) == IDC_MESSAGE) { SetTextColor(hDC, RGB(19, 112, 171)); } // Set a transparent background for the control backcolor. SetBkMode(hDC, TRANSPARENT); // set window background color. return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); } break; case UM_EXISTS: { context->UploadServiceState = PhUploadServiceViewReport; Static_SetText(GetDlgItem(hwndDlg, IDNO), L"No"); Control_Visible(GetDlgItem(hwndDlg, IDYES), TRUE); Static_SetText(context->MessageHandle, L"File already analysed."); Static_SetText(context->StatusHandle, L"View existing report?"); } break; case UM_LAUNCH: { context->UploadServiceState = PhUploadServiceMaximum; if (!PhIsNullOrEmptyString(context->LaunchCommand)) { PhShellExecute(hwndDlg, context->LaunchCommand->Buffer, NULL); } DestroyWindow(hwndDlg); } break; case UM_ERROR: { PPH_STRING errorMessage = (PPH_STRING)lParam; context->UploadServiceState = PhUploadServiceMaximum; if (errorMessage) { Static_SetText(GetDlgItem(hwndDlg, IDC_MESSAGE), errorMessage->Buffer); PhClearReference(&errorMessage); } else { Static_SetText(GetDlgItem(hwndDlg, IDC_MESSAGE), L"Error"); } Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), L""); Static_SetText(GetDlgItem(hwndDlg, IDNO), L"Close"); } break; } return FALSE; } NTSTATUS PhUploadToDialogThreadStart( _In_ PVOID Parameter ) { BOOL result; MSG message; HWND dialogHandle; PH_AUTO_POOL autoPool; PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter; PhInitializeAutoPool(&autoPool); dialogHandle = CreateDialogParam( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROGRESS), PhMainWndHandle, UploadDlgProc, (LPARAM)Parameter ); ShowWindow(dialogHandle, SW_SHOW); SetForegroundWindow(dialogHandle); while (result = GetMessage(&message, NULL, 0, 0)) { if (result == -1) break; if (!IsDialogMessage(dialogHandle, &message)) { TranslateMessage(&message); DispatchMessage(&message); } PhDrainAutoPool(&autoPool); } PhDeleteAutoPool(&autoPool); return STATUS_SUCCESS; } VOID UploadToOnlineService( _In_ PPH_STRING FileName, _In_ ULONG Service ) { HANDLE dialogThread = NULL; PUPLOAD_CONTEXT context; context = (PUPLOAD_CONTEXT)PhAllocate(sizeof(UPLOAD_CONTEXT)); memset(context, 0, sizeof(UPLOAD_CONTEXT)); context->Service = Service; context->FileName = PhDuplicateString(FileName); context->BaseFileName = PhGetBaseName(context->FileName); if (dialogThread = PhCreateThread(0, PhUploadToDialogThreadStart, (PVOID)context)) NtClose(dialogThread); }