mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Add x86_64 debug support
This commit is contained in:
@@ -6,3 +6,4 @@
|
||||
from .arch_x86 import ArchX86
|
||||
from .arch_mips import ArchMIPS
|
||||
from .arch_arm import ArchARM, ArchCORTEX_M
|
||||
from .arch_x8664 import ArchX8664
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
from typing import Mapping
|
||||
|
||||
from .arch import Arch
|
||||
|
||||
class ArchX8664(Arch):
|
||||
'''
|
||||
This is currently mostly just a copy of x86 - other than the size of archbits. Some of this may be wrong.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@property
|
||||
def arch_insn_size(self):
|
||||
'''
|
||||
Architecture maximum instruction size. x86_64 instructions are a maximum size of 15 bytes.
|
||||
|
||||
@returns bytes
|
||||
'''
|
||||
|
||||
return 15
|
||||
|
||||
@property
|
||||
def regs(self):
|
||||
return (
|
||||
"rax", "rbx", "rcx", "rdx",
|
||||
"rsp", "rbp", "rsi", "rdi",
|
||||
"rip", "r8", "r9", "r10",
|
||||
"r11", "r12", "r13", "r14",
|
||||
"r15", "ss", "cs", "ds", "es",
|
||||
"fs", "gs", "eflags"
|
||||
)
|
||||
|
||||
@property
|
||||
def archbit(self):
|
||||
'''
|
||||
Architecture maximum register size. x86 is a maximum of 4 bytes.
|
||||
|
||||
@returns bytes
|
||||
'''
|
||||
|
||||
return 8
|
||||
|
||||
def read_insn(self, address: int) -> bytes:
|
||||
# Due to the variadicc length of x86 instructions
|
||||
# always assume the maximum size for disassembler to tell
|
||||
# what it is.
|
||||
|
||||
return self.read_mem(address, self.arch_insn_size)
|
||||
|
||||
@staticmethod
|
||||
def get_flags(bits: int) -> Mapping[str, bool]:
|
||||
|
||||
return {
|
||||
"CF" : bits & 0x0001 != 0, # CF, carry flag
|
||||
"PF" : bits & 0x0004 != 0, # PF, parity flag
|
||||
"AF" : bits & 0x0010 != 0, # AF, adjust flag
|
||||
"ZF" : bits & 0x0040 != 0, # ZF, zero flag
|
||||
"SF" : bits & 0x0080 != 0, # SF, sign flag
|
||||
"OF" : bits & 0x0800 != 0, # OF, overflow flag
|
||||
}
|
||||
@@ -6,3 +6,4 @@
|
||||
from .branch_predictor_x86 import BranchPredictorX86
|
||||
from .branch_predictor_mips import BranchPredictorMIPS
|
||||
from .branch_predictor_arm import BranchPredictorARM, BranchPredictorCORTEX_M
|
||||
from .branch_predictor_x8664 import BranchPredictorX8664
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
|
||||
|
||||
import ast, re
|
||||
|
||||
from .branch_predictor import *
|
||||
from ..arch import ArchX8664
|
||||
from ..misc import check_and_eval
|
||||
|
||||
class BranchPredictorX8664(BranchPredictor, ArchX8664):
|
||||
"""
|
||||
predictor for X86
|
||||
"""
|
||||
|
||||
class ParseError(Exception):
|
||||
"""
|
||||
indicate parser error
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self, ql):
|
||||
super().__init__(ql)
|
||||
ArchX8664.__init__(self)
|
||||
|
||||
def predict(self):
|
||||
prophecy = self.Prophecy()
|
||||
line = self.disasm(self.cur_addr)
|
||||
|
||||
jump_table = {
|
||||
# conditional jump
|
||||
|
||||
"jo" : (lambda C, P, A, Z, S, O: O == 1),
|
||||
"jno" : (lambda C, P, A, Z, S, O: O == 0),
|
||||
|
||||
"js" : (lambda C, P, A, Z, S, O: S == 1),
|
||||
"jns" : (lambda C, P, A, Z, S, O: S == 0),
|
||||
|
||||
"je" : (lambda C, P, A, Z, S, O: Z == 1),
|
||||
"jz" : (lambda C, P, A, Z, S, O: Z == 1),
|
||||
|
||||
"jne" : (lambda C, P, A, Z, S, O: Z == 0),
|
||||
"jnz" : (lambda C, P, A, Z, S, O: Z == 0),
|
||||
|
||||
"jb" : (lambda C, P, A, Z, S, O: C == 1),
|
||||
"jc" : (lambda C, P, A, Z, S, O: C == 1),
|
||||
"jnae" : (lambda C, P, A, Z, S, O: C == 1),
|
||||
|
||||
"jnb" : (lambda C, P, A, Z, S, O: C == 0),
|
||||
"jnc" : (lambda C, P, A, Z, S, O: C == 0),
|
||||
"jae" : (lambda C, P, A, Z, S, O: C == 0),
|
||||
|
||||
"jbe" : (lambda C, P, A, Z, S, O: C == 1 or Z == 1),
|
||||
"jna" : (lambda C, P, A, Z, S, O: C == 1 or Z == 1),
|
||||
|
||||
"ja" : (lambda C, P, A, Z, S, O: C == 0 and Z == 0),
|
||||
"jnbe" : (lambda C, P, A, Z, S, O: C == 0 and Z == 0),
|
||||
|
||||
"jl" : (lambda C, P, A, Z, S, O: S != O),
|
||||
"jnge" : (lambda C, P, A, Z, S, O: S != O),
|
||||
|
||||
"jge" : (lambda C, P, A, Z, S, O: S == O),
|
||||
"jnl" : (lambda C, P, A, Z, S, O: S == O),
|
||||
|
||||
"jle" : (lambda C, P, A, Z, S, O: Z == 1 or S != O),
|
||||
"jng" : (lambda C, P, A, Z, S, O: Z == 1 or S != O),
|
||||
|
||||
"jg" : (lambda C, P, A, Z, S, O: Z == 0 or S == O),
|
||||
"jnle" : (lambda C, P, A, Z, S, O: Z == 0 or S == O),
|
||||
|
||||
"jp" : (lambda C, P, A, Z, S, O: P == 1),
|
||||
"jpe" : (lambda C, P, A, Z, S, O: P == 1),
|
||||
|
||||
"jnp" : (lambda C, P, A, Z, S, O: P == 0),
|
||||
"jpo" : (lambda C, P, A, Z, S, O: P == 0),
|
||||
|
||||
# unconditional jump
|
||||
|
||||
"call" : (lambda *_: True),
|
||||
"jmp" : (lambda *_: True),
|
||||
|
||||
}
|
||||
|
||||
jump_reg_table = {
|
||||
"jcxz" : (lambda cx: cx == 0),
|
||||
"jecxz" : (lambda ecx: ecx == 0),
|
||||
"jrcxz" : (lambda rcx: rcx == 0),
|
||||
}
|
||||
|
||||
if line.mnemonic in jump_table:
|
||||
eflags = self.get_flags(self.ql.arch.regs.eflags).values()
|
||||
prophecy.going = jump_table.get(line.mnemonic)(*eflags)
|
||||
|
||||
elif line.mnemonic in jump_reg_table:
|
||||
prophecy.going = jump_reg_table.get(line.mnemonic)(self.ql.arch.regs.ecx)
|
||||
|
||||
if prophecy.going:
|
||||
takeaway_list = ["ptr", "dword", "[", "]"]
|
||||
|
||||
if len(line.op_str.split()) > 1:
|
||||
new_line = line.op_str.replace(":", "+")
|
||||
for each in takeaway_list:
|
||||
new_line = new_line.replace(each, " ")
|
||||
|
||||
new_line = " ".join(new_line.split())
|
||||
for each_reg in filter(lambda r: len(r) == 3, self.ql.arch.regs.register_mapping.keys()):
|
||||
if each_reg in new_line:
|
||||
new_line = re.sub(each_reg, hex(self.read_reg(each_reg)), new_line)
|
||||
|
||||
for each_reg in filter(lambda r: len(r) == 2, self.ql.arch.regs.register_mapping.keys()):
|
||||
if each_reg in new_line:
|
||||
new_line = re.sub(each_reg, hex(self.read_reg(each_reg)), new_line)
|
||||
|
||||
|
||||
prophecy.where = check_and_eval(new_line)
|
||||
|
||||
elif line.op_str in self.ql.arch.regs.register_mapping:
|
||||
prophecy.where = self.ql.arch.regs.read(line.op_str)
|
||||
|
||||
else:
|
||||
prophecy.where = read_int(line.op_str)
|
||||
else:
|
||||
prophecy.where = self.cur_addr + line.size
|
||||
|
||||
return prophecy
|
||||
@@ -6,7 +6,7 @@
|
||||
from qiling.const import QL_ARCH
|
||||
|
||||
from .context import Context
|
||||
from .arch import ArchCORTEX_M, ArchARM, ArchMIPS, ArchX86
|
||||
from .arch import ArchCORTEX_M, ArchARM, ArchMIPS, ArchX86, ArchX8664
|
||||
from .misc import check_and_eval
|
||||
import re, math
|
||||
|
||||
@@ -16,6 +16,7 @@ def setup_memory_Manager(ql):
|
||||
|
||||
arch_type = {
|
||||
QL_ARCH.X86: ArchX86,
|
||||
QL_ARCH.X8664: ArchX8664,
|
||||
QL_ARCH.MIPS: ArchMIPS,
|
||||
QL_ARCH.ARM: ArchARM,
|
||||
QL_ARCH.CORTEX_M: ArchCORTEX_M,
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
from .render_x86 import ContextRenderX86
|
||||
from .render_mips import ContextRenderMIPS
|
||||
from .render_arm import ContextRenderARM, ContextRenderCORTEX_M
|
||||
from .render_x8664 import ContextRenderX8664
|
||||
@@ -89,10 +89,18 @@ class Render:
|
||||
helper function for redering stack dump
|
||||
"""
|
||||
|
||||
# Loops over stack range (last 10 addresses)
|
||||
for idx in range(self.stack_num):
|
||||
addr = arch_sp + idx * self.pointersize
|
||||
if (val := self.try_read_pointer(addr)[0]):
|
||||
print(f"$sp+0x{idx*self.pointersize:02x}│ [0x{addr:08x}] —▸ 0x{self.unpack(val):08x}", end="")
|
||||
|
||||
'''
|
||||
@NOTE: Implemented new class arch_x8664 in order to bugfix issue with only dereferencing 32-bit pointers
|
||||
on 64-bit emulation passes.
|
||||
'''
|
||||
if (val := self.try_read_pointer(addr)[0]): # defined to be try_read_pointer(addr)[0] - dereferneces pointer
|
||||
|
||||
# @TODO: Bug here where the values on the stack are being displayed in 32-bit format
|
||||
print(f"RSP + 0x{idx*self.pointersize:02x}│ [0x{addr:08x}] —▸ 0x{self.unpack(val):08x}", end="")
|
||||
|
||||
# try to dereference wether it's a pointer
|
||||
if (buf := self.try_read_pointer(addr))[0] is not None:
|
||||
@@ -180,8 +188,9 @@ class ContextRender(Context, Render):
|
||||
display context stack dump
|
||||
"""
|
||||
|
||||
print(f"{self.ql.arch.regs.arch_sp:x}")
|
||||
self.render_stack_dump(self.ql.arch.regs.arch_sp)
|
||||
|
||||
|
||||
@Render.divider_printer("[ REGISTERS ]")
|
||||
def context_reg(self, saved_states: Mapping["str", int]) -> None:
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
|
||||
|
||||
from .render import *
|
||||
from ..arch import ArchX8664
|
||||
|
||||
class ContextRenderX8664(ContextRender, ArchX8664):
|
||||
"""
|
||||
Context render for X86_64
|
||||
"""
|
||||
|
||||
def __init__(self, ql, predictor):
|
||||
super().__init__(ql, predictor)
|
||||
ArchX8664.__init__(self)
|
||||
|
||||
@Render.divider_printer("[ REGISTERS ]")
|
||||
def context_reg(self, saved_reg_dump):
|
||||
cur_regs = self.dump_regs()
|
||||
diff_reg = self.reg_diff(cur_regs, saved_reg_dump)
|
||||
self.render_regs_dump(cur_regs, diff_reg=diff_reg)
|
||||
print(color.GREEN, "EFLAGS: [CF: {flags[CF]}, PF: {flags[PF]}, AF: {flags[AF]}, ZF: {flags[ZF]}, SF: {flags[SF]}, OF: {flags[OF]}]".format(flags=self.get_flags(self.ql.arch.regs.eflags)), color.END, sep="")
|
||||
|
||||
@Render.divider_printer("[ DISASM ]")
|
||||
def context_asm(self):
|
||||
lines = {}
|
||||
past_list = []
|
||||
|
||||
cur_addr = self.cur_addr
|
||||
while len(past_list) < 10:
|
||||
line = self.disasm(cur_addr)
|
||||
past_list.append(line)
|
||||
cur_addr += line.size
|
||||
|
||||
fd_list = []
|
||||
cur_insn = None
|
||||
for each in past_list:
|
||||
if each.address > self.cur_addr:
|
||||
fd_list.append(each)
|
||||
|
||||
elif each.address == self.cur_addr:
|
||||
cur_insn = each
|
||||
|
||||
"""
|
||||
only forward and current instruction will be printed,
|
||||
because we don't have a solid method to disasm backward instructions,
|
||||
since it's x86 instruction length is variadic
|
||||
"""
|
||||
|
||||
lines.update({
|
||||
"current": cur_insn,
|
||||
"forward": fd_list,
|
||||
})
|
||||
|
||||
self.render_assembly(lines)
|
||||
@@ -16,6 +16,7 @@ from .misc import read_int
|
||||
|
||||
from .render import (
|
||||
ContextRenderX86,
|
||||
ContextRenderX8664,
|
||||
ContextRenderARM,
|
||||
ContextRenderCORTEX_M,
|
||||
ContextRenderMIPS
|
||||
@@ -23,6 +24,7 @@ from .render import (
|
||||
|
||||
from .branch_predictor import (
|
||||
BranchPredictorX86,
|
||||
BranchPredictorX8664,
|
||||
BranchPredictorARM,
|
||||
BranchPredictorCORTEX_M,
|
||||
BranchPredictorMIPS,
|
||||
@@ -64,6 +66,7 @@ def setup_branch_predictor(ql):
|
||||
|
||||
return {
|
||||
QL_ARCH.X86: BranchPredictorX86,
|
||||
QL_ARCH.X8664: BranchPredictorX8664,
|
||||
QL_ARCH.ARM: BranchPredictorARM,
|
||||
QL_ARCH.CORTEX_M: BranchPredictorCORTEX_M,
|
||||
QL_ARCH.MIPS: BranchPredictorMIPS,
|
||||
@@ -76,6 +79,7 @@ def setup_context_render(ql, predictor):
|
||||
|
||||
return {
|
||||
QL_ARCH.X86: ContextRenderX86,
|
||||
QL_ARCH.X8664: ContextRenderX8664,
|
||||
QL_ARCH.ARM: ContextRenderARM,
|
||||
QL_ARCH.CORTEX_M: ContextRenderCORTEX_M,
|
||||
QL_ARCH.MIPS: ContextRenderMIPS,
|
||||
|
||||
Reference in New Issue
Block a user