/ src / processor / disassembler_x86.cc
disassembler_x86.cc
  1  // Copyright 2010 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  // disassembler_x86.cc: simple x86 disassembler.
 30  //
 31  // Provides single step disassembly of x86 bytecode and flags instructions
 32  // that utilize known bad register values.
 33  //
 34  // Author: Cris Neckar
 35  
 36  #ifdef HAVE_CONFIG_H
 37  #include <config.h>  // Must come first
 38  #endif
 39  
 40  #include "processor/disassembler_x86.h"
 41  
 42  #include <string.h>
 43  
 44  namespace google_breakpad {
 45  
 46  DisassemblerX86::DisassemblerX86(const uint8_t* bytecode,
 47                                   uint32_t size,
 48                                   uint32_t virtual_address) :
 49                                       bytecode_(bytecode),
 50                                       size_(size),
 51                                       virtual_address_(virtual_address),
 52                                       current_byte_offset_(0),
 53                                       current_inst_offset_(0),
 54                                       instr_valid_(false),
 55                                       register_valid_(false),
 56                                       pushed_bad_value_(false),
 57                                       end_of_block_(false),
 58                                       flags_(0) {
 59    libdis::x86_init(libdis::opt_none, NULL, NULL);
 60  }
 61  
 62  DisassemblerX86::~DisassemblerX86() {
 63    if (instr_valid_)
 64      libdis::x86_oplist_free(&current_instr_);
 65  
 66    libdis::x86_cleanup();
 67  }
 68  
 69  uint32_t DisassemblerX86::NextInstruction() {
 70    if (instr_valid_)
 71      libdis::x86_oplist_free(&current_instr_);
 72  
 73    if (current_byte_offset_ >= size_) {
 74      instr_valid_ = false;
 75      return 0;
 76    }
 77    uint32_t instr_size = 0;
 78    instr_size = libdis::x86_disasm((unsigned char*)bytecode_, size_,
 79                            virtual_address_, current_byte_offset_,
 80                            &current_instr_);
 81    if (instr_size == 0) {
 82      instr_valid_ = false;
 83      return 0;
 84    }
 85  
 86    current_byte_offset_ += instr_size;
 87    current_inst_offset_++;
 88    instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
 89    if (!instr_valid_)
 90      return 0;
 91  
 92    if (current_instr_.type == libdis::insn_return)
 93      end_of_block_ = true;
 94    libdis::x86_op_t* src = libdis::x86_get_src_operand(&current_instr_);
 95    libdis::x86_op_t* dest = libdis::x86_get_dest_operand(&current_instr_);
 96  
 97    if (register_valid_) {
 98      switch (current_instr_.group) {
 99        // Flag branches based off of bad registers and calls that occur
100        // after pushing bad values.
101        case libdis::insn_controlflow:
102          switch (current_instr_.type) {
103            case libdis::insn_jmp:
104            case libdis::insn_jcc:
105            case libdis::insn_call:
106            case libdis::insn_callcc:
107              if (dest) {
108                switch (dest->type) {
109                  case libdis::op_expression:
110                    if (dest->data.expression.base.id == bad_register_.id)
111                      flags_ |= DISX86_BAD_BRANCH_TARGET;
112                    break;
113                  case libdis::op_register:
114                    if (dest->data.reg.id == bad_register_.id)
115                      flags_ |= DISX86_BAD_BRANCH_TARGET;
116                    break;
117                  default:
118                    if (pushed_bad_value_ &&
119                        (current_instr_.type == libdis::insn_call ||
120                        current_instr_.type == libdis::insn_callcc))
121                      flags_ |= DISX86_BAD_ARGUMENT_PASSED;
122                    break;
123                }
124              }
125              break;
126            default:
127              break;
128          }
129          break;
130  
131        // Flag block data operations that use bad registers for src or dest.
132        case libdis::insn_string:
133          if (dest && dest->type == libdis::op_expression &&
134              dest->data.expression.base.id == bad_register_.id)
135            flags_ |= DISX86_BAD_BLOCK_WRITE;
136          if (src && src->type == libdis::op_expression &&
137              src->data.expression.base.id == bad_register_.id)
138            flags_ |= DISX86_BAD_BLOCK_READ;
139          break;
140  
141        // Flag comparisons based on bad data.
142        case libdis::insn_comparison:
143          if ((dest && dest->type == libdis::op_expression &&
144              dest->data.expression.base.id == bad_register_.id) ||
145              (src && src->type == libdis::op_expression &&
146              src->data.expression.base.id == bad_register_.id) ||
147              (dest && dest->type == libdis::op_register &&
148              dest->data.reg.id == bad_register_.id) ||
149              (src && src->type == libdis::op_register &&
150              src->data.reg.id == bad_register_.id))
151            flags_ |= DISX86_BAD_COMPARISON;
152          break;
153  
154        // Flag any other instruction which derefs a bad register for
155        // src or dest.
156        default:
157          if (dest && dest->type == libdis::op_expression &&
158              dest->data.expression.base.id == bad_register_.id)
159            flags_ |= DISX86_BAD_WRITE;
160          if (src && src->type == libdis::op_expression &&
161              src->data.expression.base.id == bad_register_.id)
162            flags_ |= DISX86_BAD_READ;
163          break;
164      }
165    }
166  
167    // When a register is marked as tainted check if it is pushed.
168    // TODO(cdn): may also want to check for MOVs into EBP offsets.
169    if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
170      switch (dest->type) {
171        case libdis::op_expression:
172          if (dest->data.expression.base.id == bad_register_.id ||
173              dest->data.expression.index.id == bad_register_.id)
174            pushed_bad_value_ = true;
175          break;
176        case libdis::op_register:
177          if (dest->data.reg.id == bad_register_.id)
178            pushed_bad_value_ = true;
179          break;
180        default:
181          break;
182      }
183    }
184  
185    // Check if a tainted register value is clobbered.
186    // For conditional MOVs and XCHGs assume that
187    // there is a hit.
188    if (register_valid_) {
189      switch (current_instr_.type) {
190        case libdis::insn_xor:
191          if (src && src->type == libdis::op_register &&
192              dest && dest->type == libdis::op_register &&
193              src->data.reg.id == bad_register_.id &&
194              src->data.reg.id == dest->data.reg.id)
195            register_valid_ = false;
196          break;
197        case libdis::insn_pop:
198        case libdis::insn_mov:
199        case libdis::insn_movcc:
200          if (dest && dest->type == libdis::op_register &&
201              dest->data.reg.id == bad_register_.id)
202            register_valid_ = false;
203          break;
204        case libdis::insn_popregs:
205          register_valid_ = false;
206          break;
207        case libdis::insn_xchg:
208        case libdis::insn_xchgcc:
209          if (dest && dest->type == libdis::op_register &&
210              src && src->type == libdis::op_register) {
211            if (dest->data.reg.id == bad_register_.id)
212              memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
213            else if (src->data.reg.id == bad_register_.id)
214              memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
215          }
216          break;
217        default:
218          break;
219      }
220    }
221  
222    return instr_size;
223  }
224  
225  bool DisassemblerX86::setBadRead() {
226    if (!instr_valid_)
227      return false;
228  
229    libdis::x86_op_t* operand = libdis::x86_get_src_operand(&current_instr_);
230    if (!operand || operand->type != libdis::op_expression)
231      return false;
232  
233    memcpy(&bad_register_, &operand->data.expression.base,
234           sizeof(libdis::x86_reg_t));
235    register_valid_ = true;
236    return true;
237  }
238  
239  bool DisassemblerX86::setBadWrite() {
240    if (!instr_valid_)
241      return false;
242  
243    libdis::x86_op_t* operand = libdis::x86_get_dest_operand(&current_instr_);
244    if (!operand || operand->type != libdis::op_expression)
245      return false;
246  
247    memcpy(&bad_register_, &operand->data.expression.base,
248           sizeof(libdis::x86_reg_t));
249    register_valid_ = true;
250    return true;
251  }
252  
253  }  // namespace google_breakpad