mirror of
https://github.com/hasherezade/tiny_tracer
synced 2026-06-08 14:36:37 +00:00
453 lines
15 KiB
C++
453 lines
15 KiB
C++
#include "AntiVm.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <map>
|
|
|
|
#include "ProcessInfo.h"
|
|
#include "Util.h"
|
|
#include "TraceLog.h"
|
|
#include "Settings.h"
|
|
#include "PinLocker.h"
|
|
#include "TinyTracer.h"
|
|
|
|
#include "EvasionWatch.h"
|
|
|
|
#define ANTIVM_LABEL "[ANTIVM] --> "
|
|
|
|
using namespace LEVEL_PINCLIENT;
|
|
|
|
/* ================================================================== */
|
|
// Global variables used by AntiVm
|
|
/* ================================================================== */
|
|
#define PATH_BUFSIZE 512
|
|
#define WMI_NUMBER_CORES "NUMBEROFCORES"
|
|
#define WMI_PROCESSOR "PROCESSORID"
|
|
#define WMI_SIZE "SIZE"
|
|
#define WMI_DEVICE_ID "DEVICEID"
|
|
#define WMI_MAC_ADDRESS "MACADDRESS"
|
|
#define WMI_TEMPERATURE "CURRENTTEMPERATURE"
|
|
#define WMI_SERIAL "SERIALNUMBER"
|
|
#define WMI_MODEL "MODEL"
|
|
#define WMI_MANUFACTURER "MANUFACTURER"
|
|
#define WMI_GPU_ADAPTER "ADAPTERCOMPATIBILITY"
|
|
#define WMI_PRODUCT "PRODUCT"
|
|
#define WMI_NAME "NAME"
|
|
|
|
typedef VOID AntiVmCallBack(const ADDRINT addr, const CHAR* name, uint32_t argCount, VOID* arg1, VOID* arg2, VOID* arg3, VOID* arg4, VOID* arg5, VOID* arg6);
|
|
|
|
|
|
/* ================================================================== */
|
|
// Global variables used by AntiVm
|
|
/* ================================================================== */
|
|
|
|
class AntiVmWatch : public EvasionWatch
|
|
{
|
|
public:
|
|
AntiVmWatch() { Init(); }
|
|
virtual BOOL Init();
|
|
};
|
|
|
|
AntiVmWatch m_AntiVm;
|
|
|
|
namespace AntiVm {
|
|
std::map<THREADID, FuncData> m_funcData;
|
|
}; // namespace AntiVm
|
|
/* ==================================================================== */
|
|
// Log info with AntiVm label
|
|
/* ==================================================================== */
|
|
|
|
VOID LogAntiVm(const WatchedType wType, const ADDRINT Address, const char* msg, const char* link = nullptr)
|
|
{
|
|
LogMsgAtAddress(wType, Address, ANTIVM_LABEL, msg, link);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
// Process API calls (related to AntiVm techniques)
|
|
/* ==================================================================== */
|
|
|
|
VOID AntiVm_WmiQueries(const ADDRINT addr, const THREADID tid, const CHAR* name, uint32_t argCount, VOID* arg1, VOID* arg2, VOID* arg3, VOID* arg4, VOID* arg5)
|
|
{
|
|
if (!argCount) return;
|
|
|
|
PinLocker locker;
|
|
const WatchedType wType = isWatchedAddress(addr);
|
|
if (wType == WatchedType::NOT_WATCHED) return;
|
|
|
|
const wchar_t* wmi_query = reinterpret_cast<const wchar_t*>(arg2);
|
|
if (!wmi_query) return;
|
|
|
|
char wmi_query_field[PATH_BUFSIZE] = { 0 };
|
|
util::wstr_to_str(wmi_query, wmi_query_field, PATH_BUFSIZE);
|
|
|
|
if (util::iequals(wmi_query_field, WMI_NUMBER_CORES) || util::iequals(wmi_query_field, WMI_PROCESSOR)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - number of CPU cores",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_SIZE)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - hard disk size",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_DEVICE_ID)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - device ID",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_MAC_ADDRESS)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - MAC address",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_TEMPERATURE)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - system temperatures",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_SERIAL)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - BIOS serial number",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_MODEL) || util::iequals(wmi_query_field, WMI_MANUFACTURER)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - system model and/or manufacturer",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_GPU_ADAPTER)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - video controller adapter",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
else if (util::iequals(wmi_query_field, WMI_PRODUCT) || util::iequals(wmi_query_field, WMI_NAME)) {
|
|
return LogAntiVm(wType, addr, "^ WMI query - system device names",
|
|
"https://evasions.checkpoint.com/techniques/wmi.html#generic-wmi-queries");
|
|
}
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
// Add to monitored functions all the API calls needed for AntiVM.
|
|
/* ==================================================================== */
|
|
|
|
//Functions handles:
|
|
|
|
VOID AntiVm_NtQuerySystemInformation(const ADDRINT Address, const THREADID tid, const CHAR* name, uint32_t argCount, VOID* arg1, VOID* arg2, VOID* arg3, VOID* arg4, VOID* arg5, BOOL isAfter = FALSE)
|
|
{
|
|
if (!argCount) return;
|
|
|
|
PinLocker locker;
|
|
const WatchedType wType = isWatchedAddress(Address);
|
|
if (wType == WatchedType::NOT_WATCHED) return;
|
|
|
|
enum SystemInformationClass {
|
|
SystemFirmwareTableInformation = 0x4C
|
|
};
|
|
if (int((size_t)arg1) == SystemInformationClass::SystemFirmwareTableInformation) {
|
|
if (!isAfter) {
|
|
return LogAntiVm(wType, Address, "^ ntdll!NtQuerySystemInformation (SystemFirmwareTableInformation)",
|
|
"https://revers.engineering/evading-trivial-acpi-checks/");
|
|
}
|
|
else {
|
|
std::stringstream ss;
|
|
ss << "^ ntdll!NtQuerySystemInformation (SystemFirmwareTableInformation). Bypass: ";
|
|
size_t buf_size = (size_t)arg3;
|
|
if (PIN_CheckWriteAccess(arg2) && PIN_CheckWriteAccess((VOID*)((ADDRINT)arg2 + (buf_size - 1)))) {
|
|
::memset(arg2, 0, buf_size);
|
|
ss << "OK";
|
|
}
|
|
else {
|
|
ss << "Failed";
|
|
}
|
|
return LogAntiVm(wType, Address, ss.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID AntiVm_NtQuerySystemInformation_before(const ADDRINT Address, const THREADID tid, const CHAR* name, uint32_t argCount, VOID* arg1, VOID* arg2, VOID* arg3, VOID* arg4, VOID* arg5)
|
|
{
|
|
storeData(AntiVm::m_funcData, tid, name, argCount, arg1, arg2, arg3, arg4, arg5);
|
|
return AntiVm_NtQuerySystemInformation(Address, tid, name, argCount, arg1, arg2, arg3, arg4, arg5, FALSE);
|
|
}
|
|
|
|
VOID AntiVm_NtQuerySystemInformation_after(const ADDRINT Address, const THREADID tid, const CHAR* name, ADDRINT status)
|
|
{
|
|
if (status != 0) {
|
|
return; // failed
|
|
}
|
|
FuncData data;
|
|
if (!retrieveData(AntiVm::m_funcData, tid, name, data)) {
|
|
return;
|
|
}
|
|
return AntiVm_NtQuerySystemInformation(Address, tid, name, data.argsNum, data.args[0], data.args[1], data.args[2], data.args[3], data.args[4], TRUE);
|
|
}
|
|
|
|
VOID AntiVm::MonitorSyscallEntry(THREADID tid, const CHAR* name, const CONTEXT* ctxt, SYSCALL_STANDARD std, const ADDRINT Address)
|
|
{
|
|
EvasionFuncInfo* wfunc = m_AntiVm.fetchSyscallFuncInfo(name, m_Settings.antivm);
|
|
if (!wfunc) return;
|
|
|
|
EvasionWatchBeforeCallBack* callback = wfunc->callbackBefore;
|
|
if (!callback) {
|
|
return;
|
|
}
|
|
|
|
const size_t argCount = wfunc->paramCount;
|
|
const size_t args_max = 5;
|
|
VOID* syscall_args[args_max] = { 0 };
|
|
|
|
for (size_t i = 0; i < args_max; i++) {
|
|
if (i == argCount) break;
|
|
syscall_args[i] = reinterpret_cast<VOID*>(PIN_GetSyscallArgument(ctxt, std, i));
|
|
}
|
|
callback(Address,
|
|
tid,
|
|
name, argCount,
|
|
syscall_args[0],
|
|
syscall_args[1],
|
|
syscall_args[2],
|
|
syscall_args[3],
|
|
syscall_args[4]);
|
|
}
|
|
|
|
VOID AntiVm::MonitorSyscallExit(THREADID tid, const CHAR* name, const CONTEXT* ctxt, SYSCALL_STANDARD std, const ADDRINT Address)
|
|
{
|
|
EvasionFuncInfo* wfunc = m_AntiVm.fetchSyscallFuncInfo(name, m_Settings.antivm);
|
|
if (!wfunc) return;
|
|
|
|
EvasionWatchAfterCallBack* callback = wfunc->callbackAfter;
|
|
if (!callback) {
|
|
return;
|
|
}
|
|
|
|
callback(Address,
|
|
tid,
|
|
name,
|
|
PIN_GetContextReg(ctxt, REG_GAX)
|
|
);
|
|
}
|
|
|
|
//---
|
|
namespace AntiVm {
|
|
std::map<THREADID, ADDRINT> cpuidThreads;
|
|
}; //namespace AntiVm
|
|
|
|
|
|
namespace AntiVm
|
|
{
|
|
VOID CpuidCheck(CONTEXT* ctxt, THREADID tid)
|
|
{
|
|
PinLocker locker;
|
|
|
|
const ADDRINT Address = (ADDRINT)PIN_GetContextReg(ctxt, REG_INST_PTR);
|
|
|
|
const WatchedType wType = isWatchedAddress(Address);
|
|
if (wType == WatchedType::NOT_WATCHED) return;
|
|
|
|
ADDRINT opId = (ADDRINT)PIN_GetContextReg(ctxt, REG_GAX);
|
|
cpuidThreads[tid] = opId;
|
|
if (opId == 0x0) {
|
|
return LogAntiVm(wType, Address, "CPUID - vendor check",
|
|
"https://unprotect.it/technique/cpuid/");
|
|
}
|
|
if (opId == 0x1) {
|
|
return LogAntiVm(wType, Address, "CPUID - HyperVisor bit check",
|
|
"https://unprotect.it/technique/cpuid/");
|
|
}
|
|
if (opId == 0x80000002 || opId == 0x80000003 || opId == 0x80000004) {
|
|
return LogAntiVm(wType, Address, "CPUID - brand check",
|
|
"https://unprotect.it/technique/cpuid/");
|
|
}
|
|
if (opId == 0x40000000) {
|
|
return LogAntiVm(wType, Address, "CPUID - HyperVisor vendor check",
|
|
"https://unprotect.it/technique/cpuid/");
|
|
}
|
|
if (opId == 0x40000002) {
|
|
return LogAntiVm(wType, Address, "CPUID - HyperVisor system identity");
|
|
}
|
|
if (opId == 0x40000003) {
|
|
return LogAntiVm(wType, Address, "CPUID - HyperVisor feature identification");
|
|
}
|
|
}
|
|
|
|
BOOL _AlterCpuidValue(CONTEXT* ctxt, THREADID tid, const REG reg, ADDRINT& regVal)
|
|
{
|
|
const BOOL isHyperVisorSet = m_Settings.isHyperVSet;
|
|
BOOL isSet = FALSE;
|
|
const ADDRINT Address = (ADDRINT)PIN_GetContextReg(ctxt, REG_INST_PTR);
|
|
|
|
const WatchedType wType = isWatchedAddress(Address);
|
|
if (wType == WatchedType::NOT_WATCHED) return FALSE;
|
|
|
|
auto itr = cpuidThreads.find(tid);
|
|
if (itr == cpuidThreads.end()) return FALSE;
|
|
|
|
const ADDRINT opId = itr->second;
|
|
std::stringstream ss;
|
|
ss << "CPUID - HyperVisor res:" << std::hex;
|
|
|
|
const ADDRINT prev = regVal;
|
|
|
|
if (opId == 0x1) {
|
|
if (reg == REG_GCX) {
|
|
ss << " ECX: " << regVal;
|
|
const ADDRINT hv_bit = (ADDRINT)0x1 << 31;
|
|
if (isHyperVisorSet) {
|
|
regVal |= hv_bit; //set bit
|
|
}
|
|
else {
|
|
regVal &= ~hv_bit; //clear bit
|
|
}
|
|
isSet = TRUE;
|
|
}
|
|
}
|
|
|
|
if (opId == 0x40000000) {
|
|
if (reg == REG_GAX) {
|
|
ss << " EAX: " << regVal;
|
|
if (isHyperVisorSet) {
|
|
regVal = 0x40000006;
|
|
} else {
|
|
regVal = 0xc1c;
|
|
}
|
|
} else if (reg == REG_GBX) {
|
|
ss << " EBX: " << regVal;
|
|
if (isHyperVisorSet) {
|
|
regVal = 0x7263694d;
|
|
}
|
|
else {
|
|
regVal = 0x14b4;
|
|
}
|
|
} else if (reg == REG_GCX) {
|
|
ss << " ECX: " << regVal;
|
|
if (isHyperVisorSet) {
|
|
regVal = 0x666f736f;
|
|
}
|
|
else {
|
|
regVal = 0x64;
|
|
}
|
|
} else if (reg == REG_GDX) {
|
|
ss << " EDX: " << regVal;
|
|
if (isHyperVisorSet) {
|
|
regVal = 0x76482074;
|
|
}
|
|
else {
|
|
regVal = 0;
|
|
}
|
|
}
|
|
isSet = TRUE;
|
|
}
|
|
else if (opId == 0x40000003) {
|
|
if (reg == REG_GAX) {
|
|
ss << " EAX: " << regVal;
|
|
if (isHyperVisorSet) {
|
|
regVal = 0x3fff;
|
|
}
|
|
else {
|
|
regVal = 0x14b4;
|
|
}
|
|
} else if (reg == REG_GBX) {
|
|
ss << " EBX: " << regVal;
|
|
if (isHyperVisorSet) {
|
|
regVal = 0x2bb9ff;
|
|
}
|
|
else {
|
|
regVal = 0x64;
|
|
}
|
|
} else if (reg == REG_GCX) {
|
|
ss << " ECX: " << regVal;
|
|
regVal = 0;
|
|
} else if (reg == REG_GDX) {
|
|
ss << " EDX: " << regVal;
|
|
regVal = 0;
|
|
}
|
|
isSet = TRUE;
|
|
}
|
|
if (isSet && ss.str().length()) {
|
|
if (prev != regVal) {
|
|
ss << " -> " << regVal;
|
|
}
|
|
LogAntiVm(wType, Address, ss.str().c_str());
|
|
}
|
|
return isSet;
|
|
}
|
|
|
|
ADDRINT AlterCpuidValue(CONTEXT* ctxt, THREADID tid, const REG reg)
|
|
{
|
|
PinLocker locker;
|
|
ADDRINT regVal = PIN_GetContextReg(ctxt, reg);
|
|
_AlterCpuidValue(ctxt, tid, reg, regVal);
|
|
return regVal;
|
|
}
|
|
|
|
}; //namespace AntiVm
|
|
|
|
|
|
VOID AntiVm::InstrumentCPUIDCheck(INS ins)
|
|
{
|
|
INS_InsertCall(
|
|
ins,
|
|
IPOINT_BEFORE, (AFUNPTR)AntiVm::CpuidCheck,
|
|
IARG_CONTEXT,
|
|
IARG_THREAD_ID,
|
|
IARG_END
|
|
);
|
|
|
|
INS_InsertCall(
|
|
ins,
|
|
IPOINT_AFTER, (AFUNPTR)AntiVm::AlterCpuidValue,
|
|
IARG_CONTEXT,
|
|
IARG_THREAD_ID,
|
|
IARG_UINT32, REG_GAX,
|
|
IARG_RETURN_REGS,
|
|
REG_GAX,
|
|
IARG_END
|
|
);
|
|
|
|
INS_InsertCall(
|
|
ins,
|
|
IPOINT_AFTER, (AFUNPTR)AntiVm::AlterCpuidValue,
|
|
IARG_CONTEXT,
|
|
IARG_THREAD_ID,
|
|
IARG_UINT32, REG_GBX,
|
|
IARG_RETURN_REGS,
|
|
REG_GBX,
|
|
IARG_END
|
|
);
|
|
|
|
INS_InsertCall(
|
|
ins,
|
|
IPOINT_AFTER, (AFUNPTR)AntiVm::AlterCpuidValue,
|
|
IARG_CONTEXT,
|
|
IARG_THREAD_ID,
|
|
IARG_UINT32, REG_GCX,
|
|
IARG_RETURN_REGS,
|
|
REG_GCX,
|
|
IARG_END
|
|
);
|
|
|
|
INS_InsertCall(
|
|
ins,
|
|
IPOINT_AFTER, (AFUNPTR)AntiVm::AlterCpuidValue,
|
|
IARG_CONTEXT,
|
|
IARG_THREAD_ID,
|
|
IARG_UINT32, REG_GDX,
|
|
IARG_RETURN_REGS,
|
|
REG_GDX,
|
|
IARG_END
|
|
);
|
|
}
|
|
|
|
//---
|
|
|
|
BOOL AntiVmWatch::Init()
|
|
{
|
|
watchedFuncs.appendFunc(EvasionFuncInfo("ntdll", "NtQuerySystemInformation", 4, AntiVm_NtQuerySystemInformation_before, AntiVm_NtQuerySystemInformation_after));
|
|
// API needed to trace WMI queries:
|
|
#ifdef _WIN64
|
|
watchedFuncs.appendFunc(EvasionFuncInfo("fastprox", "?Get@CWbemObject@@UEAAJPEBGJPEAUtagVARIANT@@PEAJ2@Z", 5, AntiVm_WmiQueries));
|
|
#else
|
|
watchedFuncs.appendFunc(EvasionFuncInfo("fastprox", "?Get@CWbemObject@@UAGJPBGJPAUtagVARIANT@@PAJ2@Z", 5, AntiVm_WmiQueries));
|
|
#endif
|
|
isInit = TRUE;
|
|
return isInit;
|
|
}
|
|
|
|
VOID AntiVm::MonitorAntiVmFunctions(IMG Image)
|
|
{
|
|
m_AntiVm.installCallbacks(Image, nullptr, m_Settings.antivm);
|
|
}
|