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 
x86_disasm(unsigned char * buf,unsigned int buf_len,uint32_t buf_rva,unsigned int offset,x86_insn_t * insn)16 unsigned 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 
x86_disasm_range(unsigned char * buf,uint32_t buf_rva,unsigned int offset,unsigned int len,DISASM_CALLBACK func,void * arg)73 unsigned 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 
follow_insn_dest(x86_insn_t * insn)103 static 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 
insn_doesnt_return(x86_insn_t * insn)111 static inline int insn_doesnt_return( x86_insn_t *insn ) {
112         return( (insn->type == insn_jmp || insn->type == insn_return) ? 1: 0 );
113 }
114 
internal_resolver(x86_op_t * op,x86_insn_t * insn)115 static 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 
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)127 unsigned 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 */
x86_invariant_disasm(unsigned char * buf,int buf_len,x86_invariant_t * inv)196 size_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 }
x86_size_disasm(unsigned char * buf,unsigned int buf_len)204 size_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