Add Authentihash support for PE (#15987) ##bin
* Add Certificate Table parser to PE plugin * Add SpcIndirectDataContent ASN.1 structure parser * Add Authentihash calculation and check * Refactor r_bin_file_hash * Add tests for Authentihash check
This commit is contained in:
parent
e71cd20268
commit
2c6fc43b7e
|
@ -856,54 +856,37 @@ R_API bool r_bin_file_close(RBin *bin, int bd) {
|
|||
return false;
|
||||
}
|
||||
|
||||
R_API bool r_bin_file_hash(RBin *bin, ut64 limit, const char *file, RList/*<RBinFileHash>*/ **old_file_hashes) {
|
||||
r_return_val_if_fail (bin, false);
|
||||
R_API RList *r_bin_file_hash(RBin *bin, ut64 limit) {
|
||||
r_return_val_if_fail (bin, NULL);
|
||||
|
||||
char hash[128];
|
||||
RHash *ctx;
|
||||
ut64 buf_len = 0, r = 0;
|
||||
RBinFile *bf = bin->cur;
|
||||
if (!bf) {
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
RBinObject *o = bf->o;
|
||||
if (!o || !o->info) {
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
RIODesc *iod = r_io_desc_get (bin->iob.io, bf->fd);
|
||||
if (!iod) {
|
||||
return false;
|
||||
}
|
||||
if (!file && iod) {
|
||||
file = iod->name;
|
||||
return NULL;
|
||||
}
|
||||
buf_len = r_io_desc_size (iod);
|
||||
// By SLURP_LIMIT normally cannot compute ...
|
||||
if (buf_len > limit) {
|
||||
if (old_file_hashes) {
|
||||
// if (bin->verbose) {
|
||||
eprintf ("Warning: r_bin_file_hash: file exceeds bin.hashlimit\n");
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
eprintf ("Warning: r_bin_file_hash: file exceeds bin.hashlimit\n");
|
||||
return NULL;
|
||||
}
|
||||
const size_t blocksize = 64000;
|
||||
ut8 *buf = malloc (blocksize);
|
||||
if (!buf) {
|
||||
eprintf ("Cannot allocate computation buffer\n");
|
||||
return false;
|
||||
}
|
||||
if (old_file_hashes) {
|
||||
*old_file_hashes = NULL;
|
||||
}
|
||||
if (!r_list_empty (o->info->file_hashes)) {
|
||||
if (old_file_hashes && o->info->file_hashes) {
|
||||
*old_file_hashes = o->info->file_hashes;
|
||||
} else {
|
||||
r_list_free (o->info->file_hashes);
|
||||
}
|
||||
o->info->file_hashes = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx = r_hash_new (false, R_HASH_MD5 | R_HASH_SHA1 | R_HASH_SHA256);
|
||||
while (r + blocksize < buf_len) {
|
||||
r_io_desc_seek (iod, r, R_IO_SEEK_SET);
|
||||
|
@ -928,12 +911,12 @@ R_API bool r_bin_file_hash(RBin *bin, ut64 limit, const char *file, RList/*<RBin
|
|||
r_hash_do_end (ctx, R_HASH_MD5);
|
||||
r_hex_bin2str (ctx->digest, R_HASH_SIZE_MD5, hash);
|
||||
|
||||
o->info->file_hashes = r_list_newf ((RListFree) r_bin_file_hash_free);
|
||||
RList *file_hashes = r_list_newf ((RListFree) r_bin_file_hash_free);
|
||||
RBinFileHash *md5h = R_NEW0 (RBinFileHash);
|
||||
if (md5h) {
|
||||
md5h->type = strdup ("md5");
|
||||
md5h->hex = strdup (hash);
|
||||
r_list_push (o->info->file_hashes, md5h);
|
||||
r_list_push (file_hashes, md5h);
|
||||
}
|
||||
r_hash_do_end (ctx, R_HASH_SHA1);
|
||||
r_hex_bin2str (ctx->digest, R_HASH_SIZE_SHA1, hash);
|
||||
|
@ -942,7 +925,7 @@ R_API bool r_bin_file_hash(RBin *bin, ut64 limit, const char *file, RList/*<RBin
|
|||
if (sha1h) {
|
||||
sha1h->type = strdup ("sha1");
|
||||
sha1h->hex = strdup (hash);
|
||||
r_list_push (o->info->file_hashes, sha1h);
|
||||
r_list_push (file_hashes, sha1h);
|
||||
}
|
||||
r_hash_do_end (ctx, R_HASH_SHA256);
|
||||
r_hex_bin2str (ctx->digest, R_HASH_SIZE_SHA256, hash);
|
||||
|
@ -951,13 +934,13 @@ R_API bool r_bin_file_hash(RBin *bin, ut64 limit, const char *file, RList/*<RBin
|
|||
if (sha256h) {
|
||||
sha256h->type = strdup ("sha256");
|
||||
sha256h->hex = strdup (hash);
|
||||
r_list_push (o->info->file_hashes, sha256h);
|
||||
r_list_push (file_hashes, sha256h);
|
||||
}
|
||||
// TODO: add here more rows
|
||||
|
||||
free (buf);
|
||||
r_hash_free (ctx);
|
||||
return true;
|
||||
return file_hashes;
|
||||
}
|
||||
|
||||
R_IPI RBinClass *r_bin_class_new(const char *name, const char *super, int view) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <r_hash.h>
|
||||
#include <r_types.h>
|
||||
#include <r_util.h>
|
||||
#include "pe.h"
|
||||
|
@ -838,6 +839,66 @@ int PE_(bin_pe_get_actual_checksum)(struct PE_(r_bin_pe_obj_t)* bin) {
|
|||
return computed_cs;
|
||||
}
|
||||
|
||||
static char* PE_(bin_pe_get_claimed_authentihash)(struct PE_(r_bin_pe_obj_t)* bin) {
|
||||
if (!bin->spcinfo) {
|
||||
return NULL;
|
||||
}
|
||||
RASN1Binary *digest = bin->spcinfo->messageDigest.digest;
|
||||
return r_hex_bin2strdup (digest->binary, digest->length);
|
||||
}
|
||||
|
||||
static char* PE_(bin_pe_get_actual_authentihash)(struct PE_(r_bin_pe_obj_t)* bin) {
|
||||
if (!bin->spcinfo) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *hashtype = bin->spcinfo->messageDigest.digestAlgorithm.algorithm->string;
|
||||
ut64 algobit = r_hash_name_to_bits (hashtype);
|
||||
if (!(algobit & (R_HASH_MD5 | R_HASH_SHA1))) {
|
||||
eprintf ("Authenticode only supports md5, sha1.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut32 checksum_paddr = bin->nt_header_offset + 4 + sizeof (PE_(image_file_header)) + 0x40;
|
||||
ut32 security_entry_offset = bin->nt_header_offset + sizeof (PE_(image_nt_headers)) - 96;
|
||||
PE_(image_data_directory) *data_dir_security = &bin->data_directory[PE_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
PE_DWord security_dir_offset = data_dir_security->VirtualAddress;
|
||||
ut32 security_dir_size = data_dir_security->Size;
|
||||
|
||||
RBuffer *buf = r_buf_new ();
|
||||
r_buf_append_buf_slice (buf, bin->b, 0, checksum_paddr);
|
||||
r_buf_append_buf_slice (buf, bin->b,
|
||||
checksum_paddr + 4,
|
||||
security_entry_offset - checksum_paddr - 4);
|
||||
r_buf_append_buf_slice (buf, bin->b,
|
||||
security_entry_offset + 8,
|
||||
security_dir_offset - security_entry_offset - 8);
|
||||
r_buf_append_buf_slice (buf, bin->b,
|
||||
security_dir_offset + security_dir_size,
|
||||
r_buf_size (bin->b) - security_dir_offset - security_dir_size);
|
||||
|
||||
ut64 len;
|
||||
const ut8 *data = r_buf_data (buf, &len);
|
||||
char *hashstr = NULL;
|
||||
RHash *ctx = r_hash_new (true, algobit);
|
||||
r_hash_do_begin (ctx, algobit);
|
||||
int digest_size = r_hash_calculate (ctx, algobit, data, len);
|
||||
r_hash_do_end (ctx, algobit);
|
||||
hashstr = r_hex_bin2strdup (ctx->digest, digest_size);
|
||||
|
||||
r_buf_free (buf);
|
||||
r_hash_free (ctx);
|
||||
return hashstr;
|
||||
}
|
||||
|
||||
const char* PE_(bin_pe_get_authentihash)(struct PE_(r_bin_pe_obj_t)* bin) {
|
||||
return bin->authentihash;
|
||||
}
|
||||
|
||||
int PE_(bin_pe_is_authhash_valid)(struct PE_(r_bin_pe_obj_t)* bin) {
|
||||
return bin->is_authhash_valid;
|
||||
}
|
||||
|
||||
static void computeOverlayOffset(ut64 offset, ut64 size, ut64 file_size, ut64* largest_offset, ut64* largest_size) {
|
||||
if (offset + size <= file_size && offset + size > (*largest_offset + *largest_size)) {
|
||||
*largest_offset = offset;
|
||||
|
@ -2736,37 +2797,84 @@ R_API void PE_(bin_pe_parse_resource)(struct PE_(r_bin_pe_obj_t) *bin) {
|
|||
_store_resource_sdb (bin);
|
||||
}
|
||||
|
||||
static void bin_pe_get_certificate(struct PE_ (r_bin_pe_obj_t) * bin) {
|
||||
ut64 size, vaddr;
|
||||
ut8 *data = NULL;
|
||||
int len;
|
||||
static int bin_pe_init_security(struct PE_(r_bin_pe_obj_t) * bin) {
|
||||
if (!bin || !bin->nt_headers) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
bin->cms = NULL;
|
||||
size = bin->data_directory[PE_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
|
||||
vaddr = bin->data_directory[PE_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
|
||||
if (size < 8) {
|
||||
return;
|
||||
if (bin->nt_headers->optional_header.NumberOfRvaAndSizes < 5) {
|
||||
return false;
|
||||
}
|
||||
data = calloc (1, size);
|
||||
if (!data) {
|
||||
return;
|
||||
PE_(image_data_directory) *data_dir_security = &bin->data_directory[PE_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
PE_DWord paddr = data_dir_security->VirtualAddress;
|
||||
ut32 size = data_dir_security->Size;
|
||||
if (size < 8 || paddr > bin->size || paddr + size > bin->size) {
|
||||
bprintf ("Invalid certificate table");
|
||||
return false;
|
||||
}
|
||||
if (vaddr > bin->size || vaddr + size > bin->size) {
|
||||
bprintf ("vaddr greater than the file\n");
|
||||
free (data);
|
||||
return;
|
||||
|
||||
Pe_image_security_directory *security_directory = R_NEW0 (Pe_image_security_directory);
|
||||
if (!security_directory) {
|
||||
return false;
|
||||
}
|
||||
//skipping useless header..
|
||||
len = r_buf_read_at (bin->b, vaddr + 8, data, size - 8);
|
||||
if (len < 1) {
|
||||
R_FREE (data);
|
||||
return;
|
||||
bin->security_directory = security_directory;
|
||||
|
||||
PE_DWord offset = paddr;
|
||||
while (offset < paddr + size) {
|
||||
Pe_certificate **tmp = (Pe_certificate **)realloc (security_directory->certificates, (security_directory->length + 1) * sizeof(Pe_certificate *));
|
||||
if (!tmp) {
|
||||
return false;
|
||||
}
|
||||
Pe_certificate *cert = R_NEW0 (Pe_certificate);
|
||||
if (!cert) {
|
||||
return false;
|
||||
}
|
||||
cert->dwLength = r_buf_read_le32_at (bin->b, offset);
|
||||
cert->dwLength += (8 - (cert->dwLength & 7)) & 7; // align32
|
||||
if (offset + cert->dwLength > paddr + size) {
|
||||
bprintf ("Invalid certificate entry");
|
||||
R_FREE (cert);
|
||||
return false;
|
||||
}
|
||||
cert->wRevision = r_buf_read_le16_at (bin->b, offset + 4);
|
||||
cert->wCertificateType = r_buf_read_le16_at (bin->b, offset + 6);
|
||||
if (!(cert->bCertificate = malloc (cert->dwLength - 6))) {
|
||||
R_FREE (cert);
|
||||
return false;
|
||||
}
|
||||
r_buf_read_at (bin->b, offset + 8, cert->bCertificate, cert->dwLength - 6);
|
||||
|
||||
if (!bin->cms && cert->wCertificateType == PE_WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
|
||||
bin->cms = r_pkcs7_parse_cms (cert->bCertificate, cert->dwLength - 6);
|
||||
bin->spcinfo = r_pkcs7_parse_spcinfo (bin->cms);
|
||||
}
|
||||
|
||||
security_directory->certificates = tmp;
|
||||
security_directory->certificates[security_directory->length] = cert;
|
||||
security_directory->length++;
|
||||
offset += cert->dwLength;
|
||||
}
|
||||
|
||||
if (bin->cms && bin->spcinfo) {
|
||||
char *actual_authentihash = PE_(bin_pe_get_actual_authentihash) (bin);
|
||||
char *claimed_authentihash = PE_(bin_pe_get_claimed_authentihash) (bin);
|
||||
bin->authentihash = actual_authentihash;
|
||||
bin->is_authhash_valid = !strcmp (actual_authentihash, claimed_authentihash);
|
||||
free (claimed_authentihash);
|
||||
}
|
||||
bin->cms = r_pkcs7_parse_cms (data, size);
|
||||
bin->is_signed = bin->cms != NULL;
|
||||
R_FREE (data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_security_directory(Pe_image_security_directory *security_directory) {
|
||||
if (!security_directory) {
|
||||
return;
|
||||
}
|
||||
ut64 numCert = 0;
|
||||
for (; numCert < security_directory->length; numCert++) {
|
||||
R_FREE (security_directory->certificates[numCert]);
|
||||
}
|
||||
free (security_directory->certificates);
|
||||
free (security_directory);
|
||||
}
|
||||
|
||||
static int bin_pe_init(struct PE_(r_bin_pe_obj_t)* bin) {
|
||||
|
@ -2776,10 +2884,13 @@ static int bin_pe_init(struct PE_(r_bin_pe_obj_t)* bin) {
|
|||
bin->export_directory = NULL;
|
||||
bin->import_directory = NULL;
|
||||
bin->resource_directory = NULL;
|
||||
bin->security_directory = NULL;
|
||||
bin->delay_import_directory = NULL;
|
||||
bin->optional_header = NULL;
|
||||
bin->data_directory = NULL;
|
||||
bin->big_endian = 0;
|
||||
bin->cms = NULL;
|
||||
bin->spcinfo = NULL;
|
||||
if (!bin_pe_init_hdr (bin)) {
|
||||
eprintf ("Warning: File is not PE\n");
|
||||
return false;
|
||||
|
@ -2792,7 +2903,7 @@ static int bin_pe_init(struct PE_(r_bin_pe_obj_t)* bin) {
|
|||
bin_pe_init_imports (bin);
|
||||
bin_pe_init_exports (bin);
|
||||
bin_pe_init_resource (bin);
|
||||
bin_pe_get_certificate(bin);
|
||||
bin_pe_init_security (bin);
|
||||
|
||||
bin->big_endian = PE_(r_bin_pe_is_big_endian) (bin);
|
||||
|
||||
|
@ -3831,12 +3942,15 @@ void* PE_(r_bin_pe_free)(struct PE_(r_bin_pe_obj_t)* bin) {
|
|||
free (bin->export_directory);
|
||||
free (bin->import_directory);
|
||||
free (bin->resource_directory);
|
||||
free_security_directory (bin->security_directory);
|
||||
free (bin->delay_import_directory);
|
||||
free (bin->tls_directory);
|
||||
free (bin->sections);
|
||||
free (bin->authentihash);
|
||||
r_list_free (bin->rich_entries);
|
||||
r_list_free (bin->resources);
|
||||
r_pkcs7_free_cms (bin->cms);
|
||||
r_pkcs7_free_spcinfo (bin->spcinfo);
|
||||
r_buf_free (bin->b);
|
||||
bin->b = NULL;
|
||||
free (bin);
|
||||
|
|
|
@ -96,6 +96,7 @@ struct PE_(r_bin_pe_obj_t) {
|
|||
PE_(image_tls_directory) * tls_directory;
|
||||
Pe_image_resource_directory* resource_directory;
|
||||
PE_(image_delay_import_directory) * delay_import_directory;
|
||||
Pe_image_security_directory * security_directory;
|
||||
|
||||
// these pointers pertain to the .net relevant sections
|
||||
PE_(image_clr_header) * clr_hdr;
|
||||
|
@ -127,6 +128,9 @@ struct PE_(r_bin_pe_obj_t) {
|
|||
RBuffer* b;
|
||||
Sdb *kv;
|
||||
RCMS* cms;
|
||||
SpcIndirectDataContent *spcinfo;
|
||||
char *authentihash;
|
||||
bool is_authhash_valid;
|
||||
bool is_signed;
|
||||
};
|
||||
|
||||
|
@ -158,6 +162,8 @@ struct PE_(r_bin_pe_obj_t)* PE_(r_bin_pe_new_buf)(RBuffer* buf, bool verbose);
|
|||
int PE_(r_bin_pe_get_debug_data)(struct PE_(r_bin_pe_obj_t)* bin, struct SDebugInfo* res);
|
||||
int PE_(bin_pe_get_claimed_checksum)(struct PE_(r_bin_pe_obj_t)* bin);
|
||||
int PE_(bin_pe_get_actual_checksum)(struct PE_(r_bin_pe_obj_t)* bin);
|
||||
const char* PE_(bin_pe_get_authentihash)(struct PE_(r_bin_pe_obj_t)* bin);
|
||||
int PE_(bin_pe_is_authhash_valid)(struct PE_(r_bin_pe_obj_t)* bin);
|
||||
int PE_(bin_pe_get_overlay)(struct PE_(r_bin_pe_obj_t)* bin, ut64* size);
|
||||
void PE_(r_bin_pe_check_sections)(struct PE_(r_bin_pe_obj_t)* bin, struct r_bin_pe_section_t** sects);
|
||||
struct r_bin_pe_addr_t *PE_(check_unknow) (struct PE_(r_bin_pe_obj_t) *bin);
|
||||
|
|
|
@ -442,6 +442,26 @@ typedef struct {
|
|||
ut32 Characteristics;
|
||||
} Pe32_image_tls_directory, Pe64_image_tls_directory;
|
||||
|
||||
typedef struct {
|
||||
ut32 dwLength;
|
||||
ut16 wRevision;
|
||||
ut16 wCertificateType;
|
||||
ut8 *bCertificate;
|
||||
} Pe_certificate;
|
||||
|
||||
typedef struct {
|
||||
ut32 length;
|
||||
Pe_certificate **certificates;
|
||||
} Pe_image_security_directory;
|
||||
|
||||
#define PE_WIN_CERT_REVISION_1_0 0x0100
|
||||
#define PE_WIN_CERT_REVISION_2_0 0x0200
|
||||
|
||||
#define PE_WIN_CERT_TYPE_X509 0x0001
|
||||
#define PE_WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
|
||||
#define PE_WIN_CERT_TYPE_RESERVED_1 0x0003
|
||||
#define PE_WIN_CERT_TYPE_TS_STACK_SIGNED 0x0004
|
||||
|
||||
typedef struct {
|
||||
ut32 Signature;
|
||||
Pe32_image_file_header file_header;
|
||||
|
|
|
@ -475,6 +475,16 @@ static RBinInfo* info(RBinFile *bf) {
|
|||
ret->actual_checksum = strdup (sdb_fmt ("0x%08x", actual_checksum));
|
||||
ret->pe_overlay = pe_overlay > 0;
|
||||
ret->signature = bin ? bin->is_signed : false;
|
||||
ret->file_hashes = r_list_newf ((RListFree) r_bin_file_hash_free);
|
||||
const char *authentihash = PE_(bin_pe_get_authentihash) (bf->o->bin_obj);
|
||||
if (authentihash) {
|
||||
RBinFileHash *authhash = R_NEW0 (RBinFileHash);
|
||||
if (authhash) {
|
||||
authhash->type = strdup ("authentihash");
|
||||
authhash->hex = strdup (authentihash);
|
||||
r_list_push (ret->file_hashes, authhash);
|
||||
}
|
||||
}
|
||||
Sdb *db = sdb_ns (bf->sdb, "pe", true);
|
||||
sdb_bool_set (db, "canary", has_canary (bf), 0);
|
||||
sdb_bool_set (db, "highva", haschr (bf, IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA), 0);
|
||||
|
@ -491,6 +501,8 @@ static RBinInfo* info(RBinFile *bf) {
|
|||
sdb_num_set (db, "bits", ret->bits, 0);
|
||||
sdb_set (db, "claimed_checksum", ret->claimed_checksum, 0);
|
||||
sdb_set (db, "actual_checksum", ret->actual_checksum, 0);
|
||||
sdb_set (db, "authentihash", PE_(bin_pe_get_authentihash) (bf->o->bin_obj), 0);
|
||||
sdb_bool_set (db, "is_authhash_valid", PE_(bin_pe_is_authhash_valid) (bf->o->bin_obj), 0);
|
||||
|
||||
ret->has_va = true;
|
||||
|
||||
|
|
|
@ -603,66 +603,52 @@ static int cmd_info(void *data, const char *input) {
|
|||
case 't': // "it"
|
||||
{
|
||||
ut64 limit = r_config_get_i (core->config, "bin.hashlimit");
|
||||
const char *fileName;
|
||||
RBinInfo *info = r_bin_get_info (core->bin);
|
||||
if (info) {
|
||||
fileName = info->file;
|
||||
} else {
|
||||
int fd = r_io_fd_get_current (core->io);
|
||||
RIODesc *desc = r_io_desc_get (core->io, fd);
|
||||
fileName = desc? desc->name: NULL;
|
||||
if (!info) {
|
||||
eprintf ("r_bin_get_info: Cannot get bin info\n");
|
||||
return 0;
|
||||
}
|
||||
const bool is_json = input[1] == 'j'; // "itj"
|
||||
RList *old_file_hashes = NULL;
|
||||
if (!r_bin_file_hash (core->bin, limit, fileName, &old_file_hashes)) {
|
||||
eprintf ("r_bin_file_hash: Cannot get file hashes");
|
||||
r_list_free (old_file_hashes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old_file_hashes && r_list_empty (old_file_hashes)) {
|
||||
// clean the old hashes list to reduce comparison operations in case it is allocated but empty
|
||||
r_list_free (old_file_hashes);
|
||||
old_file_hashes = NULL;
|
||||
}
|
||||
if (!info) {
|
||||
info = r_bin_get_info (core->bin);
|
||||
}
|
||||
if (!info) {
|
||||
eprintf ("r_bin_get_info: Cannot get bin info");
|
||||
r_list_free (old_file_hashes);
|
||||
RList *new_file_hashes = r_bin_file_hash (core->bin, limit);
|
||||
if (!new_file_hashes) {
|
||||
eprintf ("r_bin_file_hash: Cannot get latest file hashes\n");
|
||||
r_list_free (new_file_hashes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isInitialized = true;
|
||||
bool equal = true;
|
||||
// check are hashes changed
|
||||
if (!r_list_empty (old_file_hashes) && !r_list_empty (info->file_hashes)) {
|
||||
if (!is_equal_file_hashes (old_file_hashes, info->file_hashes, &equal)) {
|
||||
eprintf ("is_equal_file_hashes: Cannot compare file hashes");
|
||||
r_list_free (old_file_hashes);
|
||||
return 0;
|
||||
if (!info->file_hashes) {
|
||||
info->file_hashes = new_file_hashes;
|
||||
isInitialized = false;
|
||||
} else {
|
||||
// check are hashes changed
|
||||
if (!r_list_empty (new_file_hashes) && !r_list_empty (info->file_hashes)) {
|
||||
if (!is_equal_file_hashes (new_file_hashes, info->file_hashes, &equal)) {
|
||||
eprintf ("is_equal_file_hashes: Cannot compare file hashes\n");
|
||||
r_list_free (new_file_hashes);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RBinFileHash *fh_old, *fh_new;
|
||||
RListIter *hiter_old, *hiter_new;
|
||||
|
||||
const bool is_json = input[1] == 'j'; // "itj"
|
||||
if (is_json) { // "itj"
|
||||
PJ *pj = pj_new ();
|
||||
if (!pj) {
|
||||
eprintf ("JSON mode failed\n");
|
||||
r_list_free (old_file_hashes);
|
||||
r_list_free (new_file_hashes);
|
||||
return 0;
|
||||
}
|
||||
pj_o (pj);
|
||||
r_list_foreach (info->file_hashes, hiter_new, fh_new) {
|
||||
pj_ks (pj, fh_new->type ? fh_new->type : "", fh_new->hex ? fh_new->hex : "");
|
||||
r_list_foreach (info->file_hashes, hiter_old, fh_old) {
|
||||
pj_ks (pj, fh_old->type, fh_old->hex);
|
||||
}
|
||||
if (!equal) {
|
||||
// print old hashes prefixed with `o` character like `omd5` and `osha1`
|
||||
r_list_foreach (old_file_hashes, hiter_old, fh_old) {
|
||||
char *key = r_str_newf ("o%s", fh_old->type ? fh_old->type : "");
|
||||
pj_ks (pj, key, fh_old->hex ? fh_old->hex : "");
|
||||
// print new hashes prefixed with `n` character like `nmd5` and `nsha1`
|
||||
r_list_foreach (new_file_hashes, hiter_new, fh_new) {
|
||||
char *key = r_str_newf ("n%s", fh_new->type);
|
||||
pj_ks (pj, key, fh_new->hex);
|
||||
free (key);
|
||||
}
|
||||
}
|
||||
|
@ -672,34 +658,40 @@ static int cmd_info(void *data, const char *input) {
|
|||
} else { // "it"
|
||||
if (!equal) {
|
||||
eprintf ("File has been modified.\n");
|
||||
hiter_new = r_list_iterator (info->file_hashes);
|
||||
hiter_old = r_list_iterator (old_file_hashes);
|
||||
while (r_list_iter_next(hiter_new) && r_list_iter_next (hiter_old)) {
|
||||
fh_new = (RBinFileHash*)r_list_iter_get (hiter_new);
|
||||
fh_old = (RBinFileHash*)r_list_iter_get (hiter_old);
|
||||
if (strcmp (fh_new->type, fh_old->type)) {
|
||||
eprintf ("Wrong file hashes structure");
|
||||
bool isCompared = false;
|
||||
r_list_foreach (info->file_hashes, hiter_old, fh_old) {
|
||||
r_list_foreach (new_file_hashes, hiter_new, fh_new) {
|
||||
if (!!strcmp (fh_new->type, fh_old->type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp (fh_new->hex, fh_old->hex)) {
|
||||
eprintf ("= %s %s\n", fh_new->type, fh_new->hex); // output one line because hash remains same `= hashtype hashval`
|
||||
|
||||
} else {
|
||||
// output diff-like two lines, one with old hash val `- hashtype hashval` and one with new `+ hashtype hashval`
|
||||
eprintf ("- %s %s\n+ %s %s\n",
|
||||
fh_old->type, fh_old->hex,
|
||||
fh_new->type, fh_new->hex);
|
||||
}
|
||||
isCompared = true;
|
||||
break;
|
||||
}
|
||||
if (!strcmp (fh_new->hex, fh_old->hex)) {
|
||||
eprintf ("= %s %s\n", fh_new->type, fh_new->hex); // output one line because hash remains same `= hashtype hashval`
|
||||
} else {
|
||||
// output diff-like two lines, one with old hash val `- hashtype hashval` and one with new `+ hashtype hashval`
|
||||
eprintf ("- %s %s\n"
|
||||
"+ %s %s\n",
|
||||
fh_old->type, fh_old->hex,
|
||||
fh_new->type, fh_new->hex);
|
||||
if (!isCompared) {
|
||||
eprintf ("= %s %s\n", fh_new->type, fh_new->hex);
|
||||
}
|
||||
isCompared = false;
|
||||
}
|
||||
} else { // hashes are equal
|
||||
r_list_foreach (info->file_hashes, hiter_new, fh_new) {
|
||||
r_cons_printf ("%s %s\n", fh_new->type ? fh_new->type : "", fh_new->hex ? fh_new->hex : "");
|
||||
r_list_foreach (info->file_hashes, hiter_old, fh_old) {
|
||||
r_cons_printf ("%s %s\n", fh_old->type, fh_old->hex);
|
||||
}
|
||||
}
|
||||
newline = false;
|
||||
}
|
||||
|
||||
if (!r_list_empty (old_file_hashes)) {
|
||||
r_list_free (old_file_hashes);
|
||||
if (isInitialized) {
|
||||
r_list_free (new_file_hashes);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -762,7 +762,7 @@ R_API bool r_bin_file_set_cur_by_id(RBin *bin, ut32 bin_id);
|
|||
R_API bool r_bin_file_set_cur_by_name(RBin *bin, const char *name);
|
||||
R_API ut64 r_bin_file_delete_all(RBin *bin);
|
||||
R_API bool r_bin_file_delete(RBin *bin, ut32 bin_id);
|
||||
R_API bool r_bin_file_hash(RBin *bin, ut64 limit, const char *file, RList/*<RBinFileHash>*/ **old_file_hashes);
|
||||
R_API RList *r_bin_file_hash(RBin *bin, ut64 limit);
|
||||
R_API RBinPlugin *r_bin_file_cur_plugin(RBinFile *binfile);
|
||||
R_API void r_bin_file_hash_free(RBinFileHash *fhash);
|
||||
|
||||
|
|
|
@ -69,10 +69,27 @@ typedef struct r_pkcs7_container_t {
|
|||
RPKCS7SignedData signedData;
|
||||
} RCMS;
|
||||
|
||||
typedef struct {
|
||||
RASN1String *type; //OID
|
||||
RASN1Binary *data; // optional.
|
||||
} SpcAttributeTypeAndOptionalValue;
|
||||
|
||||
typedef struct {
|
||||
RX509AlgorithmIdentifier digestAlgorithm;
|
||||
RASN1Binary *digest;
|
||||
} SpcDigestInfo;
|
||||
|
||||
typedef struct {
|
||||
SpcAttributeTypeAndOptionalValue data;
|
||||
SpcDigestInfo messageDigest;
|
||||
} SpcIndirectDataContent;
|
||||
|
||||
R_API RCMS *r_pkcs7_parse_cms(const ut8 *buffer, ut32 length);
|
||||
R_API void r_pkcs7_free_cms(RCMS* container);
|
||||
R_API char *r_pkcs7_cms_to_string(RCMS* container);
|
||||
R_API PJ *r_pkcs7_cms_json(RCMS* container);
|
||||
R_API SpcIndirectDataContent *r_pkcs7_parse_spcinfo(RCMS *cms);
|
||||
R_API void r_pkcs7_free_spcinfo(SpcIndirectDataContent *spcinfo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1221,8 +1221,15 @@ R_API int r_main_radare2(int argc, char **argv) {
|
|||
if (compute_hashes && iod) {
|
||||
// TODO: recall with limit=0 ?
|
||||
ut64 limit = r_config_get_i (r->config, "bin.hashlimit");
|
||||
(void)r_bin_file_hash (r->bin, limit, iod->name, NULL);
|
||||
//eprintf ("WARNING: File hash not calculated\n");
|
||||
RList *file_hashes = r_bin_file_hash (r->bin, limit);
|
||||
if (file_hashes) {
|
||||
if (r->bin->cur->o->info->file_hashes) {
|
||||
r_list_join (r->bin->cur->o->info->file_hashes, file_hashes);
|
||||
free (file_hashes);
|
||||
} else {
|
||||
r->bin->cur->o->info->file_hashes = file_hashes;
|
||||
}
|
||||
}
|
||||
}
|
||||
npath = r_config_get (r->config, "file.path");
|
||||
if (!quiet && path && *path && npath && strcmp (path, npath)) {
|
||||
|
|
|
@ -633,3 +633,81 @@ R_API PJ *r_pkcs7_cms_json (RCMS *container) {
|
|||
}
|
||||
return pj;
|
||||
}
|
||||
|
||||
static bool r_pkcs7_parse_spcdata(SpcAttributeTypeAndOptionalValue *data, RASN1Object *object) {
|
||||
if (!data || !object || object->list.length < 1 ||
|
||||
!object->list.objects[0]) {
|
||||
return false;
|
||||
}
|
||||
data->type = r_asn1_stringify_oid (object->list.objects[0]->sector, object->list.objects[0]->length);
|
||||
RASN1Object *obj1 = object->list.objects[1];
|
||||
if (object->list.length > 1) {
|
||||
if (obj1) {
|
||||
data->data = r_asn1_create_binary (obj1->sector, obj1->length);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool r_pkcs7_parse_spcmessagedigest(SpcDigestInfo *messageDigest, RASN1Object *object) {
|
||||
if (!messageDigest || !object || object->list.length < 2 ||
|
||||
!object->list.objects[0] || !object->list.objects[1]) {
|
||||
return false;
|
||||
}
|
||||
r_x509_parse_algorithmidentifier (&messageDigest->digestAlgorithm, object->list.objects[0]);
|
||||
RASN1Object *obj1 = object->list.objects[1];
|
||||
messageDigest->digest = r_asn1_create_binary (obj1->sector, obj1->length);
|
||||
return true;
|
||||
}
|
||||
|
||||
R_API SpcIndirectDataContent *r_pkcs7_parse_spcinfo(RCMS *cms) {
|
||||
RASN1Object *object;
|
||||
RASN1Binary *content;
|
||||
SpcIndirectDataContent *spcinfo;
|
||||
if (!cms) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spcinfo = R_NEW0 (SpcIndirectDataContent);
|
||||
if (!spcinfo) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
content = cms->signedData.contentInfo.content;
|
||||
object = r_asn1_create_object (content->binary, content->length, content->binary);
|
||||
if (!object || object->list.length < 2 || !object->list.objects ||
|
||||
!object->list.objects[0] || !object->list.objects[1]) {
|
||||
r_asn1_free_object (object);
|
||||
free (spcinfo);
|
||||
return NULL;
|
||||
}
|
||||
if (object->list.objects[0]) {
|
||||
r_pkcs7_parse_spcdata (&spcinfo->data, object->list.objects[0]);
|
||||
}
|
||||
if (object->list.objects[1]) {
|
||||
r_pkcs7_parse_spcmessagedigest (&spcinfo->messageDigest, object->list.objects[1]);
|
||||
}
|
||||
r_asn1_free_object (object);
|
||||
return spcinfo;
|
||||
}
|
||||
|
||||
static void r_pkcs7_free_spcdata(SpcAttributeTypeAndOptionalValue *data) {
|
||||
if (data) {
|
||||
r_asn1_free_string (data->type);
|
||||
r_asn1_free_binary (data->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void r_pkcs7_free_spcmessagedigest(SpcDigestInfo *messageDigest) {
|
||||
if (messageDigest) {
|
||||
r_asn1_free_binary (messageDigest->digest);
|
||||
r_x509_free_algorithmidentifier (&messageDigest->digestAlgorithm);
|
||||
}
|
||||
}
|
||||
|
||||
R_API void r_pkcs7_free_spcinfo(SpcIndirectDataContent *spcinfo) {
|
||||
if (spcinfo) {
|
||||
r_pkcs7_free_spcdata (&spcinfo->data);
|
||||
r_pkcs7_free_spcmessagedigest (&spcinfo->messageDigest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
NAME=check invalid authentihash
|
||||
FILE=../bins/pe/signature.exe
|
||||
EXPECT=<<EOF
|
||||
false
|
||||
authentihash c31534a14726a96a54602c3cb9eaa916412223d5
|
||||
md5 23ea4cc8f6e59aa5121bdeb3394150ae
|
||||
sha1 e2462a381cc60582efcf4dab129f4f08ff396bec
|
||||
sha256 327ef49480f2fd20bb5f46f7f384ea1ae6ab405a16b17fc575a5e4598a42b570
|
||||
{"authentihash":"c31534a14726a96a54602c3cb9eaa916412223d5","md5":"23ea4cc8f6e59aa5121bdeb3394150ae","sha1":"e2462a381cc60582efcf4dab129f4f08ff396bec","sha256":"327ef49480f2fd20bb5f46f7f384ea1ae6ab405a16b17fc575a5e4598a42b570"}
|
||||
EOF
|
||||
CMDS=<<EOF
|
||||
k bin/cur/pe/is_authhash_valid
|
||||
it
|
||||
itj
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=check valid authentihash
|
||||
FILE=../bins/pe/authentihash.exe
|
||||
EXPECT=<<EOF
|
||||
true
|
||||
authentihash 2f425cda5b8d590f868a2f0cdbdae4d8c9d31bf7
|
||||
md5 dbd0a3bf85cc041794480df9a8dd34a8
|
||||
sha1 ae34b7b949997d6c65a1549cf234c53a7184a262
|
||||
sha256 ea4b1a9b05b70c00e56d1939c3037d5aa07117ff871f01caa84f049f9766ae9e
|
||||
{"authentihash":"2f425cda5b8d590f868a2f0cdbdae4d8c9d31bf7","md5":"dbd0a3bf85cc041794480df9a8dd34a8","sha1":"ae34b7b949997d6c65a1549cf234c53a7184a262","sha256":"ea4b1a9b05b70c00e56d1939c3037d5aa07117ff871f01caa84f049f9766ae9e"}
|
||||
EOF
|
||||
CMDS=<<EOF
|
||||
k bin/cur/pe/is_authhash_valid
|
||||
it
|
||||
itj
|
||||
EOF
|
||||
RUN
|
Loading…
Reference in New Issue