ayoubfaouzi / al-khaser

Public malware techniques used in the wild: Virtual Machine, Emulation, Debuggers, Sandbox detection.
GNU General Public License v2.0
5.94k stars 1.18k forks source link

Question about the RDTSC detection with locky trick #272

Closed M6HqVBcddw2qaN4s closed 4 months ago

M6HqVBcddw2qaN4s commented 4 months ago

I wanted to use a KVM virtual machine for malware analysis and spoofed pretty much every detection vector by manually patching and compiling KVM, QEMU and OVMF.

However, there's one detection in al-khaser that I wasn't able to patch; the RDTSC detection.

The thing is, when I don't patch KVM; the RDTSC check with VMEXIT is detected, but the one with the locky trick isnt.

And when I do patch KVM with the RDTSC interception function; the check with VMEXIT is no longer detected, but then the one with locky trick is detected. I've included the RDTSC handler patch function below, any help is appreciated. Thank you!

int kvm_emulate_rdtsc(struct kvm_vcpu *vcpu)
{
    vcpu->run->exit_reason = 123;

    u64 difference = rdtsc() - vcpu->last_exit_start;
    u64 final_time = vcpu->total_exit_time + difference;

    u64 data = rdtsc() - final_time;

    vcpu->arch.regs[VCPU_REGS_RAX] = data & -1u;
    vcpu->arch.regs[VCPU_REGS_RDX] = (data >> 32) & -1u;

    return kvm_skip_emulated_instruction(vcpu);
}
ayoubfaouzi commented 4 months ago

Hello @M6HqVBcddw2qaN4s

It looks like your kvm_emulate_rdtsc calculates the time spend to handle a VM-Exit and substract that time before entering the guest. My first question would be what type of exit reasons do you track ? Just CPUID or maybe some other exits ? Because vcpu->total_exit_time may not be calculated for all exit reasons.

Secondly, by looking at the locky trick:

tsc1 = __rdtsc();

// Waste some cycles - should be faster than CloseHandle on bare metal
GetProcessHeap();

tsc2 = __rdtsc();

// Waste some cycles - slightly longer than GetProcessHeap() on bare metal
CloseHandle(0);

tsc3 = __rdtsc();

// Did it take at least 10 times more CPU cycles to perform CloseHandle than it took to perform GetProcessHeap()?
if ((LODWORD(tsc3) - LODWORD(tsc2)) / (LODWORD(tsc2) - LODWORD(tsc1)) >= 10)
    return FALSE;
}

GetProcessHeap() don't result on a VM-Exit because it just access the PEB and returns an internal field from that struct. So unless you're trapping PEB/TEB field accesses, you need to make sure those Exit-Reason are also taken care of in related to vcpu->total_exit_time.

CloseHandle results on a syscall, which results on an MSR Read, are you trapping MSR Reads ?

This is a good watch: https://www.youtube.com/watch?v=axKl0lWYJSA

gsuberland commented 4 months ago

Please do not remove information from issues.