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