Instead of checking the Authenticode header as an FP prevention
mechanism, we now check it in the beginning if it exists. Also,
we can now do actual blacklisting with .crb rules (previously, a
blacklist rule just let you override a whitelist rule).
... | ... |
@@ -1365,7 +1365,7 @@ static int asn1_parse_countersignature(fmap_t *map, const void **asn1data, unsig |
1365 | 1365 |
return 1; |
1366 | 1366 |
} |
1367 | 1367 |
|
1368 |
-static cl_error_t asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, struct cl_engine *engine) |
|
1368 |
+static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, char **certname) |
|
1369 | 1369 |
{ |
1370 | 1370 |
struct cli_asn1 asn1, deep, deeper; |
1371 | 1371 |
uint8_t issuer[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE]; |
... | ... |
@@ -1555,6 +1555,11 @@ static cl_error_t asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size |
1555 | 1555 |
sprintf(&serial[j * 2], "%02x", x509->serial[j]); |
1556 | 1556 |
} |
1557 | 1557 |
|
1558 |
+ // TODO The raw information we print out here isn't |
|
1559 |
+ // very helpful, since it's only the first 64-bytes... |
|
1560 |
+ // Change this so that raw is only populated when the |
|
1561 |
+ // debug flag is set, and then copy/display the full |
|
1562 |
+ // contents. |
|
1558 | 1563 |
cli_dbgmsg_internal("cert:\n"); |
1559 | 1564 |
cli_dbgmsg_internal(" subject: %s\n", subject); |
1560 | 1565 |
cli_dbgmsg_internal(" serial: %s\n", serial); |
... | ... |
@@ -1593,7 +1598,26 @@ static cl_error_t asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size |
1593 | 1593 |
// in cmgr for this certificate, not a blacklist |
1594 | 1594 |
// entry for this certificate's parent |
1595 | 1595 |
ret = CL_VIRUS; |
1596 |
- cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as virus.\n", (parent->name ? parent->name : "(no name)")); |
|
1596 |
+ cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as a virus.\n", (parent->name ? parent->name : "(no name)")); |
|
1597 |
+ if (NULL != certname) { |
|
1598 |
+ // We need to pass back the certname from a |
|
1599 |
+ // place where it won't go away (and nothing |
|
1600 |
+ // above here knows to free it) so just find it |
|
1601 |
+ // again in the engine->cmgr. |
|
1602 |
+ // TODO Refactor this so that blacklist cert |
|
1603 |
+ // lookups happen against engine->cmgr so we |
|
1604 |
+ // don't have to lookup twice |
|
1605 |
+ if (cmgr != &(engine->cmgr)) { |
|
1606 |
+ parent = crtmgr_verify_crt(&(engine->cmgr), x509); |
|
1607 |
+ } |
|
1608 |
+ |
|
1609 |
+ if (NULL == parent) { |
|
1610 |
+ cli_dbgmsg("asn1_parse_mscat: Unexpected case - blacklist cert can't be found in the global list\n"); |
|
1611 |
+ *certname = "(error fetching name)"; |
|
1612 |
+ } else { |
|
1613 |
+ *certname = parent->name ? parent->name : "(no name)"; |
|
1614 |
+ } |
|
1615 |
+ } |
|
1597 | 1616 |
crtmgr_free(&newcerts); |
1598 | 1617 |
goto finish; |
1599 | 1618 |
} |
... | ... |
@@ -2046,7 +2070,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) |
2046 | 2046 |
// TODO Since we pass engine->cmgr directly here, the whole chain of trust |
2047 | 2047 |
// for this .cat file will get added to the global trust store assuming it |
2048 | 2048 |
// verifies successfully. Is this a bug for a feature? |
2049 |
- if (CL_CLEAN != asn1_parse_mscat(map, 0, map->len, &engine->cmgr, 0, &c.next, &size, engine)) |
|
2049 |
+ if (CL_CLEAN != asn1_parse_mscat(engine, map, 0, map->len, &engine->cmgr, 0, &c.next, &size, NULL)) |
|
2050 | 2050 |
return 1; |
2051 | 2051 |
|
2052 | 2052 |
if (asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE)) |
... | ... |
@@ -2179,6 +2203,9 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) |
2179 | 2179 |
engine->hm_fp->mempool = engine->mempool; |
2180 | 2180 |
#endif |
2181 | 2181 |
} |
2182 |
+ /* Load the trusted hashes into hm_fp, using the size values |
|
2183 |
+ * 1 and 2 as sentinel values corresponding to CAB and PE hashes |
|
2184 |
+ * from .cat files respectively. */ |
|
2182 | 2185 |
if (hm_addhash_bin(engine->hm_fp, tagval3.content, CLI_HASH_SHA1, hashtype, NULL)) { |
2183 | 2186 |
cli_warnmsg("asn1_load_mscat: failed to add hash\n"); |
2184 | 2187 |
return 1; |
... | ... |
@@ -2190,11 +2217,14 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) |
2190 | 2190 |
} |
2191 | 2191 |
|
2192 | 2192 |
/* Check an embedded PE Authenticode section to determine whether it's trusted. |
2193 |
- * This will return CL_CLEAN if the file should be trusted, CL_EPARSE if an |
|
2193 |
+ * This will return CL_VERIFIED if the file should be trusted, CL_EPARSE if an |
|
2194 | 2194 |
* error occurred while parsing the signature, CL_EVERIFY if parsing was |
2195 | 2195 |
* successful but there were no whitelist rules for the signature, and |
2196 |
- * CL_VIRUS if a blacklist rule was found for an embedded certificate. */ |
|
2197 |
-cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions) |
|
2196 |
+ * CL_VIRUS if a blacklist rule was found for an embedded certificate. |
|
2197 |
+ * |
|
2198 |
+ * If CL_VIRUS is returned, certname will be set to the certname of blacklist |
|
2199 |
+ * rule that matched (unless certname is NULL). */ |
|
2200 |
+cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, char **certname) |
|
2198 | 2201 |
{ |
2199 | 2202 |
unsigned int content_size; |
2200 | 2203 |
struct cli_asn1 c; |
... | ... |
@@ -2207,7 +2237,7 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2207 | 2207 |
void *ctx; |
2208 | 2208 |
unsigned int i; |
2209 | 2209 |
|
2210 |
- // TODO Move these into cli_checkfp_pe |
|
2210 |
+ // TODO Move these into cli_check_auth_header |
|
2211 | 2211 |
if (!(engine->dconf->pe & PE_CONF_CERTS)) |
2212 | 2212 |
return CL_EVERIFY; |
2213 | 2213 |
if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) |
... | ... |
@@ -2219,7 +2249,7 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2219 | 2219 |
crtmgr_free(&certs); |
2220 | 2220 |
return CL_EVERIFY; |
2221 | 2221 |
} |
2222 |
- ret = asn1_parse_mscat(map, offset, size, &certs, 1, &content, &content_size, engine); |
|
2222 |
+ ret = asn1_parse_mscat(engine, map, offset, size, &certs, 1, &content, &content_size, certname); |
|
2223 | 2223 |
crtmgr_free(&certs); |
2224 | 2224 |
if (CL_CLEAN != ret) |
2225 | 2225 |
return ret; |
... | ... |
@@ -2288,5 +2318,5 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2288 | 2288 |
} |
2289 | 2289 |
|
2290 | 2290 |
cli_dbgmsg("asn1_check_mscat: file with valid authenticode signature, whitelisted\n"); |
2291 |
- return CL_CLEAN; |
|
2291 |
+ return CL_VERIFIED; |
|
2292 | 2292 |
} |
... | ... |
@@ -31,6 +31,6 @@ struct cli_mapped_region { |
31 | 31 |
}; |
32 | 32 |
|
33 | 33 |
int asn1_load_mscat(fmap_t *map, struct cl_engine *engine); |
34 |
-cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions); |
|
34 |
+cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, char **certname); |
|
35 | 35 |
|
36 | 36 |
#endif |
... | ... |
@@ -107,8 +107,13 @@ cli_crt *crtmgr_blacklist_lookup(crtmgr *m, cli_crt *x509) |
107 | 107 |
// The CRB rules are based on subject, serial, and public key, |
108 | 108 |
// so do blacklist queries based on those fields |
109 | 109 |
|
110 |
+ // TODO the rule format specifies CodeSign / TimeSign / CertSign |
|
111 |
+ // which we could also match on, but we just ignore those fields |
|
112 |
+ // for blacklist certs for now |
|
113 |
+ |
|
110 | 114 |
// TODO Handle the case where these items aren't specified in a CRB |
111 |
- // rule entry - substitute in default values instead. |
|
115 |
+ // rule entry - substitute in default values instead (or make the |
|
116 |
+ // crb parser not permit leaving these fields blank). |
|
112 | 117 |
|
113 | 118 |
if (i->isBlacklisted && |
114 | 119 |
!memcmp(i->subject, x509->subject, sizeof(i->subject)) && |
... | ... |
@@ -407,16 +412,24 @@ cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509) |
407 | 407 |
int score = 0; |
408 | 408 |
unsigned int possible = 0; |
409 | 409 |
|
410 |
+ // For a blacklist cert, if it exists in the signature then that is |
|
411 |
+ // enough to report back a match. We can't whitelist this way, though, |
|
412 |
+ // or an attacker could just include a known-good certificate in the |
|
413 |
+ // signature and just not use it. |
|
410 | 414 |
if (NULL != (i = crtmgr_blacklist_lookup(m, x509))) { |
411 | 415 |
return i; |
412 | 416 |
} |
413 | 417 |
|
418 |
+ // Loop through each of the certificates in our trust store and see whether |
|
419 |
+ // x509 is signed with it. If it is, it's trusted |
|
420 |
+ |
|
414 | 421 |
// TODO Technically we should loop through all of the blacklisted certs |
415 | 422 |
// first to see whether one of those is used to sign x509. This case |
416 | 423 |
// will get handled if the blacklisted certificate is embedded, since we |
417 | 424 |
// will call crtmgr_verify_crt on it and match against the blacklist entry |
418 | 425 |
// that way, but the cert doesn't HAVE to be embedded. This case seems |
419 |
- // unlikely enough to ignore, though. |
|
426 |
+ // unlikely enough to ignore, though. If we ever want to blacklist a |
|
427 |
+ // stolen CA cert or something, then we will need to revisit this. |
|
420 | 428 |
|
421 | 429 |
for (i = m->crts; i; i = i->next) { |
422 | 430 |
if (i->certSign && |
... | ... |
@@ -566,7 +566,7 @@ int cli_checkfp_virus(unsigned char *digest, size_t size, cli_ctx *ctx, const ch |
566 | 566 |
const char *ptr; |
567 | 567 |
uint8_t shash1[SHA1_HASH_SIZE * 2 + 1]; |
568 | 568 |
uint8_t shash256[SHA256_HASH_SIZE * 2 + 1]; |
569 |
- int have_sha1, have_sha256, do_dsig_check = 1; |
|
569 |
+ int have_sha1, have_sha256; |
|
570 | 570 |
|
571 | 571 |
if (cli_hm_scan(digest, size, &virname, ctx->engine->hm_fp, CLI_HASH_MD5) == CL_VIRUS) { |
572 | 572 |
cli_dbgmsg("cli_checkfp(md5): Found false positive detection (fp sig: %s), size: %d\n", virname, (int)size); |
... | ... |
@@ -584,13 +584,8 @@ int cli_checkfp_virus(unsigned char *digest, size_t size, cli_ctx *ctx, const ch |
584 | 584 |
vname ? vname : "Name"); |
585 | 585 |
} |
586 | 586 |
|
587 |
- // TODO Replace this with the ability to actually perform detection with |
|
588 |
- // the blacklisted sig entries |
|
589 |
- if (vname) |
|
590 |
- do_dsig_check = strncmp("W32S.", vname, 5); |
|
591 |
- |
|
592 | 587 |
map = *ctx->fmap; |
593 |
- have_sha1 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA1) || (cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 1) && do_dsig_check); |
|
588 |
+ have_sha1 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA1) || cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 1); |
|
594 | 589 |
have_sha256 = cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA256, size) || cli_hm_have_wild(ctx->engine->hm_fp, CLI_HASH_SHA256); |
595 | 590 |
if (have_sha1 || have_sha256) { |
596 | 591 |
if ((ptr = fmap_need_off_once(map, 0, size))) { |
... | ... |
@@ -605,7 +600,9 @@ int cli_checkfp_virus(unsigned char *digest, size_t size, cli_ctx *ctx, const ch |
605 | 605 |
cli_dbgmsg("cli_checkfp(sha1): Found false positive detection (fp sig: %s)\n", virname); |
606 | 606 |
return CL_CLEAN; |
607 | 607 |
} |
608 |
- if (do_dsig_check && cli_hm_scan(&shash1[SHA1_HASH_SIZE], 1, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { |
|
608 |
+ /* See whether the hash matches those loaded in from .cat files |
|
609 |
+ * (associated with the .CAB file type) */ |
|
610 |
+ if (cli_hm_scan(&shash1[SHA1_HASH_SIZE], 1, &virname, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { |
|
609 | 611 |
cli_dbgmsg("cli_checkfp(sha1): Found false positive detection via catalog file\n"); |
610 | 612 |
return CL_CLEAN; |
611 | 613 |
} |
... | ... |
@@ -653,16 +650,6 @@ int cli_checkfp_virus(unsigned char *digest, size_t size, cli_ctx *ctx, const ch |
653 | 653 |
} |
654 | 654 |
#endif |
655 | 655 |
|
656 |
- if (do_dsig_check) { |
|
657 |
- switch (cli_checkfp_pe(ctx)) { |
|
658 |
- case CL_CLEAN: |
|
659 |
- cli_dbgmsg("cli_checkfp(pe): PE file whitelisted due to valid digital signature\n"); |
|
660 |
- return CL_CLEAN; |
|
661 |
- default: |
|
662 |
- break; |
|
663 |
- } |
|
664 |
- } |
|
665 |
- |
|
666 | 656 |
if (ctx->engine->cb_hash) |
667 | 657 |
ctx->engine->cb_hash(fmap_fd(*ctx->fmap), size, (const unsigned char *)md5, vname ? vname : "noname", ctx->cb_ctx); |
668 | 658 |
|
... | ... |
@@ -918,7 +905,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli |
918 | 918 |
struct cli_target_info info; |
919 | 919 |
fmap_t *map = *ctx->fmap; |
920 | 920 |
struct cli_matcher *hdb, *fp; |
921 |
- const char *virname = NULL; |
|
921 |
+ const char *virname; |
|
922 | 922 |
uint32_t viruses_found = 0; |
923 | 923 |
void *md5ctx, *sha1ctx, *sha256ctx; |
924 | 924 |
|
... | ... |
@@ -987,6 +974,33 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli |
987 | 987 |
"and bytecode sigs that require exe metadata\n"); |
988 | 988 |
} |
989 | 989 |
|
990 |
+ /* If it's a PE, check the Authenticode header. This would be more |
|
991 |
+ * appropriate in cli_scanpe, but cli_scanraw->cli_fmap_scandesc gets |
|
992 |
+ * called first for PEs, and we want to determine the whitelist/blacklist |
|
993 |
+ * status early on so we can skip things like embedded PE extraction |
|
994 |
+ * (which is broken for signed binaries within signed binaries). |
|
995 |
+ * |
|
996 |
+ * If we want to add support for more signature parsing in the future |
|
997 |
+ * (Ex: MachO sigs), do that here too. */ |
|
998 |
+ if (1 == info.status && i == 1) { |
|
999 |
+ char *certname = NULL; |
|
1000 |
+ ret = cli_check_auth_header(ctx, &(info.exeinfo), &certname); |
|
1001 |
+ |
|
1002 |
+ if (CL_VIRUS == ret) { |
|
1003 |
+ ret = cli_append_virus(ctx, certname); |
|
1004 |
+ } |
|
1005 |
+ |
|
1006 |
+ if ((ret == CL_VIRUS || ret == CL_VERIFIED) && !SCAN_ALLMATCHES) { |
|
1007 |
+ cli_targetinfo_destroy(&info); |
|
1008 |
+ cl_hash_destroy(md5ctx); |
|
1009 |
+ cl_hash_destroy(sha1ctx); |
|
1010 |
+ cl_hash_destroy(sha256ctx); |
|
1011 |
+ return ret; |
|
1012 |
+ } |
|
1013 |
+ |
|
1014 |
+ ret = CL_CLEAN; |
|
1015 |
+ } |
|
1016 |
+ |
|
990 | 1017 |
if (!ftonly) { |
991 | 1018 |
if ((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) || (ret = cli_ac_caloff(groot, &gdata, &info))) { |
992 | 1019 |
cli_targetinfo_destroy(&info); |
... | ... |
@@ -282,6 +282,8 @@ const char *cl_strerror(int clerror) |
282 | 282 |
return "Scanner still active"; |
283 | 283 |
case CL_ESTATE: |
284 | 284 |
return "Bad state (engine not initialized, or already initialized)"; |
285 |
+ case CL_VERIFIED: |
|
286 |
+ return "The scanned object was verified and deemed trusted"; |
|
285 | 287 |
default: |
286 | 288 |
return "Unknown error code"; |
287 | 289 |
} |
... | ... |
@@ -2,7 +2,7 @@ |
2 | 2 |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
3 | 3 |
* Copyright (C) 2007-2013 Sourcefire, Inc. |
4 | 4 |
* |
5 |
- * Authors: Alberto Wu, Tomasz Kojm |
|
5 |
+ * Authors: Alberto Wu, Tomasz Kojm, Andrew Williams |
|
6 | 6 |
* |
7 | 7 |
* Acknowledgements: The header structures were based upon a PE format |
8 | 8 |
* analysis by B. Luevelsmeyer. |
... | ... |
@@ -5505,11 +5505,16 @@ static int sort_sects(const void *first, const void *second) |
5505 | 5505 |
* - A PE file has an embedded Authenticode section |
5506 | 5506 |
* - The PE file has no embedded Authenticode section but is covered by a |
5507 | 5507 |
* catalog file that was loaded in via a -d |
5508 |
- * CL_CLEAN will be returned if the file was whitelisted based on its |
|
5508 |
+ * |
|
5509 |
+ * If peinfo is NULL, one will be created internally and used |
|
5510 |
+ * |
|
5511 |
+ * CL_VERIFIED will be returned if the file was whitelisted based on its |
|
5509 | 5512 |
* signature. CL_VIRUS will be returned if the file was blacklisted based on |
5510 | 5513 |
* its signature. Otherwise, an cl_error_t error value will be returned. |
5511 |
- */ |
|
5512 |
-cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
|
5514 |
+ * |
|
5515 |
+ * If CL_VIRUS is returned, certname will be set to the certname of blacklist |
|
5516 |
+ * rule that matched (unless certname is NULL). */ |
|
5517 |
+cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char **certname) |
|
5513 | 5518 |
{ |
5514 | 5519 |
size_t at; |
5515 | 5520 |
unsigned int i, hlen; |
... | ... |
@@ -5524,7 +5529,6 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
5524 | 5524 |
uint32_t sec_dir_offset; |
5525 | 5525 |
uint32_t sec_dir_size; |
5526 | 5526 |
struct cli_exe_info _peinfo; |
5527 |
- struct cli_exe_info *peinfo = &_peinfo; |
|
5528 | 5527 |
|
5529 | 5528 |
// If Authenticode parsing has been disabled via DCONF, then don't |
5530 | 5529 |
// continue on. |
... | ... |
@@ -5533,12 +5537,15 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
5533 | 5533 |
if (!(DCONF & PE_CONF_CATALOG)) |
5534 | 5534 |
return CL_EFORMAT; |
5535 | 5535 |
|
5536 |
- // TODO see if peinfo can be passed in (or lives in ctx or something) and |
|
5537 |
- // if so, use that to avoid having to re-parse the header |
|
5538 |
- cli_exe_info_init(peinfo, 0); |
|
5536 |
+ // If peinfo is NULL, initialize one. This makes it so that this function |
|
5537 |
+ // can be used easily by sigtool |
|
5538 |
+ if (NULL == peinfo) { |
|
5539 |
+ peinfo = &_peinfo; |
|
5540 |
+ cli_exe_info_init(peinfo, 0); |
|
5539 | 5541 |
|
5540 |
- if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) { |
|
5541 |
- return CL_EFORMAT; |
|
5542 |
+ if (cli_peheader(*ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) { |
|
5543 |
+ return CL_EFORMAT; |
|
5544 |
+ } |
|
5542 | 5545 |
} |
5543 | 5546 |
|
5544 | 5547 |
sec_dir_offset = EC32(peinfo->dirs[4].VirtualAddress); |
... | ... |
@@ -5549,21 +5556,20 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
5549 | 5549 |
// the section hashes), bail out if we don't have any Authenticode hashes |
5550 | 5550 |
// loaded from .cat files |
5551 | 5551 |
if (sec_dir_size < 8 && !cli_hm_have_size(ctx->engine->hm_fp, CLI_HASH_SHA1, 2)) { |
5552 |
- cli_exe_info_destroy(peinfo); |
|
5553 |
- return CL_BREAK; |
|
5552 |
+ ret = CL_BREAK; |
|
5553 |
+ goto finish; |
|
5554 | 5554 |
} |
5555 | 5555 |
fsize = map->len; |
5556 | 5556 |
|
5557 |
- do { |
|
5558 |
- // We'll build a list of the regions that need to be hashed and pass it to |
|
5559 |
- // asn1_check_mscat to do hash verification there (the hash algorithm is |
|
5560 |
- // specified in the PKCS7 structure). We need to hash up to 4 regions |
|
5561 |
- regions = (struct cli_mapped_region *)cli_calloc(4, sizeof(struct cli_mapped_region)); |
|
5562 |
- if (!regions) { |
|
5563 |
- cli_exe_info_destroy(peinfo); |
|
5564 |
- return CL_EMEM; |
|
5565 |
- } |
|
5566 |
- nregions = 0; |
|
5557 |
+ // We'll build a list of the regions that need to be hashed and pass it to |
|
5558 |
+ // asn1_check_mscat to do hash verification there (the hash algorithm is |
|
5559 |
+ // specified in the PKCS7 structure). We need to hash up to 4 regions |
|
5560 |
+ regions = (struct cli_mapped_region *)cli_calloc(4, sizeof(struct cli_mapped_region)); |
|
5561 |
+ if (!regions) { |
|
5562 |
+ ret = CL_EMEM; |
|
5563 |
+ goto finish; |
|
5564 |
+ } |
|
5565 |
+ nregions = 0; |
|
5567 | 5566 |
|
5568 | 5567 |
#define add_chunk_to_hash_list(_offset, _size) \ |
5569 | 5568 |
do { \ |
... | ... |
@@ -5572,74 +5578,74 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
5572 | 5572 |
nregions++; \ |
5573 | 5573 |
} while (0) |
5574 | 5574 |
|
5575 |
- // Pretty much every case below should return CL_EFORMAT |
|
5576 |
- ret = CL_EFORMAT; |
|
5575 |
+ // Pretty much every case below should return CL_EFORMAT |
|
5576 |
+ ret = CL_EFORMAT; |
|
5577 | 5577 |
|
5578 |
- /* MZ to checksum */ |
|
5579 |
- at = 0; |
|
5580 |
- hlen = peinfo->e_lfanew + sizeof(struct pe_image_file_hdr) + (peinfo->is_pe32plus ? offsetof(struct pe_image_optional_hdr64, CheckSum) : offsetof(struct pe_image_optional_hdr32, CheckSum)); |
|
5581 |
- add_chunk_to_hash_list(0, hlen); |
|
5582 |
- at = hlen + 4; |
|
5578 |
+ /* MZ to checksum */ |
|
5579 |
+ at = 0; |
|
5580 |
+ hlen = peinfo->e_lfanew + sizeof(struct pe_image_file_hdr) + (peinfo->is_pe32plus ? offsetof(struct pe_image_optional_hdr64, CheckSum) : offsetof(struct pe_image_optional_hdr32, CheckSum)); |
|
5581 |
+ add_chunk_to_hash_list(0, hlen); |
|
5582 |
+ at = hlen + 4; |
|
5583 | 5583 |
|
5584 |
- /* Checksum to security */ |
|
5585 |
- if (peinfo->is_pe32plus) |
|
5586 |
- hlen = sizeof(struct pe_image_optional_hdr64) - offsetof(struct pe_image_optional_hdr64, CheckSum) - 4; |
|
5587 |
- else |
|
5588 |
- hlen = sizeof(struct pe_image_optional_hdr32) - offsetof(struct pe_image_optional_hdr32, CheckSum) - 4; |
|
5584 |
+ /* Checksum to security */ |
|
5585 |
+ if (peinfo->is_pe32plus) |
|
5586 |
+ hlen = sizeof(struct pe_image_optional_hdr64) - offsetof(struct pe_image_optional_hdr64, CheckSum) - 4; |
|
5587 |
+ else |
|
5588 |
+ hlen = sizeof(struct pe_image_optional_hdr32) - offsetof(struct pe_image_optional_hdr32, CheckSum) - 4; |
|
5589 | 5589 |
|
5590 |
- hlen += sizeof(struct pe_image_data_dir) * 4; |
|
5591 |
- add_chunk_to_hash_list(at, hlen); |
|
5592 |
- at += hlen + 8; |
|
5590 |
+ hlen += sizeof(struct pe_image_data_dir) * 4; |
|
5591 |
+ add_chunk_to_hash_list(at, hlen); |
|
5592 |
+ at += hlen + 8; |
|
5593 | 5593 |
|
5594 |
- if (at > peinfo->hdr_size) { |
|
5595 |
- break; |
|
5596 |
- } |
|
5594 |
+ if (at > peinfo->hdr_size) { |
|
5595 |
+ goto finish; |
|
5596 |
+ } |
|
5597 | 5597 |
|
5598 |
- /* Security to End of header */ |
|
5599 |
- hlen = peinfo->hdr_size - at; |
|
5600 |
- add_chunk_to_hash_list(at, hlen); |
|
5601 |
- at += hlen; |
|
5598 |
+ /* Security to End of header */ |
|
5599 |
+ hlen = peinfo->hdr_size - at; |
|
5600 |
+ add_chunk_to_hash_list(at, hlen); |
|
5601 |
+ at += hlen; |
|
5602 | 5602 |
|
5603 |
- if (sec_dir_offset) { |
|
5603 |
+ if (sec_dir_offset) { |
|
5604 | 5604 |
|
5605 |
- // Verify that we have all the bytes we expect in the authenticode sig |
|
5606 |
- // and that the certificate table is the last thing in the file |
|
5607 |
- // (according to the MS13-098 bulletin, this is a requirement) |
|
5608 |
- if (fsize != sec_dir_size + sec_dir_offset) { |
|
5609 |
- cli_dbgmsg("cli_checkfp_pe: expected authenticode data at the end of the file\n"); |
|
5610 |
- break; |
|
5611 |
- } |
|
5605 |
+ // Verify that we have all the bytes we expect in the authenticode sig |
|
5606 |
+ // and that the certificate table is the last thing in the file |
|
5607 |
+ // (according to the MS13-098 bulletin, this is a requirement) |
|
5608 |
+ if (fsize != sec_dir_size + sec_dir_offset) { |
|
5609 |
+ cli_dbgmsg("cli_check_auth_header: expected authenticode data at the end of the file\n"); |
|
5610 |
+ goto finish; |
|
5611 |
+ } |
|
5612 | 5612 |
|
5613 |
- // Hash everything from the end of the header to the start of the |
|
5614 |
- // security section |
|
5615 |
- if (at < sec_dir_offset) { |
|
5616 |
- hlen = sec_dir_offset - at; |
|
5617 |
- add_chunk_to_hash_list(at, hlen); |
|
5618 |
- } else { |
|
5619 |
- cli_dbgmsg("cli_checkfp_pe: security directory offset appears to overlap with the PE header\n"); |
|
5620 |
- break; |
|
5621 |
- } |
|
5613 |
+ // Hash everything from the end of the header to the start of the |
|
5614 |
+ // security section |
|
5615 |
+ if (at < sec_dir_offset) { |
|
5616 |
+ hlen = sec_dir_offset - at; |
|
5617 |
+ add_chunk_to_hash_list(at, hlen); |
|
5618 |
+ } else { |
|
5619 |
+ cli_dbgmsg("cli_check_auth_header: security directory offset appears to overlap with the PE header\n"); |
|
5620 |
+ goto finish; |
|
5621 |
+ } |
|
5622 | 5622 |
|
5623 |
- // Parse the security directory header |
|
5623 |
+ // Parse the security directory header |
|
5624 | 5624 |
|
5625 |
- if (fmap_readn(map, &cert_hdr, sec_dir_offset, sizeof(cert_hdr)) != sizeof(cert_hdr)) { |
|
5626 |
- break; |
|
5627 |
- } |
|
5625 |
+ if (fmap_readn(map, &cert_hdr, sec_dir_offset, sizeof(cert_hdr)) != sizeof(cert_hdr)) { |
|
5626 |
+ goto finish; |
|
5627 |
+ } |
|
5628 | 5628 |
|
5629 |
- if (EC16(cert_hdr.revision) != WIN_CERT_REV_2) { |
|
5630 |
- cli_dbgmsg("cli_checkfp_pe: unsupported authenticode data revision\n"); |
|
5631 |
- break; |
|
5632 |
- } |
|
5629 |
+ if (EC16(cert_hdr.revision) != WIN_CERT_REV_2) { |
|
5630 |
+ cli_dbgmsg("cli_check_auth_header: unsupported authenticode data revision\n"); |
|
5631 |
+ goto finish; |
|
5632 |
+ } |
|
5633 | 5633 |
|
5634 |
- if (EC16(cert_hdr.type) != WIN_CERT_TYPE_PKCS7) { |
|
5635 |
- cli_dbgmsg("cli_checkfp_pe: unsupported authenticode data type\n"); |
|
5636 |
- break; |
|
5637 |
- } |
|
5634 |
+ if (EC16(cert_hdr.type) != WIN_CERT_TYPE_PKCS7) { |
|
5635 |
+ cli_dbgmsg("cli_check_auth_header: unsupported authenticode data type\n"); |
|
5636 |
+ goto finish; |
|
5637 |
+ } |
|
5638 | 5638 |
|
5639 |
- hlen = sec_dir_size; |
|
5639 |
+ hlen = sec_dir_size; |
|
5640 | 5640 |
|
5641 |
- if (EC32(cert_hdr.length) != hlen) { |
|
5642 |
- /* This is the case that MS13-098 aimed to address, but it got |
|
5641 |
+ if (EC32(cert_hdr.length) != hlen) { |
|
5642 |
+ /* This is the case that MS13-098 aimed to address, but it got |
|
5643 | 5643 |
* pushback to where the fix (not allowing additional, non-zero |
5644 | 5644 |
* bytes in the security directory) is now opt-in via a registry |
5645 | 5645 |
* key. Given that most machines will treat these binaries as |
... | ... |
@@ -5647,76 +5653,74 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
5647 | 5647 |
* our whitelist signatures are tailored enough to where any |
5648 | 5648 |
* instances of this are reasonable (for instance, I saw one |
5649 | 5649 |
* binary that appeared to use this to embed a license key.) */ |
5650 |
- cli_dbgmsg("cli_checkfp_pe: MS13-098 violation detected, but continuing on to verify certificate\n"); |
|
5651 |
- } |
|
5652 |
- |
|
5653 |
- at = sec_dir_offset + sizeof(cert_hdr); |
|
5654 |
- hlen -= sizeof(cert_hdr); |
|
5650 |
+ cli_dbgmsg("cli_check_auth_header: MS13-098 violation detected, but continuing on to verify certificate\n"); |
|
5651 |
+ } |
|
5655 | 5652 |
|
5656 |
- ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions); |
|
5653 |
+ at = sec_dir_offset + sizeof(cert_hdr); |
|
5654 |
+ hlen -= sizeof(cert_hdr); |
|
5657 | 5655 |
|
5658 |
- if (CL_CLEAN == ret) { |
|
5659 |
- // We validated the embedded signature. Hooray! |
|
5660 |
- break; |
|
5661 |
- } else if (CL_VIRUS == ret) { |
|
5662 |
- // A blacklist rule hit - don't continue on to check hm_fp for a match |
|
5663 |
- break; |
|
5664 |
- } |
|
5656 |
+ ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, certname); |
|
5665 | 5657 |
|
5666 |
- // Otherwise, we still need to check to see whether this file is |
|
5667 |
- // covered by a .cat file (it's common these days for driver files |
|
5668 |
- // to have .cat files covering PEs with embedded signatures) |
|
5658 |
+ if (CL_VERIFIED == ret) { |
|
5659 |
+ // We validated the embedded signature. Hooray! |
|
5660 |
+ goto finish; |
|
5661 |
+ } else if (CL_VIRUS == ret) { |
|
5662 |
+ // A blacklist rule hit - don't continue on to check hm_fp for a match |
|
5663 |
+ goto finish; |
|
5664 |
+ } |
|
5669 | 5665 |
|
5670 |
- } else { |
|
5666 |
+ // Otherwise, we still need to check to see whether this file is |
|
5667 |
+ // covered by a .cat file (it's common these days for driver files |
|
5668 |
+ // to have .cat files covering PEs with embedded signatures) |
|
5671 | 5669 |
|
5672 |
- // Hash everything from the end of the header to the end of the |
|
5673 |
- // file |
|
5674 |
- if (at < fsize) { |
|
5675 |
- hlen = fsize - at; |
|
5676 |
- add_chunk_to_hash_list(at, hlen); |
|
5677 |
- } |
|
5678 |
- } |
|
5670 |
+ } else { |
|
5679 | 5671 |
|
5680 |
- // At this point we should compute the SHA1 authenticode hash to see |
|
5681 |
- // whether we've had any hashes added from external catalog files |
|
5682 |
- // TODO Is it gauranteed that the hashing algorithm will be SHA1? If |
|
5683 |
- // not, figure out how to handle that case |
|
5684 |
- hashctx = cl_hash_init("sha1"); |
|
5685 |
- if (NULL == hashctx) { |
|
5686 |
- ret = CL_EMEM; |
|
5687 |
- break; |
|
5672 |
+ // Hash everything from the end of the header to the end of the |
|
5673 |
+ // file |
|
5674 |
+ if (at < fsize) { |
|
5675 |
+ hlen = fsize - at; |
|
5676 |
+ add_chunk_to_hash_list(at, hlen); |
|
5688 | 5677 |
} |
5678 |
+ } |
|
5689 | 5679 |
|
5690 |
- for (i = 0; i < nregions; i++) { |
|
5691 |
- const uint8_t *hptr; |
|
5692 |
- if (0 == regions[i].size) { |
|
5693 |
- continue; |
|
5694 |
- } |
|
5695 |
- if (!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))) { |
|
5696 |
- break; |
|
5697 |
- } |
|
5680 |
+ // At this point we should compute the SHA1 authenticode hash to see |
|
5681 |
+ // whether we've had any hashes added from external catalog files |
|
5682 |
+ // TODO Is it gauranteed that the hashing algorithm will be SHA1? If |
|
5683 |
+ // not, figure out how to handle that case |
|
5684 |
+ hashctx = cl_hash_init("sha1"); |
|
5685 |
+ if (NULL == hashctx) { |
|
5686 |
+ ret = CL_EMEM; |
|
5687 |
+ goto finish; |
|
5688 |
+ } |
|
5698 | 5689 |
|
5699 |
- cl_update_hash(hashctx, hptr, regions[i].size); |
|
5690 |
+ for (i = 0; i < nregions; i++) { |
|
5691 |
+ const uint8_t *hptr; |
|
5692 |
+ if (0 == regions[i].size) { |
|
5693 |
+ continue; |
|
5700 | 5694 |
} |
5701 |
- |
|
5702 |
- if (i != nregions) { |
|
5695 |
+ if (!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))) { |
|
5703 | 5696 |
break; |
5704 | 5697 |
} |
5705 | 5698 |
|
5706 |
- cl_finish_hash(hashctx, authsha1); |
|
5707 |
- hashctx = NULL; |
|
5699 |
+ cl_update_hash(hashctx, hptr, regions[i].size); |
|
5700 |
+ } |
|
5708 | 5701 |
|
5709 |
- if (cli_hm_scan(authsha1, 2, NULL, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { |
|
5710 |
- cli_dbgmsg("cli_checkfp_pe: PE file whitelisted by catalog file\n"); |
|
5711 |
- ret = CL_CLEAN; |
|
5712 |
- break; |
|
5713 |
- } |
|
5702 |
+ if (i != nregions) { |
|
5703 |
+ goto finish; |
|
5704 |
+ } |
|
5714 | 5705 |
|
5715 |
- ret = CL_EVERIFY; |
|
5716 |
- break; |
|
5706 |
+ cl_finish_hash(hashctx, authsha1); |
|
5707 |
+ hashctx = NULL; |
|
5708 |
+ |
|
5709 |
+ if (cli_hm_scan(authsha1, 2, NULL, ctx->engine->hm_fp, CLI_HASH_SHA1) == CL_VIRUS) { |
|
5710 |
+ cli_dbgmsg("cli_check_auth_header: PE file whitelisted by catalog file\n"); |
|
5711 |
+ ret = CL_CLEAN; |
|
5712 |
+ goto finish; |
|
5713 |
+ } |
|
5717 | 5714 |
|
5718 |
- } while (0); |
|
5715 |
+ ret = CL_EVERIFY; |
|
5719 | 5716 |
|
5717 |
+finish: |
|
5720 | 5718 |
if (NULL != hashctx) { |
5721 | 5719 |
cl_hash_destroy(hashctx); |
5722 | 5720 |
} |
... | ... |
@@ -5725,7 +5729,10 @@ cl_error_t cli_checkfp_pe(cli_ctx *ctx) |
5725 | 5725 |
free(regions); |
5726 | 5726 |
} |
5727 | 5727 |
|
5728 |
- cli_exe_info_destroy(peinfo); |
|
5728 |
+ // If we created the peinfo, then destroy it. Otherwise we don't own it |
|
5729 |
+ if (&_peinfo == peinfo) { |
|
5730 |
+ cli_exe_info_destroy(peinfo); |
|
5731 |
+ } |
|
5729 | 5732 |
return ret; |
5730 | 5733 |
} |
5731 | 5734 |
|
... | ... |
@@ -2,7 +2,7 @@ |
2 | 2 |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
3 | 3 |
* Copyright (C) 2007-2013 Sourcefire, Inc. |
4 | 4 |
* |
5 |
- * Authors: Alberto Wu, Tomasz Kojm |
|
5 |
+ * Authors: Alberto Wu, Tomasz Kojm, Andrew Williams |
|
6 | 6 |
* |
7 | 7 |
* Acknowledgements: The header structures were based upon a PE format |
8 | 8 |
* analysis by B. Luevelsmeyer. |
... | ... |
@@ -98,7 +98,7 @@ enum { |
98 | 98 |
int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo); |
99 | 99 |
int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx); |
100 | 100 |
|
101 |
-cl_error_t cli_checkfp_pe(cli_ctx *ctx); |
|
101 |
+cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char **certname); |
|
102 | 102 |
int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_section_t *hashes); |
103 | 103 |
|
104 | 104 |
uint32_t cli_rawaddr(uint32_t, const struct cli_exe_section *, uint16_t, unsigned int *, size_t, uint32_t); |
... | ... |
@@ -2402,6 +2402,9 @@ static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_ |
2402 | 2402 |
ret = cli_fmap_scandesc(ctx, type == CL_TYPE_TEXT_ASCII ? 0 : type, 0, &ftoffset, acmode, NULL, refhash); |
2403 | 2403 |
perf_stop(ctx, PERFT_RAW); |
2404 | 2404 |
|
2405 |
+ // TODO I think this causes embedded file extraction to stop when a |
|
2406 |
+ // signature has matched in cli_fmap_scandesc, which wouldn't be what |
|
2407 |
+ // we want if allmatch is specified. |
|
2405 | 2408 |
if (ret >= CL_TYPENO) { |
2406 | 2409 |
perf_nested_start(ctx, PERFT_RAWTYPENO, PERFT_SCAN); |
2407 | 2410 |
ctx->recursion++; |
... | ... |
@@ -2625,6 +2628,25 @@ static int cli_scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_file_ |
2625 | 2625 |
* be found recursively |
2626 | 2626 |
* through the above call |
2627 | 2627 |
*/ |
2628 |
+ // TODO This method of embedded PE extraction |
|
2629 |
+ // is kinda gross in that: |
|
2630 |
+ // - if you have an executable that contains |
|
2631 |
+ // 20 other exes, the bytes associated with |
|
2632 |
+ // the last exe will have been included in |
|
2633 |
+ // hash computations and things 20 times |
|
2634 |
+ // (as overlay data to the previously |
|
2635 |
+ // extracted exes). |
|
2636 |
+ // - if you have a signed embedded exe, it |
|
2637 |
+ // will fail to validate after extraction |
|
2638 |
+ // bc it has overlay data, which is a |
|
2639 |
+ // violation of the Authenticode spec. |
|
2640 |
+ // - this method of extraction is subject to |
|
2641 |
+ // the recursion limit, which is fairly |
|
2642 |
+ // low by default (I think 16) |
|
2643 |
+ // |
|
2644 |
+ // It'd be awesome if we could compute the PE |
|
2645 |
+ // size from the PE header and just extract |
|
2646 |
+ // that. |
|
2628 | 2647 |
} |
2629 | 2648 |
} |
2630 | 2649 |
break; |
... | ... |
@@ -3403,7 +3425,15 @@ static int magic_scandesc(cli_ctx *ctx, cli_file_t type) |
3403 | 3403 |
cli_bitset_free(ctx->hook_lsig_matches); |
3404 | 3404 |
ctx->hook_lsig_matches = old_hook_lsig_matches; |
3405 | 3405 |
return magic_scandesc_cleanup(ctx, type, hash, hashed_size, cache_clean, res, parent_property); |
3406 |
- /* CL_VIRUS = malware found, check FP and report */ |
|
3406 |
+ /* CL_VIRUS = malware found, check FP and report. |
|
3407 |
+ * Likewise, if the file was determined to be trusted, then we |
|
3408 |
+ * can also finish with the scan. (Ex: EXE with a valid |
|
3409 |
+ * Authenticode sig.) */ |
|
3410 |
+ case CL_VERIFIED: |
|
3411 |
+ // For now just conver CL_VERIFIED to CL_CLEAN, since |
|
3412 |
+ // CL_VERIFIED isn't used elsewhere |
|
3413 |
+ res = CL_CLEAN; |
|
3414 |
+ // Fall through |
|
3407 | 3415 |
case CL_VIRUS: |
3408 | 3416 |
ret = res; |
3409 | 3417 |
if (SCAN_ALLMATCHES) |
... | ... |
@@ -3455,26 +3455,25 @@ static int dumpcerts(const struct optstruct *opts) |
3455 | 3455 |
return -1; |
3456 | 3456 |
} |
3457 | 3457 |
|
3458 |
- ret = cli_checkfp_pe(&ctx); |
|
3458 |
+ ret = cli_check_auth_header(&ctx, NULL, NULL); |
|
3459 | 3459 |
|
3460 | 3460 |
switch (ret) { |
3461 |
- case CL_CLEAN: |
|
3462 |
- mprintf("*dumpcerts: CL_CLEAN after cli_checkfp_pe()!\n"); |
|
3463 |
- break; |
|
3461 |
+ case CL_VERIFIED: |
|
3464 | 3462 |
case CL_VIRUS: |
3465 |
- mprintf("*dumpcerts: CL_VIRUS after cli_checkfp_pe()!\n"); |
|
3466 |
- break; |
|
3467 |
- case CL_BREAK: |
|
3468 |
- mprintf("*dumpcerts: CL_BREAK after cli_checkfp_pe()!\n"); |
|
3463 |
+ // These shouldn't happen, since sigtool doesn't load in any sigs |
|
3469 | 3464 |
break; |
3470 | 3465 |
case CL_EVERIFY: |
3471 |
- mprintf("!dumpcerts: CL_EVERIFY after cli_checkfp_pe()!\n"); |
|
3466 |
+ // The Authenticode header was parsed successfully but there were |
|
3467 |
+ // no applicable whitelist/blacklist rules |
|
3468 |
+ break; |
|
3469 |
+ case CL_BREAK: |
|
3470 |
+ mprintf("*dumpcerts: No Authenticode signature detected\n"); |
|
3472 | 3471 |
break; |
3473 | 3472 |
case CL_EFORMAT: |
3474 | 3473 |
mprintf("!dumpcerts: An error occurred when parsing the file\n"); |
3475 | 3474 |
break; |
3476 | 3475 |
default: |
3477 |
- mprintf("!dumpcerts: Other error %d inside cli_checkfp_pe.\n", ret); |
|
3476 |
+ mprintf("!dumpcerts: Other error %d inside cli_check_auth_header.\n", ret); |
|
3478 | 3477 |
break; |
3479 | 3478 |
} |
3480 | 3479 |
|
... | ... |
@@ -163,7 +163,7 @@ EXPORTS cli_fmap_scandesc @44265 NONAME |
163 | 163 |
EXPORTS cli_hashset_destroy @44266 NONAME |
164 | 164 |
EXPORTS cli_detect_environment @44267 NONAME |
165 | 165 |
EXPORTS cli_filecopy @44268 NONAME |
166 |
-EXPORTS cli_checkfp_pe @44369 NONAME |
|
166 |
+EXPORTS cli_check_auth_header @44369 NONAME |
|
167 | 167 |
EXPORTS cli_bytefunc_describe @44370 NONAME |
168 | 168 |
EXPORTS cli_bytetype_describe @44371 NONAME |
169 | 169 |
EXPORTS cli_bytevalue_describe @44372 NONAME |