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