x86_disasm.c revision 2b4274afc4fae883d1251a7a420e24fd526a9f16
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#include "libdis.h" 6#include "ia32_insn.h" 7#include "ia32_invariant.h" 8#include "x86_operand_list.h" 9 10 11#ifdef _MSC_VER 12 #define snprintf _snprintf 13 #define inline __inline 14#endif 15 16unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len, 17 uint32_t buf_rva, unsigned int offset, 18 x86_insn_t *insn ){ 19 int len, size; 20 unsigned char bytes[MAX_INSTRUCTION_SIZE]; 21 22 if ( ! buf || ! insn || ! buf_len ) { 23 /* caller screwed up somehow */ 24 return 0; 25 } 26 27 28 /* ensure we are all NULLed up */ 29 memset( insn, 0, sizeof(x86_insn_t) ); 30 insn->addr = buf_rva + offset; 31 insn->offset = offset; 32 /* default to invalid insn */ 33 insn->type = insn_invalid; 34 insn->group = insn_none; 35 36 if ( offset >= buf_len ) { 37 /* another caller screwup ;) */ 38 x86_report_error(report_disasm_bounds, (void*)(long)buf_rva+offset); 39 return 0; 40 } 41 42 len = buf_len - offset; 43 44 /* copy enough bytes for disassembly into buffer : this 45 * helps prevent buffer overruns at the end of a file */ 46 memset( bytes, 0, MAX_INSTRUCTION_SIZE ); 47 memcpy( bytes, &buf[offset], (len < MAX_INSTRUCTION_SIZE) ? len : 48 MAX_INSTRUCTION_SIZE ); 49 50 /* actually do the disassembly */ 51 /* TODO: allow switching when more disassemblers are added */ 52 size = ia32_disasm_addr( bytes, len, insn); 53 54 /* check and see if we had an invalid instruction */ 55 if (! size ) { 56 x86_report_error(report_invalid_insn, (void*)(long)buf_rva+offset ); 57 return 0; 58 } 59 60 /* check if we overran the end of the buffer */ 61 if ( size > len ) { 62 x86_report_error( report_insn_bounds, (void*)(long)buf_rva + offset ); 63 MAKE_INVALID( insn, bytes ); 64 return 0; 65 } 66 67 /* fill bytes field of insn */ 68 memcpy( insn->bytes, bytes, size ); 69 70 return size; 71} 72 73unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva, 74 unsigned int offset, unsigned int len, 75 DISASM_CALLBACK func, void *arg ) { 76 x86_insn_t insn; 77 unsigned int buf_len, size, count = 0, bytes = 0; 78 79 /* buf_len is implied by the arguments */ 80 buf_len = len + offset; 81 82 while ( bytes < len ) { 83 size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, 84 &insn ); 85 if ( size ) { 86 /* invoke callback if it exists */ 87 if ( func ) { 88 (*func)( &insn, arg ); 89 } 90 bytes += size; 91 count ++; 92 } else { 93 /* error */ 94 bytes++; /* try next byte */ 95 } 96 97 x86_oplist_free( &insn ); 98 } 99 100 return( count ); 101} 102 103static inline int follow_insn_dest( x86_insn_t *insn ) { 104 if ( insn->type == insn_jmp || insn->type == insn_jcc || 105 insn->type == insn_call || insn->type == insn_callcc ) { 106 return(1); 107 } 108 return(0); 109} 110 111static inline int insn_doesnt_return( x86_insn_t *insn ) { 112 return( (insn->type == insn_jmp || insn->type == insn_return) ? 1: 0 ); 113} 114 115static int32_t internal_resolver( x86_op_t *op, x86_insn_t *insn ){ 116 int32_t next_addr = -1; 117 if ( x86_optype_is_address(op->type) ) { 118 next_addr = op->data.sdword; 119 } else if ( op->type == op_relative_near ) { 120 next_addr = insn->addr + insn->size + op->data.relative_near; 121 } else if ( op->type == op_relative_far ) { 122 next_addr = insn->addr + insn->size + op->data.relative_far; 123 } 124 return( next_addr ); 125} 126 127unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len, 128 uint32_t buf_rva, unsigned int offset, 129 DISASM_CALLBACK func, void *arg, 130 DISASM_RESOLVER resolver, void *r_arg ){ 131 x86_insn_t insn; 132 x86_op_t *op; 133 int32_t next_addr; 134 uint32_t next_offset; 135 unsigned int size, count = 0, bytes = 0, cont = 1; 136 137 while ( cont && bytes < buf_len ) { 138 size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, 139 &insn ); 140 141 if ( size ) { 142 /* invoke callback if it exists */ 143 if ( func ) { 144 (*func)( &insn, arg ); 145 } 146 bytes += size; 147 count ++; 148 } else { 149 /* error */ 150 bytes++; /* try next byte */ 151 } 152 153 if ( follow_insn_dest(&insn) ) { 154 op = x86_get_dest_operand( &insn ); 155 next_addr = -1; 156 157 /* if caller supplied a resolver, use it to determine 158 * the address to disassemble */ 159 if ( resolver ) { 160 next_addr = resolver(op, &insn, r_arg); 161 } else { 162 next_addr = internal_resolver(op, &insn); 163 } 164 165 if (next_addr != -1 ) { 166 next_offset = next_addr - buf_rva; 167 /* if offset is in this buffer... */ 168 if ( next_offset >= 0 && 169 next_offset < buf_len ) { 170 /* go ahead and disassemble */ 171 count += x86_disasm_forward( buf, 172 buf_len, 173 buf_rva, 174 next_offset, 175 func, arg, 176 resolver, r_arg ); 177 } else { 178 /* report unresolved address */ 179 x86_report_error( report_disasm_bounds, 180 (void*)(long)next_addr ); 181 } 182 } 183 } /* end follow_insn */ 184 185 if ( insn_doesnt_return(&insn) ) { 186 /* stop disassembling */ 187 cont = 0; 188 } 189 190 x86_oplist_free( &insn ); 191 } 192 return( count ); 193} 194 195/* invariant instruction representation */ 196size_t x86_invariant_disasm( unsigned char *buf, int buf_len, 197 x86_invariant_t *inv ){ 198 if (! buf || ! buf_len || ! inv ) { 199 return(0); 200 } 201 202 return ia32_disasm_invariant(buf, buf_len, inv); 203} 204size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ) { 205 if (! buf || ! buf_len ) { 206 return(0); 207 } 208 209 return ia32_disasm_size(buf, buf_len); 210} 211