From a896de528fcf4559b875bc6dca100b71897d6786 Mon Sep 17 00:00:00 2001 From: joshj Date: Fri, 26 Aug 2022 16:18:42 +0100 Subject: [PATCH 01/13] change kusershareddata map --- qiling/profiles/windows.ql | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql index 15cc2f39b..5decaaf1b 100644 --- a/qiling/profiles/windows.ql +++ b/qiling/profiles/windows.ql @@ -1,13 +1,12 @@ [OS64] -heap_address = 0x500000000 +heap_address = 0xfffff76000000000 heap_size = 0x5000000 -stack_address = 0x7ffffffde000 +stack_address = 0xfffff77000000000 stack_size = 0x40000 image_address = 0x400000 dll_address = 0x7ffff0000000 entry_point = 0x140000000 -# KI_USER_SHARED_DATA = 0xfffff78000000000 -KI_USER_SHARED_DATA = 0x7ffe0000 +KI_USER_SHARED_DATA = 0xfffff78000000000 [OS32] heap_address = 0x5000000 @@ -17,8 +16,7 @@ stack_size = 0x21000 image_address = 0x400000 dll_address = 0x10000000 entry_point = 0x40000 -# KI_USER_SHARED_DATA = 0xffdf0000 -KI_USER_SHARED_DATA = 0x7ffe0000 +KI_USER_SHARED_DATA = 0xffdf0000 [CODE] # ram_size 0xa00000 is 10MB From d9e6c53cb2a28129a06f3a2b9e7d3c655529b256 Mon Sep 17 00:00:00 2001 From: joshj Date: Fri, 26 Aug 2022 20:19:17 +0100 Subject: [PATCH 02/13] Started implementing x86-64 support for QDB. --- qiling/debugger/qdb/arch/__init__.py | 1 + qiling/debugger/qdb/arch/arch_x8664.py | 50 +++++++ .../debugger/qdb/branch_predictor/__init__.py | 1 + .../branch_predictor_x8664.py | 128 ++++++++++++++++++ qiling/debugger/qdb/memory.py | 3 +- qiling/debugger/qdb/render/__init__.py | 1 + qiling/debugger/qdb/render/render.py | 12 +- qiling/debugger/qdb/render/render_x8664.py | 58 ++++++++ qiling/debugger/qdb/utils.py | 4 + qiling/os/windows/api.py | 2 +- 10 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 qiling/debugger/qdb/arch/arch_x8664.py create mode 100644 qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py create mode 100644 qiling/debugger/qdb/render/render_x8664.py diff --git a/qiling/debugger/qdb/arch/__init__.py b/qiling/debugger/qdb/arch/__init__.py index 24794a4cb..4c5b7a385 100644 --- a/qiling/debugger/qdb/arch/__init__.py +++ b/qiling/debugger/qdb/arch/__init__.py @@ -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 \ No newline at end of file diff --git a/qiling/debugger/qdb/arch/arch_x8664.py b/qiling/debugger/qdb/arch/arch_x8664.py new file mode 100644 index 000000000..aaac3acc8 --- /dev/null +++ b/qiling/debugger/qdb/arch/arch_x8664.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from typing import Mapping + +from .arch import Arch + +class ArchX8664(Arch): + def __init__(self): + super().__init__() + + @property + def arch_insn_size(self): + 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): + 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 + } diff --git a/qiling/debugger/qdb/branch_predictor/__init__.py b/qiling/debugger/qdb/branch_predictor/__init__.py index 67c0578fa..69e9e6d9f 100644 --- a/qiling/debugger/qdb/branch_predictor/__init__.py +++ b/qiling/debugger/qdb/branch_predictor/__init__.py @@ -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 diff --git a/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py b/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py new file mode 100644 index 000000000..f50dad11a --- /dev/null +++ b/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py @@ -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 diff --git a/qiling/debugger/qdb/memory.py b/qiling/debugger/qdb/memory.py index 9090dafe5..9dbece6a9 100644 --- a/qiling/debugger/qdb/memory.py +++ b/qiling/debugger/qdb/memory.py @@ -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, diff --git a/qiling/debugger/qdb/render/__init__.py b/qiling/debugger/qdb/render/__init__.py index d36dfa9ae..78acc1646 100644 --- a/qiling/debugger/qdb/render/__init__.py +++ b/qiling/debugger/qdb/render/__init__.py @@ -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 \ No newline at end of file diff --git a/qiling/debugger/qdb/render/render.py b/qiling/debugger/qdb/render/render.py index b8d7eb08f..270f96a01 100644 --- a/qiling/debugger/qdb/render/render.py +++ b/qiling/debugger/qdb/render/render.py @@ -89,9 +89,16 @@ 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]): + + ''' + @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 + print(f"$sp+0x{idx*self.pointersize:02x}│ [0x{addr:08x}] —▸ 0x{self.unpack(val):08x}", end="") # try to dereference wether it's a pointer @@ -180,8 +187,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: """ diff --git a/qiling/debugger/qdb/render/render_x8664.py b/qiling/debugger/qdb/render/render_x8664.py new file mode 100644 index 000000000..3cf65458e --- /dev/null +++ b/qiling/debugger/qdb/render/render_x8664.py @@ -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 + """ + + 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) diff --git a/qiling/debugger/qdb/utils.py b/qiling/debugger/qdb/utils.py index fdbde9d85..ce0aa6c60 100644 --- a/qiling/debugger/qdb/utils.py +++ b/qiling/debugger/qdb/utils.py @@ -16,6 +16,7 @@ from .render import ( ContextRenderX86, + ContextRenderX8664, ContextRenderARM, ContextRenderCORTEX_M, ContextRenderMIPS @@ -23,6 +24,7 @@ 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, diff --git a/qiling/os/windows/api.py b/qiling/os/windows/api.py index 1960adb8b..69a8d3e6d 100644 --- a/qiling/os/windows/api.py +++ b/qiling/os/windows/api.py @@ -8,7 +8,7 @@ # See: https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types LONG = PARAM_INTN -ULONG = PARAM_INTN +ULONG = PARAM_INT32 CHAR = PARAM_INT8 UCHAR = PARAM_INT16 SHORT = PARAM_INT16 From 98d432c181be3ea66dfad1e936ea7a4e57cbf420 Mon Sep 17 00:00:00 2001 From: joshj Date: Fri, 26 Aug 2022 20:44:19 +0100 Subject: [PATCH 03/13] Documentation. --- qiling/debugger/qdb/arch/arch_x8664.py | 16 ++++++++++++++++ qiling/debugger/qdb/render/render.py | 3 ++- qiling/debugger/qdb/render/render_x8664.py | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/qiling/debugger/qdb/arch/arch_x8664.py b/qiling/debugger/qdb/arch/arch_x8664.py index aaac3acc8..686e2016e 100644 --- a/qiling/debugger/qdb/arch/arch_x8664.py +++ b/qiling/debugger/qdb/arch/arch_x8664.py @@ -8,11 +8,21 @@ 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 @@ -28,6 +38,12 @@ def regs(self): @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: diff --git a/qiling/debugger/qdb/render/render.py b/qiling/debugger/qdb/render/render.py index 270f96a01..393e7707b 100644 --- a/qiling/debugger/qdb/render/render.py +++ b/qiling/debugger/qdb/render/render.py @@ -99,7 +99,8 @@ def render_stack_dump(self, arch_sp: int) -> None: ''' if (val := self.try_read_pointer(addr)[0]): # defined to be try_read_pointer(addr)[0] - dereferneces pointer - print(f"$sp+0x{idx*self.pointersize:02x}│ [0x{addr:08x}] —▸ 0x{self.unpack(val):08x}", end="") + # @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: diff --git a/qiling/debugger/qdb/render/render_x8664.py b/qiling/debugger/qdb/render/render_x8664.py index 3cf65458e..22c687d49 100644 --- a/qiling/debugger/qdb/render/render_x8664.py +++ b/qiling/debugger/qdb/render/render_x8664.py @@ -10,7 +10,7 @@ class ContextRenderX8664(ContextRender, ArchX8664): """ - context render for X86 + Context render for X86_64 """ def __init__(self, ql, predictor): From cb1b5115507dde2de77aa936f124581fd787ea23 Mon Sep 17 00:00:00 2001 From: AHackingFrogWithSunglasses Date: Mon, 29 Aug 2022 17:00:14 +0100 Subject: [PATCH 04/13] Added KTHREAD + TIB --- qiling/loader/pe.py | 2 +- qiling/os/windows/structs.py | 92 ++++++++++++++++++++++++++++++++++++ qiling/profiles/windows.ql | 1 + 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 724a33a9a..e908ac073 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -7,7 +7,7 @@ from typing import Any, Dict, MutableMapping, NamedTuple, Optional, Mapping, Sequence, Tuple, Union from unicorn import UcError -from unicorn.x86_const import UC_X86_REG_CR4, UC_X86_REG_CR8 +from unicorn.x86_const import UC_X86_REG_CR4, UC_X86_REG_CR8, UC_X86_REG_GS from qiling import Qiling from qiling.arch.x86_const import FS_SEGMENT_ADDR, GS_SEGMENT_ADDR diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 433330150..05946fdde 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -115,6 +115,35 @@ class PEB(Struct): return PEB +class NT_TIB(struct.BaseStruct): + ''' + _NT_TIB structure + + Below output is from Windows RS4 + 0: kd> dt _NT_TIB + nt!_NT_TIB + +0x000 ExceptionList : Ptr64 _EXCEPTION_REGISTRATION_RECORD + +0x008 StackBase : Ptr64 Void + +0x010 StackLimit : Ptr64 Void + +0x018 SubSystemTib : Ptr64 Void + +0x020 FiberData : Ptr64 Void + +0x020 Version : Uint4B + +0x028 ArbitraryUserPointer : Ptr64 Void + +0x030 Self : Ptr64 _NT_TIB + ''' + + _fields_ = ( + ('ExceptionList', ctypes.c_void_p), + ('StackBase', ctypes.c_void_p), + ('StackLimit', ctypes.c_void_p), + ('SubSystemTib', ctypes.c_void_p), + ('FiberData', ctypes.c_void_p), + ('Version', ctypes.c_uint * 4), + ('ArbitraryUserPointer', ctypes.c_void_p), + ('Self', ctypes.c_void_p), + ) + + # https://docs.microsoft.com/en-us/windows/win32/api/subauth/ns-subauth-unicode_string @lru_cache(maxsize=2) @@ -239,6 +268,69 @@ class KUSER_SHARED_DATA(struct.BaseStruct): ) +class KPCR(struct.BaseStruct): + ''' + Defintion for 64-bit KPCR structure. + Follows implementation of KPRCB64. Initialisation function in `pe.py` registers this structure. + Structure has a configuration item in `Windows.ql`. + + Below output is from Windows RS4 + 0: kd> dt _KPCR + nt!_KPCR + +0x000 NtTib : _NT_TIB + +0x000 GdtBase : Ptr64 _KGDTENTRY64 + +0x008 TssBase : Ptr64 _KTSS64 + +0x010 UserRsp : Uint8B + +0x018 Self : Ptr64 _KPCR + +0x020 CurrentPrcb : Ptr64 _KPRCB + +0x028 LockArray : Ptr64 _KSPIN_LOCK_QUEUE + +0x030 Used_Self : Ptr64 Void + +0x038 IdtBase : Ptr64 _KIDTENTRY64 + +0x040 Unused : [2] Uint8B + +0x050 Irql : UChar + +0x051 SecondLevelCacheAssociativity : UChar + +0x052 ObsoleteNumber : UChar + +0x053 Fill0 : UChar + +0x054 Unused0 : [3] Uint4B + +0x060 MajorVersion : Uint2B + +0x062 MinorVersion : Uint2B + +0x064 StallScaleFactor : Uint4B + +0x068 Unused1 : [3] Ptr64 Void + +0x080 KernelReserved : [15] Uint4B + +0x0bc SecondLevelCacheSize : Uint4B + +0x0c0 HalReserved : [16] Uint4B + +0x100 Unused2 : Uint4B + +0x108 KdVersionBlock : Ptr64 Void + +0x110 Unused3 : Ptr64 Void + +0x118 PcrAlign1 : [24] Uint4B + +0x180 Prcb : _KPRCB + + + ''' + + _fields_ = ( + ('NtTib', NT_TIB), + ('IdtBase', ctypes.c_void_p), # This is meant to be a KIDTENTRY64 pointer + ('Unused', ctypes.c_ulong), # [0x2] + ('Irql', ctypes.c_void_p), # This is meant to be a KIRQL structure + ('SecondLevelCacheAssociativity', ctypes.c_char), + ('ObsoleteNumber', ctypes.c_char), + ('Fill0', ctypes.c_char), + ('Unused0', ctypes.c_ulong), # [0x3] + ('MajorVersion', ctypes.c_ushort), + ('MinorVersion', ctypes.c_ushort), + ('StallScaleFactor', ctypes.c_ulong), + ('Unused1', ctypes.c_void_p), # [0x3] + ('KernelReserved', ctypes.c_ulong), # [0x0F] + ('SecondLevelCacheSize', ctypes.c_ulong), + ('HalReserved', ctypes.c_ulong), # [0x10] + ('Unused2', ctypes.c_ulong), + ('KdVersionBlock', ctypes.c_void_p), + ('Unused3', ctypes.c_void_p), + ('PcrAlign1', ctypes.c_ulong), # [0x18] + ('Prcb', ctypes.c_void_p)) + + def make_list_entry(archbits: int): native_type = struct.get_native_type(archbits) Struct = struct.get_aligned_struct(archbits) diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql index 5decaaf1b..d8fc54bbd 100644 --- a/qiling/profiles/windows.ql +++ b/qiling/profiles/windows.ql @@ -7,6 +7,7 @@ image_address = 0x400000 dll_address = 0x7ffff0000000 entry_point = 0x140000000 KI_USER_SHARED_DATA = 0xfffff78000000000 +KPCR = 0xfffff78100000000 [OS32] heap_address = 0x5000000 From 658a00a5343bb68502a1b427463382661c0fb058 Mon Sep 17 00:00:00 2001 From: AHackingFrogWithSunglasses Date: Mon, 29 Aug 2022 18:32:02 +0100 Subject: [PATCH 05/13] Basic KPCR support --- qiling/loader/pe.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index e908ac073..40d45f8e8 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -639,6 +639,24 @@ def init_ki_user_shared_data(self): kusd_obj.NXSupportPolicy = 0 # NX_SUPPORT_POLICY_ALWAYSOFF self.ql.os.KUSER_SHARED_DATA = kusd_obj + + def init_kpcr(self): + sysconf = self.ql.os.profile['KPCR'] + osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] + + kpcr_addr = osconf.getint('KPCR') + kpcr_struct = KPCR + self.ql.mem.map(kpcr_addr, self.ql.mem.align_up(kpcr_struct.sizeof()), info='[kpcr]') + + # Initialize an instance with a few key fields + # @TODO: initialize StackBase & StackLimit + # @TODO: initialize kprcb + kpcr_obj = kpcr_struct.volatile_ref(self.ql.mem, kpcr_addr) + kpcr_obj.MajorVersion = sysconf.getint('majorVersion') + kpcr_obj.MinorVersion = sysconf.getint('minorVersion') + + self.ql.os.KPCR = kpcr_obj + def init_security_cookie(self, pe: pefile.PE, image_base: int): if not Process.directory_exists(pe, 'IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG'): From 87b3015796487f1d9d9edef48f073e2a2b1f04ff Mon Sep 17 00:00:00 2001 From: AHackingFrogWithSunglasses Date: Mon, 29 Aug 2022 18:39:27 +0100 Subject: [PATCH 06/13] KPCR pointer correctly writes into GS:[0x18] --- qiling/loader/pe.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 40d45f8e8..a1fccf26c 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -641,6 +641,10 @@ def init_ki_user_shared_data(self): self.ql.os.KUSER_SHARED_DATA = kusd_obj def init_kpcr(self): + ''' + Initialisation function for KPCR structure. This structure's pointer should be at gs:[0x18] + ''' + sysconf = self.ql.os.profile['KPCR'] osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] @@ -655,6 +659,10 @@ def init_kpcr(self): kpcr_obj.MajorVersion = sysconf.getint('majorVersion') kpcr_obj.MinorVersion = sysconf.getint('minorVersion') + # Writes KPCR pointer into GS:[0x18] + # @TODO: write into the register + offset instead of directly to mapped address + self.ql.mem.write_ptr(0x6000018, kpcr_addr) + self.ql.os.KPCR = kpcr_obj From 84fd6a0dd44c731c07aa67c65765db72f6cfb1ad Mon Sep 17 00:00:00 2001 From: AHackingFrogWithSunglasses Date: Mon, 29 Aug 2022 19:37:03 +0100 Subject: [PATCH 07/13] Implemented KPCR, KPRCB, KTHREAD with write to gs --- qiling/loader/pe.py | 61 +++++++++++++++++++++++- qiling/os/windows/structs.py | 90 +++++++++++++++++++++++++++++++++++- qiling/profiles/windows.ql | 5 ++ 3 files changed, 153 insertions(+), 3 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index a1fccf26c..3536518e5 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -645,7 +645,7 @@ def init_kpcr(self): Initialisation function for KPCR structure. This structure's pointer should be at gs:[0x18] ''' - sysconf = self.ql.os.profile['KPCR'] + sysconf = self.ql.os.profile['SYSTEM'] osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] kpcr_addr = osconf.getint('KPCR') @@ -654,10 +654,10 @@ def init_kpcr(self): # Initialize an instance with a few key fields # @TODO: initialize StackBase & StackLimit - # @TODO: initialize kprcb kpcr_obj = kpcr_struct.volatile_ref(self.ql.mem, kpcr_addr) kpcr_obj.MajorVersion = sysconf.getint('majorVersion') kpcr_obj.MinorVersion = sysconf.getint('minorVersion') + #kpcr_obj.Prcb = osconf.getint('KPRCB') # Writes KPCR pointer into GS:[0x18] # @TODO: write into the register + offset instead of directly to mapped address @@ -665,6 +665,59 @@ def init_kpcr(self): self.ql.os.KPCR = kpcr_obj + def init_kthread(self): + ''' + Initialisation function for KTHREAD structure. This structures pointer should be at gs:[0x188] + ''' + + sysconf = self.ql.os.profile['SYSTEM'] + osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] + + kthread_addr = osconf.getint('KTHREAD') + kthread_struct = KTHREAD + self.ql.mem.map(kthread_addr, self.ql.mem.align_up(kthread_struct.sizeof()), info='[kthread]') + + # Initialize an instance with key fields + kthread_obj = kthread_struct.volatile_ref(self.ql.mem, kthread_addr) + kthread_obj.ThreadLock = 11 + kthread_obj.PreviousMode = 0 + kthread_obj.InitialStack = 0x1000 + kthread_obj.StackBase = 0x1500 + kthread_obj.StackLimit = 0x2000 + + # Writes KTHREAD pointer into GS:[0x188] + self.ql.mem.write_ptr(0x6000188, kthread_addr) + + self.ql.os.KTHREAD = kthread_obj + + def init_kprocess(self): + ''' + Initialisation function for KPROCESS structure. + ''' + + def init_kprcb(self): + ''' + Initialisation function for KPCRB structure. This structures pointer should be at gs:[0x20] + ''' + + sysconf = self.ql.os.profile['SYSTEM'] + osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] + + kprcb_addr = osconf.getint('KPRCB') + kprcb_struct = KPRCB + self.ql.mem.map(kprcb_addr, self.ql.mem.align_up(kprcb_struct.sizeof()), info='[kprcb]') + + # Initialize and instance with a few key fields + kprcb_obj = kprcb_struct.volatile_ref(self.ql.mem, kprcb_addr) + #kprcb_obj.CurrentThread = osconf.getint('KTHREAD') + #kprcb_obj.IdleThread = osconf.getint('KTHREAD') + + # Writes KPRCB pointer into GS:[0x20] + self.ql.mem.write_ptr(0x6000020, kprcb_addr) + + self.ql.os.KPRCB = kprcb_obj + + def init_security_cookie(self, pe: pefile.PE, image_base: int): if not Process.directory_exists(pe, 'IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG'): @@ -771,6 +824,10 @@ def load(self, pe: Optional[pefile.PE]): self.init_registry_path() self.init_eprocess() + self.init_kprcb() + self.init_kpcr() + self.init_kthread() + # set IRQ Level in CR8 to PASSIVE_LEVEL self.ql.arch.regs.write(UC_X86_REG_CR8, 0) diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 05946fdde..a1c7007a0 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -267,6 +267,86 @@ class KUSER_SHARED_DATA(struct.BaseStruct): ('_padding0', ctypes.c_uint8 * 0x2F8) ) +class KTHREAD(struct.BaseStruct): + ''' + Definition for 64-bit KTHREAD structure + ''' + + _fields_ = ( + ('Header', ctypes.c_void_p), # Supposed to be DISPATCHER_HEADER64 + ('CycleTime', ctypes.c_uint64), + ('QuantumTarget', ctypes.c_uint64), + ('InitialStack', ctypes.c_void_p), # Supposed to be POINTER64 + ('StackLimit', ctypes.c_void_p), # Supposed to be POINTER64 + ('KernelStack', ctypes.c_void_p), # Supposed to be POINTER64 + ('ThreadLock', ctypes.c_uint64), + ('WaitRegister', ctypes.c_uint8), + ('Running', ctypes.c_uint8), + ('Alerted', ctypes.c_uint8), + ('MiscFlags', ctypes.c_uint32), + ('ApcState', ctypes.c_void_p), # Supposed to be KAPC_STATE64 + ('DeferredProcessor', ctypes.c_uint32), + ('ApcQueueLock', ctypes.c_uint64), + ('WaitStatus', ctypes.c_int64), + ('WaitBlockList', ctypes.c_void_p), # Supposed to be POINTER64 + ('WaitListEntry', ctypes.c_void_p), # Supposed to be LIST_ENTRY64 + ('Queue', ctypes.c_void_p), # Supposed to be POINTER64 + ('Teb', ctypes.c_void_p), # Supposed to be POINTER64 + ('Timer', ctypes.c_void_p), # Supposed to be KTIMER64 + ('ThreadFlags', ctypes.c_int32), + ('Spare0', ctypes.c_uint32), + ('WaitBlock', ctypes.c_void_p), # Supposed to be KWAIT_BLOCK64 * 4 + ('QueueListEntry', ctypes.c_void_p), # Supposed to be LIST_ENTRY64 + ('TrapFrame', ctypes.c_void_p), # Supposed to be POINTER64 + ('FirstArgument', ctypes.c_void_p), # Supposed to be POINTER64 + ('CallbackStack', ctypes.c_void_p), # Supposed to be POINTER64 + ('ApcStateIndex', ctypes.c_uint8), + ('BasePriority', ctypes.c_char), + ('PriorityDecrement', ctypes.c_char), + ('Preempted', ctypes.c_uint8), + ('AdjustReason', ctypes.c_uint8), + ('AdjustIncrement', ctypes.c_char), + ('PreviousMode', ctypes.c_char), + ('Saturation', ctypes.c_char), + ('SystemCallNumber', ctypes.c_uint32), + ('FreezeCount', ctypes.c_uint32), + ('UserAffinity', ctypes.c_void_p), # Supposed to be GROUP_AFFINITY64 + ('Process', ctypes.c_void_p), # Supposed to be POINTER64 + ('Affinity', ctypes.c_void_p), # Supposed to be GROUP_AFFINITY64 + ('IdealProcessor', ctypes.c_uint32), + ('UserIdealProcessor', ctypes.c_uint32), + ('ApcStatePointer', ctypes.c_void_p), # Supposed to be POINTER 64 * 2 + ('SavedApcState', ctypes.c_void_p), # Supposed to be KAPC_STATE64 + ('Win32Thread', ctypes.c_void_p), # Supposed to be POINTER64 + ('StackBase', ctypes.c_void_p), # Supposed to be POINTER64 + ('SuspendApc', ctypes.c_void_p), # Supposed to be KAPC64 + ('SuspendSemaphore', ctypes.c_void_p), # Supposed to be KSEMAPHORE64 + ('ThreadListEntry', ctypes.c_void_p), # Supposed to be LIST_ENTRY64 + ('MutantListHead', ctypes.c_void_p), # Supposed to be LIST_ENTRY64 + ('SListFaultAddress', ctypes.c_void_p), # Supposed to be POINTER64 + ('ReadOperationCount', ctypes.c_int64), + ('WriteOperationCount', ctypes.c_int64), + ('OtherOperationCount', ctypes.c_int64), + ('ReadTransferCount', ctypes.c_int64), + ('WriteTransferCount', ctypes.c_int64), + ('OtherTransferCount', ctypes.c_int64), + ('ThreadCounters', ctypes.c_void_p), # Supposed to be POINTER64 + ('XStateSave', ctypes.c_void_p) + ) # Supposed to be POINTER64 + + +class KPRCB(struct.BaseStruct): + _fields_ = ( + ('MxCsr', ctypes.c_ulong), + ('Number', ctypes.c_ushort), + ('LegacyNumber', ctypes.c_char), + ('ReservedMustBeZero', ctypes.c_char), + ('InterruptRequest', ctypes.c_bool), + ('IdleHalt', ctypes.c_bool), + ('CurrentThread', KTHREAD), + ('NextThread', KTHREAD), + ('IdleThread', KTHREAD) + ) class KPCR(struct.BaseStruct): ''' @@ -328,7 +408,15 @@ class KPCR(struct.BaseStruct): ('KdVersionBlock', ctypes.c_void_p), ('Unused3', ctypes.c_void_p), ('PcrAlign1', ctypes.c_ulong), # [0x18] - ('Prcb', ctypes.c_void_p)) + ('Prcb', KPRCB)) + + +class KPROCESS(struct.BaseStruct): + _fields_ = ( + ('') + ) + + def make_list_entry(archbits: int): diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql index d8fc54bbd..66d566de4 100644 --- a/qiling/profiles/windows.ql +++ b/qiling/profiles/windows.ql @@ -8,6 +8,8 @@ dll_address = 0x7ffff0000000 entry_point = 0x140000000 KI_USER_SHARED_DATA = 0xfffff78000000000 KPCR = 0xfffff78100000000 +KPRCB = 0xfffff78200000000 +KTHREAD = 0xfffff78300000000 [OS32] heap_address = 0x5000000 @@ -18,6 +20,9 @@ image_address = 0x400000 dll_address = 0x10000000 entry_point = 0x40000 KI_USER_SHARED_DATA = 0xffdf0000 +KPCR = 0xffdf1000 +KPRCB = 0xffdf2000 +KTHREAD = 0xffdf3000 [CODE] # ram_size 0xa00000 is 10MB From 65902f49e39fcc0b2a1cdfd920b3f999207e0e64 Mon Sep 17 00:00:00 2001 From: AHackingFrogWithSunglasses Date: Mon, 29 Aug 2022 19:42:43 +0100 Subject: [PATCH 08/13] Cleanup. --- qiling/os/windows/structs.py | 117 ----------------------------------- 1 file changed, 117 deletions(-) diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index a1c7007a0..6d0d0dcc8 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -684,19 +684,6 @@ class MDL(Struct): # ('WaitListHead', LIST_ENTRY64), # ) # -# -# class DISPATCHER_HEADER32(ctypes.Structure): -# _fields_ = ( -# ('Lock', ctypes.c_int32), -# ('SignalState', ctypes.c_int32), -# ('WaitListHead', LIST_ENTRY32), -# ('Type', ctypes.c_uint8), -# ('TimerControlFlags', ctypes.c_uint8), -# ('ThreadControlFlags', ctypes.c_uint8), -# ('TimerMiscFlags', ctypes.c_uint8), -# ) -# -# # class KAPC_STATE64(ctypes.Structure): # _fields_ = ( # ('ApcListHead', LIST_ENTRY64 * 2), @@ -706,17 +693,6 @@ class MDL(Struct): # ('UserApcPending', ctypes.c_uint8), # ) # -# -# class KAPC_STATE32(ctypes.Structure): -# _fields_ = ( -# ('ApcListHead', LIST_ENTRY32 * 2), -# ('Process', POINTER32), -# ('KernelApcInProgress', ctypes.c_uint8), -# ('KernelApcPending', ctypes.c_uint8), -# ('UserApcPending', ctypes.c_uint8), -# ) -# -# # class KTIMER64(ctypes.Structure): # _fields_ = ( # ('Header', DISPATCHER_HEADER64), @@ -726,17 +702,6 @@ class MDL(Struct): # ('Period', ctypes.c_uint32), # ) # -# -# class KTIMER32(ctypes.Structure): -# _fields_ = ( -# ('Header', DISPATCHER_HEADER32), -# ('DueTime', LARGE_INTEGER), -# ('TimerListEntry', LIST_ENTRY32), -# ('Dpc', POINTER32), -# ('Period', ctypes.c_uint32), -# ) -# -# # class KWAIT_BLOCK64(ctypes.Structure): # _fields_ = ( # ('WaitListEntry', LIST_ENTRY64), @@ -748,19 +713,6 @@ class MDL(Struct): # ('BlockState', ctypes.c_uint8), # ) # -# -# class KWAIT_BLOCK32(ctypes.Structure): -# _fields_ = ( -# ('WaitListEntry', LIST_ENTRY32), -# ('Thread', POINTER32), -# ('Object', POINTER32), -# ('NextWaitBlock', POINTER32), -# ('WaitKey', ctypes.c_uint16), -# ('WaitType', ctypes.c_uint8), -# ('BlockState', ctypes.c_uint8), -# ) -# -# # class GROUP_AFFINITY64(ctypes.Structure): # _fields_ = (('Mask', ctypes.c_uint64), ('Group', ctypes.c_uint16), # ('Reserved', ctypes.c_uint16 * 3)) @@ -792,11 +744,6 @@ class MDL(Struct): # ('Inserted', ctypes.c_uint8), # ) # -# -# class KAPC32(ctypes.Structure): -# _fields_ = () -# -# # class KSEMAPHORE64(ctypes.Structure): # _pack_ = 8 # _fields_ = (("Header", DISPATCHER_HEADER64), ("Limit", ctypes.c_int32)) @@ -825,70 +772,6 @@ class MDL(Struct): # ) # # -# class KTHREAD64(ctypes.Structure): -# _pack_ = 8 -# _fields_ = ( -# ('Header', DISPATCHER_HEADER64), -# ('CycleTime', ctypes.c_uint64), -# ('QuantumTarget', ctypes.c_uint64), -# ('InitialStack', POINTER64), -# ('StackLimit', POINTER64), -# ('KernelStack', POINTER64), -# ('ThreadLock', ctypes.c_uint64), -# ('WaitRegister', ctypes.c_uint8), # _KWAIT_STATUS_REGISTER -# ('Running', ctypes.c_uint8), -# ('Alerted', ctypes.c_uint8 * 2), -# ('MiscFlags', ctypes.c_uint32), -# ('ApcState', KAPC_STATE64), -# ('DeferredProcessor', ctypes.c_uint32), -# ('ApcQueueLock', ctypes.c_uint64), -# ('WaitStatus', ctypes.c_int64), -# ('WaitBlockList', POINTER64), -# ('WaitListEntry', LIST_ENTRY64), -# ('Queue', POINTER64), -# ('Teb', POINTER64), -# ('Timer', KTIMER64), -# ('ThreadFlags', ctypes.c_int32), -# ('Spare0', ctypes.c_uint32), -# ('WaitBlock', KWAIT_BLOCK64 * 4), -# ('QueueListEntry', LIST_ENTRY64), -# ('TrapFrame', POINTER64), -# ('FirstArgument', POINTER64), -# ('CallbackStack', POINTER64), -# ('ApcStateIndex', ctypes.c_uint8), -# ('BasePriority', ctypes.c_char), -# ('PriorityDecrement', ctypes.c_char), -# ('Preempted', ctypes.c_uint8), -# ('AdjustReason', ctypes.c_uint8), -# ('AdjustIncrement', ctypes.c_char), -# ('PreviousMode', ctypes.c_char), -# ('Saturation', ctypes.c_char), -# ('SystemCallNumber', ctypes.c_uint32), -# ('FreezeCount', ctypes.c_uint32), -# ('UserAffinity', GROUP_AFFINITY64), -# ('Process', POINTER64), -# ('Affinity', GROUP_AFFINITY64), -# ('IdealProcessor', ctypes.c_uint32), -# ('UserIdealProcessor', ctypes.c_uint32), -# ('ApcStatePointer', POINTER64 * 2), -# ('SavedApcState', KAPC_STATE64), -# ('Win32Thread', POINTER64), -# ('StackBase', POINTER64), -# ('SuspendApc', KAPC64), -# ('SuspendSemaphore', KSEMAPHORE64), -# ('ThreadListEntry', LIST_ENTRY64), -# ('MutantListHead', LIST_ENTRY64), -# ('SListFaultAddress', POINTER64), -# ('ReadOperationCount', ctypes.c_int64), -# ('WriteOperationCount', ctypes.c_int64), -# ('OtherOperationCount', ctypes.c_int64), -# ('ReadTransferCount', ctypes.c_int64), -# ('WriteTransferCount', ctypes.c_int64), -# ('OtherTransferCount', ctypes.c_int64), -# ('ThreadCounters', POINTER64), -# ('XStateSave', POINTER64)) - - # struct _RTL_PROCESS_MODULE_INFORMATION { # HANDLE Section; # PVOID MappedBase; From 7dae4704c68854b7b64c70995d10e2affd0a1cb9 Mon Sep 17 00:00:00 2001 From: AHackingFrogWithSunglasses Date: Mon, 29 Aug 2022 21:05:51 +0100 Subject: [PATCH 09/13] Begin implementing KPROCESS --- qiling/loader/pe.py | 44 ++-- qiling/os/windows/structs.py | 496 +++++++++++++++++++++++++++++++++-- qiling/profiles/windows.ql | 1 + 3 files changed, 508 insertions(+), 33 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 3536518e5..71428dfbd 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -629,14 +629,14 @@ def init_ki_user_shared_data(self): # initialize an instance with a few key fields kusd_obj = kust_struct.volatile_ref(self.ql.mem, kusd_addr) - kusd_obj.ImageNumberLow = 0x014c # IMAGE_FILE_MACHINE_I386 - kusd_obj.ImageNumberHigh = 0x8664 # IMAGE_FILE_MACHINE_AMD64 - kusd_obj.NtSystemRoot = self.ql.os.windir - kusd_obj.NtProductType = sysconf.getint('productType') - kusd_obj.NtMajorVersion = sysconf.getint('majorVersion') - kusd_obj.NtMinorVersion = sysconf.getint('minorVersion') - kusd_obj.KdDebuggerEnabled = 0 - kusd_obj.NXSupportPolicy = 0 # NX_SUPPORT_POLICY_ALWAYSOFF + kusd_obj.ImageNumberLow = 0x014c # IMAGE_FILE_MACHINE_I386 + kusd_obj.ImageNumberHigh = 0x8664 # IMAGE_FILE_MACHINE_AMD64 + kusd_obj.NtSystemRoot = self.ql.os.windir + kusd_obj.NtProductType = sysconf.getint('productType') + kusd_obj.NtMajorVersion = sysconf.getint('majorVersion') + kusd_obj.NtMinorVersion = sysconf.getint('minorVersion') + kusd_obj.KdDebuggerEnabled = 0 + kusd_obj.NXSupportPolicy = 0 # NX_SUPPORT_POLICY_ALWAYSOFF self.ql.os.KUSER_SHARED_DATA = kusd_obj @@ -657,10 +657,9 @@ def init_kpcr(self): kpcr_obj = kpcr_struct.volatile_ref(self.ql.mem, kpcr_addr) kpcr_obj.MajorVersion = sysconf.getint('majorVersion') kpcr_obj.MinorVersion = sysconf.getint('minorVersion') - #kpcr_obj.Prcb = osconf.getint('KPRCB') + #kpcr_obj.Prcb = osconf.getint('KPRCB') # Writes KPCR pointer into GS:[0x18] - # @TODO: write into the register + offset instead of directly to mapped address self.ql.mem.write_ptr(0x6000018, kpcr_addr) self.ql.os.KPCR = kpcr_obj @@ -679,11 +678,12 @@ def init_kthread(self): # Initialize an instance with key fields kthread_obj = kthread_struct.volatile_ref(self.ql.mem, kthread_addr) - kthread_obj.ThreadLock = 11 - kthread_obj.PreviousMode = 0 - kthread_obj.InitialStack = 0x1000 - kthread_obj.StackBase = 0x1500 - kthread_obj.StackLimit = 0x2000 + kthread_obj.ThreadLock = 11 + kthread_obj.PreviousMode = 0 + kthread_obj.InitialStack = 0x1000 + kthread_obj.StackBase = 0x1500 + kthread_obj.StackLimit = 0x2000 + kthread_obj.MiscFlags |= 0x400 # Writes KTHREAD pointer into GS:[0x188] self.ql.mem.write_ptr(0x6000188, kthread_addr) @@ -695,6 +695,18 @@ def init_kprocess(self): Initialisation function for KPROCESS structure. ''' + sysconf = self.ql.os.profile['SYSTEM'] + osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] + + kproc_addr = osconf.getint('KPROCESS') + kproc_struct = KPROCESS + self.ql.mem.map(kproc_addr, self.ql.mem.align_up(kproc_struct.sizeof()), info='[kprocess]') + + # Initialize an instance with key fields + kproc_obj = kproc_struct.volatile_ref(self.ql.mem, kproc_addr) + + self.ql.os.KPROCESS = kproc_obj + def init_kprcb(self): ''' Initialisation function for KPCRB structure. This structures pointer should be at gs:[0x20] @@ -709,7 +721,7 @@ def init_kprcb(self): # Initialize and instance with a few key fields kprcb_obj = kprcb_struct.volatile_ref(self.ql.mem, kprcb_addr) - #kprcb_obj.CurrentThread = osconf.getint('KTHREAD') + #kprcb_obj.CurrentThread = #kprcb_obj.IdleThread = osconf.getint('KTHREAD') # Writes KPRCB pointer into GS:[0x20] diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 6d0d0dcc8..502c72d6d 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -3,6 +3,7 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +from calendar import c import ctypes from enum import IntEnum @@ -334,28 +335,424 @@ class KTHREAD(struct.BaseStruct): ('XStateSave', ctypes.c_void_p) ) # Supposed to be POINTER64 +class KNODE(struct.BaseStruct): + ''' + Below output is from Windows 10 RS4 + ntdll!_KNODE + +0x000 IdleNonParkedCpuSet : Uint8B + +0x008 IdleSmtSet : Uint8B + +0x010 IdleCpuSet : Uint8B + +0x040 DeepIdleSet : Uint8B + +0x048 IdleConstrainedSet : Uint8B + +0x050 NonParkedSet : Uint8B + +0x058 NonIsrTargetedSet : Uint8B + +0x060 ParkLock : Int4B + +0x064 Seed : Uint4B + +0x080 SiblingMask : Uint4B + +0x088 Affinity : _GROUP_AFFINITY + +0x088 AffinityFill : [10] UChar + +0x092 NodeNumber : Uint2B + +0x094 PrimaryNodeNumber : Uint2B + +0x096 Stride : UChar + +0x097 Spare0 : UChar + +0x098 SharedReadyQueueLeaders : Uint8B + +0x0a0 ProximityId : Uint4B + +0x0a4 Lowest : Uint4B + +0x0a8 Highest : Uint4B + +0x0ac MaximumProcessors : UChar + +0x0ad Flags : _flags + +0x0ae Spare10 : UChar + +0x0b0 HeteroSets : [5] _KHETERO_PROCESSOR_SET + +0x128 PpmConfiguredQosSets : [4] Uint8B + ''' + + _fields_ = ( + ('IdleNonParkedCpuSet', ctypes.c_uint8), + ('IdleSmtSet', ctypes.c_uint8), + ('IdleCpuSet', ctypes.c_uint8), + ('DeepIdleSet', ctypes.c_uint8), + ('IdleConstrainedSet', ctypes.c_uint8), + ('NonParkedSet', ctypes.c_uint8), + ('NonIsrTargetedSet', ctypes.c_uint8), + ('ParkLock', ctypes.c_int), + ('Seed', ctypes.c_uint), + ('SiblingMask', ctypes.c_uint), + ('Affinity', ctypes.c_void_p), + ('AffinityFill', ctypes.c_char * 10), + ('NodeNumber', ctypes.c_uint), + ('PrimaryNodeNumber', ctypes.c_uint), + ('Stride', ctypes.c_char), + ('Spare0', ctypes.c_char), + ('SharedReadyQueueLeaders', ctypes.c_uint8), + ('ProximityId', ctypes.c_uint), + ('Lowest', ctypes.c_uint), + ('Highest', ctypes.c_uint), + ('MaximumProcessors', ctypes.c_char), + ('Flags', ctypes.c_void_p), + ('Spare10', ctypes.c_char), + ('HeteroSets', ctypes.c_void_p), + ('PpmConfiguredQosSets', ctypes.c_uint8 * 8) + ) + class KPRCB(struct.BaseStruct): + ''' + Definition for 64-bit KPRCB structure + + Windows 10 RS4 + ntdll!_KPRCB + +0x000 MxCsr : Uint4B + +0x004 LegacyNumber : UChar + +0x005 ReservedMustBeZero : UChar + +0x006 InterruptRequest : UChar + +0x007 IdleHalt : UChar + +0x008 CurrentThread : Ptr64 _KTHREAD + +0x010 NextThread : Ptr64 _KTHREAD + +0x018 IdleThread : Ptr64 _KTHREAD + +0x020 NestingLevel : UChar + +0x021 ClockOwner : UChar + +0x022 PendingTickFlags : UChar + +0x022 PendingTick : Pos 0, 1 Bit + +0x022 PendingBackupTick : Pos 1, 1 Bit + +0x023 IdleState : UChar + +0x024 Number : Uint4B + +0x028 RspBase : Uint8B + +0x030 PrcbLock : Uint8B + +0x038 PriorityState : Ptr64 Char + +0x040 CpuType : Char + +0x041 CpuID : Char + +0x042 CpuStep : Uint2B + +0x042 CpuStepping : UChar + +0x043 CpuModel : UChar + +0x044 MHz : Uint4B + +0x048 HalReserved : [8] Uint8B + +0x088 MinorVersion : Uint2B + +0x08a MajorVersion : Uint2B + +0x08c BuildType : UChar + +0x08d CpuVendor : UChar + +0x08e CoresPerPhysicalProcessor : UChar + +0x08f LogicalProcessorsPerCore : UChar + +0x090 PrcbPad04 : [6] Uint8B + +0x0c0 ParentNode : Ptr64 _KNODE + +0x0c8 GroupSetMember : Uint8B + +0x0d0 Group : UChar + +0x0d1 GroupIndex : UChar + +0x0d2 PrcbPad05 : [2] UChar + +0x0d4 InitialApicId : Uint4B + +0x0d8 ScbOffset : Uint4B + +0x0dc ApicMask : Uint4B + +0x0e0 AcpiReserved : Ptr64 Void + +0x0e8 CFlushSize : Uint4B + +0x0ec PrcbFlags : _KPRCBFLAG + +0x0f0 TrappedSecurityDomain : Uint8B + +0x0f8 BpbState : UChar + +0x0f8 BpbCpuIdle : Pos 0, 1 Bit + +0x0f8 BpbFlushRsbOnTrap : Pos 1, 1 Bit + +0x0f8 BpbIbpbOnReturn : Pos 2, 1 Bit + +0x0f8 BpbIbpbOnTrap : Pos 3, 1 Bit + +0x0f8 BpbStateReserved : Pos 4, 4 Bits + +0x0f9 BpbFeatures : UChar + +0x0f9 BpbClearOnIdle : Pos 0, 1 Bit + +0x0f9 BpbEnabled : Pos 1, 1 Bit + +0x0f9 BpbSmep : Pos 2, 1 Bit + +0x0f9 BpbFeaturesReserved : Pos 3, 5 Bits + +0x0fa BpbCurrentSpecCtrl : UChar + +0x0fb BpbKernelSpecCtrl : UChar + +0x0fc BpbNmiSpecCtrl : UChar + +0x0fd BpbUserSpecCtrl : UChar + +0x0fe BpbPad : [2] UChar + +0x0f0 PrcbPad11 : [2] Uint8B + +0x100 ProcessorState : _KPROCESSOR_STATE + +0x6c0 ExtendedSupervisorState : Ptr64 _XSAVE_AREA_HEADER + +0x6c8 ProcessorSignature : Uint4B + +0x6cc PrcbPad11a : Uint4B + +0x6d0 PrcbPad12 : [4] Uint8B + +0x6f0 LockQueue : [17] _KSPIN_LOCK_QUEUE + +0x800 PPLookasideList : [16] _PP_LOOKASIDE_LIST + +0x900 PPNxPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL + +0x1500 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL + +0x2100 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL + +0x2d00 PrcbPad20 : Uint8B + +0x2d08 DeferredReadyListHead : _SINGLE_LIST_ENTRY + +0x2d10 MmPageFaultCount : Int4B + +0x2d14 MmCopyOnWriteCount : Int4B + +0x2d18 MmTransitionCount : Int4B + +0x2d1c MmDemandZeroCount : Int4B + +0x2d20 MmPageReadCount : Int4B + +0x2d24 MmPageReadIoCount : Int4B + +0x2d28 MmDirtyPagesWriteCount : Int4B + +0x2d2c MmDirtyWriteIoCount : Int4B + +0x2d30 MmMappedPagesWriteCount : Int4B + +0x2d34 MmMappedWriteIoCount : Int4B + +0x2d38 KeSystemCalls : Uint4B + +0x2d3c KeContextSwitches : Uint4B + +0x2d40 PrcbPad40 : Uint4B + +0x2d44 CcFastReadNoWait : Uint4B + +0x2d48 CcFastReadWait : Uint4B + +0x2d4c CcFastReadNotPossible : Uint4B + +0x2d50 CcCopyReadNoWait : Uint4B + +0x2d54 CcCopyReadWait : Uint4B + +0x2d58 CcCopyReadNoWaitMiss : Uint4B + +0x2d5c IoReadOperationCount : Int4B + +0x2d60 IoWriteOperationCount : Int4B + +0x2d64 IoOtherOperationCount : Int4B + +0x2d68 IoReadTransferCount : _LARGE_INTEGER + +0x2d70 IoWriteTransferCount : _LARGE_INTEGER + +0x2d78 IoOtherTransferCount : _LARGE_INTEGER + +0x2d80 PacketBarrier : Int4B + +0x2d84 TargetCount : Int4B + +0x2d88 IpiFrozen : Uint4B + +0x2d8c PrcbPad30 : Uint4B + +0x2d90 IsrDpcStats : Ptr64 Void + +0x2d98 DeviceInterrupts : Uint4B + +0x2d9c LookasideIrpFloat : Int4B + +0x2da0 InterruptLastCount : Uint4B + +0x2da4 InterruptRate : Uint4B + +0x2da8 LastNonHrTimerExpiration : Uint8B + +0x2db0 PrcbPad35 : [2] Uint8B + +0x2dc0 InterruptObjectPool : _SLIST_HEADER + +0x2dd0 PrcbPad41 : [6] Uint8B + +0x2e00 DpcData : [2] _KDPC_DATA + +0x2e50 DpcStack : Ptr64 Void + +0x2e58 MaximumDpcQueueDepth : Int4B + +0x2e5c DpcRequestRate : Uint4B + +0x2e60 MinimumDpcRate : Uint4B + +0x2e64 DpcLastCount : Uint4B + +0x2e68 ThreadDpcEnable : UChar + +0x2e69 QuantumEnd : UChar + +0x2e6a DpcRoutineActive : UChar + +0x2e6b IdleSchedule : UChar + +0x2e6c DpcRequestSummary : Int4B + +0x2e6c DpcRequestSlot : [2] Int2B + +0x2e6c NormalDpcState : Int2B + +0x2e6e ThreadDpcState : Int2B + +0x2e6c DpcNormalProcessingActive : Pos 0, 1 Bit + +0x2e6c DpcNormalProcessingRequested : Pos 1, 1 Bit + +0x2e6c DpcNormalThreadSignal : Pos 2, 1 Bit + +0x2e6c DpcNormalTimerExpiration : Pos 3, 1 Bit + +0x2e6c DpcNormalDpcPresent : Pos 4, 1 Bit + +0x2e6c DpcNormalLocalInterrupt : Pos 5, 1 Bit + +0x2e6c DpcNormalSpare : Pos 6, 10 Bits + +0x2e6c DpcThreadActive : Pos 16, 1 Bit + +0x2e6c DpcThreadRequested : Pos 17, 1 Bit + +0x2e6c DpcThreadSpare : Pos 18, 14 Bits + +0x2e70 LastTimerHand : Uint4B + +0x2e74 LastTick : Uint4B + +0x2e78 ClockInterrupts : Uint4B + +0x2e7c ReadyScanTick : Uint4B + +0x2e80 InterruptObject : [256] Ptr64 Void + +0x3680 TimerTable : _KTIMER_TABLE + +0x5880 DpcGate : _KGATE + +0x5898 PrcbPad52 : Ptr64 Void + +0x58a0 CallDpc : _KDPC + +0x58e0 ClockKeepAlive : Int4B + +0x58e4 PrcbPad60 : [2] UChar + +0x58e6 NmiActive : Uint2B + +0x58e8 DpcWatchdogPeriod : Int4B + +0x58ec DpcWatchdogCount : Int4B + +0x58f0 KeSpinLockOrdering : Int4B + +0x58f4 DpcWatchdogProfileCumulativeDpcThreshold : Uint4B + +0x58f8 CachedPtes : Ptr64 Void + +0x5900 WaitListHead : _LIST_ENTRY + +0x5910 WaitLock : Uint8B + +0x5918 ReadySummary : Uint4B + +0x591c AffinitizedSelectionMask : Int4B + +0x5920 QueueIndex : Uint4B + +0x5924 PrcbPad75 : [3] Uint4B + +0x5930 TimerExpirationDpc : _KDPC + +0x5970 ScbQueue : _RTL_RB_TREE + +0x5980 DispatcherReadyListHead : [32] _LIST_ENTRY + +0x5b80 InterruptCount : Uint4B + +0x5b84 KernelTime : Uint4B + +0x5b88 UserTime : Uint4B + +0x5b8c DpcTime : Uint4B + +0x5b90 InterruptTime : Uint4B + +0x5b94 AdjustDpcThreshold : Uint4B + +0x5b98 DebuggerSavedIRQL : UChar + +0x5b99 GroupSchedulingOverQuota : UChar + +0x5b9a DeepSleep : UChar + +0x5b9b PrcbPad80 : UChar + +0x5b9c DpcTimeCount : Uint4B + +0x5ba0 DpcTimeLimit : Uint4B + +0x5ba4 PeriodicCount : Uint4B + +0x5ba8 PeriodicBias : Uint4B + +0x5bac AvailableTime : Uint4B + +0x5bb0 KeExceptionDispatchCount : Uint4B + +0x5bb4 ReadyThreadCount : Uint4B + +0x5bb8 ReadyQueueExpectedRunTime : Uint8B + +0x5bc0 StartCycles : Uint8B + +0x5bc8 TaggedCyclesStart : Uint8B + +0x5bd0 TaggedCycles : [2] Uint8B + +0x5be0 GenerationTarget : Uint8B + +0x5be8 AffinitizedCycles : Uint8B + +0x5bf0 ImportantCycles : Uint8B + +0x5bf8 UnimportantCycles : Uint8B + +0x5c00 DpcWatchdogProfileSingleDpcThreshold : Uint4B + +0x5c04 MmSpinLockOrdering : Int4B + +0x5c08 CachedStack : Ptr64 Void + +0x5c10 PageColor : Uint4B + +0x5c14 NodeColor : Uint4B + +0x5c18 NodeShiftedColor : Uint4B + +0x5c1c SecondaryColorMask : Uint4B + +0x5c20 PrcbPad81 : [7] UChar + +0x5c27 TbFlushListActive : UChar + +0x5c28 PrcbPad82 : [2] Uint8B + +0x5c38 CycleTime : Uint8B + +0x5c40 Cycles : [4] [2] Uint8B + +0x5c80 CcFastMdlReadNoWait : Uint4B + +0x5c84 CcFastMdlReadWait : Uint4B + +0x5c88 CcFastMdlReadNotPossible : Uint4B + +0x5c8c CcMapDataNoWait : Uint4B + +0x5c90 CcMapDataWait : Uint4B + +0x5c94 CcPinMappedDataCount : Uint4B + +0x5c98 CcPinReadNoWait : Uint4B + +0x5c9c CcPinReadWait : Uint4B + +0x5ca0 CcMdlReadNoWait : Uint4B + +0x5ca4 CcMdlReadWait : Uint4B + +0x5ca8 CcLazyWriteHotSpots : Uint4B + +0x5cac CcLazyWriteIos : Uint4B + +0x5cb0 CcLazyWritePages : Uint4B + +0x5cb4 CcDataFlushes : Uint4B + +0x5cb8 CcDataPages : Uint4B + +0x5cbc CcLostDelayedWrites : Uint4B + +0x5cc0 CcFastReadResourceMiss : Uint4B + +0x5cc4 CcCopyReadWaitMiss : Uint4B + +0x5cc8 CcFastMdlReadResourceMiss : Uint4B + +0x5ccc CcMapDataNoWaitMiss : Uint4B + +0x5cd0 CcMapDataWaitMiss : Uint4B + +0x5cd4 CcPinReadNoWaitMiss : Uint4B + +0x5cd8 CcPinReadWaitMiss : Uint4B + +0x5cdc CcMdlReadNoWaitMiss : Uint4B + +0x5ce0 CcMdlReadWaitMiss : Uint4B + +0x5ce4 CcReadAheadIos : Uint4B + +0x5ce8 MmCacheTransitionCount : Int4B + +0x5cec MmCacheReadCount : Int4B + +0x5cf0 MmCacheIoCount : Int4B + +0x5cf4 PrcbPad91 : Uint4B + +0x5cf8 MmInternal : Ptr64 Void + +0x5d00 PowerState : _PROCESSOR_POWER_STATE + +0x5f00 HyperPte : Ptr64 Void + +0x5f08 ScbList : _LIST_ENTRY + +0x5f18 ForceIdleDpc : _KDPC + +0x5f58 DpcWatchdogDpc : _KDPC + +0x5f98 DpcWatchdogTimer : _KTIMER + +0x5fd8 Cache : [5] _CACHE_DESCRIPTOR + +0x6014 CacheCount : Uint4B + +0x6018 CachedCommit : Uint4B + +0x601c CachedResidentAvailable : Uint4B + +0x6020 WheaInfo : Ptr64 Void + +0x6028 EtwSupport : Ptr64 Void + +0x6030 ExSaPageArray : Ptr64 Void + +0x6038 KeAlignmentFixupCount : Uint4B + +0x603c PrcbPad95 : Uint4B + +0x6040 HypercallPageList : _SLIST_HEADER + +0x6050 StatisticsPage : Ptr64 Uint8B + +0x6058 PrcbPad85 : [5] Uint8B + +0x6080 HypercallCachedPages : Ptr64 Void + +0x6088 VirtualApicAssist : Ptr64 Void + +0x6090 PackageProcessorSet : _KAFFINITY_EX + +0x6138 PackageId : Uint4B + +0x613c PrcbPad86 : Uint4B + +0x6140 SharedReadyQueueMask : Uint8B + +0x6148 SharedReadyQueue : Ptr64 _KSHARED_READY_QUEUE + +0x6150 SharedQueueScanOwner : Uint4B + +0x6154 ScanSiblingIndex : Uint4B + +0x6158 CoreProcessorSet : Uint8B + +0x6160 ScanSiblingMask : Uint8B + +0x6168 LLCMask : Uint8B + +0x6170 CacheProcessorMask : [5] Uint8B + +0x6198 ProcessorProfileControlArea : Ptr64 _PROCESSOR_PROFILE_CONTROL_AREA + +0x61a0 ProfileEventIndexAddress : Ptr64 Void + +0x61a8 DpcWatchdogProfile : Ptr64 Ptr64 Void + +0x61b0 DpcWatchdogProfileCurrentEmptyCapture : Ptr64 Ptr64 Void + +0x61b8 SchedulerAssist : Ptr64 Void + +0x61c0 SynchCounters : _SYNCH_COUNTERS + +0x6278 PrcbPad94 : Uint8B + +0x6280 FsCounters : _FILESYSTEM_DISK_COUNTERS + +0x6290 VendorString : [13] UChar + +0x629d PrcbPad100 : [3] UChar + +0x62a0 FeatureBits : Uint8B + +0x62a8 UpdateSignature : _LARGE_INTEGER + +0x62b0 PteBitCache : Uint8B + +0x62b8 PteBitOffset : Uint4B + +0x62bc PrcbPad105 : Uint4B + +0x62c0 Context : Ptr64 _CONTEXT + +0x62c8 ContextFlagsInit : Uint4B + +0x62cc PrcbPad115 : Uint4B + +0x62d0 ExtendedState : Ptr64 _XSAVE_AREA + +0x62d8 IsrStack : Ptr64 Void + +0x62e0 EntropyTimingState : _KENTROPY_TIMING_STATE + +0x6430 PrcbPad110 : Uint8B + +0x6438 PrcbPad111 : [7] Uint8B + +0x6470 AbSelfIoBoostsList : _SINGLE_LIST_ENTRY + +0x6478 AbPropagateBoostsList : _SINGLE_LIST_ENTRY + +0x6480 AbDpc : _KDPC + +0x64c0 IoIrpStackProfilerCurrent : _IOP_IRP_STACK_PROFILER + +0x6514 IoIrpStackProfilerPrevious : _IOP_IRP_STACK_PROFILER + +0x6568 SecureFault : _KSECURE_FAULT_INFORMATION + +0x6578 PrcbPad120 : Uint8B + +0x6580 LocalSharedReadyQueue : _KSHARED_READY_QUEUE + +0x67f0 PrcbPad125 : [2] Uint8B + +0x6800 TimerExpirationTraceCount : Uint4B + +0x6804 PrcbPad127 : Uint4B + +0x6808 TimerExpirationTrace : [16] _KTIMER_EXPIRATION_TRACE + +0x6908 PrcbPad128 : [7] Uint8B + +0x6940 Mailbox : Ptr64 _REQUEST_MAILBOX + +0x6948 PrcbPad130 : [7] Uint8B + +0x6980 SelfmapLockHandle : [4] _KLOCK_QUEUE_HANDLE + +0x69e0 PrcbPad135 : [1184] UChar + +0x6e80 KernelDirectoryTableBase : Uint8B + +0x6e88 RspBaseShadow : Uint8B + +0x6e90 UserRspShadow : Uint8B + +0x6e98 ShadowFlags : Uint4B + +0x6e9c VerwSelector : Uint2B + +0x6e9e PrcbPad139 : Uint2B + +0x6ea0 PrcbPad140 : [508] Uint8B + +0x7e80 RequestMailbox : [1] _REQUEST_MAILBOX + ''' + _fields_ = ( - ('MxCsr', ctypes.c_ulong), - ('Number', ctypes.c_ushort), - ('LegacyNumber', ctypes.c_char), - ('ReservedMustBeZero', ctypes.c_char), - ('InterruptRequest', ctypes.c_bool), - ('IdleHalt', ctypes.c_bool), - ('CurrentThread', KTHREAD), - ('NextThread', KTHREAD), - ('IdleThread', KTHREAD) + ('MxCsr', ctypes.c_ulong), + ('Number', ctypes.c_ushort), + ('LegacyNumber', ctypes.c_char), + ('ReservedMustBeZero', ctypes.c_char), + ('InterruptRequest', ctypes.c_bool), + ('IdleHalt', ctypes.c_bool), + ('CurrentThread', KTHREAD), + ('NextThread', KTHREAD), + ('IdleThread', KTHREAD), + ('NestingLevel', ctypes.c_char), + ('ClockOwner', ctypes.c_char), + ('PendingTickFlags', ctypes.c_char), + ('PendingTick', ctypes.c_void_p), # POS 0 : BIT 1 + ('PendingBackupTick', ctypes.c_void_p), # POS 1 : BIT 1 + ('IdleState', ctypes.c_char), + ('Number', ctypes.c_uint), + ('RspBase', ctypes.c_uint8), + ('PrcbLock', ctypes.c_uint8), + ('PriorityState', ctypes.c_char_p), + ('CpuType', ctypes.c_char), + ('CpuStep', ctypes.c_uint), + ('CpuStepping', ctypes.c_char), + ('CpuModel', ctypes.c_char), + ('MHz', ctypes.c_uint), + ('HalReserved', ctypes.c_uint8 * 8), + ('MinorVersion', ctypes.c_uint), + ('MajorVersion', ctypes.c_uint), + ('BuildType', ctypes.c_char), + ('CpuVendor', ctypes.c_char), + ('CoresPerPhysicalProcessor', ctypes.c_char), + ('LogicalProcessorPerCore', ctypes.c_char), + ('PrcbPad04', ctypes.c_uint8 * 6), + ('ParentNode', KNODE), + ('_padding0', ctypes.c_uint8 * 0x7DC0) # 0x7E80 (request mailbox) - 0xC0 (parent node) ) class KPCR(struct.BaseStruct): ''' Defintion for 64-bit KPCR structure. - Follows implementation of KPRCB64. Initialisation function in `pe.py` registers this structure. - Structure has a configuration item in `Windows.ql`. - Below output is from Windows RS4 - 0: kd> dt _KPCR + Windows 10 RS4 nt!_KPCR +0x000 NtTib : _NT_TIB +0x000 GdtBase : Ptr64 _KGDTENTRY64 @@ -384,12 +781,17 @@ class KPCR(struct.BaseStruct): +0x110 Unused3 : Ptr64 Void +0x118 PcrAlign1 : [24] Uint4B +0x180 Prcb : _KPRCB - - ''' _fields_ = ( ('NtTib', NT_TIB), + ('GdtBase', ctypes.c_void_p), # _KGDTENTRY64 + ('TssBase', ctypes.c_void_p), # _KTSS64 + ('UserRsp', ctypes.c_uint8), + ('Self', ctypes.c_void_p), # _KPCR + ('CurrentPrcb', ctypes.c_void_p), # _KPRCB + ('LockArray', ctypes.c_void_p), # _KSPIN_LOCK_QUEUE + ('UsedSelf', ctypes.c_void_p), ('IdtBase', ctypes.c_void_p), # This is meant to be a KIDTENTRY64 pointer ('Unused', ctypes.c_ulong), # [0x2] ('Irql', ctypes.c_void_p), # This is meant to be a KIRQL structure @@ -412,13 +814,73 @@ class KPCR(struct.BaseStruct): class KPROCESS(struct.BaseStruct): + ''' + Defintion for KPROCESS 64 + + Windows 10 RS4 + ntdll!_KPROCESS + +0x000 Header : _DISPATCHER_HEADER + +0x018 ProfileListHead : _LIST_ENTRY + +0x028 DirectoryTableBase : Uint8B + +0x030 ThreadListHead : _LIST_ENTRY + +0x040 ProcessLock : Uint4B + +0x044 ProcessTimerDelay : Uint4B + +0x048 DeepFreezeStartTime : Uint8B + +0x050 Affinity : _KAFFINITY_EX + +0x0f8 ReadyListHead : _LIST_ENTRY + +0x108 SwapListEntry : _SINGLE_LIST_ENTRY + +0x110 ActiveProcessors : _KAFFINITY_EX + +0x1b8 AutoAlignment : Pos 0, 1 Bit + +0x1b8 DisableBoost : Pos 1, 1 Bit + +0x1b8 DisableQuantum : Pos 2, 1 Bit + +0x1b8 DeepFreeze : Pos 3, 1 Bit + +0x1b8 TimerVirtualization : Pos 4, 1 Bit + +0x1b8 CheckStackExtents : Pos 5, 1 Bit + +0x1b8 CacheIsolationEnabled : Pos 6, 1 Bit + +0x1b8 PpmPolicy : Pos 7, 3 Bits + +0x1b8 ActiveGroupsMask : Pos 10, 20 Bits + +0x1b8 VaSpaceDeleted : Pos 30, 1 Bit + +0x1b8 ReservedFlags : Pos 31, 1 Bit + +0x1b8 ProcessFlags : Int4B + +0x1bc BasePriority : Char + +0x1bd QuantumReset : Char + +0x1be Visited : Char + +0x1bf Flags : _KEXECUTE_OPTIONS + +0x1c0 ThreadSeed : [20] Uint4B + +0x210 IdealNode : [20] Uint2B + +0x238 IdealGlobalNode : Uint2B + +0x23a Spare1 : Uint2B + +0x23c StackCount : _KSTACK_COUNT + +0x240 ProcessListEntry : _LIST_ENTRY + +0x250 CycleTime : Uint8B + +0x258 ContextSwitches : Uint8B + +0x260 SchedulingGroup : Ptr64 _KSCHEDULING_GROUP + +0x268 FreezeCount : Uint4B + +0x26c KernelTime : Uint4B + +0x270 UserTime : Uint4B + +0x274 ReadyTime : Uint4B + +0x278 UserDirectoryTableBase : Uint8B + +0x280 AddressPolicy : UChar + +0x281 Spare2 : [71] UChar + +0x2c8 InstrumentationCallback : Ptr64 Void + +0x2d0 SecureState : + ''' _fields_ = ( - ('') + ('Header', ctypes.c_void_p), + ('ProfileListHead', ctypes.c_void_p), + ('DirectoryTableBase', ctypes.c_uint8), + ('ThreadListHead', ctypes.c_void_p), + ('ProcessLock', ctypes.c_uint), + ('ProcessTimerDelay', ctypes.c_uint), + ('DeepFreezeStartTime', ctypes.c_uint8), + ('Affinity', ctypes.c_void_p), + ('ReadyListHead', ctypes.c_void_p), + ('SwapListEntry', ctypes.c_void_p), + ('ActiveProcessors', ctypes.c_void_p), ) - def make_list_entry(archbits: int): native_type = struct.get_native_type(archbits) Struct = struct.get_aligned_struct(archbits) diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql index 66d566de4..bbd792021 100644 --- a/qiling/profiles/windows.ql +++ b/qiling/profiles/windows.ql @@ -10,6 +10,7 @@ KI_USER_SHARED_DATA = 0xfffff78000000000 KPCR = 0xfffff78100000000 KPRCB = 0xfffff78200000000 KTHREAD = 0xfffff78300000000 +KPROCESS = 0xfffff78400000000 [OS32] heap_address = 0x5000000 From 2441d2a789deb20faf2e8f59c918be8d3dd683db Mon Sep 17 00:00:00 2001 From: joshj Date: Tue, 30 Aug 2022 17:40:42 +0100 Subject: [PATCH 10/13] Added pointers to key kernel structures from KPRCB --- qiling/loader/pe.py | 32 ++++++++++++++++++++++++++++++-- qiling/os/windows/structs.py | 12 ++++++++---- qiling/profiles/windows.ql | 3 +++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 71428dfbd..1e2ac2905 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -719,16 +719,44 @@ def init_kprcb(self): kprcb_struct = KPRCB self.ql.mem.map(kprcb_addr, self.ql.mem.align_up(kprcb_struct.sizeof()), info='[kprcb]') + # Get address for KTHREAD & KNODE structures + kthread_addr = osconf.getint('KTHREAD') + knode_addr = osconf.getint('KNODE') + # Initialize and instance with a few key fields kprcb_obj = kprcb_struct.volatile_ref(self.ql.mem, kprcb_addr) - #kprcb_obj.CurrentThread = - #kprcb_obj.IdleThread = osconf.getint('KTHREAD') + kprcb_obj.CurrentThread = kthread_addr # Writes _KTHREAD pointer to CurrentThread field + kprcb_obj.IdleThread = kthread_addr # Writes _KTHREAD pointer to IdleThread field + kprcb_obj.ParentNode = knode_addr # Writes _KNODE pointer to ParentNode field # Writes KPRCB pointer into GS:[0x20] self.ql.mem.write_ptr(0x6000020, kprcb_addr) + # @NOTE: Tests for writing structure pointers. + # please don't remove. + self.ql.log.warn(f"KPRCB IdleThread: {kprcb_obj.IdleThread:x}") + self.ql.log.warn(f"KPRCB CurrentThread: {kprcb_obj.CurrentThread:x}") + self.ql.log.warn(f"KPRCB ParentNode: {kprcb_obj.ParentNode:x}") + self.ql.os.KPRCB = kprcb_obj + def init_knode(self): + ''' + Initialisation function for KNODE structure. + ''' + + sysconf = self.ql.os.profile['SYSTEM'] + osconf = self.ql.os.profile[f'OS{self.ql.arch.bits}'] + + knode_addr = osconf.getint('KNODE') + knode_struct = KNODE + self.ql.mem.map(knode_addr, self.ql.mem.aign_up(knode_struct.sizeof()), info='[knode]') + + # Initialize struct with a few key fields + knode_obj = knode_struct.volatile_ref(self.ql.mem, knode_addr) + + self.ql.os.KNODE = knode_obj + def init_security_cookie(self, pe: pefile.PE, image_base: int): diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 502c72d6d..92dc8e77e 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -711,6 +711,10 @@ class KPRCB(struct.BaseStruct): +0x7e80 RequestMailbox : [1] _REQUEST_MAILBOX ''' + # We need a native pointer so that we can write the address of _KTREAD structure + native_type = struct.get_native_type(64) + pointer_type = native_type + _fields_ = ( ('MxCsr', ctypes.c_ulong), ('Number', ctypes.c_ushort), @@ -718,9 +722,9 @@ class KPRCB(struct.BaseStruct): ('ReservedMustBeZero', ctypes.c_char), ('InterruptRequest', ctypes.c_bool), ('IdleHalt', ctypes.c_bool), - ('CurrentThread', KTHREAD), - ('NextThread', KTHREAD), - ('IdleThread', KTHREAD), + ('CurrentThread', pointer_type), # _KTHREAD + ('NextThread', pointer_type), # _KTHREAD + ('IdleThread', pointer_type), # _KTHREAD ('NestingLevel', ctypes.c_char), ('ClockOwner', ctypes.c_char), ('PendingTickFlags', ctypes.c_char), @@ -744,7 +748,7 @@ class KPRCB(struct.BaseStruct): ('CoresPerPhysicalProcessor', ctypes.c_char), ('LogicalProcessorPerCore', ctypes.c_char), ('PrcbPad04', ctypes.c_uint8 * 6), - ('ParentNode', KNODE), + ('ParentNode', pointer_type), # _KNODE ('_padding0', ctypes.c_uint8 * 0x7DC0) # 0x7E80 (request mailbox) - 0xC0 (parent node) ) diff --git a/qiling/profiles/windows.ql b/qiling/profiles/windows.ql index bbd792021..3deb46377 100644 --- a/qiling/profiles/windows.ql +++ b/qiling/profiles/windows.ql @@ -11,6 +11,7 @@ KPCR = 0xfffff78100000000 KPRCB = 0xfffff78200000000 KTHREAD = 0xfffff78300000000 KPROCESS = 0xfffff78400000000 +KNODE = 0xfffff78500000000 [OS32] heap_address = 0x5000000 @@ -24,6 +25,8 @@ KI_USER_SHARED_DATA = 0xffdf0000 KPCR = 0xffdf1000 KPRCB = 0xffdf2000 KTHREAD = 0xffdf3000 +KPROCESS = 0xffdf4000 +KNODE = 0xffdf5000 [CODE] # ram_size 0xa00000 is 10MB From 7eb381aaf17082d9fdfcfa1585b199419405ac44 Mon Sep 17 00:00:00 2001 From: joshj Date: Tue, 30 Aug 2022 17:49:48 +0100 Subject: [PATCH 11/13] Added pointers to key kernel structures from KPCR --- qiling/loader/pe.py | 25 +++++++++++++++++++++---- qiling/os/windows/structs.py | 10 +++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 1e2ac2905..ed7f841f0 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -642,7 +642,9 @@ def init_ki_user_shared_data(self): def init_kpcr(self): ''' - Initialisation function for KPCR structure. This structure's pointer should be at gs:[0x18] + Initialisation function for KPCR structure. + + This structure's pointer should always be at gs:[0x18] ''' sysconf = self.ql.os.profile['SYSTEM'] @@ -652,21 +654,34 @@ def init_kpcr(self): kpcr_struct = KPCR self.ql.mem.map(kpcr_addr, self.ql.mem.align_up(kpcr_struct.sizeof()), info='[kpcr]') + # Get address for KPRCB structure + kprcb_addr = osconf.getint('KPRCB') + # Initialize an instance with a few key fields # @TODO: initialize StackBase & StackLimit kpcr_obj = kpcr_struct.volatile_ref(self.ql.mem, kpcr_addr) kpcr_obj.MajorVersion = sysconf.getint('majorVersion') kpcr_obj.MinorVersion = sysconf.getint('minorVersion') - #kpcr_obj.Prcb = osconf.getint('KPRCB') + kpcr_obj.CurrentPrcb = kprcb_addr # Writes _KPRCB pointer to CurrentPrcb field + kpcr_obj.Self = kpcr_addr # Writes _KPCR pointer to Self field + kpcr_obj.Prcb = kprcb_addr # Writes _KPRCB pointer to Prcb field # Writes KPCR pointer into GS:[0x18] self.ql.mem.write_ptr(0x6000018, kpcr_addr) + + # @NOTE: Tests for writing structure pointers. + # Please don't remove. + self.ql.log.warn(f"KPCR CurrentPrcb: {kpcr_obj.CurrentPrcb:x}") + self.ql.log.warn(f"KPCR Self: {kpcr_obj.Self:x}") + self.ql.log.warn(f"KPCR Prcb: {kpcr_obj.Prcb:x}") self.ql.os.KPCR = kpcr_obj def init_kthread(self): ''' - Initialisation function for KTHREAD structure. This structures pointer should be at gs:[0x188] + Initialisation function for KTHREAD structure. + + This structures pointer should always be at gs:[0x188] ''' sysconf = self.ql.os.profile['SYSTEM'] @@ -709,7 +724,9 @@ def init_kprocess(self): def init_kprcb(self): ''' - Initialisation function for KPCRB structure. This structures pointer should be at gs:[0x20] + Initialisation function for KPCRB structure. + + This structures pointer should always be at gs:[0x20] ''' sysconf = self.ql.os.profile['SYSTEM'] diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 92dc8e77e..f8c4af756 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -787,13 +787,17 @@ class KPCR(struct.BaseStruct): +0x180 Prcb : _KPRCB ''' + # Get 64-bit native_type + native_type = struct.get_native_type(64) + pointer_type = native_type + _fields_ = ( ('NtTib', NT_TIB), ('GdtBase', ctypes.c_void_p), # _KGDTENTRY64 ('TssBase', ctypes.c_void_p), # _KTSS64 ('UserRsp', ctypes.c_uint8), - ('Self', ctypes.c_void_p), # _KPCR - ('CurrentPrcb', ctypes.c_void_p), # _KPRCB + ('Self', pointer_type), # _KPCR + ('CurrentPrcb', pointer_type), # _KPRCB ('LockArray', ctypes.c_void_p), # _KSPIN_LOCK_QUEUE ('UsedSelf', ctypes.c_void_p), ('IdtBase', ctypes.c_void_p), # This is meant to be a KIDTENTRY64 pointer @@ -814,7 +818,7 @@ class KPCR(struct.BaseStruct): ('KdVersionBlock', ctypes.c_void_p), ('Unused3', ctypes.c_void_p), ('PcrAlign1', ctypes.c_ulong), # [0x18] - ('Prcb', KPRCB)) + ('Prcb', pointer_type)) # _KPRCB class KPROCESS(struct.BaseStruct): From 40b17e9181cff571c8e50f4724f8c94a9b2f4384 Mon Sep 17 00:00:00 2001 From: joshj Date: Tue, 30 Aug 2022 17:51:39 +0100 Subject: [PATCH 12/13] Initialise kernel structures, fix typo. --- qiling/loader/pe.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index ed7f841f0..b7dd5c081 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -767,7 +767,7 @@ def init_knode(self): knode_addr = osconf.getint('KNODE') knode_struct = KNODE - self.ql.mem.map(knode_addr, self.ql.mem.aign_up(knode_struct.sizeof()), info='[knode]') + self.ql.mem.map(knode_addr, self.ql.mem.align_up(knode_struct.sizeof()), info='[knode]') # Initialize struct with a few key fields knode_obj = knode_struct.volatile_ref(self.ql.mem, knode_addr) @@ -884,6 +884,8 @@ def load(self, pe: Optional[pefile.PE]): self.init_kprcb() self.init_kpcr() self.init_kthread() + self.init_kprocess() + self.init_knode() # set IRQ Level in CR8 to PASSIVE_LEVEL self.ql.arch.regs.write(UC_X86_REG_CR8, 0) From 753a0bc911d5e30606e8dae1f62b5b00774c2f28 Mon Sep 17 00:00:00 2001 From: joshj Date: Tue, 30 Aug 2022 22:06:57 +0100 Subject: [PATCH 13/13] finish implementing kprocess structure. --- qiling/os/windows/structs.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index f8c4af756..193b3a466 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -8,6 +8,7 @@ from enum import IntEnum from functools import lru_cache +from pickletools import uint1 from qiling.os import struct from qiling.os.windows.const import MAX_PATH @@ -885,6 +886,40 @@ class KPROCESS(struct.BaseStruct): ('ReadyListHead', ctypes.c_void_p), ('SwapListEntry', ctypes.c_void_p), ('ActiveProcessors', ctypes.c_void_p), + ('AutoAlignment', ctypes.c_int, 1), + ('DisableBoost', ctypes.c_int, 1), + ('DisableQuantum', ctypes.c_int, 1), + ('DeepFreeze', ctypes.c_int, 1), + ('TimerVirtualization', ctypes.c_int, 1), + ('CheckStackExtentns', ctypes.c_int, 1), + ('CacheIsolationEnabled', ctypes.c_int, 1), + ('PpmPolicy', ctypes.c_int, 3), + ('ActiveGroupsMask', ctypes.c_int, 20), + ('VaSpaceDeleted', ctypes.c_int, 1), + ('ReservedFlags', ctypes.c_int, 1), + ('ProcessFlags', ctypes.c_int), + ('BasePriority', ctypes.c_char), + ('QuantumReset', ctypes.c_char), + ('Visited', ctypes.c_char), + ('Flags', ctypes.c_void_p), + ('ThreadSeed', ctypes.c_uint * 20), + ('IdealNode', ctypes.c_uint * 20), + ('IdealGlobalNode', ctypes.c_uint), + ('Spare1', ctypes.c_uint), + ('StackCount', ctypes.c_void_p), + ('ProcessListEntry', ctypes.c_void_p), + ('CycleTime', ctypes.c_uint8), + ('ContextSwitches', ctypes.c_uint8), + ('SchedulingGroup', ctypes.c_void_p), + ('FreezeCount', ctypes.c_uint), + ('KernelTime', ctypes.c_uint), + ('UserTime', ctypes.c_uint), + ('ReadyTime', ctypes.c_uint), + ('UserDirectoryTableBase', ctypes.c_uint8), + ('AddressPolicy', ctypes.c_char), + ('Spare2', ctypes.c_char * 71), + ('InstrumentationCallback', ctypes.c_void_p), + ('SecureState', ctypes.c_void_p) )