1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/processor/disassembler_x86.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,241 @@ 1.4 +// copyright notice, this list of conditions and the following disclaimer 1.5 +// in the documentation and/or other materials provided with the 1.6 +// distribution. 1.7 +// * Neither the name of Google Inc. nor the names of its 1.8 +// contributors may be used to endorse or promote products derived from 1.9 +// this software without specific prior written permission. 1.10 +// 1.11 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.12 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.13 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.14 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.15 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.16 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.17 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.18 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.19 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.20 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.21 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.22 + 1.23 +// disassembler_x86.cc: simple x86 disassembler. 1.24 +// 1.25 +// Provides single step disassembly of x86 bytecode and flags instructions 1.26 +// that utilize known bad register values. 1.27 +// 1.28 +// Author: Cris Neckar 1.29 + 1.30 +#include "processor/disassembler_x86.h" 1.31 + 1.32 +#include <string.h> 1.33 +#include <unistd.h> 1.34 + 1.35 +namespace google_breakpad { 1.36 + 1.37 +DisassemblerX86::DisassemblerX86(const uint8_t *bytecode, 1.38 + uint32_t size, 1.39 + uint32_t virtual_address) : 1.40 + bytecode_(bytecode), 1.41 + size_(size), 1.42 + virtual_address_(virtual_address), 1.43 + current_byte_offset_(0), 1.44 + current_inst_offset_(0), 1.45 + instr_valid_(false), 1.46 + register_valid_(false), 1.47 + pushed_bad_value_(false), 1.48 + end_of_block_(false), 1.49 + flags_(0) { 1.50 + libdis::x86_init(libdis::opt_none, NULL, NULL); 1.51 +} 1.52 + 1.53 +DisassemblerX86::~DisassemblerX86() { 1.54 + if (instr_valid_) 1.55 + libdis::x86_oplist_free(¤t_instr_); 1.56 + 1.57 + libdis::x86_cleanup(); 1.58 +} 1.59 + 1.60 +uint32_t DisassemblerX86::NextInstruction() { 1.61 + if (instr_valid_) 1.62 + libdis::x86_oplist_free(¤t_instr_); 1.63 + 1.64 + if (current_byte_offset_ >= size_) { 1.65 + instr_valid_ = false; 1.66 + return 0; 1.67 + } 1.68 + uint32_t instr_size = 0; 1.69 + instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_, 1.70 + virtual_address_, current_byte_offset_, 1.71 + ¤t_instr_); 1.72 + if (instr_size == 0) { 1.73 + instr_valid_ = false; 1.74 + return 0; 1.75 + } 1.76 + 1.77 + current_byte_offset_ += instr_size; 1.78 + current_inst_offset_++; 1.79 + instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_); 1.80 + if (!instr_valid_) 1.81 + return 0; 1.82 + 1.83 + if (current_instr_.type == libdis::insn_return) 1.84 + end_of_block_ = true; 1.85 + libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_); 1.86 + libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_); 1.87 + 1.88 + if (register_valid_) { 1.89 + switch (current_instr_.group) { 1.90 + // Flag branches based off of bad registers and calls that occur 1.91 + // after pushing bad values. 1.92 + case libdis::insn_controlflow: 1.93 + switch (current_instr_.type) { 1.94 + case libdis::insn_jmp: 1.95 + case libdis::insn_jcc: 1.96 + case libdis::insn_call: 1.97 + case libdis::insn_callcc: 1.98 + if (dest) { 1.99 + switch (dest->type) { 1.100 + case libdis::op_expression: 1.101 + if (dest->data.expression.base.id == bad_register_.id) 1.102 + flags_ |= DISX86_BAD_BRANCH_TARGET; 1.103 + break; 1.104 + case libdis::op_register: 1.105 + if (dest->data.reg.id == bad_register_.id) 1.106 + flags_ |= DISX86_BAD_BRANCH_TARGET; 1.107 + break; 1.108 + default: 1.109 + if (pushed_bad_value_ && 1.110 + (current_instr_.type == libdis::insn_call || 1.111 + current_instr_.type == libdis::insn_callcc)) 1.112 + flags_ |= DISX86_BAD_ARGUMENT_PASSED; 1.113 + break; 1.114 + } 1.115 + } 1.116 + break; 1.117 + default: 1.118 + break; 1.119 + } 1.120 + break; 1.121 + 1.122 + // Flag block data operations that use bad registers for src or dest. 1.123 + case libdis::insn_string: 1.124 + if (dest && dest->type == libdis::op_expression && 1.125 + dest->data.expression.base.id == bad_register_.id) 1.126 + flags_ |= DISX86_BAD_BLOCK_WRITE; 1.127 + if (src && src->type == libdis::op_expression && 1.128 + src->data.expression.base.id == bad_register_.id) 1.129 + flags_ |= DISX86_BAD_BLOCK_READ; 1.130 + break; 1.131 + 1.132 + // Flag comparisons based on bad data. 1.133 + case libdis::insn_comparison: 1.134 + if ((dest && dest->type == libdis::op_expression && 1.135 + dest->data.expression.base.id == bad_register_.id) || 1.136 + (src && src->type == libdis::op_expression && 1.137 + src->data.expression.base.id == bad_register_.id) || 1.138 + (dest && dest->type == libdis::op_register && 1.139 + dest->data.reg.id == bad_register_.id) || 1.140 + (src && src->type == libdis::op_register && 1.141 + src->data.reg.id == bad_register_.id)) 1.142 + flags_ |= DISX86_BAD_COMPARISON; 1.143 + break; 1.144 + 1.145 + // Flag any other instruction which derefs a bad register for 1.146 + // src or dest. 1.147 + default: 1.148 + if (dest && dest->type == libdis::op_expression && 1.149 + dest->data.expression.base.id == bad_register_.id) 1.150 + flags_ |= DISX86_BAD_WRITE; 1.151 + if (src && src->type == libdis::op_expression && 1.152 + src->data.expression.base.id == bad_register_.id) 1.153 + flags_ |= DISX86_BAD_READ; 1.154 + break; 1.155 + } 1.156 + } 1.157 + 1.158 + // When a register is marked as tainted check if it is pushed. 1.159 + // TODO(cdn): may also want to check for MOVs into EBP offsets. 1.160 + if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { 1.161 + switch (dest->type) { 1.162 + case libdis::op_expression: 1.163 + if (dest->data.expression.base.id == bad_register_.id || 1.164 + dest->data.expression.index.id == bad_register_.id) 1.165 + pushed_bad_value_ = true; 1.166 + break; 1.167 + case libdis::op_register: 1.168 + if (dest->data.reg.id == bad_register_.id) 1.169 + pushed_bad_value_ = true; 1.170 + break; 1.171 + default: 1.172 + break; 1.173 + } 1.174 + } 1.175 + 1.176 + // Check if a tainted register value is clobbered. 1.177 + // For conditional MOVs and XCHGs assume that 1.178 + // there is a hit. 1.179 + if (register_valid_) { 1.180 + switch (current_instr_.type) { 1.181 + case libdis::insn_xor: 1.182 + if (src && src->type == libdis::op_register && 1.183 + dest && dest->type == libdis::op_register && 1.184 + src->data.reg.id == bad_register_.id && 1.185 + src->data.reg.id == dest->data.reg.id) 1.186 + register_valid_ = false; 1.187 + break; 1.188 + case libdis::insn_pop: 1.189 + case libdis::insn_mov: 1.190 + case libdis::insn_movcc: 1.191 + if (dest && dest->type == libdis::op_register && 1.192 + dest->data.reg.id == bad_register_.id) 1.193 + register_valid_ = false; 1.194 + break; 1.195 + case libdis::insn_popregs: 1.196 + register_valid_ = false; 1.197 + break; 1.198 + case libdis::insn_xchg: 1.199 + case libdis::insn_xchgcc: 1.200 + if (dest && dest->type == libdis::op_register && 1.201 + src && src->type == libdis::op_register) { 1.202 + if (dest->data.reg.id == bad_register_.id) 1.203 + memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); 1.204 + else if (src->data.reg.id == bad_register_.id) 1.205 + memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); 1.206 + } 1.207 + break; 1.208 + default: 1.209 + break; 1.210 + } 1.211 + } 1.212 + 1.213 + return instr_size; 1.214 +} 1.215 + 1.216 +bool DisassemblerX86::setBadRead() { 1.217 + if (!instr_valid_) 1.218 + return false; 1.219 + 1.220 + libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_); 1.221 + if (!operand || operand->type != libdis::op_expression) 1.222 + return false; 1.223 + 1.224 + memcpy(&bad_register_, &operand->data.expression.base, 1.225 + sizeof(libdis::x86_reg_t)); 1.226 + register_valid_ = true; 1.227 + return true; 1.228 +} 1.229 + 1.230 +bool DisassemblerX86::setBadWrite() { 1.231 + if (!instr_valid_) 1.232 + return false; 1.233 + 1.234 + libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_); 1.235 + if (!operand || operand->type != libdis::op_expression) 1.236 + return false; 1.237 + 1.238 + memcpy(&bad_register_, &operand->data.expression.base, 1.239 + sizeof(libdis::x86_reg_t)); 1.240 + register_valid_ = true; 1.241 + return true; 1.242 +} 1.243 + 1.244 +} // namespace google_breakpad