Process Hacker
upload.c
Go to the documentation of this file.
1  /*
2  * Process Hacker Online Checks -
3  * Uploader Window
4  *
5  * Copyright (C) 2010-2013 wj32
6  * Copyright (C) 2012-2015 dmex
7  *
8  * This file is part of Process Hacker.
9  *
10  * Process Hacker is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Process Hacker is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "onlnchk.h"
25 
26 static SERVICE_INFO UploadServiceInfo[] =
27 {
28  { UPLOAD_SERVICE_VIRUSTOTAL, L"www.virustotal.com", INTERNET_DEFAULT_HTTPS_PORT, WINHTTP_FLAG_SECURE, L"???", L"file" },
29  { UPLOAD_SERVICE_JOTTI, L"virusscan.jotti.org", INTERNET_DEFAULT_HTTP_PORT, 0, L"/processupload.php", L"scanfile" },
30  { UPLOAD_SERVICE_CIMA, L"camas.comodo.com", INTERNET_DEFAULT_HTTP_PORT, 0, L"/cgi-bin/submit", L"file" }
31 };
32 
33 static HFONT InitializeFont(
34  _In_ HWND hwnd
35  )
36 {
37  HFONT fontHandle;
38  NONCLIENTMETRICS metrics = { sizeof(NONCLIENTMETRICS) };
39 
40  if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, 0))
41  {
42  metrics.lfMessageFont.lfHeight = -15;
43  //metrics.lfMessageFont.lfWeight = FW_MEDIUM;
44  //metrics.lfMessageFont.lfQuality = CLEARTYPE_QUALITY | ANTIALIASED_QUALITY;
45 
46  fontHandle = CreateFontIndirect(&metrics.lfMessageFont);
47  }
48  else
49  {
50  LOGFONT font;
51 
52  GetObject((HFONT)GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &font);
53 
54  font.lfHeight = -15;
55 
56  fontHandle = CreateFontIndirect(&font);
57  }
58 
59  SendMessage(hwnd, WM_SETFONT, (WPARAM)fontHandle, TRUE);
60 
61  return fontHandle;
62 }
63 
64 static BOOL ReadRequestString(
65  _In_ HINTERNET Handle,
66  _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data,
67  _Out_ ULONG *DataLength
68  )
69 {
70  BYTE buffer[PAGE_SIZE];
71  PSTR data;
72  ULONG allocatedLength;
73  ULONG dataLength;
74  ULONG returnLength;
75 
76  allocatedLength = sizeof(buffer);
77  data = (PSTR)PhAllocate(allocatedLength);
78  dataLength = 0;
79 
80  // Zero the buffer
81  memset(buffer, 0, PAGE_SIZE);
82 
83  while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength))
84  {
85  if (returnLength == 0)
86  break;
87 
88  if (allocatedLength < dataLength + returnLength)
89  {
90  allocatedLength *= 2;
91  data = (PSTR)PhReAllocate(data, allocatedLength);
92  }
93 
94  // Copy the returned buffer into our pointer
95  memcpy(data + dataLength, buffer, returnLength);
96  // Zero the returned buffer for the next loop
97  //memset(buffer, 0, returnLength);
98 
99  dataLength += returnLength;
100  }
101 
102  if (allocatedLength < dataLength + 1)
103  {
104  allocatedLength++;
105  data = (PSTR)PhReAllocate(data, allocatedLength);
106  }
107 
108  // Ensure that the buffer is null-terminated.
109  data[dataLength] = 0;
110 
111  *DataLength = dataLength;
112  *Data = data;
113 
114  return TRUE;
115 }
116 
117 static VOID RaiseUploadError(
118  _In_ PUPLOAD_CONTEXT Context,
119  _In_ PWSTR Error,
120  _In_ ULONG ErrorCode
121  )
122 {
123  if (Context->DialogHandle)
124  {
125  PostMessage(
126  Context->DialogHandle,
127  UM_ERROR,
128  0,
129  (LPARAM)PhFormatString(L"Error: [%lu] %s", ErrorCode, Error)
130  );
131  }
132 }
133 
134 static PSERVICE_INFO GetUploadServiceInfo(
135  _In_ ULONG Id
136  )
137 {
138  ULONG i;
139 
140  for (i = 0; i < _countof(UploadServiceInfo); i++)
141  {
142  if (UploadServiceInfo[i].Id == Id)
143  return &UploadServiceInfo[i];
144  }
145 
146  return NULL;
147 }
148 
149 static BOOLEAN PerformSubRequest(
150  _In_ PUPLOAD_CONTEXT Context,
151  _In_ PWSTR HostName,
152  _In_ PWSTR ObjectName,
153  _Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data,
154  _Out_opt_ PULONG DataLength
155  )
156 {
157  BOOLEAN result = FALSE;
158  HINTERNET connectHandle = NULL;
159  HINTERNET requestHandle = NULL;
160 
161  __try
162  {
163  // Connect to the online service.
164  if (!(connectHandle = WinHttpConnect(
165  Context->HttpHandle,
166  HostName,
167  INTERNET_DEFAULT_HTTP_PORT,
168  0
169  )))
170  {
171  RaiseUploadError(Context, L"Unable to connect to the service", GetLastError());
172  __leave;
173  }
174 
175  // Create the request.
176  if (!(requestHandle = WinHttpOpenRequest(
177  connectHandle,
178  NULL, // GET
179  ObjectName,
180  NULL,// HTTP/1.1
181  WINHTTP_NO_REFERER,
182  WINHTTP_DEFAULT_ACCEPT_TYPES,
183  WINHTTP_FLAG_REFRESH
184  )))
185  {
186  RaiseUploadError(Context, L"Unable to create the request", GetLastError());
187  __leave;
188  }
189 
190  // Send the request.
191  if (!WinHttpSendRequest(requestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
192  {
193  RaiseUploadError(Context, L"Unable to send the request", GetLastError());
194  __leave;
195  }
196 
197  // Wait for the send request to complete and recieve the response.
198  if (WinHttpReceiveResponse(requestHandle, NULL))
199  {
200  BYTE buffer[PAGE_SIZE];
201  PSTR data;
202  ULONG allocatedLength;
203  ULONG dataLength;
204  ULONG returnLength;
205 
206  allocatedLength = sizeof(buffer);
207  data = PhAllocate(allocatedLength);
208  dataLength = 0;
209 
210  while (WinHttpReadData(requestHandle, buffer, PAGE_SIZE, &returnLength))
211  {
212  if (returnLength == 0)
213  break;
214 
215  if (allocatedLength < dataLength + returnLength)
216  {
217  allocatedLength *= 2;
218  data = PhReAllocate(data, allocatedLength);
219  }
220 
221  memcpy(data + dataLength, buffer, returnLength);
222  dataLength += returnLength;
223  }
224 
225  if (allocatedLength < dataLength + 1)
226  {
227  allocatedLength++;
228  data = PhReAllocate(data, allocatedLength);
229  }
230 
231  // Ensure that the buffer is null-terminated.
232  data[dataLength] = 0;
233 
234  *Data = data;
235 
236  if (DataLength)
237  {
238  *DataLength = dataLength;
239  }
240  }
241  else
242  {
243  RaiseUploadError(Context, L"Unable to receive the response", GetLastError());
244  __leave;
245  }
246 
247  result = TRUE;
248  }
249  __finally
250  {
251  if (requestHandle)
252  WinHttpCloseHandle(requestHandle);
253  if (connectHandle)
254  WinHttpCloseHandle(connectHandle);
255  }
256 
257  return result;
258 }
259 
260 static NTSTATUS HashFileAndResetPosition(
261  _In_ HANDLE FileHandle,
262  _In_ PLARGE_INTEGER FileSize,
263  _In_ ULONG Algorithm,
264  _Out_ PVOID Hash
265  )
266 {
267  NTSTATUS status;
268  IO_STATUS_BLOCK iosb;
269  PH_HASH_CONTEXT hashContext;
270  sha256_context sha256;
271  ULONG64 bytesRemaining;
272  FILE_POSITION_INFORMATION positionInfo;
273  UCHAR buffer[PAGE_SIZE];
274 
275  bytesRemaining = FileSize->QuadPart;
276 
277  switch (Algorithm)
278  {
279  case HASH_SHA1:
280  PhInitializeHash(&hashContext, Sha1HashAlgorithm);
281  break;
282  case HASH_SHA256:
283  sha256_starts(&sha256);
284  break;
285  }
286 
287  while (bytesRemaining)
288  {
289  status = NtReadFile(
290  FileHandle,
291  NULL,
292  NULL,
293  NULL,
294  &iosb,
295  buffer,
296  sizeof(buffer),
297  NULL,
298  NULL
299  );
300 
301  if (!NT_SUCCESS(status))
302  break;
303 
304  switch (Algorithm)
305  {
306  case HASH_SHA1:
307  PhUpdateHash(&hashContext, buffer, (ULONG)iosb.Information);
308  break;
309  case HASH_SHA256:
310  sha256_update(&sha256, (PUCHAR)buffer, (ULONG)iosb.Information);
311  break;
312  }
313 
314  bytesRemaining -= (ULONG)iosb.Information;
315  }
316 
317  if (status == STATUS_END_OF_FILE)
318  status = STATUS_SUCCESS;
319 
320  if (NT_SUCCESS(status))
321  {
322  switch (Algorithm)
323  {
324  case HASH_SHA1:
325  PhFinalHash(&hashContext, Hash, 20, NULL);
326  break;
327  case HASH_SHA256:
328  sha256_finish(&sha256, Hash);
329  break;
330  }
331 
332  positionInfo.CurrentByteOffset.QuadPart = 0;
333  status = NtSetInformationFile(
334  FileHandle,
335  &iosb,
336  &positionInfo,
339  );
340  }
341 
342  return status;
343 }
344 
345 static NTSTATUS UploadFileThreadStart(
346  _In_ PVOID Parameter
347  )
348 {
349  NTSTATUS status = STATUS_SUCCESS;
350  ULONG httpStatus = 0;
351  ULONG httpStatusLength = sizeof(ULONG);
352  ULONG httpPostSeed = 0;
353  ULONG totalUploadLength = 0;
354  ULONG totalUploadedLength = 0;
355  ULONG totalPostHeaderWritten = 0;
356  ULONG totalPostFooterWritten = 0;
357  ULONG totalWriteLength = 0;
358  LARGE_INTEGER timeNow;
359  LARGE_INTEGER timeStart;
360  ULONG64 timeTicks = 0;
361  ULONG64 timeBitsPerSecond = 0;
362 
363  HANDLE fileHandle = INVALID_HANDLE_VALUE;
364  IO_STATUS_BLOCK isb;
365  PSERVICE_INFO serviceInfo = NULL;
366  HINTERNET connectHandle = NULL;
367  HINTERNET requestHandle = NULL;
368 
369  PPH_STRING postBoundary = NULL;
370  PPH_BYTES asciiPostData = NULL;
371  PPH_BYTES asciiFooterData = NULL;
372  PH_STRING_BUILDER httpRequestHeaders = { 0 };
373  PH_STRING_BUILDER httpPostHeader = { 0 };
374  PH_STRING_BUILDER httpPostFooter = { 0 };
375  BYTE buffer[PAGE_SIZE];
376 
377  PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter;
378 
379  serviceInfo = GetUploadServiceInfo(context->Service);
380 
381  __try
382  {
383  // Open the file and check its size.
384  status = PhCreateFileWin32(
385  &fileHandle,
386  context->FileName->Buffer,
387  FILE_GENERIC_READ,
388  0,
389  FILE_SHARE_READ | FILE_SHARE_DELETE,
390  FILE_OPEN,
392  );
393 
394  if (!NT_SUCCESS(status))
395  {
396  RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status));
397  __leave;
398  }
399 
400  // Connect to the online service.
401  if (!(connectHandle = WinHttpConnect(
402  context->HttpHandle,
403  serviceInfo->HostName,
404  serviceInfo->HostPort,
405  0
406  )))
407  {
408  RaiseUploadError(context, L"Unable to connect to the service", GetLastError());
409  __leave;
410  }
411 
412  // Create the request.
413  if (!(requestHandle = WinHttpOpenRequest(
414  connectHandle,
415  L"POST",
416  context->ObjectName->Buffer,
417  NULL, // HTTP/1.1
418  WINHTTP_NO_REFERER,
419  WINHTTP_DEFAULT_ACCEPT_TYPES,
420  WINHTTP_FLAG_REFRESH | serviceInfo->HostFlags
421  )))
422  {
423  RaiseUploadError(context, L"Unable to create the request", GetLastError());
424  __leave;
425  }
426 
427  // TODO? Set timeouts and disable http redirection
428  //ULONG timeout = 5 * 60 * 1000; // 5 minutes
429  //WinHttpSetTimeouts(requestHandle, timeout, timeout, timeout, timeout);
430 
431  // Create and POST data.
432  PhInitializeStringBuilder(&httpRequestHeaders, MAX_PATH);
433  PhInitializeStringBuilder(&httpPostHeader, MAX_PATH);
434  PhInitializeStringBuilder(&httpPostFooter, MAX_PATH);
435 
436  // build request boundary string
437  postBoundary = PhFormatString(
438  L"------------------------%I64u",
439  (ULONG64)RtlRandomEx(&httpPostSeed) | ((ULONG64)RtlRandomEx(&httpPostSeed) << 31)
440  );
441  // build request header string
442  PhAppendFormatStringBuilder(&httpRequestHeaders,
443  L"Content-Type: multipart/form-data; boundary=%s\r\n",
444  postBoundary->Buffer
445  );
446  // POST boundary header
447  PhAppendFormatStringBuilder(&httpPostHeader,
448  L"--%s\r\n",
449  postBoundary->Buffer
450  );
451  PhAppendFormatStringBuilder(&httpPostHeader,
452  L"Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n",
453  serviceInfo->FileNameFieldName,
454  context->BaseFileName->Buffer
455  );
456  PhAppendFormatStringBuilder(&httpPostHeader,
457  L"Content-Type: application/octet-stream\r\n\r\n"
458  );
459  // POST boundary footer
460  PhAppendFormatStringBuilder(&httpPostFooter,
461  L"\r\n--%s--\r\n\r\n",
462  postBoundary->Buffer
463  );
464 
465  // add headers
466  if (!WinHttpAddRequestHeaders(requestHandle,
467  httpRequestHeaders.String->Buffer,
468  -1L,
469  WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD
470  ))
471  {
472  RaiseUploadError(context, L"Unable to add request headers", GetLastError());
473  __leave;
474  }
475 
476  // All until now has been just for this; Calculate the total request length.
477  totalUploadLength = (ULONG)PhCountStringZ(httpPostHeader.String->Buffer) + context->TotalFileLength + (ULONG)PhCountStringZ(httpPostFooter.String->Buffer);
478 
479  // Send the request.
480  if (!WinHttpSendRequest(requestHandle,
481  WINHTTP_NO_ADDITIONAL_HEADERS, 0,
482  WINHTTP_NO_REQUEST_DATA, 0,
483  totalUploadLength, 0
484  ))
485  {
486  RaiseUploadError(context, L"Unable to send the request", GetLastError());
487  __leave;
488  }
489 
490  // Convert to ASCII
491  asciiPostData = PhConvertUtf16ToAscii(httpPostHeader.String->Buffer, '-');
492  asciiFooterData = PhConvertUtf16ToAscii(httpPostFooter.String->Buffer, '-');
493 
494  // Start the clock.
495  PhQuerySystemTime(&timeStart);
496 
497  // Write the header
498  if (!WinHttpWriteData(
499  requestHandle,
500  asciiPostData->Buffer,
501  (ULONG)asciiPostData->Length,
502  &totalPostHeaderWritten
503  ))
504  {
505  RaiseUploadError(context, L"Unable to write the post header", GetLastError());
506  __leave;
507  }
508 
509  // Upload the file...
510  while (TRUE)
511  {
512  status = NtReadFile(
513  fileHandle,
514  NULL,
515  NULL,
516  NULL,
517  &isb,
518  buffer,
519  PAGE_SIZE,
520  NULL,
521  NULL
522  );
523 
524  if (!NT_SUCCESS(status))
525  break;
526 
527  if (!WinHttpWriteData(requestHandle, buffer, (ULONG)isb.Information, &totalWriteLength))
528  {
529  RaiseUploadError(context, L"Unable to upload the file data", GetLastError());
530  __leave;
531  }
532 
533  totalUploadedLength += totalWriteLength;
534 
535  PhQuerySystemTime(&timeNow);
536 
537  timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC;
538  timeBitsPerSecond = totalUploadedLength / __max(timeTicks, 1);
539 
540  {
541  FLOAT percent = ((FLOAT)totalUploadedLength / context->TotalFileLength * 100);
542  PPH_STRING totalLength = PhFormatSize(context->TotalFileLength, -1);
543  PPH_STRING totalDownloadedLength = PhFormatSize(totalUploadedLength, -1);
544  PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1);
545 
546  PPH_STRING dlLengthString = PhFormatString(
547  L"%s of %s @ %s/s",
548  totalDownloadedLength->Buffer,
549  totalLength->Buffer,
550  totalSpeed->Buffer
551  );
552 
553  Static_SetText(context->StatusHandle, dlLengthString->Buffer);
554 
555  PhDereferenceObject(dlLengthString);
556  PhDereferenceObject(totalSpeed);
557  PhDereferenceObject(totalLength);
558  PhDereferenceObject(totalDownloadedLength);
559 
560  // Update the progress bar position
561  PostMessage(context->ProgressHandle, PBM_SETPOS, (INT)percent, 0);
562  }
563  }
564 
565  // Write the footer bytes
566  if (!WinHttpWriteData(
567  requestHandle,
568  asciiFooterData->Buffer,
569  (ULONG)asciiFooterData->Length,
570  &totalPostFooterWritten
571  ))
572  {
573  RaiseUploadError(context, L"Unable to write the post footer", GetLastError());
574  __leave;
575  }
576 
577  // Wait for the send request to complete and recieve the response.
578  if (!WinHttpReceiveResponse(requestHandle, NULL))
579  {
580  RaiseUploadError(context, L"Unable to receive the response", GetLastError());
581  __leave;
582  }
583 
584  // Handle service-specific actions.
585  WinHttpQueryHeaders(
586  requestHandle,
587  WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
588  NULL,
589  &httpStatus,
590  &httpStatusLength,
591  NULL
592  );
593 
594  if (httpStatus == HTTP_STATUS_OK || httpStatus == HTTP_STATUS_REDIRECT_METHOD || httpStatus == HTTP_STATUS_REDIRECT)
595  {
596  switch (context->Service)
597  {
599  {
600  ULONG bufferLength = 0;
601 
602  // Use WinHttpQueryOption to obtain a buffer size.
603  if (!WinHttpQueryOption(requestHandle, WINHTTP_OPTION_URL, NULL, &bufferLength))
604  {
605  PPH_STRING buffer = PhCreateStringEx(NULL, bufferLength);
606 
607  // Use WinHttpQueryOption again, this time to retrieve the URL in the new buffer
608  if (WinHttpQueryOption(requestHandle, WINHTTP_OPTION_URL, buffer->Buffer, &bufferLength))
609  {
610  // Format the retrieved URL...
611  context->LaunchCommand = PhDuplicateString(buffer);
612  }
613 
614  PhDereferenceObject(buffer);
615  }
616  }
617  break;
619  {
620  PSTR hrefEquals = NULL;
621  PSTR quote = NULL;
622  PSTR buffer = NULL;
623  ULONG bufferLength = 0;
624 
625  //This service returns some JavaScript that redirects the user to the new location.
626  if (!ReadRequestString(requestHandle, &buffer, &bufferLength))
627  {
628  RaiseUploadError(context, L"Unable to complete the request", GetLastError());
629  __leave;
630  }
631 
632  // The JavaScript looks like this: top.location.href="...";
633  hrefEquals = strstr(buffer, "href=\"");
634  if (hrefEquals)
635  {
636  hrefEquals += 6;
637  quote = strchr(hrefEquals, '"');
638 
639  if (quote)
640  {
641  context->LaunchCommand = PhFormatString(
642  L"http://virusscan.jotti.org%.*S",
643  quote - hrefEquals,
644  hrefEquals
645  );
646  }
647  }
648  else
649  {
650  PSTR tooManyFiles = strstr(buffer, "Too many files");
651 
652  if (tooManyFiles)
653  {
654  RaiseUploadError(
655  context,
656  L"Unable to scan the file:\n\n"
657  L"Too many files have been scanned from this IP in a short period. "
658  L"Please try again later",
659  0
660  );
661 
662  __leave;
663  }
664  }
665  }
666  break;
667  case UPLOAD_SERVICE_CIMA:
668  {
669  PSTR urlEquals = NULL;
670  PSTR quote = NULL;
671  PSTR buffer = NULL;
672  ULONG bufferLength = 0;
673 
674  // This service returns some HTML that redirects the user to the new location.
675  if (!ReadRequestString(requestHandle, &buffer, &bufferLength))
676  {
677  RaiseUploadError(context, L"Unable to complete the CIMA request", GetLastError());
678  __leave;
679  }
680 
681  // The HTML looks like this:
682  // <META http-equiv="Refresh" content="0; url=...">
683  urlEquals = strstr(buffer, "url=");
684 
685  if (urlEquals)
686  {
687  urlEquals += 4;
688  quote = strchr(urlEquals, '"');
689 
690  if (quote)
691  {
692  context->LaunchCommand = PhFormatString(
693  L"http://camas.comodo.com%.*S",
694  quote - urlEquals,
695  urlEquals
696  );
697  }
698  }
699  }
700  break;
701  }
702  }
703  else
704  {
705  RaiseUploadError(context, L"Unable to complete the request", STATUS_FVE_PARTIAL_METADATA);
706  __leave;
707  }
708 
709  if (!PhIsNullOrEmptyString(context->LaunchCommand))
710  {
711  PostMessage(context->DialogHandle, UM_LAUNCH, 0, 0);
712  }
713  else
714  {
715  RaiseUploadError(context, L"Unable to complete the Launch request (please try again after a few minutes)", ERROR_INVALID_DATA);
716  __leave;
717  }
718  }
719  __finally
720  {
721  if (postBoundary)
722  {
723  PhDereferenceObject(postBoundary);
724  }
725 
726  if (asciiFooterData)
727  {
728  PhDereferenceObject(asciiFooterData);
729  }
730 
731  if (asciiPostData)
732  {
733  PhDereferenceObject(asciiPostData);
734  }
735 
736  if (httpPostFooter.String)
737  {
738  PhDeleteStringBuilder(&httpPostFooter);
739  }
740 
741  if (httpPostHeader.String)
742  {
743  PhDeleteStringBuilder(&httpPostHeader);
744  }
745 
746  if (httpRequestHeaders.String)
747  {
748  PhDeleteStringBuilder(&httpRequestHeaders);
749  }
750 
751  if (fileHandle != INVALID_HANDLE_VALUE)
752  {
753  NtClose(fileHandle);
754  }
755  }
756 
757  return status;
758 }
759 
760 static NTSTATUS UploadCheckThreadStart(
761  _In_ PVOID Parameter
762  )
763 {
764  NTSTATUS status = STATUS_SUCCESS;
765  BOOLEAN fileExists = FALSE;
766  LARGE_INTEGER fileSize64;
767  PSTR subRequestBuffer = NULL;
768  HINTERNET connectHandle = NULL;
769  HINTERNET requestHandle = NULL;
770  PSERVICE_INFO serviceInfo = NULL;
771  PPH_STRING hashString = NULL;
772  PPH_STRING subObjectName = NULL;
773  HANDLE fileHandle = INVALID_HANDLE_VALUE;
774 
775  PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter;
776 
777  serviceInfo = GetUploadServiceInfo(context->Service);
778 
779  __try
780  {
781  // Open the file and check its size.
782  status = PhCreateFileWin32(
783  &fileHandle,
784  context->FileName->Buffer,
785  FILE_GENERIC_READ,
786  0,
787  FILE_SHARE_READ | FILE_SHARE_DELETE,
788  FILE_OPEN,
790  );
791 
792  if (!NT_SUCCESS(status))
793  {
794  RaiseUploadError(context, L"Unable to open the file", RtlNtStatusToDosError(status));
795  __leave;
796  }
797 
798  if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize64)))
799  {
800  if (context->Service == UPLOAD_SERVICE_VIRUSTOTAL)
801  {
802  if (fileSize64.QuadPart > 128 * 1024 * 1024) // 128 MB
803  {
804  RaiseUploadError(context, L"The file is too large (over 128 MB)", ERROR_FILE_TOO_LARGE);
805  __leave;
806  }
807  }
808  else
809  {
810  if (fileSize64.QuadPart > 20 * 1024 * 1024) // 20 MB
811  {
812  RaiseUploadError(context, L"The file is too large (over 20 MB)", ERROR_FILE_TOO_LARGE);
813  __leave;
814  }
815  }
816 
817  context->TotalFileLength = fileSize64.LowPart;
818  }
819 
820  // Get proxy configuration and create winhttp handle (used for all winhttp sessions + requests).
821  {
822  PPH_STRING phVersion = NULL;
823  PPH_STRING userAgent = NULL;
824  WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 };
825 
826  // Create a user agent string.
827  phVersion = PhGetPhVersion();
828  userAgent = PhConcatStrings2(L"ProcessHacker_", phVersion->Buffer);
829 
830  // Query the current system proxy
831  WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
832 
833  // Open the HTTP session with the system proxy configuration if available
834  context->HttpHandle = WinHttpOpen(
835  userAgent->Buffer,
836  proxyConfig.lpszProxy != NULL ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
837  proxyConfig.lpszProxy,
838  proxyConfig.lpszProxyBypass,
839  0
840  );
841 
842  PhClearReference(&phVersion);
843  PhClearReference(&userAgent);
844 
845  if (!context->HttpHandle)
846  __leave;
847  }
848 
849  switch (context->Service)
850  {
852  {
853  PSTR uploadUrl = NULL;
854  PSTR quote = NULL;
855  ULONG bufferLength = 0;
856  UCHAR hash[32];
857 
858  status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA256, hash);
859  if (!NT_SUCCESS(status))
860  {
861  RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status));
862  __leave;
863  }
864 
865  hashString = PhBufferToHexString(hash, 32);
866  subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", hashString->Buffer);
867  context->LaunchCommand = PhFormatString(L"http://www.virustotal.com/file/%s/analysis/", hashString->Buffer);
868 
869  if (!PerformSubRequest(context, serviceInfo->HostName, subObjectName->Buffer, &subRequestBuffer, &bufferLength))
870  __leave;
871 
872  if (strstr(subRequestBuffer, "\"file_exists\": true"))
873  {
874  fileExists = TRUE;
875  }
876 
877  uploadUrl = strstr(subRequestBuffer, "\"upload_url\": \"https://www.virustotal.com");
878  if (!uploadUrl)
879  {
880  RaiseUploadError(context, L"Unable to complete the request (no upload URL provided)", ERROR_INVALID_DATA);
881  __leave;
882  }
883 
884  uploadUrl += 41;
885  quote = strchr(uploadUrl, '"');
886  if (!quote)
887  {
888  RaiseUploadError(context, L"Unable to complete the request (invalid upload URL)", ERROR_INVALID_DATA);
889  __leave;
890  }
891 
892  context->ObjectName = PhZeroExtendToUtf16Ex(uploadUrl, quote - uploadUrl);
893 
894  // Create the default upload URL
895  if (!context->ObjectName)
896  context->ObjectName = PhCreateString(serviceInfo->UploadObjectName);
897  }
898  break;
900  {
901  PSTR uploadId = NULL;
902  PSTR quote = NULL;
903  ULONG bufferLength = 0;
904  UCHAR hash[20];
905 
906  status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA1, hash);
907  if (!NT_SUCCESS(status))
908  {
909  RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status));
910  __leave;
911  }
912 
913  hashString = PhBufferToHexString(hash, 20);
914  subObjectName = PhConcatStrings2(L"/nestor/getfileforhash.php?hash=", hashString->Buffer);
915 
916  if (!PerformSubRequest(context, serviceInfo->HostName, subObjectName->Buffer, &subRequestBuffer, &bufferLength))
917  __leave;
918 
919  if (uploadId = strstr(subRequestBuffer, "\"id\":"))
920  {
921  uploadId += 6;
922  quote = strchr(uploadId, '"');
923 
924  if (quote)
925  {
926  fileExists = TRUE;
927  context->LaunchCommand = PhFormatString(L"http://virusscan.jotti.org/en/scanresult/%.*S", quote - uploadId, uploadId);
928  }
929  }
930 
931  // Create the default upload URL
932  if (!context->ObjectName)
933  context->ObjectName = PhCreateString(serviceInfo->UploadObjectName);
934  }
935  break;
936  case UPLOAD_SERVICE_CIMA:
937  {
938  PSTR quote = NULL;
939  ULONG bufferLength = 0;
940  UCHAR hash[32];
941  ULONG status = 0;
942  ULONG statusLength = sizeof(statusLength);
943 
944  status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA256, hash);
945  if (!NT_SUCCESS(status))
946  {
947  RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status));
948  __leave;
949  }
950 
951  hashString = PhBufferToHexString(hash, 32);
952  subObjectName = PhConcatStrings2(L"/cgi-bin/submit?file=", hashString->Buffer);
953  context->LaunchCommand = PhFormatString(L"http://camas.comodo.com/cgi-bin/submit?file=%s", hashString->Buffer);
954 
955  // Connect to the CIMA online service.
956  if (!(connectHandle = WinHttpConnect(
957  context->HttpHandle,
958  serviceInfo->HostName,
959  INTERNET_DEFAULT_HTTP_PORT,
960  0
961  )))
962  {
963  RaiseUploadError(context, L"Unable to connect to the CIMA service", GetLastError());
964  __leave;
965  }
966 
967  // Create the request.
968  if (!(requestHandle = WinHttpOpenRequest(
969  connectHandle,
970  NULL, // Get
971  subObjectName->Buffer,
972  NULL,// HTTP/1.1
973  WINHTTP_NO_REFERER,
974  WINHTTP_DEFAULT_ACCEPT_TYPES,
975  WINHTTP_FLAG_REFRESH
976  )))
977  {
978  RaiseUploadError(context, L"Unable to create the CIMA request", GetLastError());
979  __leave;
980  }
981 
982  // Send the request.
983  if (!WinHttpSendRequest(requestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
984  {
985  RaiseUploadError(context, L"Unable to send the CIMA request", GetLastError());
986  __leave;
987  }
988 
989  // Wait for the send request to complete and recieve the response.
990  if (!WinHttpReceiveResponse(requestHandle, NULL))
991  {
992  RaiseUploadError(context, L"Unable to recieve the CIMA response", GetLastError());
993  __leave;
994  }
995 
996  WinHttpQueryHeaders(
997  requestHandle,
998  WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
999  NULL,
1000  &status,
1001  &statusLength,
1002  NULL
1003  );
1004 
1005  if (status == HTTP_STATUS_OK)
1006  {
1007  fileExists = TRUE;
1008  }
1009 
1010  if (!context->ObjectName)
1011  {
1012  // Create the default upload URL
1013  context->ObjectName = PhCreateString(serviceInfo->UploadObjectName);
1014  }
1015  }
1016  break;
1017  }
1018 
1019  // Do we need to prompt the user?
1020  if (fileExists && !PhIsNullOrEmptyString(context->LaunchCommand))
1021  {
1022  PostMessage(context->DialogHandle, UM_EXISTS, 0, 0);
1023  __leave;
1024  }
1025 
1026  // No existing file found... Start the upload.
1027  if (!NT_SUCCESS(UploadFileThreadStart(context)))
1028  __leave;
1029  }
1030  __finally
1031  {
1032  PhMoveReference(&hashString, NULL);
1033  PhMoveReference(&subObjectName, NULL);
1034 
1035  if (requestHandle)
1036  {
1037  WinHttpCloseHandle(requestHandle);
1038  }
1039 
1040  if (connectHandle)
1041  {
1042  WinHttpCloseHandle(connectHandle);
1043  }
1044 
1045  if (fileHandle != INVALID_HANDLE_VALUE)
1046  {
1047  NtClose(fileHandle);
1048  }
1049  }
1050 
1051  return status;
1052 }
1053 
1054 static INT_PTR CALLBACK UploadDlgProc(
1055  _In_ HWND hwndDlg,
1056  _In_ UINT uMsg,
1057  _In_ WPARAM wParam,
1058  _In_ LPARAM lParam
1059  )
1060 {
1061  PUPLOAD_CONTEXT context = NULL;
1062 
1063  if (uMsg == WM_INITDIALOG)
1064  {
1065  context = (PUPLOAD_CONTEXT)lParam;
1066  SetProp(hwndDlg, L"Context", (HANDLE)context);
1067  }
1068  else
1069  {
1070  context = (PUPLOAD_CONTEXT)GetProp(hwndDlg, L"Context");
1071 
1072  if (uMsg == WM_NCDESTROY)
1073  {
1074  PhMoveReference(&context->FileName, NULL);
1075  PhMoveReference(&context->BaseFileName, NULL);
1076  PhMoveReference(&context->WindowFileName, NULL);
1077  PhMoveReference(&context->LaunchCommand, NULL);
1078  PhMoveReference(&context->ObjectName, NULL);
1079 
1080  if (context->MessageFont)
1081  DeleteObject(context->MessageFont);
1082 
1083  if (context->HttpHandle)
1084  WinHttpCloseHandle(context->HttpHandle);
1085 
1086  RemoveProp(hwndDlg, L"Context");
1087  PhFree(context);
1088  }
1089  }
1090 
1091  if (!context)
1092  return FALSE;
1093 
1094  switch (uMsg)
1095  {
1096  case WM_INITDIALOG:
1097  {
1098  HANDLE dialogThread = NULL;
1099  HWND parentWindow = GetParent(hwndDlg);
1100 
1101  PhCenterWindow(hwndDlg, (IsWindowVisible(parentWindow) && !IsIconic(parentWindow)) ? parentWindow : NULL);
1102 
1103  context->DialogHandle = hwndDlg;
1104  context->StatusHandle = GetDlgItem(hwndDlg, IDC_STATUS);
1105  context->ProgressHandle = GetDlgItem(hwndDlg, IDC_PROGRESS1);
1106  context->MessageHandle = GetDlgItem(hwndDlg, IDC_MESSAGE);
1107  context->MessageFont = InitializeFont(context->MessageHandle);
1108  context->WindowFileName = PhFormatString(L"Uploading: %s", context->BaseFileName->Buffer);
1110 
1111  // Reset the window status...
1112  Static_SetText(context->MessageHandle, context->WindowFileName->Buffer);
1113 
1114  switch (context->Service)
1115  {
1117  Static_SetText(hwndDlg, L"Uploading to VirusTotal...");
1118  break;
1119  case UPLOAD_SERVICE_JOTTI:
1120  Static_SetText(hwndDlg, L"Uploading to Jotti...");
1121  break;
1122  case UPLOAD_SERVICE_CIMA:
1123  Static_SetText(hwndDlg, L"Uploading to Comodo...");
1124  break;
1125  }
1126 
1127  if (dialogThread = PhCreateThread(0, UploadCheckThreadStart, (PVOID)context))
1128  NtClose(dialogThread);
1129  }
1130  break;
1131  case WM_COMMAND:
1132  {
1133  switch (LOWORD(wParam))
1134  {
1135  case IDYES:
1136  {
1138 
1139  if (!PhIsNullOrEmptyString(context->LaunchCommand))
1140  {
1141  PhShellExecute(hwndDlg, context->LaunchCommand->Buffer, NULL);
1142  }
1143 
1144  PostQuitMessage(0);
1145  }
1146  break;
1147  case IDNO:
1148  {
1150  {
1151  HANDLE dialogThread = NULL;
1152 
1153  // Set state to uploading...
1155 
1156  // Reset the window status...
1157  Static_SetText(context->MessageHandle, context->WindowFileName->Buffer);
1158  Static_SetText(GetDlgItem(hwndDlg, IDNO), L"Cancel");
1159  Control_Visible(GetDlgItem(hwndDlg, IDYES), FALSE);
1160 
1161  // Start the upload thread...
1162  if (dialogThread = PhCreateThread(0, UploadFileThreadStart, (PVOID)context))
1163  NtClose(dialogThread);
1164  }
1165  else
1166  {
1168  PostQuitMessage(0);
1169  }
1170  }
1171  break;
1172  case IDCANCEL:
1173  {
1175  PostQuitMessage(0);
1176  }
1177  }
1178  break;
1179  }
1180  break;
1181  case WM_CTLCOLORBTN:
1182  case WM_CTLCOLORDLG:
1183  case WM_CTLCOLORSTATIC:
1184  {
1185  HDC hDC = (HDC)wParam;
1186  HWND hwndChild = (HWND)lParam;
1187 
1188  // Check for our static label and change the color.
1189  if (GetDlgCtrlID(hwndChild) == IDC_MESSAGE)
1190  {
1191  SetTextColor(hDC, RGB(19, 112, 171));
1192  }
1193 
1194  // Set a transparent background for the control backcolor.
1195  SetBkMode(hDC, TRANSPARENT);
1196 
1197  // set window background color.
1198  return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
1199  }
1200  break;
1201  case UM_EXISTS:
1202  {
1204 
1205  Static_SetText(GetDlgItem(hwndDlg, IDNO), L"No");
1206  Control_Visible(GetDlgItem(hwndDlg, IDYES), TRUE);
1207 
1208  Static_SetText(context->MessageHandle, L"File already analysed.");
1209  Static_SetText(context->StatusHandle, L"View existing report?");
1210  }
1211  break;
1212  case UM_LAUNCH:
1213  {
1215 
1216  if (!PhIsNullOrEmptyString(context->LaunchCommand))
1217  {
1218  PhShellExecute(hwndDlg, context->LaunchCommand->Buffer, NULL);
1219  }
1220 
1221  PostQuitMessage(0);
1222  }
1223  break;
1224  case UM_ERROR:
1225  {
1226  PPH_STRING errorMessage = (PPH_STRING)lParam;
1227 
1229 
1230  if (errorMessage)
1231  {
1232  Static_SetText(GetDlgItem(hwndDlg, IDC_MESSAGE), errorMessage->Buffer);
1233  PhMoveReference(&errorMessage, NULL);
1234  }
1235  else
1236  {
1237  Static_SetText(GetDlgItem(hwndDlg, IDC_MESSAGE), L"Error");
1238  }
1239 
1240  Static_SetText(GetDlgItem(hwndDlg, IDC_STATUS), L"");
1241  Static_SetText(GetDlgItem(hwndDlg, IDNO), L"Close");
1242  }
1243  break;
1244  }
1245 
1246  return FALSE;
1247 }
1248 
1249 static NTSTATUS PhUploadToDialogThreadStart(
1250  _In_ PVOID Parameter
1251  )
1252 {
1253  BOOL result;
1254  MSG message;
1255  HWND dialogHandle;
1256  PH_AUTO_POOL autoPool;
1257  PUPLOAD_CONTEXT context = (PUPLOAD_CONTEXT)Parameter;
1258 
1259  PhInitializeAutoPool(&autoPool);
1260 
1261  dialogHandle = CreateDialogParam(
1263  MAKEINTRESOURCE(IDD_PROGRESS),
1265  UploadDlgProc,
1266  (LPARAM)Parameter
1267  );
1268 
1269  ShowWindow(dialogHandle, SW_SHOW);
1270  SetForegroundWindow(dialogHandle);
1271 
1272  while (result = GetMessage(&message, NULL, 0, 0))
1273  {
1274  if (result == -1)
1275  break;
1276 
1277  if (!IsDialogMessage(dialogHandle, &message))
1278  {
1279  TranslateMessage(&message);
1280  DispatchMessage(&message);
1281  }
1282 
1283  PhDrainAutoPool(&autoPool);
1284  }
1285 
1286  PhDeleteAutoPool(&autoPool);
1287  DestroyWindow(dialogHandle);
1288 
1289  return STATUS_SUCCESS;
1290 }
1291 
1293  _In_ PPH_STRING FileName,
1294  _In_ ULONG Service
1295  )
1296 {
1297  HANDLE dialogThread = NULL;
1298  PUPLOAD_CONTEXT context;
1299 
1300  context = (PUPLOAD_CONTEXT)PhAllocate(sizeof(UPLOAD_CONTEXT));
1301  memset(context, 0, sizeof(UPLOAD_CONTEXT));
1302 
1303  context->Service = Service;
1304  context->FileName = PhDuplicateString(FileName);
1305  context->BaseFileName = PhGetBaseName(context->FileName);
1306 
1307  if (dialogThread = PhCreateThread(0, PhUploadToDialogThreadStart, (PVOID)context))
1308  NtClose(dialogThread);
1309 }