| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "libdis.h" |
| #include "ia32_insn.h" |
| #include "ia32_invariant.h" |
| #include "x86_operand_list.h" |
| |
| |
| #ifdef _MSC_VER |
| #define snprintf _snprintf |
| #define inline __inline |
| #endif |
| |
| unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len, |
| uint32_t buf_rva, unsigned int offset, |
| x86_insn_t *insn ){ |
| int len, size; |
| unsigned char bytes[MAX_INSTRUCTION_SIZE]; |
| |
| if ( ! buf || ! insn || ! buf_len ) { |
| /* caller screwed up somehow */ |
| return 0; |
| } |
| |
| |
| /* ensure we are all NULLed up */ |
| memset( insn, 0, sizeof(x86_insn_t) ); |
| insn->addr = buf_rva + offset; |
| insn->offset = offset; |
| /* default to invalid insn */ |
| insn->type = insn_invalid; |
| insn->group = insn_none; |
| |
| if ( offset >= buf_len ) { |
| /* another caller screwup ;) */ |
| x86_report_error(report_disasm_bounds, (void*)(long)(buf_rva+offset)); |
| return 0; |
| } |
| |
| len = buf_len - offset; |
| |
| /* copy enough bytes for disassembly into buffer : this |
| * helps prevent buffer overruns at the end of a file */ |
| memset( bytes, 0, MAX_INSTRUCTION_SIZE ); |
| memcpy( bytes, &buf[offset], (len < MAX_INSTRUCTION_SIZE) ? len : |
| MAX_INSTRUCTION_SIZE ); |
| |
| /* actually do the disassembly */ |
| /* TODO: allow switching when more disassemblers are added */ |
| size = ia32_disasm_addr( bytes, len, insn); |
| |
| /* check and see if we had an invalid instruction */ |
| if (! size ) { |
| x86_report_error(report_invalid_insn, (void*)(long)(buf_rva+offset)); |
| return 0; |
| } |
| |
| /* check if we overran the end of the buffer */ |
| if ( size > len ) { |
| x86_report_error( report_insn_bounds, (void*)(long)(buf_rva + offset)); |
| MAKE_INVALID( insn, bytes ); |
| return 0; |
| } |
| |
| /* fill bytes field of insn */ |
| memcpy( insn->bytes, bytes, size ); |
| |
| return size; |
| } |
| |
| unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva, |
| unsigned int offset, unsigned int len, |
| DISASM_CALLBACK func, void *arg ) { |
| x86_insn_t insn; |
| unsigned int buf_len, size, count = 0, bytes = 0; |
| |
| /* buf_len is implied by the arguments */ |
| buf_len = len + offset; |
| |
| while ( bytes < len ) { |
| size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, |
| &insn ); |
| if ( size ) { |
| /* invoke callback if it exists */ |
| if ( func ) { |
| (*func)( &insn, arg ); |
| } |
| bytes += size; |
| count ++; |
| } else { |
| /* error */ |
| bytes++; /* try next byte */ |
| } |
| |
| x86_oplist_free( &insn ); |
| } |
| |
| return( count ); |
| } |
| |
| static inline int follow_insn_dest( x86_insn_t *insn ) { |
| if ( insn->type == insn_jmp || insn->type == insn_jcc || |
| insn->type == insn_call || insn->type == insn_callcc ) { |
| return(1); |
| } |
| return(0); |
| } |
| |
| static inline int insn_doesnt_return( x86_insn_t *insn ) { |
| return( (insn->type == insn_jmp || insn->type == insn_return) ? 1: 0 ); |
| } |
| |
| static int32_t internal_resolver( x86_op_t *op, x86_insn_t *insn ){ |
| int32_t next_addr = -1; |
| if ( x86_optype_is_address(op->type) ) { |
| next_addr = op->data.sdword; |
| } else if ( op->type == op_relative_near ) { |
| next_addr = insn->addr + insn->size + op->data.relative_near; |
| } else if ( op->type == op_relative_far ) { |
| next_addr = insn->addr + insn->size + op->data.relative_far; |
| } |
| return( next_addr ); |
| } |
| |
| unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len, |
| uint32_t buf_rva, unsigned int offset, |
| DISASM_CALLBACK func, void *arg, |
| DISASM_RESOLVER resolver, void *r_arg ){ |
| x86_insn_t insn; |
| x86_op_t *op; |
| int32_t next_addr; |
| uint32_t next_offset; |
| unsigned int size, count = 0, bytes = 0, cont = 1; |
| |
| while ( cont && bytes < buf_len ) { |
| size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, |
| &insn ); |
| |
| if ( size ) { |
| /* invoke callback if it exists */ |
| if ( func ) { |
| (*func)( &insn, arg ); |
| } |
| bytes += size; |
| count ++; |
| } else { |
| /* error */ |
| bytes++; /* try next byte */ |
| } |
| |
| if ( follow_insn_dest(&insn) ) { |
| op = x86_get_dest_operand( &insn ); |
| next_addr = -1; |
| |
| /* if caller supplied a resolver, use it to determine |
| * the address to disassemble */ |
| if ( resolver ) { |
| next_addr = resolver(op, &insn, r_arg); |
| } else { |
| next_addr = internal_resolver(op, &insn); |
| } |
| |
| if (next_addr != -1 ) { |
| next_offset = next_addr - buf_rva; |
| /* if offset is in this buffer... */ |
| if ( (uint32_t)next_addr >= buf_rva && |
| next_offset < buf_len ) { |
| /* go ahead and disassemble */ |
| count += x86_disasm_forward( buf, |
| buf_len, |
| buf_rva, |
| next_offset, |
| func, arg, |
| resolver, r_arg ); |
| } else { |
| /* report unresolved address */ |
| x86_report_error( report_disasm_bounds, |
| (void*)(long)next_addr ); |
| } |
| } |
| } /* end follow_insn */ |
| |
| if ( insn_doesnt_return(&insn) ) { |
| /* stop disassembling */ |
| cont = 0; |
| } |
| |
| x86_oplist_free( &insn ); |
| } |
| return( count ); |
| } |
| |
| /* invariant instruction representation */ |
| size_t x86_invariant_disasm( unsigned char *buf, int buf_len, |
| x86_invariant_t *inv ){ |
| if (! buf || ! buf_len || ! inv ) { |
| return(0); |
| } |
| |
| return ia32_disasm_invariant(buf, buf_len, inv); |
| } |
| size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ) { |
| if (! buf || ! buf_len ) { |
| return(0); |
| } |
| |
| return ia32_disasm_size(buf, buf_len); |
| } |