From fa1eb9d39966cdda4d8c1a1abd7d5a165c8b6d06 Mon Sep 17 00:00:00 2001 From: Axel Tillequin Date: Mon, 4 Mar 2024 14:50:52 +0100 Subject: [PATCH] add support for Intel CET_IBT (endbr/notrack) --- amoco/arch/x64/asm.py | 4 ++++ amoco/arch/x64/spec_ia32e.py | 9 +++++++++ amoco/arch/x86/asm.py | 4 ++++ amoco/arch/x86/parsers.py | 5 ++++- amoco/arch/x86/spec_ia32.py | 29 +++++++++++++++++++++++++---- tests/test_arch_x64.py | 13 ++++++++++++- 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/amoco/arch/x64/asm.py b/amoco/arch/x64/asm.py index b891d1f..7394555 100644 --- a/amoco/arch/x64/asm.py +++ b/amoco/arch/x64/asm.py @@ -1819,3 +1819,7 @@ def i_XLATB(i, fmap): fmap[rip] = fmap[rip] + i.length _b = fmap(mem(rbx + al.zeroextend(64), 8)) fmap[al] = _b + +i_ENDBR32 = i_NOP +i_ENDBR64 = i_NOP + diff --git a/amoco/arch/x64/spec_ia32e.py b/amoco/arch/x64/spec_ia32e.py index e8eb5fb..480e94f 100644 --- a/amoco/arch/x64/spec_ia32e.py +++ b/amoco/arch/x64/spec_ia32e.py @@ -370,6 +370,8 @@ def ia32_rm64(obj, Mod, RM, data): op1, data = getModRM(obj, Mod, RM, data, REX) obj.operands = [op1] obj.misc["absolute"] = True + if obj.misc['segreg'] is env.ds: + setpfx(obj, ('notrack',True), 0) obj.type = type_control_flow @@ -1089,6 +1091,13 @@ def ia32_movbe_crc32(obj, s, Mod, RM, REG, data): obj.operands = [op1, op2] obj.type = type_data_processing +# ENDBR (added by Intel in 2017 to protect against ROP) +@ispec_ia32("32>[ {f3}{0f}{1e}{fb} ]", mnemonic="ENDBR32") +@ispec_ia32("32>[ {f3}{0f}{1e}{fa} ]", mnemonic="ENDBR64") +def ia32_endbr(obj): + obj.operands = [] + obj.type = type_cpu_state + # FPU instructions: # ----------------- diff --git a/amoco/arch/x86/asm.py b/amoco/arch/x86/asm.py index 496838a..bc7d367 100644 --- a/amoco/arch/x86/asm.py +++ b/amoco/arch/x86/asm.py @@ -1800,3 +1800,7 @@ def i_PEXTRW(i, fmap): else: v = top(16) fmap[op1] = v.zeroextend(op1.size) + +i_ENDBR32 = i_NOP +i_ENDBR64 = i_NOP + diff --git a/amoco/arch/x86/parsers.py b/amoco/arch/x86/parsers.py index 65651cd..126c3d7 100644 --- a/amoco/arch/x86/parsers.py +++ b/amoco/arch/x86/parsers.py @@ -78,6 +78,7 @@ def att_syntax_gen(env, CONDITION_CODES, cpu_addrsize, instruction): "repe", "repne", "repnz", + "notrack", ] ) @@ -318,13 +319,15 @@ def action_instr(toks): i = instruction(b"") i.mnemonic = toks[0].upper() # Remove prefixes - if i.mnemonic in ("REP", "REPZ", "REPNZ", "REPE", "REPNE", "LOCK"): + if i.mnemonic in ("REP", "REPZ", "REPNZ", "REPE", "REPNE", "LOCK", "NOTRACK"): if i.mnemonic in ("REP", "REPZ", "REPE"): i.misc.update({"pfx": ["rep", None, None, None], "rep": True}) if i.mnemonic in ("REPNZ", "REPNE"): i.misc.update({"pfx": ["repne", None, None, None], "repne": True}) if i.mnemonic in ("LOCK",): i.misc.update({"pfx": ["lock", None, None, None], "lock": True}) + if i.mnemonic in ("NOTRACK",): + i.misc.update({"pfx": ["notrack", None, None, None], "notrack": True}) del toks[0] # toks.pop(0) is broken for pyparsing 2.0.2 # https://bugs.launchpad.net/ubuntu/+source/pyparsing/+bug/1381564 i.mnemonic = toks[0].upper() diff --git a/amoco/arch/x86/spec_ia32.py b/amoco/arch/x86/spec_ia32.py index 6970e49..e5726e6 100644 --- a/amoco/arch/x86/spec_ia32.py +++ b/amoco/arch/x86/spec_ia32.py @@ -315,8 +315,6 @@ def ia32_rm8(obj, Mod, RM, data): # r/m16/32 @ispec_ia32("*>[ {ff} /0 ]", mnemonic="INC", type=type_data_processing) @ispec_ia32("*>[ {ff} /1 ]", mnemonic="DEC", type=type_data_processing) -@ispec_ia32("*>[ {ff} /2 ]", mnemonic="CALL", type=type_control_flow) -@ispec_ia32("*>[ {ff} /4 ]", mnemonic="JMP", type=type_control_flow) @ispec_ia32("*>[ {ff} /6 ]", mnemonic="PUSH", type=type_data_processing) @ispec_ia32("*>[ {8f} /0 ]", mnemonic="POP", type=type_data_processing) @ispec_ia32("*>[ {f7} /2 ]", mnemonic="NOT", type=type_data_processing) @@ -328,8 +326,16 @@ def ia32_rm8(obj, Mod, RM, data): def ia32_rm32(obj, Mod, RM, data): op1, data = getModRM(obj, Mod, RM, data) obj.operands = [op1] - if obj.mnemonic in ("JMP", "CALL"): - obj.misc["absolute"] = True + +@ispec_ia32("*>[ {ff} /2 ]", mnemonic="CALL") +@ispec_ia32("*>[ {ff} /4 ]", mnemonic="JMP") +def ia32_rm32(obj, Mod, RM, data): + op1, data = getModRM(obj, Mod, RM, data) + obj.operands = [op1] + obj.misc["absolute"] = True + if obj.misc['segreg'] is env.ds: + setpfx(obj, ('notrack',True), 0) + obj.type = type_control_flow # r/m32/48 @@ -339,6 +345,14 @@ def ia32_rm32(obj, Mod, RM, data): @ispec_ia32("*>[ {0f}{01} /1 ]", mnemonic="SIDT", type=type_system) @ispec_ia32("*>[ {0f}{01} /2 ]", mnemonic="LGDT", type=type_system) @ispec_ia32("*>[ {0f}{01} /3 ]", mnemonic="LIDT", type=type_system) +def ia32_far(obj, Mod, RM, data): + op1, data = getModRM(obj, Mod, RM, data) + if op1._is_reg: + raise InstructionError(obj) + size = obj.misc["opdsz"] or env.internals["mode"] + op1.size = size+16 + obj.operands = [op1] + @ispec_ia32("*>[ {0f}{01} /4 ]", mnemonic="SMSW", type=type_system) @ispec_ia32("*>[ {0f}{01} /7 ]", mnemonic="INVLPG", type=type_system) def ia32_op48(obj, Mod, RM, data): @@ -1012,6 +1026,13 @@ def ia32_movbe_crc32(obj, s, Mod, RM, REG, data): obj.operands = [op1, op2] obj.type = type_data_processing +# ENDBR (added by Intel in 2017 to protect against ROP) +@ispec_ia32("32>[ {f3}{0f}{1e}{fb} ]", mnemonic="ENDBR32") +@ispec_ia32("32>[ {f3}{0f}{1e}{fa} ]", mnemonic="ENDBR64") +def ia32_endbr(obj): + obj.operands = [] + obj.type = type_cpu_state + # FPU instructions: # ----------------- diff --git a/tests/test_arch_x64.py b/tests/test_arch_x64.py index d04f7df..0292525 100644 --- a/tests/test_arch_x64.py +++ b/tests/test_arch_x64.py @@ -203,7 +203,18 @@ def test_decoder_028(): assert i.operands[0].ref == 'xmm2' assert i.operands[1].size == 64 -def test_decoder_29(): +def test_decoder_029(): + i = cpu.disassemble(b'\xf3\x0f\x1e\xfa') + assert i.mnemonic=='ENDBR64' + assert str(i).strip()=='endbr64' + +def test_decoder_030(): + i = cpu.disassemble(b'\x3e\xff\xe0') + assert i.mnemonic=='JMP' + assert i.misc['notrack']==True + assert str(i).startswith('notrack') + +def test_decoder_30(): i = cpu.disassemble(b'\x66\x48\x0f\x6e\xce') assert i.mnemonic == "MOVQ" assert i.operands[0].ref == "xmm1"