PageRenderTime 56ms CodeModel.GetById 18ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/processor/disassembler_x86.cc

http://github.com/tomahawk-player/tomahawk
C++ | 241 lines | 181 code | 25 blank | 35 comment | 90 complexity | 33c2ec589908537ab395d19a9d99f9bf MD5 | raw file
  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 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  if (instr_valid_)
 52    libdis::x86_oplist_free(&current_instr_);
 53
 54  libdis::x86_cleanup();
 55}
 56
 57u_int32_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  u_int32_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