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