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