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
31 namespace google_breakpad {
32
DisassemblerX86(const uint8_t * bytecode,uint32_t size,uint32_t virtual_address)33 DisassemblerX86::DisassemblerX86(const uint8_t *bytecode,
34 uint32_t size,
35 uint32_t virtual_address) :
36 bytecode_(bytecode),
37 size_(size),
38 virtual_address_(virtual_address),
39 current_byte_offset_(0),
40 current_inst_offset_(0),
41 instr_valid_(false),
42 register_valid_(false),
43 pushed_bad_value_(false),
44 end_of_block_(false),
45 flags_(0) {
46 libdis::x86_init(libdis::opt_none, NULL, NULL);
47 }
48
~DisassemblerX86()49 DisassemblerX86::~DisassemblerX86() {
50 if (instr_valid_)
51 libdis::x86_oplist_free(¤t_instr_);
52
53 libdis::x86_cleanup();
54 }
55
NextInstruction()56 uint32_t DisassemblerX86::NextInstruction() {
57 if (instr_valid_)
58 libdis::x86_oplist_free(¤t_instr_);
59
60 if (current_byte_offset_ >= size_) {
61 instr_valid_ = false;
62 return 0;
63 }
64 uint32_t instr_size = 0;
65 instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
66 virtual_address_, current_byte_offset_,
67 ¤t_instr_);
68 if (instr_size == 0) {
69 instr_valid_ = false;
70 return 0;
71 }
72
73 current_byte_offset_ += instr_size;
74 current_inst_offset_++;
75 instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_);
76 if (!instr_valid_)
77 return 0;
78
79 if (current_instr_.type == libdis::insn_return)
80 end_of_block_ = true;
81 libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_);
82 libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_);
83
84 if (register_valid_) {
85 switch (current_instr_.group) {
86 // Flag branches based off of bad registers and calls that occur
87 // after pushing bad values.
88 case libdis::insn_controlflow:
89 switch (current_instr_.type) {
90 case libdis::insn_jmp:
91 case libdis::insn_jcc:
92 case libdis::insn_call:
93 case libdis::insn_callcc:
94 if (dest) {
95 switch (dest->type) {
96 case libdis::op_expression:
97 if (dest->data.expression.base.id == bad_register_.id)
98 flags_ |= DISX86_BAD_BRANCH_TARGET;
99 break;
100 case libdis::op_register:
101 if (dest->data.reg.id == bad_register_.id)
102 flags_ |= DISX86_BAD_BRANCH_TARGET;
103 break;
104 default:
105 if (pushed_bad_value_ &&
106 (current_instr_.type == libdis::insn_call ||
107 current_instr_.type == libdis::insn_callcc))
108 flags_ |= DISX86_BAD_ARGUMENT_PASSED;
109 break;
110 }
111 }
112 break;
113 default:
114 break;
115 }
116 break;
117
118 // Flag block data operations that use bad registers for src or dest.
119 case libdis::insn_string:
120 if (dest && dest->type == libdis::op_expression &&
121 dest->data.expression.base.id == bad_register_.id)
122 flags_ |= DISX86_BAD_BLOCK_WRITE;
123 if (src && src->type == libdis::op_expression &&
124 src->data.expression.base.id == bad_register_.id)
125 flags_ |= DISX86_BAD_BLOCK_READ;
126 break;
127
128 // Flag comparisons based on bad data.
129 case libdis::insn_comparison:
130 if ((dest && dest->type == libdis::op_expression &&
131 dest->data.expression.base.id == bad_register_.id) ||
132 (src && src->type == libdis::op_expression &&
133 src->data.expression.base.id == bad_register_.id) ||
134 (dest && dest->type == libdis::op_register &&
135 dest->data.reg.id == bad_register_.id) ||
136 (src && src->type == libdis::op_register &&
137 src->data.reg.id == bad_register_.id))
138 flags_ |= DISX86_BAD_COMPARISON;
139 break;
140
141 // Flag any other instruction which derefs a bad register for
142 // src or dest.
143 default:
144 if (dest && dest->type == libdis::op_expression &&
145 dest->data.expression.base.id == bad_register_.id)
146 flags_ |= DISX86_BAD_WRITE;
147 if (src && src->type == libdis::op_expression &&
148 src->data.expression.base.id == bad_register_.id)
149 flags_ |= DISX86_BAD_READ;
150 break;
151 }
152 }
153
154 // When a register is marked as tainted check if it is pushed.
155 // TODO(cdn): may also want to check for MOVs into EBP offsets.
156 if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
157 switch (dest->type) {
158 case libdis::op_expression:
159 if (dest->data.expression.base.id == bad_register_.id ||
160 dest->data.expression.index.id == bad_register_.id)
161 pushed_bad_value_ = true;
162 break;
163 case libdis::op_register:
164 if (dest->data.reg.id == bad_register_.id)
165 pushed_bad_value_ = true;
166 break;
167 default:
168 break;
169 }
170 }
171
172 // Check if a tainted register value is clobbered.
173 // For conditional MOVs and XCHGs assume that
174 // there is a hit.
175 if (register_valid_) {
176 switch (current_instr_.type) {
177 case libdis::insn_xor:
178 if (src && src->type == libdis::op_register &&
179 dest && dest->type == libdis::op_register &&
180 src->data.reg.id == bad_register_.id &&
181 src->data.reg.id == dest->data.reg.id)
182 register_valid_ = false;
183 break;
184 case libdis::insn_pop:
185 case libdis::insn_mov:
186 case libdis::insn_movcc:
187 if (dest && dest->type == libdis::op_register &&
188 dest->data.reg.id == bad_register_.id)
189 register_valid_ = false;
190 break;
191 case libdis::insn_popregs:
192 register_valid_ = false;
193 break;
194 case libdis::insn_xchg:
195 case libdis::insn_xchgcc:
196 if (dest && dest->type == libdis::op_register &&
197 src && src->type == libdis::op_register) {
198 if (dest->data.reg.id == bad_register_.id)
199 memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
200 else if (src->data.reg.id == bad_register_.id)
201 memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
202 }
203 break;
204 default:
205 break;
206 }
207 }
208
209 return instr_size;
210 }
211
setBadRead()212 bool DisassemblerX86::setBadRead() {
213 if (!instr_valid_)
214 return false;
215
216 libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_);
217 if (!operand || operand->type != libdis::op_expression)
218 return false;
219
220 memcpy(&bad_register_, &operand->data.expression.base,
221 sizeof(libdis::x86_reg_t));
222 register_valid_ = true;
223 return true;
224 }
225
setBadWrite()226 bool DisassemblerX86::setBadWrite() {
227 if (!instr_valid_)
228 return false;
229
230 libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_);
231 if (!operand || operand->type != libdis::op_expression)
232 return false;
233
234 memcpy(&bad_register_, &operand->data.expression.base,
235 sizeof(libdis::x86_reg_t));
236 register_valid_ = true;
237 return true;
238 }
239
240 } // namespace google_breakpad
241