// iterator for instruction "single-stepping" CAPSTONE_EXPORT bool CAPSTONE_API cs_disasm_iter(csh ud, const uint8_t **code, size_t *size, uint64_t *address, cs_insn *insn) { struct cs_struct *handle; uint16_t insn_size; MCInst mci; bool r; handle = (struct cs_struct *)(uintptr_t)ud; if (!handle) { return false; } handle->errnum = CS_ERR_OK; MCInst_Init(&mci); mci.csh = handle; // relative branches need to know the address & size of current insn mci.address = *address; // save all the information for non-detailed mode mci.flat_insn = insn; mci.flat_insn->address = *address; #ifdef CAPSTONE_DIET // zero out mnemonic & op_str mci.flat_insn->mnemonic[0] = '\0'; mci.flat_insn->op_str[0] = '\0'; #endif r = handle->disasm(ud, *code, *size, &mci, &insn_size, *address, handle->getinsn_info); if (r) { SStream ss; SStream_Init(&ss); mci.flat_insn->size = insn_size; // map internal instruction opcode to public insn ID handle->insn_id(handle, insn, mci.Opcode); handle->printer(&mci, &ss, handle->printer_info); fill_insn(handle, insn, ss.buffer, &mci, handle->post_printer, *code); *code += insn_size; *size -= insn_size; *address += insn_size; } else { // encounter a broken instruction size_t skipdata_bytes; // if there is no request to skip data, or remaining data is too small, // then bail out if (!handle->skipdata || handle->skipdata_size > *size) return false; if (handle->skipdata_setup.callback) { skipdata_bytes = handle->skipdata_setup.callback(*code, *size, 0, handle->skipdata_setup.user_data); if (skipdata_bytes > *size) // remaining data is not enough return false; if (!skipdata_bytes) // user requested not to skip data, so bail out return false; } else skipdata_bytes = handle->skipdata_size; // we have to skip some amount of data, depending on arch & mode insn->id = 0; // invalid ID for this "data" instruction insn->address = *address; insn->size = (uint16_t)skipdata_bytes; memcpy(insn->bytes, *code, skipdata_bytes); #ifdef CAPSTONE_DIET insn->mnemonic[0] = '\0'; insn->op_str[0] = '\0'; #else strncpy(insn->mnemonic, handle->skipdata_setup.mnemonic, sizeof(insn->mnemonic) - 1); skipdata_opstr(insn->op_str, *code, skipdata_bytes); #endif *code += skipdata_bytes; *size -= skipdata_bytes; *address += skipdata_bytes; } return true; }
// dynamicly allocate memory to contain disasm insn // NOTE: caller must free() the allocated memory itself to avoid memory leaking size_t cs_disasm_ex(csh ud, const uint8_t *buffer, size_t size, uint64_t offset, size_t count, cs_insn **insn) { struct cs_struct *handle = (struct cs_struct *)(uintptr_t)ud; MCInst mci; uint16_t insn_size; size_t c = 0; unsigned int f = 0; cs_insn insn_cache[INSN_CACHE_SIZE]; void *total = NULL; size_t total_size = 0; if (!handle) { // FIXME: how to handle this case: // handle->errnum = CS_ERR_HANDLE; return 0; } handle->errnum = CS_ERR_OK; // reset previous prefix for X86 handle->prev_prefix = 0; memset(insn_cache, 0, sizeof(insn_cache)); while (size > 0) { MCInst_Init(&mci); mci.csh = handle; bool r = handle->disasm(ud, buffer, size, &mci, &insn_size, offset, handle->getinsn_info); if (r) { SStream ss; SStream_Init(&ss); // relative branches need to know the address & size of current insn mci.insn_size = insn_size; mci.address = offset; if (handle->detail) { // save all the information for non-detailed mode mci.flat_insn.address = offset; mci.flat_insn.size = insn_size; // allocate memory for @detail pointer insn_cache[f].detail = cs_mem_calloc(1, sizeof(cs_detail)); } handle->printer(&mci, &ss, handle->printer_info); fill_insn(handle, &insn_cache[f], ss.buffer, &mci, handle->post_printer, buffer); if (!handle->check_combine || !handle->check_combine(handle, &insn_cache[f])) { f++; if (f == ARR_SIZE(insn_cache)) { // resize total to contain newly disasm insns total_size += (sizeof(cs_insn) * INSN_CACHE_SIZE); void *tmp = cs_mem_realloc(total, total_size); if (tmp == NULL) { // insufficient memory cs_mem_free(total); handle->errnum = CS_ERR_MEM; return 0; } total = tmp; memcpy((void*)((uintptr_t)total + total_size - sizeof(insn_cache)), insn_cache, sizeof(insn_cache)); // reset f back to 0 f = 0; } c++; } else { // combine this instruction with previous prefix "instruction" cs_insn *prev = get_prev_insn(insn_cache, f, total, total_size); handle->combine(handle, &insn_cache[f], prev); } buffer += insn_size; size -= insn_size; offset += insn_size; if (count > 0) { // x86 hacky if (!handle->prev_prefix) { if (c == count) break; } else { // only combine 1 prefix with regular instruction if (c == count + 1) { // the last insn is redundant c--; f--; // free allocated detail pointer of the last redundant instruction if (handle->detail) cs_mem_free(insn_cache[f].detail); break; } } } } else { // encounter a broken instruction // XXX: TODO: JOXEAN continue here break; } } if (f) { // resize total to contain newly disasm insns void *tmp = cs_mem_realloc(total, total_size + f * sizeof(insn_cache[0])); if (tmp == NULL) { // insufficient memory cs_mem_free(total); handle->errnum = CS_ERR_MEM; return 0; } total = tmp; memcpy((void*)((uintptr_t)total + total_size), insn_cache, f * sizeof(insn_cache[0])); } *insn = total; return c; }
// dynamicly allocate memory to contain disasm insn // NOTE: caller must free() the allocated memory itself to avoid memory leaking CAPSTONE_EXPORT size_t CAPSTONE_API cs_disasm(csh ud, const uint8_t *buffer, size_t size, uint64_t offset, size_t count, cs_insn **insn) { struct cs_struct *handle; MCInst mci; uint16_t insn_size; size_t c = 0, i; unsigned int f = 0; // index of the next instruction in the cache cs_insn *insn_cache; // cache contains disassembled instructions void *total = NULL; size_t total_size = 0; // total size of output buffer containing all insns bool r; void *tmp; size_t skipdata_bytes; uint64_t offset_org; // save all the original info of the buffer size_t size_org; const uint8_t *buffer_org; unsigned int cache_size = INSN_CACHE_SIZE; size_t next_offset; handle = (struct cs_struct *)(uintptr_t)ud; if (!handle) { // FIXME: how to handle this case: // handle->errnum = CS_ERR_HANDLE; return 0; } handle->errnum = CS_ERR_OK; // reset IT block of ARM structure if (handle->arch == CS_ARCH_ARM) handle->ITBlock.size = 0; #ifdef CAPSTONE_USE_SYS_DYN_MEM if (count > 0 && count <= INSN_CACHE_SIZE) cache_size = (unsigned int) count; #endif // save the original offset for SKIPDATA buffer_org = buffer; offset_org = offset; size_org = size; total_size = sizeof(cs_insn) * cache_size; total = cs_mem_malloc(total_size); if (total == NULL) { // insufficient memory handle->errnum = CS_ERR_MEM; return 0; } insn_cache = total; while (size > 0) { MCInst_Init(&mci); mci.csh = handle; // relative branches need to know the address & size of current insn mci.address = offset; if (handle->detail) { // allocate memory for @detail pointer insn_cache->detail = cs_mem_malloc(sizeof(cs_detail)); } else { insn_cache->detail = NULL; } // save all the information for non-detailed mode mci.flat_insn = insn_cache; mci.flat_insn->address = offset; #ifdef CAPSTONE_DIET // zero out mnemonic & op_str mci.flat_insn->mnemonic[0] = '\0'; mci.flat_insn->op_str[0] = '\0'; #endif r = handle->disasm(ud, buffer, size, &mci, &insn_size, offset, handle->getinsn_info); if (r) { SStream ss; SStream_Init(&ss); mci.flat_insn->size = insn_size; // map internal instruction opcode to public insn ID handle->insn_id(handle, insn_cache, mci.Opcode); handle->printer(&mci, &ss, handle->printer_info); fill_insn(handle, insn_cache, ss.buffer, &mci, handle->post_printer, buffer); next_offset = insn_size; } else { // encounter a broken instruction // free memory of @detail pointer if (handle->detail) { cs_mem_free(insn_cache->detail); } // if there is no request to skip data, or remaining data is too small, // then bail out if (!handle->skipdata || handle->skipdata_size > size) break; if (handle->skipdata_setup.callback) { skipdata_bytes = handle->skipdata_setup.callback(buffer_org, size_org, (size_t)(offset - offset_org), handle->skipdata_setup.user_data); if (skipdata_bytes > size) // remaining data is not enough break; if (!skipdata_bytes) // user requested not to skip data, so bail out break; } else skipdata_bytes = handle->skipdata_size; // we have to skip some amount of data, depending on arch & mode insn_cache->id = 0; // invalid ID for this "data" instruction insn_cache->address = offset; insn_cache->size = (uint16_t)skipdata_bytes; memcpy(insn_cache->bytes, buffer, skipdata_bytes); #ifdef CAPSTONE_DIET insn_cache->mnemonic[0] = '\0'; insn_cache->op_str[0] = '\0'; #else strncpy(insn_cache->mnemonic, handle->skipdata_setup.mnemonic, sizeof(insn_cache->mnemonic) - 1); skipdata_opstr(insn_cache->op_str, buffer, skipdata_bytes); #endif insn_cache->detail = NULL; next_offset = skipdata_bytes; } // one more instruction entering the cache f++; // one more instruction disassembled c++; if (count > 0 && c == count) // already got requested number of instructions break; if (f == cache_size) { // full cache, so expand the cache to contain incoming insns cache_size = cache_size * 8 / 5; // * 1.6 ~ golden ratio total_size += (sizeof(cs_insn) * cache_size); tmp = cs_mem_realloc(total, total_size); if (tmp == NULL) { // insufficient memory if (handle->detail) { insn_cache = (cs_insn *)total; for (i = 0; i < c; i++, insn_cache++) cs_mem_free(insn_cache->detail); } cs_mem_free(total); *insn = NULL; handle->errnum = CS_ERR_MEM; return 0; } total = tmp; // continue to fill in the cache after the last instruction insn_cache = (cs_insn *)((char *)total + sizeof(cs_insn) * c); // reset f back to 0, so we fill in the cache from begining f = 0; } else insn_cache++; buffer += next_offset; size -= next_offset; offset += next_offset; } if (!c) { // we did not disassemble any instruction cs_mem_free(total); total = NULL; } else if (f != cache_size) { // total did not fully use the last cache, so downsize it tmp = cs_mem_realloc(total, total_size - (cache_size - f) * sizeof(*insn_cache)); if (tmp == NULL) { // insufficient memory // free all detail pointers if (handle->detail) { insn_cache = (cs_insn *)total; for (i = 0; i < c; i++, insn_cache++) cs_mem_free(insn_cache->detail); } cs_mem_free(total); *insn = NULL; handle->errnum = CS_ERR_MEM; return 0; } total = tmp; } *insn = total; return c; }