Skip to content

Qdb x86-64 support & Windows kernel structures #1217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qiling/debugger/qdb/arch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
66 changes: 66 additions & 0 deletions qiling/debugger/qdb/arch/arch_x8664.py
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions qiling/debugger/qdb/branch_predictor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
128 changes: 128 additions & 0 deletions qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion qiling/debugger/qdb/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions qiling/debugger/qdb/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 12 additions & 3 deletions qiling/debugger/qdb/render/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,18 @@ def render_stack_dump(self, arch_sp: int) -> None:
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:
Expand Down Expand Up @@ -180,8 +188,9 @@ def context_stack(self) -> None:
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:
"""
Expand Down
58 changes: 58 additions & 0 deletions qiling/debugger/qdb/render/render_x8664.py
Original file line number Diff line number Diff line change
@@ -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)
4 changes: 4 additions & 0 deletions qiling/debugger/qdb/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

from .render import (
ContextRenderX86,
ContextRenderX8664,
ContextRenderARM,
ContextRenderCORTEX_M,
ContextRenderMIPS
)

from .branch_predictor import (
BranchPredictorX86,
BranchPredictorX8664,
BranchPredictorARM,
BranchPredictorCORTEX_M,
BranchPredictorMIPS,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading