1 // Copyright 2010 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // disassembler_x86.cc: simple x86 disassembler.
30 //
31 // Provides single step disassembly of x86 bytecode and flags instructions
32 // that utilize known bad register values.
33 //
34 // Author: Cris Neckar
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h> // Must come first
38 #endif
39
40 #include "processor/disassembler_x86.h"
41
42 #include <string.h>
43
44 namespace google_breakpad {
45
DisassemblerX86(const uint8_t * bytecode,uint32_t size,uint32_t virtual_address)46 DisassemblerX86::DisassemblerX86(const uint8_t* bytecode,
47 uint32_t size,
48 uint32_t virtual_address) :
49 bytecode_(bytecode),
50 size_(size),
51 virtual_address_(virtual_address),
52 current_byte_offset_(0),
53 current_inst_offset_(0),
54 instr_valid_(false),
55 register_valid_(false),
56 pushed_bad_value_(false),
57 end_of_block_(false),
58 flags_(0) {
59 libdis::x86_init(libdis::opt_none, NULL, NULL);
60 }
61
~DisassemblerX86()62 DisassemblerX86::~DisassemblerX86() {
63 if (instr_valid_)
64 libdis::x86_oplist_free(¤t_instr_);
65
66 libdis::x86_cleanup();
67 }
68
NextInstruction()69 uint32_t DisassemblerX86::NextInstruction() {
70 if (instr_valid_)
71 libdis::x86_oplist_free(¤t_instr_);
72
73 if (current_byte_offset_ >= size_) {
74 instr_valid_ = false;
75 return 0;
76 }
77 uint32_t instr_size = 0;
78 instr_size = libdis::x86_disasm((unsigned char*)bytecode_, size_,
79 virtual_address_, current_byte_offset_,
80 ¤t_instr_);
81 if (instr_size == 0) {
82 instr_valid_ = false;
83 return 0;
84 }
85
86 current_byte_offset_ += instr_size;
87 current_inst_offset_++;
88 instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_);
89 if (!instr_valid_)
90 return 0;
91
92 if (current_instr_.type == libdis::insn_return)
93 end_of_block_ = true;
94 libdis::x86_op_t* src = libdis::x86_get_src_operand(¤t_instr_);
95 libdis::x86_op_t* dest = libdis::x86_get_dest_operand(¤t_instr_);
96
97 if (register_valid_) {
98 switch (current_instr_.group) {
99 // Flag branches based off of bad registers and calls that occur
100 // after pushing bad values.
101 case libdis::insn_controlflow:
102 switch (current_instr_.type) {
103 case libdis::insn_jmp:
104 case libdis::insn_jcc:
105 case libdis::insn_call:
106 case libdis::insn_callcc:
107 if (dest) {
108 switch (dest->type) {
109 case libdis::op_expression:
110 if (dest->data.expression.base.id == bad_register_.id)
111 flags_ |= DISX86_BAD_BRANCH_TARGET;
112 break;
113 case libdis::op_register:
114 if (dest->data.reg.id == bad_register_.id)
115 flags_ |= DISX86_BAD_BRANCH_TARGET;
116 break;
117 default:
118 if (pushed_bad_value_ &&
119 (current_instr_.type == libdis::insn_call ||
120 current_instr_.type == libdis::insn_callcc))
121 flags_ |= DISX86_BAD_ARGUMENT_PASSED;
122 break;
123 }
124 }
125 break;
126 default:
127 break;
128 }
129 break;
130
131 // Flag block data operations that use bad registers for src or dest.
132 case libdis::insn_string:
133 if (dest && dest->type == libdis::op_expression &&
134 dest->data.expression.base.id == bad_register_.id)
135 flags_ |= DISX86_BAD_BLOCK_WRITE;
136 if (src && src->type == libdis::op_expression &&
137 src->data.expression.base.id == bad_register_.id)
138 flags_ |= DISX86_BAD_BLOCK_READ;
139 break;
140
141 // Flag comparisons based on bad data.
142 case libdis::insn_comparison:
143 if ((dest && dest->type == libdis::op_expression &&
144 dest->data.expression.base.id == bad_register_.id) ||
145 (src && src->type == libdis::op_expression &&
146 src->data.expression.base.id == bad_register_.id) ||
147 (dest && dest->type == libdis::op_register &&
148 dest->data.reg.id == bad_register_.id) ||
149 (src && src->type == libdis::op_register &&
150 src->data.reg.id == bad_register_.id))
151 flags_ |= DISX86_BAD_COMPARISON;
152 break;
153
154 // Flag any other instruction which derefs a bad register for
155 // src or dest.
156 default:
157 if (dest && dest->type == libdis::op_expression &&
158 dest->data.expression.base.id == bad_register_.id)
159 flags_ |= DISX86_BAD_WRITE;
160 if (src && src->type == libdis::op_expression &&
161 src->data.expression.base.id == bad_register_.id)
162 flags_ |= DISX86_BAD_READ;
163 break;
164 }
165 }
166
167 // When a register is marked as tainted check if it is pushed.
168 // TODO(cdn): may also want to check for MOVs into EBP offsets.
169 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
170 switch (dest->type) {
171 case libdis::op_expression:
172 if (dest->data.expression.base.id == bad_register_.id ||
173 dest->data.expression.index.id == bad_register_.id)
174 pushed_bad_value_ = true;
175 break;
176 case libdis::op_register:
177 if (dest->data.reg.id == bad_register_.id)
178 pushed_bad_value_ = true;
179 break;
180 default:
181 break;
182 }
183 }
184
185 // Check if a tainted register value is clobbered.
186 // For conditional MOVs and XCHGs assume that
187 // there is a hit.
188 if (register_valid_) {
189 switch (current_instr_.type) {
190 case libdis::insn_xor:
191 if (src && src->type == libdis::op_register &&
192 dest && dest->type == libdis::op_register &&
193 src->data.reg.id == bad_register_.id &&
194 src->data.reg.id == dest->data.reg.id)
195 register_valid_ = false;
196 break;
197 case libdis::insn_pop:
198 case libdis::insn_mov:
199 case libdis::insn_movcc:
200 if (dest && dest->type == libdis::op_register &&
201 dest->data.reg.id == bad_register_.id)
202 register_valid_ = false;
203 break;
204 case libdis::insn_popregs:
205 register_valid_ = false;
206 break;
207 case libdis::insn_xchg:
208 case libdis::insn_xchgcc:
209 if (dest && dest->type == libdis::op_register &&
210 src && src->type == libdis::op_register) {
211 if (dest->data.reg.id == bad_register_.id)
212 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
213 else if (src->data.reg.id == bad_register_.id)
214 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
215 }
216 break;
217 default:
218 break;
219 }
220 }
221
222 return instr_size;
223 }
224
setBadRead()225 bool DisassemblerX86::setBadRead() {
226 if (!instr_valid_)
227 return false;
228
229 libdis::x86_op_t* operand = libdis::x86_get_src_operand(¤t_instr_);
230 if (!operand || operand->type != libdis::op_expression)
231 return false;
232
233 memcpy(&bad_register_, &operand->data.expression.base,
234 sizeof(libdis::x86_reg_t));
235 register_valid_ = true;
236 return true;
237 }
238
setBadWrite()239 bool DisassemblerX86::setBadWrite() {
240 if (!instr_valid_)
241 return false;
242
243 libdis::x86_op_t* operand = libdis::x86_get_dest_operand(¤t_instr_);
244 if (!operand || operand->type != libdis::op_expression)
245 return false;
246
247 memcpy(&bad_register_, &operand->data.expression.base,
248 sizeof(libdis::x86_reg_t));
249 register_valid_ = true;
250 return true;
251 }
252
253 } // namespace google_breakpad
254