|
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 |
|
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 |
|
50 DisassemblerX86::~DisassemblerX86() { |
|
51 if (instr_valid_) |
|
52 libdis::x86_oplist_free(¤t_instr_); |
|
53 |
|
54 libdis::x86_cleanup(); |
|
55 } |
|
56 |
|
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 |
|
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 |
|
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 |