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
32namespace google_breakpad {
33
34DisassemblerX86::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
50DisassemblerX86::~DisassemblerX86() {
51  if (instr_valid_)
52    libdis::x86_oplist_free(&current_instr_);
53
54  libdis::x86_cleanup();
55}
56
57uint32_t DisassemblerX86::NextInstruction() {
58  if (instr_valid_)
59    libdis::x86_oplist_free(&current_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                          &current_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(&current_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(&current_instr_);
83  libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_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
213bool DisassemblerX86::setBadRead() {
214  if (!instr_valid_)
215    return false;
216
217  libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_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
227bool DisassemblerX86::setBadWrite() {
228  if (!instr_valid_)
229    return false;
230
231  libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_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