/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. // disassembler_x86.cc: simple x86 disassembler.
  20. //
  21. // Provides single step disassembly of x86 bytecode and flags instructions
  22. // that utilize known bad register values.
  23. //
  24. // Author: Cris Neckar
  25. #include "processor/disassembler_x86.h"
  26. #include <string.h>
  27. #include <unistd.h>
  28. namespace google_breakpad {
  29. DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode,
  30. u_int32_t size,
  31. u_int32_t virtual_address) :
  32. bytecode_(bytecode),
  33. size_(size),
  34. virtual_address_(virtual_address),
  35. current_byte_offset_(0),
  36. current_inst_offset_(0),
  37. instr_valid_(false),
  38. register_valid_(false),
  39. pushed_bad_value_(false),
  40. end_of_block_(false),
  41. flags_(0) {
  42. libdis::x86_init(libdis::opt_none, NULL, NULL);
  43. }
  44. DisassemblerX86::~DisassemblerX86() {
  45. if (instr_valid_)
  46. libdis::x86_oplist_free(&current_instr_);
  47. libdis::x86_cleanup();
  48. }
  49. u_int32_t DisassemblerX86::NextInstruction() {
  50. if (instr_valid_)
  51. libdis::x86_oplist_free(&current_instr_);
  52. if (current_byte_offset_ >= size_) {
  53. instr_valid_ = false;
  54. return 0;
  55. }
  56. u_int32_t instr_size = 0;
  57. instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
  58. virtual_address_, current_byte_offset_,
  59. &current_instr_);
  60. if (instr_size == 0) {
  61. instr_valid_ = false;
  62. return 0;
  63. }
  64. current_byte_offset_ += instr_size;
  65. current_inst_offset_++;
  66. instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
  67. if (!instr_valid_)
  68. return 0;
  69. if (current_instr_.type == libdis::insn_return)
  70. end_of_block_ = true;
  71. libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_);
  72. libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_);
  73. if (register_valid_) {
  74. switch (current_instr_.group) {
  75. // Flag branches based off of bad registers and calls that occur
  76. // after pushing bad values.
  77. case libdis::insn_controlflow:
  78. switch (current_instr_.type) {
  79. case libdis::insn_jmp:
  80. case libdis::insn_jcc:
  81. case libdis::insn_call:
  82. case libdis::insn_callcc:
  83. if (dest) {
  84. switch (dest->type) {
  85. case libdis::op_expression:
  86. if (dest->data.expression.base.id == bad_register_.id)
  87. flags_ |= DISX86_BAD_BRANCH_TARGET;
  88. break;
  89. case libdis::op_register:
  90. if (dest->data.reg.id == bad_register_.id)
  91. flags_ |= DISX86_BAD_BRANCH_TARGET;
  92. break;
  93. default:
  94. if (pushed_bad_value_ &&
  95. (current_instr_.type == libdis::insn_call ||
  96. current_instr_.type == libdis::insn_callcc))
  97. flags_ |= DISX86_BAD_ARGUMENT_PASSED;
  98. break;
  99. }
  100. }
  101. break;
  102. default:
  103. break;
  104. }
  105. break;
  106. // Flag block data operations that use bad registers for src or dest.
  107. case libdis::insn_string:
  108. if (dest && dest->type == libdis::op_expression &&
  109. dest->data.expression.base.id == bad_register_.id)
  110. flags_ |= DISX86_BAD_BLOCK_WRITE;
  111. if (src && src->type == libdis::op_expression &&
  112. src->data.expression.base.id == bad_register_.id)
  113. flags_ |= DISX86_BAD_BLOCK_READ;
  114. break;
  115. // Flag comparisons based on bad data.
  116. case libdis::insn_comparison:
  117. if ((dest && dest->type == libdis::op_expression &&
  118. dest->data.expression.base.id == bad_register_.id) ||
  119. (src && src->type == libdis::op_expression &&
  120. src->data.expression.base.id == bad_register_.id) ||
  121. (dest && dest->type == libdis::op_register &&
  122. dest->data.reg.id == bad_register_.id) ||
  123. (src && src->type == libdis::op_register &&
  124. src->data.reg.id == bad_register_.id))
  125. flags_ |= DISX86_BAD_COMPARISON;
  126. break;
  127. // Flag any other instruction which derefs a bad register for
  128. // src or dest.
  129. default:
  130. if (dest && dest->type == libdis::op_expression &&
  131. dest->data.expression.base.id == bad_register_.id)
  132. flags_ |= DISX86_BAD_WRITE;
  133. if (src && src->type == libdis::op_expression &&
  134. src->data.expression.base.id == bad_register_.id)
  135. flags_ |= DISX86_BAD_READ;
  136. break;
  137. }
  138. }
  139. // When a register is marked as tainted check if it is pushed.
  140. // TODO(cdn): may also want to check for MOVs into EBP offsets.
  141. if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
  142. switch (dest->type) {
  143. case libdis::op_expression:
  144. if (dest->data.expression.base.id == bad_register_.id ||
  145. dest->data.expression.index.id == bad_register_.id)
  146. pushed_bad_value_ = true;
  147. break;
  148. case libdis::op_register:
  149. if (dest->data.reg.id == bad_register_.id)
  150. pushed_bad_value_ = true;
  151. break;
  152. default:
  153. break;
  154. }
  155. }
  156. // Check if a tainted register value is clobbered.
  157. // For conditional MOVs and XCHGs assume that
  158. // there is a hit.
  159. if (register_valid_) {
  160. switch (current_instr_.type) {
  161. case libdis::insn_xor:
  162. if (src && src->type == libdis::op_register &&
  163. dest && dest->type == libdis::op_register &&
  164. src->data.reg.id == bad_register_.id &&
  165. src->data.reg.id == dest->data.reg.id)
  166. register_valid_ = false;
  167. break;
  168. case libdis::insn_pop:
  169. case libdis::insn_mov:
  170. case libdis::insn_movcc:
  171. if (dest && dest->type == libdis::op_register &&
  172. dest->data.reg.id == bad_register_.id)
  173. register_valid_ = false;
  174. break;
  175. case libdis::insn_popregs:
  176. register_valid_ = false;
  177. break;
  178. case libdis::insn_xchg:
  179. case libdis::insn_xchgcc:
  180. if (dest && dest->type == libdis::op_register &&
  181. src && src->type == libdis::op_register) {
  182. if (dest->data.reg.id == bad_register_.id)
  183. memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
  184. else if (src->data.reg.id == bad_register_.id)
  185. memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
  186. }
  187. break;
  188. default:
  189. break;
  190. }
  191. }
  192. return instr_size;
  193. }
  194. bool DisassemblerX86::setBadRead() {
  195. if (!instr_valid_)
  196. return false;
  197. libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_);
  198. if (!operand || operand->type != libdis::op_expression)
  199. return false;
  200. memcpy(&bad_register_, &operand->data.expression.base,
  201. sizeof(libdis::x86_reg_t));
  202. register_valid_ = true;
  203. return true;
  204. }
  205. bool DisassemblerX86::setBadWrite() {
  206. if (!instr_valid_)
  207. return false;
  208. libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_);
  209. if (!operand || operand->type != libdis::op_expression)
  210. return false;
  211. memcpy(&bad_register_, &operand->data.expression.base,
  212. sizeof(libdis::x86_reg_t));
  213. register_valid_ = true;
  214. return true;
  215. }
  216. } // namespace google_breakpad