diff --git a/2.x/trunk/CHANGELOG.txt b/2.x/trunk/CHANGELOG.txt
index a80385f36..e10ba64b2 100644
--- a/2.x/trunk/CHANGELOG.txt
+++ b/2.x/trunk/CHANGELOG.txt
@@ -2,6 +2,8 @@ Process Hacker
2.27
* NEW/IMPROVED:
+ * Updated OnlineChecks plugin:
+ * 2012-01-16: Updated VirusTotal uploader and added hash checking
* FIXED:
2.26
diff --git a/2.x/trunk/ProcessHacker/ProcessHacker.rc b/2.x/trunk/ProcessHacker/ProcessHacker.rc
index da20b7b1a..16647a7dd 100644
--- a/2.x/trunk/ProcessHacker/ProcessHacker.rc
+++ b/2.x/trunk/ProcessHacker/ProcessHacker.rc
@@ -584,10 +584,10 @@ CAPTION "About"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,213,174,50,14
- ICON IDI_PROCESSHACKER,IDC_STATIC,16,15,21,20
+ ICON IDI_PROCESSHACKER,IDC_STATIC,16,15,20,20
LTEXT "Process Hacker",IDC_ABOUT_NAME,45,14,192,8
LTEXT "Licensed under the GNU GPL, v3.",IDC_STATIC,45,27,193,8
- LTEXT "Copyright (c) 2008-2011 wj32",IDC_STATIC,15,40,97,8
+ LTEXT "Copyright (c) 2008-2012 wj32",IDC_STATIC,15,40,97,8
PUSHBUTTON "Diagnostics",IDC_DIAGNOSTICS,160,174,50,14
CONTROL "Process Hacker on SourceForge.net",IDC_LINK_SF,
"SysLink",WS_TABSTOP,7,177,130,11
diff --git a/2.x/trunk/plugins/OnlineChecks/CHANGELOG.txt b/2.x/trunk/plugins/OnlineChecks/CHANGELOG.txt
index 44a57ce00..e32846aee 100644
--- a/2.x/trunk/plugins/OnlineChecks/CHANGELOG.txt
+++ b/2.x/trunk/plugins/OnlineChecks/CHANGELOG.txt
@@ -1,3 +1,6 @@
+1.3
+ * 2012-01-16: Updated VirusTotal uploader and added hash checking
+
1.2
* 2011-06-22: Added Comodo Instant Malware Analysis
diff --git a/2.x/trunk/plugins/OnlineChecks/OnlineChecks.rc b/2.x/trunk/plugins/OnlineChecks/OnlineChecks.rc
index b0cdbc230..59e8b6845 100644
--- a/2.x/trunk/plugins/OnlineChecks/OnlineChecks.rc
+++ b/2.x/trunk/plugins/OnlineChecks/OnlineChecks.rc
@@ -53,8 +53,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,2,0,0
- PRODUCTVERSION 1,2,0,0
+ FILEVERSION 1,3,0,0
+ PRODUCTVERSION 1,3,0,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -71,12 +71,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "wj32"
VALUE "FileDescription", "Online Checks plugin for Process Hacker"
- VALUE "FileVersion", "1.2"
+ VALUE "FileVersion", "1.3"
VALUE "InternalName", "OnlineChecks"
VALUE "LegalCopyright", "Licensed under the GNU GPL, v3."
VALUE "OriginalFilename", "OnlineChecks.dll"
VALUE "ProductName", "Online Checks plugin for Process Hacker"
- VALUE "ProductVersion", "1.2"
+ VALUE "ProductVersion", "1.3"
END
END
BLOCK "VarFileInfo"
diff --git a/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj b/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj
index 921490133..0edcd96fc 100644
--- a/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj
+++ b/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj
@@ -163,11 +163,13 @@
+
+
diff --git a/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj.filters b/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj.filters
index 32871f344..ca8ba0cc4 100644
--- a/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj.filters
+++ b/2.x/trunk/plugins/OnlineChecks/OnlineChecks.vcxproj.filters
@@ -21,6 +21,9 @@
Source Files
+
+ Source Files
+
@@ -29,6 +32,9 @@
Header Files
+
+ Header Files
+
diff --git a/2.x/trunk/plugins/OnlineChecks/sha256.c b/2.x/trunk/plugins/OnlineChecks/sha256.c
new file mode 100644
index 000000000..0b945816f
--- /dev/null
+++ b/2.x/trunk/plugins/OnlineChecks/sha256.c
@@ -0,0 +1,369 @@
+/*
+ * FIPS-180-2 compliant SHA-256 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include
+
+#include "sha256.h"
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint32) (b)[(i) ] << 24 ) \
+ | ( (uint32) (b)[(i) + 1] << 16 ) \
+ | ( (uint32) (b)[(i) + 2] << 8 ) \
+ | ( (uint32) (b)[(i) + 3] ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (uint8) ( (n) >> 24 ); \
+ (b)[(i) + 1] = (uint8) ( (n) >> 16 ); \
+ (b)[(i) + 2] = (uint8) ( (n) >> 8 ); \
+ (b)[(i) + 3] = (uint8) ( (n) ); \
+}
+
+void sha256_starts( sha256_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x6A09E667;
+ ctx->state[1] = 0xBB67AE85;
+ ctx->state[2] = 0x3C6EF372;
+ ctx->state[3] = 0xA54FF53A;
+ ctx->state[4] = 0x510E527F;
+ ctx->state[5] = 0x9B05688C;
+ ctx->state[6] = 0x1F83D9AB;
+ ctx->state[7] = 0x5BE0CD19;
+}
+
+void sha256_process( sha256_context *ctx, uint8 data[64] )
+{
+ uint32 temp1, temp2, W[64];
+ uint32 A, B, C, D, E, F, G, H;
+
+ GET_UINT32( W[0], data, 0 );
+ GET_UINT32( W[1], data, 4 );
+ GET_UINT32( W[2], data, 8 );
+ GET_UINT32( W[3], data, 12 );
+ GET_UINT32( W[4], data, 16 );
+ GET_UINT32( W[5], data, 20 );
+ GET_UINT32( W[6], data, 24 );
+ GET_UINT32( W[7], data, 28 );
+ GET_UINT32( W[8], data, 32 );
+ GET_UINT32( W[9], data, 36 );
+ GET_UINT32( W[10], data, 40 );
+ GET_UINT32( W[11], data, 44 );
+ GET_UINT32( W[12], data, 48 );
+ GET_UINT32( W[13], data, 52 );
+ GET_UINT32( W[14], data, 56 );
+ GET_UINT32( W[15], data, 60 );
+
+#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)
+#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))
+
+#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3))
+#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10))
+
+#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))
+#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))
+
+#define F0(x,y,z) ((x & y) | (z & (x | y)))
+#define F1(x,y,z) (z ^ (x & (y ^ z)))
+
+#define R(t) \
+( \
+ W[t] = S1(W[t - 2]) + W[t - 7] + \
+ S0(W[t - 15]) + W[t - 16] \
+)
+
+#define P(a,b,c,d,e,f,g,h,x,K) \
+{ \
+ temp1 = h + S3(e) + F1(e,f,g) + K + x; \
+ temp2 = S2(a) + F0(a,b,c); \
+ d += temp1; h = temp1 + temp2; \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+ F = ctx->state[5];
+ G = ctx->state[6];
+ H = ctx->state[7];
+
+ P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 );
+ P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 );
+ P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF );
+ P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 );
+ P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B );
+ P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 );
+ P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 );
+ P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 );
+ P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 );
+ P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 );
+ P( G, H, A, B, C, D, E, F, W[10], 0x243185BE );
+ P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 );
+ P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 );
+ P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE );
+ P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 );
+ P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 );
+ P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 );
+ P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 );
+ P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 );
+ P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC );
+ P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F );
+ P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA );
+ P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC );
+ P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA );
+ P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 );
+ P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D );
+ P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 );
+ P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 );
+ P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 );
+ P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 );
+ P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 );
+ P( B, C, D, E, F, G, H, A, R(31), 0x14292967 );
+ P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 );
+ P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 );
+ P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC );
+ P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 );
+ P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 );
+ P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB );
+ P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E );
+ P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 );
+ P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 );
+ P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B );
+ P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 );
+ P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 );
+ P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 );
+ P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 );
+ P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 );
+ P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 );
+ P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 );
+ P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 );
+ P( G, H, A, B, C, D, E, F, R(50), 0x2748774C );
+ P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 );
+ P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 );
+ P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A );
+ P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F );
+ P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 );
+ P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE );
+ P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F );
+ P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 );
+ P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 );
+ P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA );
+ P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB );
+ P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 );
+ P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 );
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+ ctx->state[5] += F;
+ ctx->state[6] += G;
+ ctx->state[7] += H;
+}
+
+void sha256_update( sha256_context *ctx, uint8 *input, uint32 length )
+{
+ uint32 left, fill;
+
+ if( ! length ) return;
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += length;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if( ctx->total[0] < length )
+ ctx->total[1]++;
+
+ if( left && length >= fill )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, fill );
+ sha256_process( ctx, ctx->buffer );
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+ while( length >= 64 )
+ {
+ sha256_process( ctx, input );
+ length -= 64;
+ input += 64;
+ }
+
+ if( length )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, length );
+ }
+}
+
+static uint8 sha256_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void sha256_finish( sha256_context *ctx, uint8 digest[32] )
+{
+ uint32 last, padn;
+ uint32 high, low;
+ uint8 msglen[8];
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_UINT32( high, msglen, 0 );
+ PUT_UINT32( low, msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ sha256_update( ctx, sha256_padding, padn );
+ sha256_update( ctx, msglen, 8 );
+
+ PUT_UINT32( ctx->state[0], digest, 0 );
+ PUT_UINT32( ctx->state[1], digest, 4 );
+ PUT_UINT32( ctx->state[2], digest, 8 );
+ PUT_UINT32( ctx->state[3], digest, 12 );
+ PUT_UINT32( ctx->state[4], digest, 16 );
+ PUT_UINT32( ctx->state[5], digest, 20 );
+ PUT_UINT32( ctx->state[6], digest, 24 );
+ PUT_UINT32( ctx->state[7], digest, 28 );
+}
+
+#ifdef TEST
+
+#include
+#include
+
+/*
+ * those are the standard FIPS-180-2 test vectors
+ */
+
+static char *msg[] =
+{
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ NULL
+};
+
+static char *val[] =
+{
+ "ba7816bf8f01cfea414140de5dae2223" \
+ "b00361a396177a9cb410ff61f20015ad",
+ "248d6a61d20638b8e5c026930c3e6039" \
+ "a33ce45964ff2167f6ecedd419db06c1",
+ "cdc76e5c9914fb9281a1c7e284d73e67" \
+ "f1809a48a497200e046d39ccc7112cd0"
+};
+
+int main( int argc, char *argv[] )
+{
+ FILE *f;
+ int i, j;
+ char output[65];
+ sha256_context ctx;
+ unsigned char buf[1000];
+ unsigned char sha256sum[32];
+
+ if( argc < 2 )
+ {
+ printf( "\n SHA-256 Validation Tests:\n\n" );
+
+ for( i = 0; i < 3; i++ )
+ {
+ printf( " Test %d ", i + 1 );
+
+ sha256_starts( &ctx );
+
+ if( i < 2 )
+ {
+ sha256_update( &ctx, (uint8 *) msg[i],
+ strlen( msg[i] ) );
+ }
+ else
+ {
+ memset( buf, 'a', 1000 );
+
+ for( j = 0; j < 1000; j++ )
+ {
+ sha256_update( &ctx, (uint8 *) buf, 1000 );
+ }
+ }
+
+ sha256_finish( &ctx, sha256sum );
+
+ for( j = 0; j < 32; j++ )
+ {
+ sprintf( output + j * 2, "%02x", sha256sum[j] );
+ }
+
+ if( memcmp( output, val[i], 64 ) )
+ {
+ printf( "failed!\n" );
+ return( 1 );
+ }
+
+ printf( "passed.\n" );
+ }
+
+ printf( "\n" );
+ }
+ else
+ {
+ if( ! ( f = fopen( argv[1], "rb" ) ) )
+ {
+ perror( "fopen" );
+ return( 1 );
+ }
+
+ sha256_starts( &ctx );
+
+ while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
+ {
+ sha256_update( &ctx, buf, i );
+ }
+
+ sha256_finish( &ctx, sha256sum );
+
+ for( j = 0; j < 32; j++ )
+ {
+ printf( "%02x", sha256sum[j] );
+ }
+
+ printf( " %s\n", argv[1] );
+ }
+
+ return( 0 );
+}
+
+#endif
diff --git a/2.x/trunk/plugins/OnlineChecks/sha256.h b/2.x/trunk/plugins/OnlineChecks/sha256.h
new file mode 100644
index 000000000..cf883a2eb
--- /dev/null
+++ b/2.x/trunk/plugins/OnlineChecks/sha256.h
@@ -0,0 +1,25 @@
+#ifndef _SHA256_H
+#define _SHA256_H
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+ uint32 total[2];
+ uint32 state[8];
+ uint8 buffer[64];
+}
+sha256_context;
+
+void sha256_starts( sha256_context *ctx );
+void sha256_update( sha256_context *ctx, uint8 *input, uint32 length );
+void sha256_finish( sha256_context *ctx, uint8 digest[32] );
+
+#endif /* sha256.h */
+
diff --git a/2.x/trunk/plugins/OnlineChecks/upload.c b/2.x/trunk/plugins/OnlineChecks/upload.c
index 630876430..d1fde295a 100644
--- a/2.x/trunk/plugins/OnlineChecks/upload.c
+++ b/2.x/trunk/plugins/OnlineChecks/upload.c
@@ -2,7 +2,7 @@
* Process Hacker Online Checks -
* uploader
*
- * Copyright (C) 2010-2011 wj32
+ * Copyright (C) 2010-2012 wj32
*
* This file is part of Process Hacker.
*
@@ -23,8 +23,12 @@
#include
#include
#include "onlnchk.h"
+#include "sha256.h"
#include "resource.h"
+#define HASH_SHA1 1
+#define HASH_SHA256 2
+
#define UM_LAUNCH_COMMAND (WM_APP + 1)
#define UM_ERROR (WM_APP + 2)
@@ -35,6 +39,7 @@ typedef struct _UPLOAD_CONTEXT
PPH_STRING FileName;
ULONG Service;
HWND WindowHandle;
+ PH_QUEUED_LOCK Lock;
HANDLE ThreadHandle;
PPH_STRING LaunchCommand;
@@ -45,6 +50,8 @@ typedef struct _SERVICE_INFO
{
ULONG Id;
PWSTR HostName;
+ ULONG HostPort;
+ ULONG HostFlags;
PWSTR UploadObjectName;
PSTR FileNameFieldName;
} SERVICE_INFO, *PSERVICE_INFO;
@@ -58,9 +65,9 @@ INT_PTR CALLBACK UploadDlgProc(
SERVICE_INFO UploadServiceInfo[] =
{
- { UPLOAD_SERVICE_VIRUSTOTAL, L"www.virustotal.com", L"/file-upload/file_upload", "file" },
- { UPLOAD_SERVICE_JOTTI, L"virusscan.jotti.org", L"/processupload.php", "scanfile" },
- { UPLOAD_SERVICE_CIMA, L"camas.comodo.com", L"/cgi-bin/submit", "file" }
+ { UPLOAD_SERVICE_VIRUSTOTAL, L"www.virustotal.com", INTERNET_DEFAULT_HTTPS_PORT, INTERNET_FLAG_SECURE, L"???", "file" },
+ { UPLOAD_SERVICE_JOTTI, L"virusscan.jotti.org", INTERNET_DEFAULT_HTTP_PORT, 0, L"/processupload.php", "scanfile" },
+ { UPLOAD_SERVICE_CIMA, L"camas.comodo.com", INTERNET_DEFAULT_HTTP_PORT, 0, L"/cgi-bin/submit", "file" }
};
PUPLOAD_CONTEXT CreateUploadContext(
@@ -144,8 +151,24 @@ static VOID RaiseUploadError(
PhSwapReference(&errorMessage, NULL);
+ PhAcquireQueuedLockExclusive(&Context->Lock);
+
if (Context->WindowHandle)
PostMessage(Context->WindowHandle, UM_ERROR, 0, 0);
+
+ PhReleaseQueuedLockExclusive(&Context->Lock);
+}
+
+static VOID SendLaunchCommand(
+ __in PUPLOAD_CONTEXT Context
+ )
+{
+ PhAcquireQueuedLockExclusive(&Context->Lock);
+
+ if (Context->WindowHandle)
+ PostMessage(Context->WindowHandle, UM_LAUNCH_COMMAND, 0, 0);
+
+ PhReleaseQueuedLockExclusive(&Context->Lock);
}
static PSERVICE_INFO GetUploadServiceInfo(
@@ -163,6 +186,202 @@ static PSERVICE_INFO GetUploadServiceInfo(
return NULL;
}
+static BOOLEAN PerformSubRequest(
+ __in PUPLOAD_CONTEXT Context,
+ __in PWSTR HostName,
+ __in PWSTR ObjectName,
+ __out_bcount(BufferLength) PVOID Buffer,
+ __in ULONG BufferLength,
+ __out PULONG ReturnLength
+ )
+{
+ BOOLEAN result = FALSE;
+ PPH_STRING userAgent;
+ HINTERNET internetHandle = NULL;
+ HINTERNET connectHandle = NULL;
+ HINTERNET requestHandle = NULL;
+
+ // Create a user agent string.
+ {
+ PPH_STRING phVersion;
+
+ phVersion = PhGetPhVersion();
+ userAgent = PhConcatStrings2(L"Process Hacker ", phVersion->Buffer);
+ PhDereferenceObject(phVersion);
+ }
+
+ // Create the internet handle.
+
+ internetHandle = InternetOpen(userAgent->Buffer, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+ PhDereferenceObject(userAgent);
+
+ if (!internetHandle)
+ {
+ RaiseUploadError(Context, L"Unable to initialize internet access", GetLastError());
+ goto ExitCleanup;
+ }
+
+ // Set the timeouts.
+ {
+ ULONG timeout = 5 * 60 * 1000; // 5 minutes
+
+ InternetSetOption(internetHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeout, sizeof(ULONG));
+ InternetSetOption(internetHandle, INTERNET_OPTION_SEND_TIMEOUT, &timeout, sizeof(ULONG));
+ InternetSetOption(internetHandle, INTERNET_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(ULONG));
+ InternetSetOption(internetHandle, INTERNET_OPTION_DATA_SEND_TIMEOUT, &timeout, sizeof(ULONG));
+ InternetSetOption(internetHandle, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT, &timeout, sizeof(ULONG));
+ }
+
+ // Connect to the online service.
+
+ connectHandle = InternetConnect(
+ internetHandle,
+ HostName,
+ 80,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ 0,
+ 0
+ );
+
+ if (!connectHandle)
+ {
+ RaiseUploadError(Context, L"Unable to connect to the service", GetLastError());
+ goto ExitCleanup;
+ }
+
+ // Create the request.
+
+ {
+ static PWSTR acceptTypes[2] = { L"*/*", NULL };
+
+ requestHandle = HttpOpenRequest(
+ connectHandle,
+ L"GET",
+ ObjectName,
+ L"HTTP/1.1",
+ L"",
+ acceptTypes,
+ INTERNET_FLAG_RELOAD,
+ 0
+ );
+ }
+
+ if (!requestHandle)
+ {
+ RaiseUploadError(Context, L"Unable to create the request", GetLastError());
+ goto ExitCleanup;
+ }
+
+ // Send the request.
+
+ if (!HttpSendRequest(requestHandle, NULL, 0, NULL, 0))
+ {
+ RaiseUploadError(Context, L"Unable to send the request", GetLastError());
+ goto ExitCleanup;
+ }
+
+ // Handle service-specific actions.
+
+ if (!InternetReadFile(requestHandle, Buffer, BufferLength, ReturnLength))
+ {
+ RaiseUploadError(Context, L"Unable to complete the request", GetLastError());
+ goto ExitCleanup;
+ }
+
+ result = TRUE;
+
+ExitCleanup:
+ if (requestHandle)
+ InternetCloseHandle(requestHandle);
+ if (connectHandle)
+ InternetCloseHandle(connectHandle);
+ if (internetHandle)
+ InternetCloseHandle(internetHandle);
+
+ return result;
+}
+
+static NTSTATUS HashFileAndResetPosition(
+ __in HANDLE FileHandle,
+ __in PLARGE_INTEGER FileSize,
+ __in ULONG Algorithm,
+ __out PVOID Hash
+ )
+{
+ NTSTATUS status;
+ UCHAR buffer[PAGE_SIZE * 4];
+ IO_STATUS_BLOCK iosb;
+ PH_HASH_CONTEXT hashContext;
+ sha256_context sha256;
+ ULONG64 bytesRemaining;
+ FILE_POSITION_INFORMATION positionInfo;
+
+ 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;
+}
+
static NTSTATUS UploadWorkerThreadStart(
__in PVOID Parameter
)
@@ -172,17 +391,21 @@ static NTSTATUS UploadWorkerThreadStart(
PSERVICE_INFO serviceInfo;
PPH_STRING userAgent;
HANDLE fileHandle = NULL;
+ LARGE_INTEGER fileSize64;
ULONG fileSize;
+ PPH_STRING objectName = NULL;
HINTERNET internetHandle = NULL;
HINTERNET connectHandle = NULL;
HINTERNET requestHandle = NULL;
PPH_STRING boundary = NULL;
PPH_ANSI_STRING boundaryAnsi = NULL;
PH_STRING_BUILDER headers = { 0 };
- PPH_ANSI_STRING baseFileNameAnsi;
+ PPH_ANSI_STRING baseFileNameAnsi = NULL;
PUCHAR data = NULL;
ULONG dataLength = 0;
ULONG dataCursor = 0;
+ UCHAR buffer[PAGE_SIZE];
+ ULONG bufferSize;
serviceInfo = GetUploadServiceInfo(context->Service);
@@ -206,8 +429,6 @@ static NTSTATUS UploadWorkerThreadStart(
if (NT_SUCCESS(status))
{
- LARGE_INTEGER fileSize64;
-
if (NT_SUCCESS(status = PhGetFileSize(fileHandle, &fileSize64)))
{
if (fileSize64.QuadPart > 20 * 1024 * 1024) // 20 MB
@@ -226,6 +447,124 @@ static NTSTATUS UploadWorkerThreadStart(
goto ExitCleanup;
}
+ switch (context->Service)
+ {
+ case UPLOAD_SERVICE_VIRUSTOTAL:
+ {
+ UCHAR hash[32];
+ PPH_STRING hashString;
+ PPH_STRING subObjectName;
+ PSTR uploadUrl;
+ PSTR quote;
+
+ status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA256, hash);
+
+ if (!NT_SUCCESS(status))
+ {
+ RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status));
+ goto ExitCleanup;
+ }
+
+ hashString = PhBufferToHexString(hash, 32);
+ subObjectName = PhConcatStrings2(L"/file/upload/?sha256=", hashString->Buffer);
+
+ if (!PerformSubRequest(context, serviceInfo->HostName, subObjectName->Buffer, buffer, sizeof(buffer) - 1, &bufferSize))
+ {
+ PhDereferenceObject(hashString);
+ PhDereferenceObject(subObjectName);
+ goto ExitCleanup;
+ }
+
+ PhDereferenceObject(subObjectName);
+
+ buffer[bufferSize] = 0;
+
+ if (strstr(buffer, "\"file_exists\": true"))
+ {
+ // No upload needed; show the results immediately.
+ context->LaunchCommand = PhFormatString(L"http://www.virustotal.com/file/%s/analysis/", hashString->Buffer);
+ PhDereferenceObject(hashString);
+ SendLaunchCommand(context);
+ goto ExitCleanup;
+ }
+
+ PhDereferenceObject(hashString);
+
+ uploadUrl = strstr(buffer, "\"upload_url\": \"https://www.virustotal.com");
+
+ if (!uploadUrl)
+ {
+ RaiseUploadError(context, L"Unable to complete the request (no upload URL provided)", 0);
+ goto ExitCleanup;
+ }
+
+ uploadUrl += 41;
+ quote = strchr(uploadUrl, '"');
+
+ if (!quote)
+ {
+ RaiseUploadError(context, L"Unable to complete the request (invalid upload URL)", 0);
+ goto ExitCleanup;
+ }
+
+ objectName = PhCreateStringFromAnsiEx(uploadUrl, quote - uploadUrl);
+ }
+ break;
+ case UPLOAD_SERVICE_JOTTI:
+ {
+ UCHAR hash[20];
+ PPH_STRING hashString;
+ PPH_STRING subObjectName;
+ PSTR id;
+ PSTR quote;
+
+ status = HashFileAndResetPosition(fileHandle, &fileSize64, HASH_SHA1, hash);
+
+ if (!NT_SUCCESS(status))
+ {
+ RaiseUploadError(context, L"Unable to hash the file", RtlNtStatusToDosError(status));
+ goto ExitCleanup;
+ }
+
+ hashString = PhBufferToHexString(hash, 20);
+ subObjectName = PhConcatStrings2(L"/nestor/getfileforhash.php?hash=", hashString->Buffer);
+
+ if (!PerformSubRequest(context, serviceInfo->HostName, subObjectName->Buffer, buffer, sizeof(buffer) - 1, &bufferSize))
+ {
+ PhDereferenceObject(hashString);
+ PhDereferenceObject(subObjectName);
+ goto ExitCleanup;
+ }
+
+ PhDereferenceObject(hashString);
+ PhDereferenceObject(subObjectName);
+
+ buffer[bufferSize] = 0;
+
+ if (id = strstr(buffer, "\"id\":"))
+ {
+ id += 6;
+ quote = strchr(id, '"');
+
+ if (quote)
+ {
+ // No upload needed; show the results immediately.
+ context->LaunchCommand = PhFormatString(L"http://virusscan.jotti.org/en/scanresult/%.*S", quote - id, id);
+ SendLaunchCommand(context);
+ goto ExitCleanup;
+ }
+ }
+
+ objectName = PhCreateString(serviceInfo->UploadObjectName);
+ }
+ break;
+ default:
+ {
+ objectName = PhCreateString(serviceInfo->UploadObjectName);
+ }
+ break;
+ }
+
// Create a user agent string.
{
PPH_STRING phVersion;
@@ -262,7 +601,7 @@ static NTSTATUS UploadWorkerThreadStart(
connectHandle = InternetConnect(
internetHandle,
serviceInfo->HostName,
- 80,
+ serviceInfo->HostPort,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
@@ -284,11 +623,11 @@ static NTSTATUS UploadWorkerThreadStart(
requestHandle = HttpOpenRequest(
connectHandle,
L"POST",
- serviceInfo->UploadObjectName,
+ objectName->Buffer,
L"HTTP/1.1",
L"",
acceptTypes,
- INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_AUTO_REDIRECT,
+ INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_AUTO_REDIRECT | serviceInfo->HostFlags,
0
);
}
@@ -426,8 +765,6 @@ static NTSTATUS UploadWorkerThreadStart(
// Handle service-specific actions.
{
- UCHAR buffer[PAGE_SIZE];
- ULONG bufferSize;
ULONG index;
bufferSize = sizeof(buffer);
@@ -475,7 +812,7 @@ static NTSTATUS UploadWorkerThreadStart(
if (hrefEquals)
{
hrefEquals += 6;
- quote = strchr(hrefEquals, '\"');
+ quote = strchr(hrefEquals, '"');
if (quote)
{
@@ -535,7 +872,7 @@ static NTSTATUS UploadWorkerThreadStart(
if (urlEquals)
{
urlEquals += 4;
- quote = strchr(urlEquals, '\"');
+ quote = strchr(urlEquals, '"');
if (quote)
{
@@ -562,7 +899,7 @@ static NTSTATUS UploadWorkerThreadStart(
break;
}
- PostMessage(context->WindowHandle, UM_LAUNCH_COMMAND, 0, 0);
+ SendLaunchCommand(context);
}
ExitCleanup:
@@ -582,6 +919,8 @@ ExitCleanup:
InternetCloseHandle(connectHandle);
if (internetHandle)
InternetCloseHandle(internetHandle);
+ if (objectName)
+ PhDereferenceObject(objectName);
if (fileHandle)
NtClose(fileHandle);
@@ -630,7 +969,9 @@ INT_PTR CALLBACK UploadDlgProc(
break;
case WM_DESTROY:
{
+ PhAcquireQueuedLockExclusive(&context->Lock);
context->WindowHandle = NULL;
+ PhReleaseQueuedLockExclusive(&context->Lock);
}
break;
case WM_COMMAND: