2
* Copyright (C) 2014 Sourcefire, Inc.
4
* Authors: Kevin Lin <klin@sourcefire.com>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License version 2 as
8
* published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22
#include "clamav-config.h"
34
#include <openssl/ssl.h>
35
#include <openssl/err.h>
36
#include "libclamav/crypto.h"
41
#include "prtn_intxn.h"
45
//#define DEBUG_MBR_PARSE
46
//#define DEBUG_EBR_PARSE
48
#ifndef PRTN_INTXN_DETECTION
49
# define PRTN_INTXN_DETECTION "heuristic.mbrprtnintersect"
52
#ifdef DEBUG_MBR_PARSE
53
# define mbr_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
55
# define mbr_parsemsg(...) ;
58
#ifdef DEBUG_EBR_PARSE
59
# define ebr_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
61
# define ebr_parsemsg(...) ;
71
static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, off_t extlba,
72
size_t extlbasize, size_t sectorsize);
73
static void mbr_printbr(struct mbr_boot_record *record);
74
static int mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize);
75
static int mbr_check_ebr(struct mbr_boot_record *record);
76
static int mbr_primary_prtn_intxn(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize);
77
static int mbr_extended_prtn_intxn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t sectorsize);
79
int cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen) {
80
struct mbr_boot_record mbr;
82
size_t sectorsize = 512;
84
if (len < sectorsize) {
88
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
89
memcpy(&mbr, buff+mbr_base, sizeof(mbr));
90
mbr_convert_to_host(&mbr);
92
if ((mbr.entries[0].type == MBR_PROTECTIVE) || (mbr.entries[0].type == MBR_HYBRID))
95
return mbr_check_mbr(&mbr, maplen, sectorsize);
98
int cli_mbr_check2(cli_ctx *ctx, size_t sectorsize) {
99
struct mbr_boot_record mbr;
100
off_t pos = 0, mbr_base = 0;
103
if (!ctx || !ctx->fmap) {
104
cli_errmsg("cli_scanmbr: Invalid context\n");
108
/* sector size calculation, actual value is OS dependent */
110
sectorsize = MBR_SECTOR_SIZE;
112
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
114
/* size of total file must be a multiple of the sector size */
115
maplen = (*ctx->fmap)->real_len;
116
if ((maplen % sectorsize) != 0) {
117
cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n",
118
(unsigned long)maplen, (unsigned long)sectorsize);
122
/* sector 0 (first sector) is the master boot record */
123
pos = (MBR_SECTOR * sectorsize) + mbr_base;
125
/* read the master boot record */
126
if (fmap_readn(*ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) {
127
cli_dbgmsg("cli_scanmbr: Invalid master boot record\n");
131
/* convert the little endian to host, include the internal */
132
mbr_convert_to_host(&mbr);
134
if ((mbr.entries[0].type == MBR_PROTECTIVE) || (mbr.entries[0].type == MBR_HYBRID))
137
return mbr_check_mbr(&mbr, maplen, sectorsize);
140
/* sets sectorsize to default value if specfied to be 0 */
141
int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
143
struct mbr_boot_record mbr;
144
enum MBR_STATE state = SEEN_NOTHING;
145
int ret = CL_CLEAN, detection = CL_CLEAN;
146
off_t pos = 0, mbr_base = 0, partoff = 0;
147
unsigned i = 0, prtncount = 0;
148
size_t maplen, partsize;
150
mbr_parsemsg("The start of something magnificant: MBR parsing\n");
152
if (!ctx || !ctx->fmap) {
153
cli_errmsg("cli_scanmbr: Invalid context\n");
157
/* sector size calculation, actual value is OS dependent */
159
sectorsize = MBR_SECTOR_SIZE;
161
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
163
/* size of total file must be a multiple of the sector size */
164
maplen = (*ctx->fmap)->real_len;
165
if ((maplen % sectorsize) != 0) {
166
cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n",
167
(unsigned long)maplen, (unsigned long)sectorsize);
171
/* sector 0 (first sector) is the master boot record */
172
pos = (MBR_SECTOR * sectorsize) + mbr_base;
174
/* read the master boot record */
175
if (fmap_readn(*ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) {
176
cli_dbgmsg("cli_scanmbr: Invalid master boot record\n");
180
/* convert the little endian to host, include the internal */
181
mbr_convert_to_host(&mbr);
184
ret = mbr_check_mbr(&mbr, maplen, sectorsize);
185
if (ret != CL_CLEAN) {
189
/* MBR is valid, examine bootstrap code */
190
ret = cli_map_scan(*ctx->fmap, 0, sectorsize, ctx, CL_TYPE_ANY);
191
if (ret != CL_CLEAN) {
192
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
193
detection = CL_VIRUS;
198
/* check that the partition table has no intersections - HEURISTICS */
199
if ((ctx->options & CL_SCAN_PARTITION_INTXN) && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
200
ret = mbr_primary_prtn_intxn(ctx, mbr, sectorsize);
201
if (ret != CL_CLEAN) {
202
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
203
detection = CL_VIRUS;
209
/* MBR is valid, examine partitions */
211
cli_dbgmsg("MBR Signature: %x\n", mbr.signature);
212
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES && prtncount < ctx->engine->maxpartitions; ++i) {
213
cli_dbgmsg("MBR Partition Entry %u:\n", i);
214
cli_dbgmsg("Status: %u\n", mbr.entries[i].status);
215
cli_dbgmsg("Type: %x\n", mbr.entries[i].type);
216
cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n",
217
mbr.entries[i].firstLBA, mbr.entries[i].numLBA,
218
(unsigned long)(mbr.entries[i].firstLBA * sectorsize),
219
(unsigned long)(mbr.entries[i].numLBA * sectorsize));
221
/* Handle MBR entry based on type */
222
if (mbr.entries[i].type == MBR_EMPTY) {
223
/* empty partiton entry */
226
else if (mbr.entries[i].type == MBR_EXTENDED) {
227
if (state == SEEN_EXTENDED) {
228
cli_dbgmsg("cli_scanmbr: detected a master boot record "
229
"with multiple extended partitions\n");
231
state = SEEN_EXTENDED; /* used only to detect mutiple extended partitions */
233
ret = mbr_scanextprtn(ctx, &prtncount, mbr.entries[i].firstLBA,
234
mbr.entries[i].numLBA, sectorsize);
235
if (ret != CL_CLEAN) {
236
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
237
detection = CL_VIRUS;
245
partoff = mbr.entries[i].firstLBA * sectorsize;
246
partsize = mbr.entries[i].numLBA * sectorsize;
247
mbr_parsemsg("cli_map_scan: [%u, +%u)\n", partoff, partsize);
248
ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY);
249
if (ret != CL_CLEAN) {
250
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
251
detection = CL_VIRUS;
258
if (prtncount >= ctx->engine->maxpartitions) {
259
cli_dbgmsg("cli_scanmbr: maximum partitions reached\n");
265
static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t extlbasize, size_t sectorsize)
267
struct mbr_boot_record ebr;
268
enum MBR_STATE state = SEEN_NOTHING;
269
int ret = CL_CLEAN, detection = CL_CLEAN;
270
off_t pos = 0, mbr_base = 0, logiclba = 0, extoff = 0, partoff = 0;
271
size_t partsize, extsize;
272
unsigned i = 0, j = 0;
274
ebr_parsemsg("The start of something exhausting: EBR parsing\n");
276
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
279
extoff = extlba * sectorsize;
280
extsize = extlbasize * sectorsize;
282
pos = extlba * sectorsize; /* start of extended partition */
284
/* read the extended boot record */
285
pos += (logiclba * sectorsize) + mbr_base;
286
if (fmap_readn(*ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) {
287
cli_dbgmsg("cli_scanebr: Invalid extended boot record\n");
291
/* convert the little endian to host */
292
mbr_convert_to_host(&ebr);
295
ret = mbr_check_ebr(&ebr);
296
if (ret != CL_CLEAN) {
301
state = SEEN_NOTHING;
304
/* EBR is valid, examine partitions */
305
cli_dbgmsg("EBR Partition Entry %u:\n", i++);
306
cli_dbgmsg("EBR Signature: %x\n", ebr.signature);
307
for (j = 0; j < MBR_MAX_PARTITION_ENTRIES; ++j) {
309
cli_dbgmsg("Logical Partition Entry %u:\n", j);
310
cli_dbgmsg("Status: %u\n", ebr.entries[j].status);
311
cli_dbgmsg("Type: %x\n", ebr.entries[j].type);
312
cli_dbgmsg("Blocks: [%u, +%u), ([%lu, +%lu))\n",
313
ebr.entries[j].firstLBA, ebr.entries[j].numLBA,
314
(unsigned long)(ebr.entries[j].firstLBA * sectorsize),
315
(unsigned long)(ebr.entries[j].numLBA * sectorsize));
317
if (ebr.entries[j].type == MBR_EMPTY) {
318
/* empty partiton entry */
330
cli_warnmsg("cli_scanebr: detected a logical boot record "
331
"without a partition record\n");
334
cli_warnmsg("cli_scanebr: undefined state for EBR parsing\n");
338
else if (ebr.entries[j].type == MBR_EXTENDED) {
341
state = SEEN_EXTENDED;
346
cli_warnmsg("cli_scanebr: detected a logical boot record "
347
"without a partition record\n");
350
cli_warnmsg("cli_scanebr: detected a logical boot record "
351
"with multiple extended partition records\n");
354
cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n");
358
logiclba = ebr.entries[j].firstLBA;
363
state = SEEN_PARTITION;
366
cli_warnmsg("cli_scanebr: detected a logical boot record "
367
"with multiple partition records\n");
368
logiclba = 0; /* no extended partitions are possible */
371
cli_warnmsg("cli_scanebr: detected a logical boot record "
372
"with extended partition record first\n");
375
cli_warnmsg("cli_scanebr: detected a logical boot record "
376
"with empty partition record first\n");
377
logiclba = 0; /* no extended partitions are possible */
380
cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n");
384
partoff = (extlba + logiclba + ebr.entries[j].firstLBA) * sectorsize;
385
partsize = ebr.entries[j].numLBA * sectorsize;
386
if (partoff + partsize > extoff + extsize) {
387
cli_dbgmsg("cli_scanebr: Invalid extended partition entry\n");
391
ret = cli_map_scan(*ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY);
392
if (ret != CL_CLEAN) {
393
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
394
detection = CL_VIRUS;
401
/* check the last two entries to be empty */
402
if (ebr.entries[j].type != MBR_EMPTY) {
403
cli_dbgmsg("cli_scanebr: detected a non-empty partition "
404
"entry at index %u\n", j);
405
/* should we attempt to use these entries? */
410
} while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions);
412
cli_dbgmsg("cli_scanmbr: examined %u logical partitions\n", i);
417
void mbr_convert_to_host(struct mbr_boot_record *record)
419
struct mbr_partition_entry *entry;
422
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
423
entry = &record->entries[i];
425
entry->firstLBA = le32_to_host(entry->firstLBA);
426
entry->numLBA = le32_to_host(entry->numLBA);
428
record->signature = be16_to_host(record->signature);
431
static void mbr_printbr(struct mbr_boot_record *record)
435
cli_dbgmsg("signature: %x\n", record->signature);
436
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
437
cli_dbgmsg("entry %u:\n", i);
438
cli_dbgmsg("\tstatus: %x\n", record->entries[i].status);
439
cli_dbgmsg("\tfirstCHS: [%u, %u, %u]\n", record->entries[i].firstCHS[0],
440
record->entries[i].firstCHS[1], record->entries[i].firstCHS[2]);
441
cli_dbgmsg("\ttype: %x\n", record->entries[i].type);
442
cli_dbgmsg("\tlastCHS: [%u, %u, %u]\n", record->entries[i].lastCHS[0],
443
record->entries[i].lastCHS[1], record->entries[i].lastCHS[2]);
444
cli_dbgmsg("\tfirstLBA: %u\n", record->entries[i].firstLBA);
445
cli_dbgmsg("\tnumLBA: %u\n", record->entries[i].numLBA);
449
static int mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize)
455
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
457
if ((record->entries[i].status != MBR_STATUS_INACTIVE) &&
458
(record->entries[i].status != MBR_STATUS_ACTIVE)) {
459
cli_dbgmsg("cli_scanmbr: Invalid boot record status\n");
463
partoff = record->entries[i].firstLBA * sectorsize;
464
partsize = record->entries[i].numLBA * sectorsize;
465
if (partoff + partsize > maplen) {
466
cli_dbgmsg("cli_scanmbr: Invalid partition entry\n");
471
/* check the signature */
472
if (record->signature != MBR_SIGNATURE) {
473
cli_dbgmsg("cli_scanmbr: Invalid boot record signature\n");
477
/* check the maplen */
478
if ((maplen / sectorsize) < 2) {
479
cli_dbgmsg("cli_scanmbr: file is too small to hold disk image\n");
486
static int mbr_check_ebr(struct mbr_boot_record *record)
490
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES-2; ++i) {
492
if ((record->entries[i].status != MBR_STATUS_INACTIVE) &&
493
(record->entries[i].status != MBR_STATUS_ACTIVE)) {
494
cli_dbgmsg("cli_scanmbr: Invalid boot record status\n");
499
/* check the signature */
500
if (record->signature != MBR_SIGNATURE) {
501
cli_dbgmsg("cli_scanmbr: Invalid boot record signature\n");
508
/* this includes the overall bounds of extended partitions */
509
static int mbr_primary_prtn_intxn(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize)
511
prtn_intxn_list_t prtncheck;
512
unsigned i = 0, pitxn = 0, prtncount = 0;
513
int ret = CL_CLEAN, tmp = CL_CLEAN;
515
prtn_intxn_list_init(&prtncheck);
517
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES && prtncount < ctx->engine->maxpartitions; ++i) {
518
if (mbr.entries[i].type == MBR_EMPTY) {
519
/* empty partiton entry */
523
tmp = prtn_intxn_list_check(&prtncheck, &pitxn, mbr.entries[i].firstLBA,
524
mbr.entries[i].numLBA);
525
if (tmp != CL_CLEAN) {
526
if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) {
527
cli_dbgmsg("cli_scanmbr: detected intersection with partitions "
528
"[%u, %u]\n", pitxn, i);
529
cli_append_virus(ctx, PRTN_INTXN_DETECTION);
533
else if (tmp == CL_VIRUS) {
534
cli_dbgmsg("cli_scanmbr: detected intersection with partitions "
535
"[%u, %u]\n", pitxn, i);
536
cli_append_virus(ctx, PRTN_INTXN_DETECTION);
537
prtn_intxn_list_free(&prtncheck);
541
prtn_intxn_list_free(&prtncheck);
546
if (mbr.entries[i].type == MBR_EXTENDED) {
547
/* check the logical partitions */
548
tmp = mbr_extended_prtn_intxn(ctx, &prtncount,
549
mbr.entries[i].firstLBA, sectorsize);
550
if (tmp != CL_CLEAN) {
551
if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) {
555
else if (tmp == CL_VIRUS) {
556
prtn_intxn_list_free(&prtncheck);
560
prtn_intxn_list_free(&prtncheck);
571
prtn_intxn_list_free(&prtncheck);
575
/* checks internal logical partitions */
576
static int mbr_extended_prtn_intxn(cli_ctx *ctx, unsigned *prtncount, off_t extlba, size_t sectorsize)
578
struct mbr_boot_record ebr;
579
prtn_intxn_list_t prtncheck;
581
int ret = CL_CLEAN, tmp = CL_CLEAN, mbr_base = 0;
582
off_t pos = 0, logiclba = 0;
584
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
586
prtn_intxn_list_init(&prtncheck);
590
pos = extlba * sectorsize; /* start of extended partition */
592
/* read the extended boot record */
593
pos += (logiclba * sectorsize) + mbr_base;
594
if (fmap_readn(*ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) {
595
cli_dbgmsg("cli_scanebr: Invalid extended boot record\n");
596
prtn_intxn_list_free(&prtncheck);
600
/* convert the little endian to host */
601
mbr_convert_to_host(&ebr);
606
/* assume that logical record is first and extended is second */
607
tmp = prtn_intxn_list_check(&prtncheck, &pitxn, logiclba, ebr.entries[0].numLBA);
608
if (tmp != CL_CLEAN) {
609
if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) {
610
cli_dbgmsg("cli_scanebr: detected intersection with partitions "
611
"[%u, %u]\n", pitxn, i);
612
cli_append_virus(ctx, PRTN_INTXN_DETECTION);
616
else if (tmp == CL_VIRUS) {
617
cli_dbgmsg("cli_scanebr: detected intersection with partitions "
618
"[%u, %u]\n", pitxn, i);
619
cli_append_virus(ctx, PRTN_INTXN_DETECTION);
620
prtn_intxn_list_free(&prtncheck);
624
prtn_intxn_list_free(&prtncheck);
629
/* assume extended is second entry */
630
if (ebr.entries[1].type != MBR_EXTENDED) {
631
cli_dbgmsg("cli_scanebr: second entry for EBR is not an extended partition\n");
635
logiclba = ebr.entries[1].firstLBA;
638
} while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions);
640
prtn_intxn_list_free(&prtncheck);