mirror of
https://github.com/NaC-L/Mergen.git
synced 2026-05-12 09:40:34 +00:00
lifter: model XGETBV deterministically (#107)
* lifter: model XGETBV deterministically Add XGETBV opcode support and model selector 0 as a deterministic XCR0 value (0x7: x87+SSE+AVX enabled), with zero returned for other selectors. This follows the existing CPUID deterministic-model approach for static lifting/deobfuscation. Verification: - build_iced lifter rewrite_microtests - rewrite_microtests.exe xgetbv_returns_deterministic_xcr0 int29_fastfail_lowered_to_noreturn_call solve_path_widens_mapped_rva_target normalize_runtime_target_widens_mapped_rva_target - python test.py quick - python test.py vmp * rewrite: seed deterministic XGETBV handler The XGETBV semantics patch is deterministic by design, so the full-handler oracle pipeline must not use Unicorn's host-specific result. Add a manual handler seed entry for xgetbv bytes and computed expected outputs, then regenerate the enriched seed and oracle vectors to match the lifter model (selector 0 -> EAX=0x7, EDX=0). Verification: - scripts\rewrite\run_all_handlers.cmd - python test.py quick - python test.py vmp --------- Co-authored-by: yusufcanislek <yusuf.canislek@meetdandy.com>
This commit is contained in:
@@ -535,6 +535,25 @@ MERGEN_LIFTER_DEFINITION_TEMPLATES(void)::lift_cpuid() {
|
||||
ConstantInt::get(Type::getInt32Ty(context), 0xBFEBFBFF));
|
||||
}
|
||||
|
||||
MERGEN_LIFTER_DEFINITION_TEMPLATES(void)::lift_xgetbv() {
|
||||
LLVMContext& context = builder->getContext();
|
||||
auto* selector = createZExtOrTruncFolder(
|
||||
GetRegisterValue(Register::ECX), Type::getInt32Ty(context));
|
||||
|
||||
// Deterministic XCR0 model for static lifting. The startup path only queries
|
||||
// selector 0 and checks bits 1:2 (SSE/AVX state enabled). Return XCR0=0x7
|
||||
// so x87 (bit 0, architecturally always enabled) is also modeled correctly.
|
||||
auto* selectorIsZero = createICMPFolder(
|
||||
CmpInst::ICMP_EQ, selector, ConstantInt::get(Type::getInt32Ty(context), 0));
|
||||
auto* eaxValue = createSelectFolder(
|
||||
selectorIsZero, ConstantInt::get(Type::getInt32Ty(context), 0x7),
|
||||
ConstantInt::get(Type::getInt32Ty(context), 0), "xgetbv.eax");
|
||||
auto* edxValue = ConstantInt::get(Type::getInt32Ty(context), 0);
|
||||
|
||||
SetRegisterValue(Register::EAX, eaxValue);
|
||||
SetRegisterValue(Register::EDX, edxValue);
|
||||
}
|
||||
|
||||
uint64_t alternative_pext(uint64_t source, uint64_t mask) {
|
||||
uint64_t result = 0;
|
||||
int bit_position = 0;
|
||||
|
||||
@@ -97,6 +97,7 @@ OPCODE(test, TEST)
|
||||
OPCODE(cmp, CMP)
|
||||
OPCODE(rdtsc, RDTSC)
|
||||
OPCODE(cpuid, CPUID)
|
||||
OPCODE(xgetbv, XGETBV)
|
||||
OPCODE(pext, PEXT)
|
||||
//
|
||||
OPCODE(setnz, SETNZ)
|
||||
|
||||
@@ -429,6 +429,28 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool runXgetbvReturnsDeterministicXcr0(std::string& details) {
|
||||
LifterUnderTest lifter;
|
||||
lifter.SetRegisterValue(RegisterUnderTest::RCX,
|
||||
makeI64(lifter.builder->getContext(), 0));
|
||||
static constexpr uint8_t kXgetbv[] = {0x0F, 0x01, 0xD0};
|
||||
lifter.liftBytes(kXgetbv, sizeof(kXgetbv));
|
||||
|
||||
auto eaxAfter = readConstantAPInt(
|
||||
lifter.GetRegisterValue(RegisterUnderTest::EAX));
|
||||
auto edxAfter = readConstantAPInt(
|
||||
lifter.GetRegisterValue(RegisterUnderTest::EDX));
|
||||
if (!eaxAfter.has_value() || eaxAfter->getZExtValue() != 0x7) {
|
||||
details = " xgetbv should set EAX to modeled XCR0 low bits (0x7)\n";
|
||||
return false;
|
||||
}
|
||||
if (!edxAfter.has_value() || edxAfter->getZExtValue() != 0) {
|
||||
details = " xgetbv should clear EDX for the modeled XCR0 high bits\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool runLoopGeneralizationConditionalBranchAllowed(std::string& details) {
|
||||
LifterUnderTest lifter;
|
||||
lifter.currentPathSolveContext =
|
||||
@@ -1248,6 +1270,8 @@ private:
|
||||
&InstructionTester::runLoopGeneralizationIndirectJumpBlockedWhenUnresolved);
|
||||
runCustom("int29_fastfail_lowered_to_noreturn_call",
|
||||
&InstructionTester::runInt29FastfailLoweredToNoReturnCall);
|
||||
runCustom("xgetbv_returns_deterministic_xcr0",
|
||||
&InstructionTester::runXgetbvReturnsDeterministicXcr0);
|
||||
runCustom("loop_generalization_indirect_jump_allowed_when_resolved",
|
||||
&InstructionTester::runLoopGeneralizationIndirectJumpAllowedWhenResolved);
|
||||
runCustom("loop_generalization_ret_blocked",
|
||||
|
||||
@@ -6106,6 +6106,42 @@
|
||||
"flags": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "smoke_xgetbv_xgetbv",
|
||||
"handler": "xgetbv",
|
||||
"oracle_mode": "computed",
|
||||
"instruction_bytes": [
|
||||
15,
|
||||
1,
|
||||
208
|
||||
],
|
||||
"initial": {
|
||||
"registers": {
|
||||
"RAX": "0x1122334455667788",
|
||||
"RBX": "0x8877665544332211",
|
||||
"RCX": "0x0",
|
||||
"RDX": "0x2"
|
||||
},
|
||||
"flags": {
|
||||
"FLAG_CF": 0,
|
||||
"FLAG_PF": 0,
|
||||
"FLAG_AF": 0,
|
||||
"FLAG_ZF": 0,
|
||||
"FLAG_SF": 0,
|
||||
"FLAG_OF": 0,
|
||||
"FLAG_DF": 0,
|
||||
"FLAG_IF": 1
|
||||
}
|
||||
},
|
||||
"expected": {
|
||||
"registers": {
|
||||
"RAX": "0x7",
|
||||
"RDX": "0x0"
|
||||
},
|
||||
"flags": {}
|
||||
},
|
||||
"oracle_observations": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -162,6 +162,14 @@ MANUAL_HANDLER_CASES = {
|
||||
"flags": {},
|
||||
},
|
||||
},
|
||||
"xgetbv": {
|
||||
"mnemonic": "xgetbv",
|
||||
"instruction_bytes": [0x0F, 0x01, 0xD0],
|
||||
"initial": {
|
||||
"registers": {"RCX": "0x0"},
|
||||
"flags": {},
|
||||
},
|
||||
},
|
||||
# ---- Control flow ----
|
||||
"call": {
|
||||
"mnemonic": "call",
|
||||
|
||||
@@ -184,6 +184,12 @@ def _compute_stosx(initial):
|
||||
def _compute_cli(_initial):
|
||||
return {"registers": {}, "flags": {"FLAG_IF": 0}}
|
||||
|
||||
def _compute_xgetbv(initial):
|
||||
rcx = _get_initial_register(initial, "RCX", 0) & 0xFFFFFFFF
|
||||
eax = 0x7 if rcx == 0 else 0
|
||||
return {"registers": {"RAX": hex(eax), "RDX": hex(0)}, "flags": {}}
|
||||
|
||||
|
||||
|
||||
COMPUTED_HANDLERS = {
|
||||
"push": _compute_push,
|
||||
@@ -197,6 +203,7 @@ COMPUTED_HANDLERS = {
|
||||
"movs_x": _compute_movs_x,
|
||||
"stosx": _compute_stosx,
|
||||
"cli": _compute_cli,
|
||||
"xgetbv": _compute_xgetbv,
|
||||
}
|
||||
|
||||
# Non-deterministic system instructions — truly untestable
|
||||
|
||||
@@ -4656,6 +4656,39 @@
|
||||
},
|
||||
"oracle": "none",
|
||||
"source": "capstone-auto-discovery"
|
||||
},
|
||||
{
|
||||
"name": "smoke_xgetbv_xgetbv",
|
||||
"handler": "xgetbv",
|
||||
"instruction_bytes": [
|
||||
15,
|
||||
1,
|
||||
208
|
||||
],
|
||||
"initial": {
|
||||
"registers": {
|
||||
"RAX": "0x1122334455667788",
|
||||
"RBX": "0x8877665544332211",
|
||||
"RCX": "0x0",
|
||||
"RDX": "0x2"
|
||||
},
|
||||
"flags": {
|
||||
"FLAG_CF": 0,
|
||||
"FLAG_PF": 0,
|
||||
"FLAG_AF": 0,
|
||||
"FLAG_ZF": 0,
|
||||
"FLAG_SF": 0,
|
||||
"FLAG_OF": 0,
|
||||
"FLAG_DF": 0,
|
||||
"FLAG_IF": 1
|
||||
}
|
||||
},
|
||||
"expected": {
|
||||
"registers": {},
|
||||
"flags": {}
|
||||
},
|
||||
"oracle": "none",
|
||||
"source": "capstone-auto-discovery"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5125,6 +5125,42 @@
|
||||
},
|
||||
"oracle": "unicorn",
|
||||
"source": "capstone-auto-discovery"
|
||||
},
|
||||
{
|
||||
"name": "smoke_xgetbv_xgetbv",
|
||||
"handler": "xgetbv",
|
||||
"instruction_bytes": [
|
||||
15,
|
||||
1,
|
||||
208
|
||||
],
|
||||
"initial": {
|
||||
"registers": {
|
||||
"RAX": "0x1122334455667788",
|
||||
"RBX": "0x8877665544332211",
|
||||
"RCX": "0x0",
|
||||
"RDX": "0x2"
|
||||
},
|
||||
"flags": {
|
||||
"FLAG_CF": 0,
|
||||
"FLAG_PF": 0,
|
||||
"FLAG_AF": 0,
|
||||
"FLAG_ZF": 0,
|
||||
"FLAG_SF": 0,
|
||||
"FLAG_OF": 0,
|
||||
"FLAG_DF": 0,
|
||||
"FLAG_IF": 1
|
||||
}
|
||||
},
|
||||
"expected": {
|
||||
"registers": {
|
||||
"RAX": "0x7",
|
||||
"RDX": "0x0"
|
||||
},
|
||||
"flags": {}
|
||||
},
|
||||
"oracle": "computed",
|
||||
"source": "capstone-auto-discovery"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user