From d3ffe37c47dfdbf8af44087095fd3b85f9be803f Mon Sep 17 00:00:00 2001 From: Nguyen Anh Quynh Date: Wed, 9 Apr 2014 23:49:30 +0800 Subject: [PATCH] API: support SKIPDATA option (off by default) --- cs.c | 127 ++++++++++++++++++++++++++++++++++++++++++--- cs_priv.h | 3 ++ include/capstone.h | 31 ++++++++++- 3 files changed, 151 insertions(+), 10 deletions(-) diff --git a/cs.c b/cs.c index 302450898..168d111a4 100644 --- a/cs.c +++ b/cs.c @@ -17,6 +17,9 @@ #define INSN_CACHE_SIZE 8 #endif +// default SKIPDATA mnemonic +#define SKIPDATA_MNEM ".db" + cs_err (*arch_init[MAX_ARCH])(cs_struct *) = { NULL }; cs_err (*arch_option[MAX_ARCH]) (cs_struct *, cs_opt_type, size_t value) = { NULL }; void (*arch_destroy[MAX_ARCH]) (cs_struct *) = { NULL }; @@ -187,6 +190,9 @@ cs_err cs_open(cs_arch arch, cs_mode mode, csh *handle) // by default, do not break instruction into details ud->detail = CS_OPT_OFF; + // default skipdata setup + ud->skipdata_setup.mnemonic = SKIPDATA_MNEM; + cs_err err = arch_init[ud->arch](ud); if (err) { cs_mem_free(ud); @@ -287,6 +293,27 @@ static void fill_insn(struct cs_struct *handle, cs_insn *insn, char *buffer, MCI #endif } +// how many bytes will we skip when encountering data (CS_OPT_SKIPDATA)? +static uint8_t skipdata_size(cs_struct *handle) +{ + switch(handle->arch) { + default: + // should never reach + return -1; + case CS_ARCH_ARM: + case CS_ARCH_ARM64: + case CS_ARCH_MIPS: + case CS_ARCH_PPC: + case CS_ARCH_SPARC: + case CS_ARCH_SYSZ: + // skip 2 bytes due to instruction alignment + return 2; + case CS_ARCH_X86: + // X86 has no restriction on instruction alignment + return 1; + } +} + cs_err cs_option(csh ud, cs_opt_type type, size_t value) { archs_enable(); @@ -309,9 +336,25 @@ cs_err cs_option(csh ud, cs_opt_type type, size_t value) if (!handle) return CS_ERR_CSH; - if (type == CS_OPT_DETAIL) { - handle->detail = value; - return CS_ERR_OK; + switch(type) { + default: + break; + case CS_OPT_DETAIL: + handle->detail = value; + return CS_ERR_OK; + case CS_OPT_SKIPDATA: + handle->skipdata = (value == CS_OPT_ON); + if (handle->skipdata) { + if (handle->skipdata_size == 0) { + // set the default skipdata size + handle->skipdata_size = skipdata_size(handle); + } + } + return CS_ERR_OK; + case CS_OPT_SKIPDATA_SETUP: + if (value) + handle->skipdata_setup = *((cs_opt_skipdata *)value); + return CS_ERR_OK; } return arch_option[handle->arch](handle, type, value); @@ -330,6 +373,26 @@ static cs_insn *get_prev_insn(cs_insn *cache, unsigned int f, void *total, size_ return &cache[f - 1]; } +static void skipdata_opstr(char *opstr, const uint8_t *buffer, size_t size) +{ + char *p = opstr; + int len; + size_t i; + + if (!size) { + opstr[0] = '\0'; + return; + } + + len = sprintf(p, "0x%02x", buffer[0]); + p+= len; + + for(i = 1; i < size; i++) { + len = sprintf(p, ", 0x%02x", buffer[i]); + p+= len; + } +} + // 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) @@ -343,6 +406,8 @@ size_t cs_disasm_ex(csh ud, const uint8_t *buffer, size_t size, uint64_t offset, void *total = NULL; size_t total_size = 0; bool r; + void *tmp; + size_t skipdata_bytes; if (!handle) { // FIXME: how to handle this case: @@ -384,11 +449,8 @@ size_t cs_disasm_ex(csh ud, const uint8_t *buffer, size_t size, uint64_t offset, 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 - void *tmp; - total_size += (sizeof(cs_insn) * INSN_CACHE_SIZE); tmp = cs_mem_realloc(total, total_size); if (tmp == NULL) { // insufficient memory @@ -436,8 +498,57 @@ size_t cs_disasm_ex(csh ud, const uint8_t *buffer, size_t size, uint64_t offset, } } else { // encounter a broken instruction - // XXX: TODO: JOXEAN continue here - break; + // 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(offset, + 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[f].id = 0; // invalid ID for this "data" instruction + insn_cache[f].address = offset; + insn_cache[f].size = skipdata_bytes; + memcpy(insn_cache[f].bytes, buffer, skipdata_bytes); + strncpy(insn_cache[f].mnemonic, handle->skipdata_setup.mnemonic, + sizeof(insn_cache[f].mnemonic) - 1); + skipdata_opstr(insn_cache[f].op_str, buffer, skipdata_bytes); + insn_cache[f].detail = NULL; + + f++; + if (f == ARR_SIZE(insn_cache)) { + // resize total to contain newly disasm insns + + total_size += (sizeof(cs_insn) * INSN_CACHE_SIZE); + 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; + } + + buffer += skipdata_bytes; + size -= skipdata_bytes; + offset += skipdata_bytes; + c++; } } diff --git a/cs_priv.h b/cs_priv.h index 21155de7e..f2fec45aa 100644 --- a/cs_priv.h +++ b/cs_priv.h @@ -56,6 +56,9 @@ struct cs_struct { CombineInsn_t combine; GetRegisterName_t get_regname; uint8_t prev_prefix; // save previous prefix for combining instructions - X86 only. + bool skipdata; // set this to True if we skip data when disassembling + uint8_t skipdata_size; // how many bytes to skip + cs_opt_skipdata skipdata_setup; // user-defined skipdata setup }; #define MAX_ARCH 8 diff --git a/include/capstone.h b/include/capstone.h index d3588edd9..c9be3964a 100644 --- a/include/capstone.h +++ b/include/capstone.h @@ -90,18 +90,45 @@ typedef enum cs_opt_type { CS_OPT_DETAIL, // Break down instruction structure into details CS_OPT_MODE, // Change engine's mode at run-time CS_OPT_MEM, // User-defined dynamic memory related functions + CS_OPT_SKIPDATA, // Skip data when disassembling + CS_OPT_SKIPDATA_SETUP, // Setup user-defined function for SKIPDATA option } cs_opt_type; // Runtime option value (associated with option type above) typedef enum cs_opt_value { - CS_OPT_OFF = 0, // Turn OFF an option - default option for CS_OPT_DETAIL. - CS_OPT_ON = 3, // Turn ON an option (CS_OPT_DETAIL). + CS_OPT_OFF = 0, // Turn OFF an option - default option of CS_OPT_DETAIL, CS_OPT_SKIPDATA. + CS_OPT_ON = 3, // Turn ON an option (CS_OPT_DETAIL, CS_OPT_SKIPDATA). CS_OPT_SYNTAX_DEFAULT = 0, // Default asm syntax (CS_OPT_SYNTAX). CS_OPT_SYNTAX_INTEL, // X86 Intel asm syntax - default on X86 (CS_OPT_SYNTAX). CS_OPT_SYNTAX_ATT, // X86 ATT asm syntax (CS_OPT_SYNTAX). CS_OPT_SYNTAX_NOREGNAME, // Prints register name with only number (CS_OPT_SYNTAX) } cs_opt_value; +// User-defined callback function for SKIPDATA option +// @offset: offset of the input buffer passed to cs_disasm_ex(). +// This indicates the position of data Capstone is examining +// @user_data: user-data passed to cs_option() via @user_data field in +// cs_opt_skipdata struct below. +// @return: return number of bytes to skip, or 0 to stop disassembling. +typedef size_t (*cs_skipdata_cb_t)(size_t offset, void* user_data); + +// User-defined setup for SKIPDATA option +typedef struct cs_opt_skipdata { + // Capstone considers data to skip as special "instructions". + // User can specify the string for this instruction's "mnemonic" here. + // By default (if @mnemonic is NULL), Capstone use ".db". + const char *mnemonic; + // User-defined callback function to be called when Capstone hits data. + // If the returned value from this callback is positive (>0), Capstone will skip exactly + // that number of bytes & continue. Otherwise, if the callback returns 0, Capstone stops + // disassembling and returns immediately from cs_disasm_ex() + // NOTE: if this callback pointer is NULL, Capstone skip 1 byte on X86, and 2 bytes on + // every other architectures. + cs_skipdata_cb_t callback; // default value is NULL + // User-defined data to be passed to @callback function pointer. + void *user_data; +} cs_opt_skipdata; + #include "arm.h" #include "arm64.h"