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