Files
clamav/common/exeScanner.c
kang-grace 23dfe8fc4c ClamScan, ClamDScan: process memory scanning (Windows)
Add the process memory scanning feature from ClamWin's ClamScan.
This commit extends that feature to make it available in ClamDScan 
as well.

This adds three new options to ClamScan and ClamDScan on Windows:
* --memory
* --kill
* --unload

--allmatch and --stream are available for ClamDScan.

To reduce code duplication, this refactors clamd related code
used in both scanmem.c and proto.c into clamdcom. 
Moved send_fdpass(), send_stream(), chkpath(), dconnect(), and
dsresult(); as well as some type definitions.

Special thanks to Gianluigi Tiesi for allowing us to integrate the 
Windows process memory scanning feature from ClamWin into the ClamAV.
2021-08-27 09:14:45 -07:00

331 lines
10 KiB
C

/*
* Copyright (C) 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "exescanner.h"
/* -1 = wildchar - -2 = stop here */
sigs_t signatures[] = {
{{0x60, 0xbe, -1, -1, -1, -1, 0x8d, 0xbe, -1, -1, -1, 0xff, 0x57, -2},
"UPX",
.0f},
{{0x94, 0xbc, 0x5d, 0x07, 0x42, 0x00, 0xb9, 0x1d, 0x00, 0x00, 0x00, 0x80,
0x34, 0x0c, 0x44, 0xe2},
"UPXSHiT",
.0f},
{{0xbe, 0xa4, 0x01, 0x40, 0x00, 0xad, 0x93, 0xad, 0x97, 0xad, 0x56, 0x96,
0xb2, 0x80, 0xa4, 0xb6},
"FSG 1.33",
.0f},
{{0x4d, 0x5a, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x50, 0x45, 0x00,
0x00},
"FSG 2.00",
.0f},
{{0x4d, 0x5a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x33, 0x32, 0x2e, 0x44,
0x4c, 0x4c, 0x00, 0x00},
"WinUpack 0.39",
.0f},
{{0xbe, 0x88, 0x01, 0x40, 0x00, 0xad, 0x8b, 0xf8, 0x95, 0xad, 0x91, 0xf3,
0xa5, 0xad, 0xb5, 0x1c},
"Upack 2.4/2.9",
.0f},
{{0xbe, 0x48, 0x01, 0x40, 0x00, 0xad, 0x8b, 0xf8, 0x95, 0xa5, 0x33, 0xc0,
0x33, 0xc9, 0xab, 0x48},
"Upack 1.1/1.2",
.0f},
{{0x83, 0xec, 0x20, 0x53, 0x55, 0x56, 0x33, 0xdb, 0x57, 0x89, 0x5c, 0x24,
0x18, 0xc7, 0x44, 0x24},
"NullSoft PiMP",
.0f},
{{0xe9, -1, -1, -1, 0xff, 0x0c, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00},
"Mew 11 1.2",
.0f},
{{0x60, 0xe9, 0x3d, 0x04, 0x00, 0x00, -2}, "ASPack 2.11", .0f},
{{0x60, 0xe8, 0x03, 0x00, 0x00, 0x00, 0xe9, 0xeb, 0x04, 0x5d, 0x45, 0x55,
0xc3, 0xe8, 0x01, 0x00},
"ASPack 2.12",
.0f},
{{0x55, 0x83, 0xc4, 0x04, 0x76, 0x08, 0x7a, 0x06, 0x74, 0x04, 0x66, 0x83,
0xea, 0x00, 0xf5, 0x50},
"Morphine 1.4/2.7",
.0f},
{{0x56, 0x72, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x0b, 0xd2, 0xf9,
0x84, 0xdb, 0x68, 0x34},
"Morphine 1.4/2.7 [2]",
.0f},
{{0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5d,
0x8b, 0xd5, 0x81, 0xed},
"PEDiminisher 0.1",
.0f},
{{0xe8, 0xf6, 0x03, 0x00, 0x00, 0xe9, 0x9e, 0xfd, 0xff, 0xff, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc},
"MSVC8 Release",
-1.0f},
{{0xe9, -1, -1, -1, 0x00, 0xe9, -1, -1, -1, 0x00, 0xe9, -1, -1, -1, 0x00,
0xe9},
"MSVC8 Debug",
-1.0f},
{{0xe8, -1, -1, 0x00, 0x00, 0xe9, 0x16, 0xfe, 0xff, 0xff, -2},
"MSVC6 Release",
-2.0f},
{{0xe9, 0x96, 0xee, 0x0e, 0x00, 0xb8, 0x6c, 0x02, 0x58, 0x00, 0xe8, 0xae,
0xe4, 0x0e, 0x00, 0x83},
"MSVC6 Release (2)",
-1.0f},
{{0x55, 0x8b, 0xec, 0x6a, 0xff, 0x68, 0xb0, 0x41, 0x40, 0x00, 0x68, 0x10,
0x36, 0x40, 0x00, 0x64},
"MSVC6 Release (3)",
-1.0f},
{{0x55, 0x8b, 0xec, 0x53, 0x8b, 0x5d, 0x08, 0x56, 0x8b, 0x75, 0x0c, 0x57,
0x8b, 0x7d, 0x10, 0x85},
"MSVC6 Release (4)",
-1.0f},
{{0x83, 0x7c, 0x24, 0x08, 0x01, 0x75, 0x05, 0xe8, -1, -1, 0x00, 0x00, 0xff,
0x74, 0x24, 0x04},
"MSVC6 Release DLL",
-1.0f},
{{0xff, 0x25, -1, -1, -1, -1, 0xcc, 0xcc, 0x03, 0x30, 0x01, 0x00, 0x07,
0x00, 0x00, 0x00},
"DotNet",
-1.0f},
{{0x55, 0x89, 0xe5, -2}, "MinGW", -1.0f},
{{0}, 0, 0}};
int sigcmp(const uint8_t *data, const int16_t *sig, size_t n)
{
uint8_t *d = (uint8_t *)data;
int16_t *s = (int16_t *)sig;
while (n-- != 0) {
if (*s == -2)
return 0;
if ((*s != -1) && (*d != *s))
return (*d < *s) ? -1 : +1;
d++;
s++;
}
return 0;
}
sigs_t *checksig(uint8_t *data)
{
int i = 0;
while (signatures[i].name) {
if (!sigcmp(data, signatures[i].sig, 16))
return &signatures[i];
i++;
}
return NULL;
}
double calc_entropy(const unsigned char *data, size_t size)
{
double entropy = .0f;
size_t p[256];
size_t c, i;
memset(p, 0, sizeof(p));
for (c = 0; c < size; c++)
p[data[c]]++;
for (i = 0; i < 256; i++)
if (p[i])
entropy -= ((double)p[i] / size) * log((double)p[i] / size);
return entropy;
}
#define FILLBYTES(dst) \
if (IsBadReadPtr(seek, sizeof(dst))) { \
logg("!exeScanner: Bad pointer!!!\n"); \
goto cleanup; \
} \
memcpy(&dst, seek, sizeof(dst));
/* Packed exe heuristic detection, errors are handled as like of non packed data
*/
int is_packed(const char *filename)
{
int packed = 0;
int i = 0, c = 0;
int badsection = 0;
double entropy = 0.0;
sigs_t *sig = NULL;
uint16_t e_mz;
uint32_t e_lfanew, e_magic;
uint32_t epoff = 0;
unsigned char *seek = NULL, *s_start = NULL, *ep = NULL, *lpMapAddress = NULL;
PIMAGE_FILE_HEADER pehdr;
PIMAGE_OPTIONAL_HEADER32 opthdr;
PIMAGE_SECTION_HEADER sechdr;
char secname[IMAGE_SIZEOF_SHORT_NAME];
HANDLE hFile = INVALID_HANDLE_VALUE, hMapFile = NULL;
hFile = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
elogg("exeScanner: CreateFileA failed %lu\n", GetLastError());
return packed; /* Returning packed, the module is loaded so it must exists
on disk */
}
hMapFile = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, "exeScanner");
if (!hMapFile) {
elogg("exeScanner: CreateFileMappingA() failed %lu\n", GetLastError());
goto cleanup;
}
lpMapAddress = (LPBYTE)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
if (!lpMapAddress) {
elogg("exeScanner: MapViewOfFile() failed %lu\n", GetLastError());
goto cleanup;
}
seek = lpMapAddress;
/* DOS Signature 'MZ' */
FILLBYTES(e_mz);
if (e_mz != IMAGE_DOS_SIGNATURE) {
elogg("exeScanner: DOS Signature not found\n");
goto cleanup;
}
seek += 0x3c;
FILLBYTES(e_lfanew);
if (!e_lfanew) {
elogg("exeScanner: Invalid PE offset\n");
goto cleanup;
}
seek = lpMapAddress + e_lfanew;
/* PE Signature 'PE' */
FILLBYTES(e_magic);
if (e_magic != IMAGE_NT_SIGNATURE) {
elogg("exeScanner: PE Signature not found\n");
goto cleanup;
}
seek += sizeof(e_magic);
if (IsBadReadPtr(seek, sizeof(IMAGE_FILE_HEADER)))
goto cleanup;
pehdr = (PIMAGE_FILE_HEADER)seek;
seek += sizeof(IMAGE_FILE_HEADER);
if (IsBadReadPtr(seek, sizeof(IMAGE_OPTIONAL_HEADER32)))
goto cleanup;
opthdr = (PIMAGE_OPTIONAL_HEADER32)seek;
seek += sizeof(IMAGE_OPTIONAL_HEADER32);
if (pehdr->Machine != IMAGE_FILE_MACHINE_I386) {
elogg("exeScanner: Not an x86 executable\n");
goto cleanup;
}
/* Invalid sections number */
if ((pehdr->NumberOfSections < 1) || (pehdr->NumberOfSections > 32)) {
elogg("exeScanner: Invalid sections number\n");
packed = 1;
goto cleanup;
}
for (i = 0; i < pehdr->NumberOfSections; i++) {
double section_entropy = .0f;
if (IsBadReadPtr(seek, sizeof(IMAGE_SECTION_HEADER)))
goto cleanup;
sechdr = (PIMAGE_SECTION_HEADER)seek;
seek += sizeof(IMAGE_SECTION_HEADER);
if (opthdr->AddressOfEntryPoint >= sechdr->VirtualAddress)
epoff = opthdr->AddressOfEntryPoint - sechdr->VirtualAddress +
sechdr->PointerToRawData;
s_start = lpMapAddress + sechdr->PointerToRawData;
if (!IsBadReadPtr(s_start, sechdr->SizeOfRawData))
section_entropy = calc_entropy(s_start, sechdr->SizeOfRawData);
entropy = MAX(entropy, section_entropy);
/* Sanitize the section name */
memcpy(secname, sechdr->Name, IMAGE_SIZEOF_SHORT_NAME);
for (c = 0; (c < IMAGE_SIZEOF_SHORT_NAME) && secname[c]; c++)
if (!isprint(secname[c]))
secname[c] = '?';
secname[IMAGE_SIZEOF_SHORT_NAME - 1] = 0;
elogg("exeScanner: Section name: [%s] - Entropy %f\n", secname,
section_entropy);
if (!sechdr->SizeOfRawData)
badsection = 1;
}
elogg("exeScanner: Max entropy = %f\n", entropy);
/* EP Check */
elogg("exeScanner: Entry Point rva: 0x%lx - raw: 0x%lx\n",
opthdr->AddressOfEntryPoint, epoff);
ep = lpMapAddress + epoff;
if (!IsBadReadPtr(ep, EP_SIGNATURE_SIZE)) {
#ifdef DUMP_SIGNATURE
int i;
for (i = 0; i < EP_SIGNATURE_SIZE; i++)
elogg("%02x ", ep[i]);
elogg("\n[C Code]: ");
for (i = 0; i < EP_SIGNATURE_SIZE - 1; i++)
elogg("0x%02x, ", ep[i]);
elogg("0x%02x\n", ep[i]);
#endif
if ((sig = checksig(ep))) {
elogg("exeScanner: Signature check: %s\n", sig->name);
entropy += sig->score;
packed = (sig->score >= .0f);
if (sig->score < .0f)
elogg(
"exeScanner: Whitelisted signature found, lowering entropy to %f\n",
entropy);
} else
elogg("exeScanner: Signature check: Nothing found\n");
} else
elogg("exeScanner: Invalid address of Entry Point\n");
if (badsection) {
if ((entropy == .0f) || (entropy > ENTROPY_THRESHOLD)) {
elogg("exeScanner: found zero SizeOfRawData and entropy %f\n", entropy);
packed = 1;
goto cleanup;
}
}
cleanup:
if (lpMapAddress)
UnmapViewOfFile(lpMapAddress);
if (hMapFile)
CloseHandle(hMapFile);
CloseHandle(hFile);
return packed;
}