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 ( (uint32_t)next_addr >= buf_rva &&
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