void bt_dis_unref(struct bt_dis *dis) { if (!dis) return; if (__sync_sub_and_fetch(&dis->ref_count, 1)) return; dis_free(dis); }
/** * Get the dataset present in a .BEK file * * @param fd The file descriptor to the .BEK file * @param bek_dataset The extracted dataset * @return TRUE if result can be trusted, FALSE otherwise */ int get_bek_dataset(int fd, void** bek_dataset) { if(!bek_dataset) { dis_printf(L_ERROR, "Invalid parameter given to get_bek_dataset().\n"); return FALSE; } bitlocker_dataset_t dataset; /* Read the dataset header */ ssize_t nb_read = dis_read(fd, &dataset, sizeof(bitlocker_dataset_t)); // Check if we read all we wanted if(nb_read != sizeof(bitlocker_dataset_t)) { dis_printf( L_ERROR, "get_bek_dataset::Error, not all byte read (bek dataset header).\n" ); return FALSE; } if(dataset.size <= sizeof(bitlocker_dataset_t)) { dis_printf( L_ERROR, "get_bek_dataset::Error, dataset size < dataset header size.\n" ); return FALSE; } *bek_dataset = dis_malloc(dataset.size); memset(*bek_dataset, 0, dataset.size); memcpy(*bek_dataset, &dataset, sizeof(bitlocker_dataset_t)); size_t rest = dataset.size - sizeof(bitlocker_dataset_t); /* Read the data included in the dataset */ nb_read = dis_read(fd, *bek_dataset + sizeof(bitlocker_dataset_t), rest); // Check if we read all we wanted if((size_t) nb_read != rest) { dis_printf( L_ERROR, "get_bek_dataset::Error, not all byte read (bek dataset content).\n" ); dis_free(*bek_dataset); return FALSE; } return TRUE; }
int dis_destroy(dis_context_t dis_ctx) { /* Finish cleaning things */ if(dis_ctx->io_data.vmk) dis_free(dis_ctx->io_data.vmk); if(dis_ctx->io_data.fvek) dis_free(dis_ctx->io_data.fvek); dis_crypt_destroy(dis_ctx->io_data.crypt); dis_metadata_destroy(dis_ctx->metadata); dis_free_args(dis_ctx); dis_close(dis_ctx->io_data.volume_fd); dis_stdio_end(); dis_free(dis_ctx); return EXIT_SUCCESS; }
/* * Return the previous instruction. On x86, we have no choice except to * disassemble everything from the start of the symbol, and stop when we have * reached our instruction address. If we're not in the middle of a known * symbol, then we return the same address to indicate failure. */ static uint64_t dis_i386_previnstr(dis_handle_t *dhp, uint64_t pc, int n) { uint64_t *hist, addr, start; int cur, nseen; uint64_t res = pc; if (n <= 0) return (pc); if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 || start == pc) return (res); hist = dis_zalloc(sizeof (uint64_t) * n); for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) { hist[cur] = addr; cur = (cur + 1) % n; nseen++; /* if we cannot make forward progress, give up */ if (dis_disassemble(dhp, addr, NULL, 0) != 0) goto done; } if (addr != pc) { /* * We scanned past %pc, but didn't find an instruction that * started at %pc. This means that either the caller specified * an invalid address, or we ran into something other than code * during our scan. Virtually any combination of bytes can be * construed as a valid Intel instruction, so any non-code bytes * we encounter will have thrown off the scan. */ goto done; } res = hist[(cur + n - MIN(n, nseen)) % n]; done: dis_free(hist, sizeof (uint64_t) * n); return (res); }
struct bt_dis *bt_dis_new(void *primary) { struct bt_dis *dis; dis = g_try_new0(struct bt_dis, 1); if (!dis) return NULL; dis->gatt_op = queue_new(); if (!dis->gatt_op) { dis_free(dis); return NULL; } if (primary) dis->primary = g_memdup(primary, sizeof(*dis->primary)); return bt_dis_ref(dis); }
dis_handle_t * dis_handle_create(int flags, void *data, dis_lookup_f lookup_func, dis_read_f read_func) { dis_handle_t *dhp; dis_arch_t *arch = NULL; int i; /* Select an architecture based on flags */ for (i = 0; dis_archs[i] != NULL; i++) { if (dis_archs[i]->da_supports_flags(flags)) { arch = dis_archs[i]; break; } } if (arch == NULL) { (void) dis_seterrno(E_DIS_UNSUPARCH); return (NULL); } if ((dhp = dis_zalloc(sizeof (dis_handle_t))) == NULL) { (void) dis_seterrno(E_DIS_NOMEM); return (NULL); } dhp->dh_arch = arch; dhp->dh_lookup = lookup_func; dhp->dh_read = read_func; dhp->dh_flags = flags; dhp->dh_data = data; /* * Allow the architecture-specific code to allocate * its private data. */ if (arch->da_handle_attach(dhp) != 0) { dis_free(dhp, sizeof (dis_handle_t)); /* dis errno already set */ return (NULL); } return (dhp); }
dis_context_t dis_new() { /* Allocate dislocker's context */ dis_context_t dis_ctx = dis_malloc(sizeof(struct _dis_ctx)); memset(dis_ctx, 0, sizeof(struct _dis_ctx)); #ifndef __DIS_CORE_DUMPS /* As we manage passwords and secrets, do not authorize core dumps */ struct rlimit limit; limit.rlim_cur = 0; limit.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &limit) != 0) { fprintf(stderr, "Cannot disable core dumps.\n"); dis_free(dis_ctx); return NULL; } #endif dis_ctx->fve_fd = -1; return dis_ctx; }
static void dis_i386_handle_detach(dis_handle_t *dhp) { dis_free(dhp->dh_arch_private, sizeof (dis_handle_i386_t)); dhp->dh_arch_private = NULL; }
/** * Get the VMK datum using a bek file (external key) * * @param dataset The dataset of BitLocker's metadata on the volume * @param bek_file The path to the .BEK file to use * @param vmk_datum The datum_key_t found, containing the unencrypted VMK * @return TRUE if result can be trusted, FALSE otherwise */ int get_vmk_from_bekfile2(dis_metadata_t dis_meta, char* bek_file, void** vmk_datum) { // Check parameters if(!dis_meta || !vmk_datum) return FALSE; guid_t key_guid = {0,}; char rec_id[37] = {0,}; bitlocker_dataset_t* bek_dataset = NULL; uint8_t* recovery_key = NULL; size_t rk_size = 0; int result = FALSE; int fd_bek = 0; if(bek_file) { /* Check if the bek file exists */ fd_bek = dis_open(bek_file, O_RDONLY); if(fd_bek < 0) { dis_printf(L_ERROR, "Cannot open FVEK file (%s)\n", bek_file); return FALSE; } } else { dis_printf( L_ERROR, "Using bekfile method (USB) but missing the bekfile name. Abort.\n" ); return FALSE; } dis_printf( L_INFO, "Using the bekfile '%s' to decrypt the VMK.\n", bek_file ); /* * We need the recovery key id which can be found in the bek file * to find its match in a datum of the volume's metadata */ if(!get_bek_dataset(fd_bek, (void**) &bek_dataset)) { dis_printf(L_ERROR, "Unable to retrieve the dataset. Abort.\n"); dis_close(fd_bek); return FALSE; } /* We have what we wanted, so close the file */ dis_close(fd_bek); /* Get the external datum */ void* dataset = dis_metadata_set_dataset(dis_meta, bek_dataset); get_next_datum( dis_meta, UINT16_MAX, DATUMS_VALUE_EXTERNAL_KEY, NULL, vmk_datum ); dis_metadata_set_dataset(dis_meta, dataset); /* Check the result datum */ if(!*vmk_datum || !datum_value_type_must_be(*vmk_datum, DATUMS_VALUE_EXTERNAL_KEY)) { dis_printf( L_ERROR, "Error processing the bekfile: datum of type %hd not found. " "Internal failure, abort.\n", DATUMS_VALUE_EXTERNAL_KEY ); *vmk_datum = NULL; memclean(bek_dataset, bek_dataset->size); return FALSE; } /* Now that we are sure of the type, take care of copying the recovery key id */ datum_external_t* datum_exte = (datum_external_t*) *vmk_datum; memcpy(key_guid, datum_exte->guid, 16); format_guid(key_guid, rec_id); dis_printf( L_INFO, "Bekfile GUID found: '%s', looking for the same in metadata...\n", rec_id ); /* Grab the datum nested in the last, we will need it to decrypt the VMK */ if(!get_nested_datumvaluetype(*vmk_datum, DATUMS_VALUE_KEY, vmk_datum) || !*vmk_datum) { dis_printf( L_ERROR, "Error processing the bekfile: no nested datum found. " "Internal failure, abort.\n" ); *vmk_datum = NULL; memclean(bek_dataset, bek_dataset->size); return FALSE; } if(!get_payload_safe(*vmk_datum, (void**) &recovery_key, &rk_size)) { dis_printf( L_ERROR, "Error getting the key to decrypt VMK from the bekfile. " "Internal failure, abort.\n" ); *vmk_datum = NULL; memclean(bek_dataset, bek_dataset->size); return FALSE; } memclean(bek_dataset, bek_dataset->size); /* * Now that we have the key to decrypt the VMK, we need to * find the VMK datum in the BitLocker metadata in order to * decrypt the VMK using this already found key in the bekfile */ if(!get_vmk_datum_from_guid(dis_meta, key_guid, vmk_datum)) { format_guid(key_guid, rec_id); dis_printf( L_ERROR, "\n\tError, can't find a valid and matching VMK datum.\n" "\tThe GUID researched was '%s', check if you have the right " "bek file for the right volume.\n" "\tAbort.\n", rec_id ); *vmk_datum = NULL; dis_free(recovery_key); return FALSE; } dis_printf( L_INFO, "VMK datum of id '%s' found. Trying to reach the Key datum...\n", rec_id ); /* * We have the datum containing other data, so get in there and take the * nested one with type 5 (aes-ccm) */ if(!get_nested_datumvaluetype(*vmk_datum, DATUMS_VALUE_AES_CCM, vmk_datum)) { dis_printf( L_ERROR, "Error looking for the nested datum in the VMK one. " "Internal failure, abort.\n" ); *vmk_datum = NULL; dis_free(recovery_key); return FALSE; } dis_printf(L_INFO, "Key datum found and payload extracted!\n"); result = get_vmk( (datum_aes_ccm_t*) *vmk_datum, recovery_key, rk_size, (datum_key_t**) vmk_datum ); dis_free(recovery_key); return result; }
void dis_handle_destroy(dis_handle_t *dhp) { dhp->dh_arch->da_handle_detach(dhp); dis_free(dhp, sizeof (dis_handle_t)); }
/** * Get the VMK datum using a recovery password * * @param dis_metadata The metadata structure * @param recovery_password The recovery password provided by the user * @param vmk_datum The datum_key_t found, containing the unencrypted VMK * @return TRUE if result can be trusted, FALSE otherwise */ int get_vmk_from_rp2(dis_metadata_t dis_meta, uint8_t* recovery_password, void** vmk_datum) { // Check parameters if(!dis_meta) return FALSE; uint8_t* recovery_key = NULL; uint8_t salt[16] = {0,}; int result = FALSE; /* If the recovery password wasn't provide, ask for it */ if(!recovery_password) if(!prompt_rp(&recovery_password)) { dis_printf(L_ERROR, "Cannot get valid recovery password. Abort.\n"); return FALSE; } dis_printf(L_DEBUG, "Using the recovery password: '******'.\n", (char *)recovery_password); /* * We need a salt contained in the VMK datum associated to the recovery * password, so go get this salt and the VMK datum first * We use here the range which should be upper (or equal) than 0x800 */ if(!get_vmk_datum_from_range(dis_meta, 0x800, 0xfff, (void**) vmk_datum)) { dis_printf( L_ERROR, "Error, can't find a valid and matching VMK datum. Abort.\n" ); *vmk_datum = NULL; return FALSE; } /* * We have the datum containing other data, so get in there and take the * nested one with type 3 (stretch key) */ void* stretch_datum = NULL; if(!get_nested_datumvaluetype( *vmk_datum, DATUMS_VALUE_STRETCH_KEY, &stretch_datum ) || !stretch_datum) { char* type_str = datumvaluetypestr(DATUMS_VALUE_STRETCH_KEY); dis_printf( L_ERROR, "Error looking for the nested datum of type %hd (%s) in the VMK one" ". Internal failure, abort.\n", DATUMS_VALUE_STRETCH_KEY, type_str ); dis_free(type_str); *vmk_datum = NULL; return FALSE; } /* The salt is in here, don't forget to keep it somewhere! */ memcpy(salt, ((datum_stretch_key_t*) stretch_datum)->salt, 16); /* Get data which can be decrypted with this password */ void* aesccm_datum = NULL; if(!get_nested_datumvaluetype( *vmk_datum, DATUMS_VALUE_AES_CCM, &aesccm_datum ) || !aesccm_datum) { dis_printf( L_ERROR, "Error finding the AES_CCM datum including the VMK. " "Internal failure, abort.\n" ); *vmk_datum = NULL; return FALSE; } /* * We have all the things we need to compute the intermediate key from * the recovery password, so do it! */ recovery_key = dis_malloc(32 * sizeof(uint8_t)); if(!intermediate_key(recovery_password, salt, recovery_key)) { dis_printf( L_ERROR, "Error computing the recovery password to the recovery key. " "Abort.\n" ); *vmk_datum = NULL; dis_free(recovery_key); return FALSE; } /* As the computed key length is always the same, use a direct value */ result = get_vmk( (datum_aes_ccm_t*) aesccm_datum, recovery_key, 32, (datum_key_t**) vmk_datum ); dis_free(recovery_key); return result; }