From cf61fe6805fa17c8fa1a21e33c3aee5d3cb89ecc Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 24 May 2023 20:47:43 +0300 Subject: [PATCH] Correct x86 segment selector and descriptor bits --- qiling/arch/x86_const.py | 52 ++++++++++++++++++++------------------ qiling/arch/x86_utils.py | 18 ++++++------- qiling/os/linux/syscall.py | 4 +-- qiling/os/linux/thread.py | 2 +- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/qiling/arch/x86_const.py b/qiling/arch/x86_const.py index d4716ba6..0bdfe5b4 100644 --- a/qiling/arch/x86_const.py +++ b/qiling/arch/x86_const.py @@ -5,34 +5,38 @@ from unicorn.x86_const import * -QL_X86_F_GRANULARITY = 0x8 -QL_X86_F_PROT_32 = 0x4 -QL_X86_F_LONG = 0x2 -QL_X86_F_AVAILABLE = 0x1 +# segment descriptor bits [47:40] +QL_X86_A_DATA_A = (1 << 0) +QL_X86_A_DATA_W = (1 << 1) +QL_X86_A_DATA_E = (1 << 2) +QL_X86_A_DATA = (0 << 3) -QL_X86_A_PRESENT = 0x80 +QL_X86_A_CODE_A = (1 << 0) +QL_X86_A_CODE_R = (1 << 1) +QL_X86_A_CODE_C = (1 << 2) +QL_X86_A_CODE = (1 << 3) -QL_X86_A_PRIV_3 = 0x60 -QL_X86_A_PRIV_2 = 0x40 -QL_X86_A_PRIV_1 = 0x20 -QL_X86_A_PRIV_0 = 0x0 +QL_X86_A_DESC_SYSTEM = (0 << 4) # S +QL_X86_A_DESC_CODE = (1 << 4) +QL_X86_A_DESC_DATA = (1 << 4) -QL_X86_A_CODE = 0x10 -QL_X86_A_DATA = 0x10 -QL_X86_A_TSS = 0x0 -QL_X86_A_GATE = 0x0 -QL_X86_A_EXEC = 0x8 +QL_X86_A_PRIV_0 = (0b00 << 5) # DPL +QL_X86_A_PRIV_1 = (0b01 << 5) +QL_X86_A_PRIV_2 = (0b10 << 5) +QL_X86_A_PRIV_3 = (0b11 << 5) -QL_X86_A_DATA_WRITABLE = 0x2 -QL_X86_A_CODE_READABLE = 0x2 -QL_X86_A_DIR_CON_BIT = 0x4 +QL_X86_A_PRESENT = (1 << 7) -QL_X86_S_GDT = 0x0 -QL_X86_S_LDT = 0x4 -QL_X86_S_PRIV_3 = 0x3 -QL_X86_S_PRIV_2 = 0x2 -QL_X86_S_PRIV_1 = 0x1 -QL_X86_S_PRIV_0 = 0x0 +# segment descriptor bits [55:52] +QL_X86_F_AVAILABLE = (1 << 0) # AVL +QL_X86_F_LONG = (1 << 1) # L +QL_X86_F_OPSIZE_32 = (1 << 2) # D/B +QL_X86_F_GRANULARITY = (1 << 3) # G + +# segment selector bits +QL_X86_SEGSEL_RPL_MASK = 0b11 +QL_X86_SEGSEL_TI_GDT = (0 << 2) +QL_X86_SEGSEL_TI_LDT = (1 << 2) QL_X86_GDT_ADDR = 0x30000 QL_X86_GDT_LIMIT = 0x1000 @@ -43,8 +47,6 @@ IA32_FS_BASE_MSR = 0xC0000100 IA32_GS_BASE_MSR = 0xC0000101 IA32_APIC_BASE_MSR = 0x1B -# WINDOWS SETUP VALUE -# Linux also needs these GS_SEGMENT_ADDR = 0x6000000 GS_SEGMENT_SIZE = (20 << 20) # 20 MB diff --git a/qiling/arch/x86_utils.py b/qiling/arch/x86_utils.py index 587477ad..4726ef74 100644 --- a/qiling/arch/x86_utils.py +++ b/qiling/arch/x86_utils.py @@ -88,12 +88,12 @@ class GDTManager: @staticmethod def make_selector(idx: int, rpl: int) -> int: - assert rpl & ~0b11 == 0 + assert rpl & ~QL_X86_SEGSEL_RPL_MASK == 0 - return (idx << 3) | QL_X86_S_GDT | rpl + return (idx << 3) | QL_X86_SEGSEL_TI_GDT | rpl def register_gdt_segment(self, index: int, seg_base: int, seg_limit: int, access: int) -> int: - flags = QL_X86_F_PROT_32 + flags = QL_X86_F_OPSIZE_32 # is this a huge segment? if seg_limit > (1 << 16): @@ -139,7 +139,7 @@ class SegmentManager: class SegmentManager86(SegmentManager): def setup_cs_ds_ss_es(self, base: int, size: int) -> None: # While debugging the linux kernel segment, the cs segment was found on the third segment of gdt. - access = QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_CODE | QL_X86_A_CODE | QL_X86_A_CODE_C | QL_X86_A_CODE_R selector = self.gdtm.register_gdt_segment(3, base, size - 1, access) self.arch.regs.cs = selector @@ -147,7 +147,7 @@ class SegmentManager86(SegmentManager): # TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0. # While debugging the Linux kernel segment, I found that the three segments DS, SS, and ES all point to the same location in the GDT table. # This position is the fifth segment table of GDT. - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_0 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(5, base, size - 1, access) self.arch.regs.ds = selector @@ -155,13 +155,13 @@ class SegmentManager86(SegmentManager): self.arch.regs.es = selector def setup_fs(self, base: int, size: int) -> None: - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(14, base, size - 1, access) self.arch.regs.fs = selector def setup_gs(self, base: int, size: int) -> None: - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(15, base, size - 1, access) self.arch.regs.gs = selector @@ -170,14 +170,14 @@ class SegmentManager86(SegmentManager): class SegmentManager64(SegmentManager): def setup_cs_ds_ss_es(self, base: int, size: int) -> None: # While debugging the linux kernel segment, the cs segment was found on the sixth segment of gdt. - access = QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_CODE | QL_X86_A_CODE | QL_X86_A_CODE_C | QL_X86_A_CODE_R selector = self.gdtm.register_gdt_segment(6, base, size - 1, access) self.arch.regs.cs = selector # TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0. # When I debug the Linux kernel, I find that only the SS is set to the fifth segment table, and the rest are not set. - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_0 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(5, base, size - 1, access) # self.arch.regs.ds = selector diff --git a/qiling/os/linux/syscall.py b/qiling/os/linux/syscall.py index c61951c9..6092eefd 100644 --- a/qiling/os/linux/syscall.py +++ b/qiling/os/linux/syscall.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -48,7 +48,7 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int): index = ql.os.gdtm.get_free_idx(12) if index in (12, 13, 14): - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W ql.os.gdtm.register_gdt_segment(index, base, limit, access) ql.mem.write_ptr(u_info_addr, index, 4) diff --git a/qiling/os/linux/thread.py b/qiling/os/linux/thread.py index 0c3864bf..a4162743 100644 --- a/qiling/os/linux/thread.py +++ b/qiling/os/linux/thread.py @@ -383,7 +383,7 @@ class QlLinuxX86Thread(QlLinuxThread): index = self.ql.os.gdtm.get_free_idx(12) if index in (12, 13, 14): - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W self.ql.os.gdtm.register_gdt_segment(index, base, limit, access) self.ql.mem.write_ptr(tls_addr, index, 4)