1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
|
/*
* Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2014 Sourcefire, Inc.
*
* Authors: Kevin Lin <klin@sourcefire.com>
*
* 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.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <errno.h>
#if HAVE_STRING_H
#include <string.h>
#endif
#include <ctype.h>
#include <fcntl.h>
#include <zlib.h>
#include "clamav.h"
#include "cltypes.h"
#include "others.h"
#include "gpt.h"
#include "mbr.h"
#include "str.h"
#include "prtn_intxn.h"
#include "scanners.h"
#include "dconf.h"
//#define DEBUG_GPT_PARSE
//#define DEBUG_GPT_PRINT
#ifdef DEBUG_GPT_PARSE
# define gpt_parsemsg(...) cli_dbgmsg( __VA_ARGS__)
#else
# define gpt_parsemsg(...) ;
#endif
#ifdef DEBUG_GPT_PRINT
# define gpt_printmsg(...) cli_dbgmsg( __VA_ARGS__)
#else
# define gpt_printmsg(...) ;
#endif
enum GPT_SCANSTATE {
INVALID,
PRIMARY_ONLY,
SECONDARY_ONLY,
BOTH
};
static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize);
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize);
static void gpt_printName(uint16_t name[], const char* msg);
static void gpt_printGUID(uint8_t GUID[], const char* msg);
static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
/* returns 0 on failing to detect sectorsize */
size_t gpt_detect_size(fmap_t *map)
{
unsigned char *buff;
buff = (unsigned char*)fmap_need_off_once(map, 512, 8);
if (!buff) return 0;
if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
return 512;
buff = (unsigned char*)fmap_need_off_once(map, 1024, 8);
if (!buff) return 0;
if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
return 1024;
buff = (unsigned char*)fmap_need_off_once(map, 2048, 8);
if (!buff) return 0;
if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
return 2048;
buff = (unsigned char*)fmap_need_off_once(map, 4096, 8);
if (!buff) return 0;
if (0 == strncmp((const char *)buff, GPT_SIGNATURE_STR, 8))
return 4096;
return 0;
}
/* attempts to detect sector size is input as 0 */
int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
{
struct gpt_header phdr, shdr;
enum GPT_SCANSTATE state = INVALID;
int ret = CL_CLEAN, detection = CL_CLEAN;
size_t maplen;
off_t pos = 0;
gpt_parsemsg("The beginning of something big: GPT parsing\n");
if (!ctx || !ctx->fmap) {
cli_errmsg("cli_scangpt: Invalid context\n");
return CL_ENULLARG;
}
/* sector size calculatation */
if (sectorsize == 0) {
sectorsize = gpt_detect_size((*ctx->fmap));
cli_dbgmsg("cli_scangpt: detected %lu sector size\n", (unsigned long)sectorsize);
}
if (sectorsize == 0) {
cli_errmsg("cli_scangpt: could not detemine sector size\n");
return CL_EFORMAT;
}
/* size of total file must be a multiple of the sector size */
maplen = (*ctx->fmap)->real_len;
if ((maplen % sectorsize) != 0) {
cli_dbgmsg("cli_scangpt: File sized %lu is not a multiple of sector size %lu\n",
(unsigned long)maplen, (unsigned long)sectorsize);
return CL_EFORMAT;
}
/* check the protective mbr */
ret = gpt_check_mbr(ctx, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
pos = GPT_PRIMARY_HDR_LBA * sectorsize; /* sector 1 (second sector) is the primary gpt header */
/* read primary gpt header */
cli_dbgmsg("cli_scangpt: Using primary GPT header\n");
if (fmap_readn(*ctx->fmap, &phdr, pos, sizeof(phdr)) != sizeof(phdr)) {
cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
return CL_EFORMAT;
}
pos = maplen - sectorsize; /* last sector is the secondary gpt header */
if (gpt_validate_header(ctx, phdr, sectorsize)) {
cli_dbgmsg("cli_scangpt: Primary GPT header is invalid\n");
cli_dbgmsg("cli_scangpt: Using secondary GPT header\n");
state = SECONDARY_ONLY;
/* read secondary gpt header */
if (fmap_readn(*ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
return CL_EFORMAT;
}
if (gpt_validate_header(ctx, shdr, sectorsize)) {
cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
cli_dbgmsg("cli_scangpt: Disk is unusable\n");
return CL_EFORMAT;
}
}
else {
cli_dbgmsg("cli_scangpt: Checking secondary GPT header\n");
state = PRIMARY_ONLY;
/* check validity of secondary header; still using the primary */
if (fmap_readn(*ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
}
else if (gpt_validate_header(ctx, shdr, sectorsize)) {
cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
}
/* check that the two partition table crc32 checksum match,
* may want a different hashing function */
else if (phdr.tableCRC32 != shdr.tableCRC32){
cli_dbgmsg("cli_scangpt: Primary and secondary GPT header table CRC32 differ\n");
cli_dbgmsg("cli_scangpt: Set to scan primary and secondary partition tables\n");
state = BOTH;
}
else {
cli_dbgmsg("cli_scangpt: Secondary GPT header check OK\n");
}
}
/* check that the partition table has no intersections - HEURISTICS */
if ((ctx->options & CL_SCAN_PARTITION_INTXN) && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
ret = gpt_prtn_intxn(ctx, phdr, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
ret = gpt_prtn_intxn(ctx, shdr, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
}
/* scanning partitions */
switch (state) {
case PRIMARY_ONLY:
cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions only\n");
ret = gpt_scan_partitions(ctx, phdr, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
break;
case SECONDARY_ONLY:
cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions only\n");
ret = gpt_scan_partitions(ctx, shdr, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
break;
case BOTH:
cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions\n");
ret = gpt_scan_partitions(ctx, phdr, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions\n");
ret = gpt_scan_partitions(ctx, shdr, sectorsize);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
break;
default:
cli_dbgmsg("cli_scangpt: State is invalid\n");
}
return detection;
}
static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
{
struct gpt_partition_entry gpe;
int ret = CL_CLEAN, detection = CL_CLEAN;
size_t maplen, part_size = 0;
off_t pos = 0, part_off = 0;
unsigned i = 0, j = 0;
uint32_t max_prtns = 0;
/* convert endian to host */
hdr.signature = be64_to_host(hdr.signature);
hdr.revision = be32_to_host(hdr.revision);
hdr.headerSize = le32_to_host(hdr.headerSize);
hdr.headerCRC32 = le32_to_host(hdr.headerCRC32);
hdr.reserved = le32_to_host(hdr.reserved);
hdr.currentLBA = le64_to_host(hdr.currentLBA);
hdr.backupLBA = le64_to_host(hdr.backupLBA);
hdr.firstUsableLBA = le64_to_host(hdr.firstUsableLBA);
hdr.lastUsableLBA = le64_to_host(hdr.lastUsableLBA);
hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA);
hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
hdr.tableEntrySize = le32_to_host(hdr.tableEntrySize);
hdr.tableCRC32 = le32_to_host(hdr.tableCRC32);
/* print header info for the debug */
cli_dbgmsg("GPT Header:\n");
cli_dbgmsg("Signature: 0x%llx\n", hdr.signature);
cli_dbgmsg("Revision: %x\n", hdr.revision);
gpt_printGUID(hdr.DiskGUID, "DISK GUID");
cli_dbgmsg("Partition Entry Count: %u\n", hdr.tableNumEntries);
cli_dbgmsg("Partition Entry Size: %u\n", hdr.tableEntrySize);
maplen = (*ctx->fmap)->real_len;
/* check engine maxpartitions limit */
if (hdr.tableNumEntries < ctx->engine->maxpartitions) {
max_prtns = hdr.tableNumEntries;
}
else {
max_prtns = ctx->engine->maxpartitions;
}
/* use the partition tables to pass partitions to cli_map_scan */
pos = hdr.tableStartLBA * sectorsize;
for (i = 0; i < max_prtns; ++i) {
/* read in partition entry */
if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
return CL_EFORMAT;
}
/* convert the endian to host */
gpe.firstLBA = le64_to_host(gpe.firstLBA);
gpe.lastLBA = le64_to_host(gpe.lastLBA);
gpe.attributes = le64_to_host(gpe.attributes);
for (j = 0; j < 36; ++j) {
gpe.name[i] = le16_to_host(gpe.name[i]);
}
/* check that partition is not empty and within a valid location */
if (gpe.firstLBA == 0) {
/* empty partition, invalid */
}
else if ((gpe.firstLBA > gpe.lastLBA) ||
(gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) {
cli_dbgmsg("cli_scangpt: GPT partition exists outside specified bounds\n");
gpt_parsemsg("%llu < %llu, %llu > %llu\n", gpe.firstLBA, hdr.firstUsableLBA,
gpe.lastLBA, hdr.lastUsableLBA);
/* partition exists outside bounds specified by header or invalid */
}
else if (((gpe.lastLBA+1) * sectorsize) > maplen) {
/* partition exists outside bounds of the file map */
}
else {
/* print partition entry data for debug */
cli_dbgmsg("GPT Partition Entry %u:\n", i);
gpt_printName(gpe.name, "Name");
gpt_printGUID(gpe.typeGUID, "Type GUID");
gpt_printGUID(gpe.uniqueGUID, "Unique GUID");
cli_dbgmsg("Attributes: %llx\n", gpe.attributes);
cli_dbgmsg("Blocks: [%llu(%llu) -> %llu(%llu)]\n",
gpe.firstLBA, (gpe.firstLBA * sectorsize),
gpe.lastLBA, ((gpe.lastLBA+1) * sectorsize));
/* send the partition to cli_map_scan */
part_off = gpe.firstLBA * sectorsize;
part_size = (gpe.lastLBA - gpe.firstLBA + 1) * sectorsize;
ret = cli_map_scan(*ctx->fmap, part_off, part_size, ctx, CL_TYPE_PART_ANY);
if (ret != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (ret == CL_VIRUS))
detection = CL_VIRUS;
else
return ret;
}
}
/* increment the offsets to next partition entry */
pos += hdr.tableEntrySize;
}
if (i >= ctx->engine->maxpartitions) {
cli_dbgmsg("cli_scangpt: max partitions reached\n");
}
return detection;
}
static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
{
uint32_t crc32_calc, crc32_ref;
uint64_t tableLastLBA, lastLBA;
size_t maplen, ptable_start, ptable_len;
unsigned char *ptable;
maplen = (*ctx->fmap)->real_len;
/* checking header crc32 checksum */
crc32_ref = le32_to_host(hdr.headerCRC32);
hdr.headerCRC32 = 0; /* checksum is calculated with field = 0 */
crc32_calc = crc32(0, (unsigned char*)&hdr, sizeof(hdr));
if (crc32_calc != crc32_ref) {
cli_dbgmsg("cli_scangpt: GPT header checksum mismatch\n");
gpt_parsemsg("%x != %x\n", crc32_calc, crc32_ref);
return CL_EFORMAT;
}
/* convert endian to host to check partition table */
hdr.signature = be64_to_host(hdr.signature);
hdr.revision = be32_to_host(hdr.revision);
hdr.headerSize = le32_to_host(hdr.headerSize);
hdr.headerCRC32 = crc32_ref;
hdr.reserved = le32_to_host(hdr.reserved);
hdr.currentLBA = le64_to_host(hdr.currentLBA);
hdr.backupLBA = le64_to_host(hdr.backupLBA);
hdr.firstUsableLBA = le64_to_host(hdr.firstUsableLBA);
hdr.lastUsableLBA = le64_to_host(hdr.lastUsableLBA);
hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA);
hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
hdr.tableEntrySize = le32_to_host(hdr.tableEntrySize);
hdr.tableCRC32 = le32_to_host(hdr.tableCRC32);;
ptable_start = hdr.tableStartLBA * sectorsize;
ptable_len = hdr.tableNumEntries * hdr.tableEntrySize;
tableLastLBA = (hdr.tableStartLBA + (ptable_len / sectorsize)) - 1;
lastLBA = (maplen / sectorsize) - 1;
/** HEADER CHECKS **/
gpt_printSectors(ctx, sectorsize);
/* check signature */
if (hdr.signature != GPT_SIGNATURE) {
cli_dbgmsg("cli_scangpt: Invalid GPT header signature %llx\n",
hdr.signature);
return CL_EFORMAT;
}
/* check header size */
if (hdr.headerSize != sizeof(hdr)) {
cli_dbgmsg("cli_scangpt: GPT header size does not match stated size\n");
return CL_EFORMAT;
}
/* check reserved value == 0 */
if (hdr.reserved != GPT_HDR_RESERVED) {
cli_dbgmsg("cli_scangpt: GPT header reserved is not expected value\n");
return CL_EFORMAT;
}
/* check that sectors are in a valid configuration */
if (!((hdr.currentLBA == GPT_PRIMARY_HDR_LBA && hdr.backupLBA == lastLBA) ||
(hdr.currentLBA == lastLBA && hdr.backupLBA == GPT_PRIMARY_HDR_LBA))) {
cli_dbgmsg("cli_scangpt: GPT secondary header is not last LBA\n");
return CL_EFORMAT;
}
if (hdr.firstUsableLBA > hdr.lastUsableLBA) {
cli_dbgmsg("cli_scangpt: GPT first usable sectors is after last usable sector\n");
return CL_EFORMAT;
}
if (hdr.firstUsableLBA <= GPT_PRIMARY_HDR_LBA || hdr.lastUsableLBA >= lastLBA) {
cli_dbgmsg("cli_scangpt: GPT usable sectors intersects header sector\n");
return CL_EFORMAT;
}
if ((hdr.tableStartLBA <= hdr.firstUsableLBA && tableLastLBA >= hdr.firstUsableLBA) ||
(hdr.tableStartLBA >= hdr.firstUsableLBA && hdr.tableStartLBA <= hdr.lastUsableLBA)) {
cli_dbgmsg("cli_scangpt: GPT usable sectors intersects partition table\n");
return CL_EFORMAT;
}
if (hdr.tableStartLBA <= GPT_PRIMARY_HDR_LBA || tableLastLBA >= lastLBA) {
cli_dbgmsg("cli_scangpt: GPT partition table intersects header sector\n");
return CL_EFORMAT;
}
/* check that valid table entry size */
if (hdr.tableEntrySize != sizeof(struct gpt_partition_entry)) {
cli_dbgmsg("cli_scangpt: cannot parse gpt with partition entry sized %u\n",
hdr.tableEntrySize);
return CL_EFORMAT;
}
/* check valid table */
if ((ptable_start + ptable_len) > maplen) {
cli_dbgmsg("cli_scangpt: GPT partition table extends over fmap limit\n");
return CL_EFORMAT;
}
/** END HEADER CHECKS **/
/* checking partition table crc32 checksum */
ptable = (unsigned char*)fmap_need_off_once((*ctx->fmap), ptable_start, ptable_len);
crc32_calc = crc32(0, ptable, ptable_len);
if (crc32_calc != hdr.tableCRC32) {
cli_dbgmsg("cli_scangpt: GPT partition table checksum mismatch\n");
gpt_parsemsg("%x != %x\n", crc32_calc, hdr.tableCRC32);
return CL_EFORMAT;
}
return CL_SUCCESS;
}
static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
{
struct mbr_boot_record pmbr;
off_t pos = 0, mbr_base = 0;
int ret = CL_CLEAN;
unsigned i = 0;
/* read the mbr */
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
pos = (MBR_SECTOR * sectorsize) + mbr_base;
if (fmap_readn(*ctx->fmap, &pmbr, pos, sizeof(pmbr)) != sizeof(pmbr)) {
cli_dbgmsg("cli_scangpt: Invalid primary MBR header\n");
return CL_EFORMAT;
}
/* convert mbr */
mbr_convert_to_host(&pmbr);
/* check the protective mbr - warning */
if (pmbr.entries[0].type == MBR_PROTECTIVE) {
/* check the efi partition matches the gpt spec */
if (pmbr.entries[0].firstLBA != GPT_PRIMARY_HDR_LBA) {
cli_warnmsg("cli_scangpt: protective MBR first LBA is incorrect %u\n",
pmbr.entries[0].firstLBA);
}
/* other entries are empty */
for (i = 1; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
if (pmbr.entries[i].type != MBR_EMPTY) {
cli_warnmsg("cli_scangpt: protective MBR has non-empty partition\n");
break;
}
}
}
else if (pmbr.entries[0].type == MBR_HYBRID) {
/* hybrid mbr detected */
cli_warnmsg("cli_scangpt: detected a hybrid MBR\n");
}
else {
/* non-protective mbr detected */
cli_warnmsg("cli_scangpt: detected a non-protective MBR\n");
}
/* scan the bootloader segment - pushed to scanning mbr */
/* check if MBR size matches GPT size */
/* check if the MBR and GPT partitions align - heuristic */
/* scan the MBR partitions - additional scans */
return ret;
}
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize)
{
#ifdef DEBUG_GPT_PARSE
struct gpt_header phdr, shdr;
off_t ppos = 0, spos = 0;
size_t pptable_len, sptable_len, maplen;
uint64_t ptableLastLBA, stableLastLBA;
/* sector size calculation */
sectorsize = GPT_DEFAULT_SECTOR_SIZE;
maplen = (*ctx->fmap)->real_len;
ppos = 1 * sectorsize; /* sector 1 (second sector) is the primary gpt header */
spos = maplen - sectorsize; /* last sector is the secondary gpt header */
/* read in the primary and secondary gpt headers */
if (fmap_readn(*ctx->fmap, &phdr, ppos, sizeof(phdr)) != sizeof(phdr)) {
cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
return;
}
if (fmap_readn(*ctx->fmap, &shdr, spos, sizeof(shdr)) != sizeof(shdr)) {
cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
return;
}
pptable_len = phdr.tableNumEntries * phdr.tableEntrySize;
sptable_len = shdr.tableNumEntries * shdr.tableEntrySize;
ptableLastLBA = (phdr.tableStartLBA + (pptable_len / sectorsize)) - 1;
stableLastLBA = (shdr.tableStartLBA + (sptable_len / sectorsize)) - 1;
gpt_parsemsg("0: MBR\n");
gpt_parsemsg("%llu: Primary GPT Header\n", phdr.currentLBA);
gpt_parsemsg("%llu-%llu: Primary GPT Partition Table\n", phdr.tableStartLBA, ptableLastLBA);
gpt_parsemsg("%llu-%llu: Usuable LBAs\n", phdr.firstUsableLBA, phdr.lastUsableLBA);
gpt_parsemsg("%llu-%llu: Secondary GPT Partition Table\n", shdr.tableStartLBA, stableLastLBA);
gpt_parsemsg("%llu: Secondary GPT Header\n", phdr.backupLBA);
#else
UNUSEDPARAM(ctx);
UNUSEDPARAM(sectorsize);
return;
#endif
}
static void gpt_printName(uint16_t name[], const char* msg)
{
char *namestr;
namestr = (char*)cli_utf16toascii((char*)name, 72);
cli_dbgmsg("%s: %s\n", msg, namestr);
free(namestr);
}
static void gpt_printGUID(uint8_t GUID[], const char* msg)
{
cli_dbgmsg("%s: %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
msg, GUID[0], GUID[1], GUID[2], GUID[3], GUID[4], GUID[5], GUID[6], GUID[7],
GUID[8], GUID[9], GUID[10], GUID[11], GUID[12], GUID[13], GUID[14], GUID[15]);
}
static int gpt_prtn_intxn(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
{
prtn_intxn_list_t prtncheck;
struct gpt_partition_entry gpe;
unsigned i, pitxn;
int ret = CL_CLEAN, tmp = CL_CLEAN;
off_t pos;
size_t maplen;
uint32_t max_prtns = 0;
maplen = (*ctx->fmap)->real_len;
/* convert endian to host to check partition table */
hdr.tableStartLBA = le64_to_host(hdr.tableStartLBA);
hdr.tableNumEntries = le32_to_host(hdr.tableNumEntries);
prtn_intxn_list_init(&prtncheck);
/* check engine maxpartitions limit */
if (hdr.tableNumEntries < ctx->engine->maxpartitions) {
max_prtns = hdr.tableNumEntries;
}
else {
max_prtns = ctx->engine->maxpartitions;
}
pos = hdr.tableStartLBA * sectorsize;
for (i = 0; i < max_prtns; ++i) {
/* read in partition entry */
if (fmap_readn(*ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
prtn_intxn_list_free(&prtncheck);
return CL_EFORMAT;
}
/* convert the endian to host */
gpe.firstLBA = le64_to_host(gpe.firstLBA);
gpe.lastLBA = le64_to_host(gpe.lastLBA);
if (gpe.firstLBA == 0) {
/* empty partition, invalid */
}
else if ((gpe.firstLBA > gpe.lastLBA) ||
(gpe.firstLBA < hdr.firstUsableLBA) || (gpe.lastLBA > hdr.lastUsableLBA)) {
/* partition exists outside bounds specified by header or invalid */
}
else if (((gpe.lastLBA+1) * sectorsize) > maplen) {
/* partition exists outside bounds of the file map */
}
else {
tmp = prtn_intxn_list_check(&prtncheck, &pitxn, gpe.firstLBA, gpe.lastLBA - gpe.firstLBA + 1);
if (tmp != CL_CLEAN) {
if ((ctx->options & CL_SCAN_ALLMATCHES) && (tmp == CL_VIRUS)) {
cli_dbgmsg("cli_scangpt: detected intersection with partitions "
"[%u, %u]\n", pitxn, i);
cli_append_virus(ctx, PRTN_INTXN_DETECTION);
ret = tmp;
tmp = 0;
}
else if (tmp == CL_VIRUS) {
cli_dbgmsg("cli_scangpt: detected intersection with partitions "
"[%u, %u]\n", pitxn, i);
cli_append_virus(ctx, PRTN_INTXN_DETECTION);
prtn_intxn_list_free(&prtncheck);
return CL_VIRUS;
}
else {
prtn_intxn_list_free(&prtncheck);
return tmp;
}
}
}
/* increment the offsets to next partition entry */
pos += hdr.tableEntrySize;
}
prtn_intxn_list_free(&prtncheck);
return ret;
}
|