Guest User

Scoring-Based Evasion PoC

a guest
Jul 13th, 2026
183
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 57.96 KB | Cybersecurity | 0 0
  1. // COMPILATION: cl /EHsc /W4 /std:c++17 poc.cpp /Fe:poc.exe user32.lib advapi32.lib
  2.  
  3. #include <windows.h>
  4. #include <tlhelp32.h>
  5. #include <lmcons.h>
  6.  
  7. #include <intrin.h>
  8.  
  9. #include <stdint.h>
  10. #include <stdbool.h>
  11. #include <stddef.h>
  12.  
  13. #include <cstdio>
  14. #include <cstring>
  15. #include <cwchar>
  16. #include <cctype>
  17. #include <cwctype>
  18. #include <string>
  19. #include <algorithm>
  20. #include <utility>
  21.  
  22. using uint = unsigned int;
  23.  
  24. typedef struct cpuid_result_t {
  25.     uint32_t eax;
  26.     uint32_t ebx;
  27.     uint32_t ecx;
  28.     uint32_t edx;
  29. } cpuid_result_t;
  30.  
  31. static cpuid_result_t cpuid(uint32_t leaf) {
  32.     int regs[4] = {0};
  33.  
  34.     __cpuid(regs, (int)leaf);
  35.  
  36.     cpuid_result_t result;
  37.     result.eax = (uint32_t)regs[0];
  38.     result.ebx = (uint32_t)regs[1];
  39.     result.ecx = (uint32_t)regs[2];
  40.     result.edx = (uint32_t)regs[3];
  41.  
  42.     return result;
  43. }
  44.  
  45. static inline uint32_t ROTL(uint32_t v, unsigned int r) {
  46.     r &= 31;  // limita la rotazione a 0..31
  47.     return (v << r) | (v >> ((32 - r) & 31));
  48. }
  49.  
  50. static inline wchar_t toupper_w(wchar_t c) {
  51.     return (wchar_t)towupper((wint_t)c);
  52. }
  53.  
  54. static wchar_t* wcslower(const wchar_t* src, wchar_t* dst, size_t dst_count) {
  55.     if (src == nullptr || dst == nullptr || dst_count == 0) {
  56.         return nullptr;
  57.     }
  58.  
  59.     size_t i = 0;
  60.  
  61.     for (; i + 1 < dst_count && src[i] != L'\0'; ++i) {
  62.         wchar_t ch = src[i];
  63.  
  64.         if (ch >= L'A' && ch <= L'Z') {
  65.             ch = (wchar_t)(ch + 0x20);
  66.         }
  67.  
  68.         dst[i] = ch;
  69.     }
  70.  
  71.     dst[i] = L'\0';
  72.  
  73.     return dst;
  74. }
  75.  
  76. uint32_t g_score = 0;
  77. uint32_t g_integrity = 0;
  78.  
  79. const uint32_t XOR_MASK      = 0x5961b6f7;
  80. const uint32_t HASH_SEED     = 0xffaadf05; // initial integrity mask only
  81. const uint32_t INTEGRITY_XOR = 0xd3de4abd; // per-round integrity XOR constant
  82.  
  83. // per-round integrity step data
  84. uint32_t DAT_140044520[11] = {
  85.     0,0,0,0,0,0,0,0,0,0,0
  86. };
  87.  
  88. uint Check0_ProcessNameBlocklist(void);
  89. uint Check1_CoreAndThreadCount(void);
  90. uint Check2_PhysicalMemory(void);
  91. uint Check3_DisplayAdapterAllowlist(void);
  92. uint32_t Check4_UsernameHostnameBlocklist(void);
  93. uint Check5_SystemUptime(void);
  94. uint Check6_SleepAcceleration(void);
  95. uint Check7_RemoteSession(void);
  96. uint Check8_RdtscHypervisorJitter(void);
  97. uint Check9_CpuidBrandMismatch(void);
  98. uint Check10_GpuVendorBlocklist(void);
  99.  
  100. // ══════════════════════════════════════════════════════════
  101. //  FUN_140006d08  —  Anti-Sandbox Orchestrator
  102. //
  103. //  Runs 11 anti-sandbox checks, IDs 0–10.
  104. //  The global suspicion score is stored XOR-masked in g_score.
  105. //  The integrity value is stored in g_integrity and updated every round.
  106. //
  107. //  All API calls are dynamically resolved at first use:
  108. //    → GetProcAddress-equivalent via PEB walk: FUN_1400270c4
  109. //    → API name strings are obfuscated, decoded once, then cached.
  110. //
  111. //  Important scoring caveat:
  112. //    The decompiled code does NOT implement one uniform:
  113. //
  114. //        RunCheck(check_id) → delta
  115. //
  116. //    model for all checks.
  117. //
  118. //    Instead, several checks use distinct control-flow paths:
  119. //      - Check 0 can directly inject +99 into g_score.
  120. //      - Checks 3 and 4 force current_score + 7 on hit.
  121. //      - Checks 6, 8, 9, and 10 explicitly carry the current score,
  122. //        then add the check result.
  123. //      - Check 7 does not carry the existing score on its path.
  124. // ══════════════════════════════════════════════════════════
  125.  
  126. void AntiSandboxSuite() {
  127.  
  128.     // ── Scoring globals initialized to XOR-masked zero ────
  129.     g_score     = 0 ^ XOR_MASK;      // DAT_140055f18 = 0x5961b6f7
  130.     g_integrity = 0 ^ HASH_SEED;     // DAT_140055f1c = 0xffaadf05
  131.     // True value of both globals is 0 at start.
  132.  
  133.     // ── 11-iteration check loop ───────────────────────────
  134.     int check_ids[] = {
  135.         2,   // RAM
  136.         7,   // Remote session
  137.         3,   // Display adapter name
  138.         4,   // Username / computer name
  139.         8,   // CPUID/RDTSC timing
  140.         5,   // System uptime
  141.         9,   // CPUID vendor/brand mismatch
  142.         1,   // CPU topology
  143.         10,  // GPU PCI vendor
  144.         0,   // Process enumeration
  145.         6    // Sleep acceleration
  146.     };
  147.  
  148.     int pre_delays[] = {
  149.         168, // before Check 2
  150.         256, // before Check 7
  151.         371, // before Check 3
  152.         358, // before Check 4
  153.         468, // before Check 8
  154.         221, // before Check 5
  155.         252, // before Check 9
  156.         266, // before Check 1
  157.         109, // before Check 10
  158.         424, // before Check 0
  159.         309  // before Check 6
  160.     };
  161.     uint check_round  = 11;
  162.  
  163.     do {
  164.         uint round = 11 - check_round;
  165.  
  166.         // Optional pre-check delay, if delay > 0.
  167.         if (pre_delays[round] > 0) {
  168.             Sleep(pre_delays[round]);    // FUN_14002cf10 = Sleep()
  169.         }
  170.  
  171.         int check_id = check_ids[round];
  172.  
  173.         // ── Mixed score-update model from the decompiled CFG ──
  174.         //
  175.         // This is intentionally NOT represented as:
  176.         //
  177.         //     g_score = (g_score ^ XOR_MASK) + RunCheck(check_id);
  178.         //     g_score = g_score ^ XOR_MASK;
  179.         //
  180.         // because the actual decompiled control flow has several
  181.         // non-uniform accumulation paths.
  182.         uint score_register = 0;
  183.  
  184.         switch (check_id) {
  185.  
  186.             case 0: {
  187.                 // Check 0:
  188.                 // Running process name blocklist.
  189.                 uint delta = Check0_ProcessNameBlocklist();
  190.                 score_register = (g_score ^ XOR_MASK) + delta;
  191.                 break;
  192.             }
  193.  
  194.             case 1: {
  195.                 // Check 1:
  196.                 // CPU logical core count and thread/affinity topology.
  197.                 uint delta = Check1_CoreAndThreadCount();
  198.                 score_register = (g_score ^ XOR_MASK) + delta;
  199.                 break;
  200.             }
  201.  
  202.             case 2: {
  203.                 // Check 2:
  204.                 // Physical RAM size.
  205.                 uint delta = Check2_PhysicalMemory();
  206.                 score_register = (g_score ^ XOR_MASK) + delta;
  207.                 break;
  208.             }
  209.  
  210.             case 3: {
  211.                 // Check 3:
  212.                 // Display-adapter name allowlist using EnumDisplayDevicesW.
  213.                 //
  214.                 // FUN_140022398-style return semantics from the pseudocode:
  215.                 //   1 -> expected / whitelisted GPU adapter substring found -> no score update
  216.                 //   0 -> no whitelisted GPU adapter substring found          -> score += 7
  217.                 //
  218.                 // This is an allowlist-style check:
  219.                 //   match     -> environment looks acceptable for this check
  220.                 //   no match  -> suspicious / sandbox score increased
  221.                 //
  222.                 // The scoring is inverted compared with normal
  223.                 // "detection == suspicious" checks: on the referenced CFG,
  224.                 // absence of a known/expected GPU substring causes the +7 crash-path score.
  225.                 uint whitelisted_gpu_detected = Check3_DisplayAdapterAllowlist();
  226.  
  227.                 if (whitelisted_gpu_detected == 0) {
  228.                     score_register = (g_score ^ XOR_MASK) + 7;
  229.                 } else {
  230.                     score_register = g_score ^ XOR_MASK;
  231.                 }
  232.  
  233.                 break;
  234.             }
  235.  
  236.             case 4: {
  237.                 // Check 4:
  238.                 // Username / hostname blocklist.
  239.                 //
  240.                 // Forced current_score + 7 path on username/hostname hit.
  241.                 if (Check4_UsernameHostnameBlocklist()) {
  242.                     score_register = (g_score ^ XOR_MASK) + 7;
  243.                 } else {
  244.                     score_register = g_score ^ XOR_MASK;
  245.                 }
  246.  
  247.                 break;
  248.             }
  249.  
  250.             case 5: {
  251.                 // Check 5:
  252.                 // System uptime.
  253.                 uint delta = Check5_SystemUptime();
  254.                 score_register = (g_score ^ XOR_MASK) + delta;
  255.                 break;
  256.             }
  257.  
  258.             case 6: {
  259.                 // Check 6:
  260.                 // Sleep acceleration detection.
  261.                 //
  262.                 // Carries the existing unmasked score forward first,
  263.                 // then adds the check result:
  264.                 //
  265.                 //     local_res8 = DAT_140055f18 ^ 0x5961b6f7;
  266.                 //     local_res8 = local_res8 + delta;
  267.                 uint delta = Check6_SleepAcceleration();
  268.  
  269.                 score_register = g_score ^ XOR_MASK;
  270.                 score_register = score_register + delta;
  271.                 break;
  272.             }
  273.  
  274.             case 7: {
  275.                 // Check 7:
  276.                 // Remote / VM desktop session.
  277.                 uint delta = Check7_RemoteSession();
  278.  
  279.                 score_register = g_score ^ XOR_MASK;
  280.                 score_register = score_register + delta;
  281.                 break;
  282.             }
  283.  
  284.             case 8: {
  285.                 // Check 8:
  286.                 // __rdtsc / __cpuid hypervisor jitter timing.
  287.                 //
  288.                 // Carries the existing score forward, then adds delta.
  289.                 uint delta = Check8_RdtscHypervisorJitter();
  290.  
  291.                 score_register = g_score ^ XOR_MASK;
  292.                 score_register = score_register + delta;
  293.                 break;
  294.             }
  295.  
  296.             case 9: {
  297.                 // Check 9:
  298.                 // __cpuid vendor vs brand mismatch.
  299.                 //
  300.                 // Carries the existing score forward, then adds delta.
  301.                 uint delta = Check9_CpuidBrandMismatch();
  302.  
  303.                 score_register = g_score ^ XOR_MASK;
  304.                 score_register = score_register + delta;
  305.                 break;
  306.             }
  307.  
  308.             case 10: {
  309.                 // Check 10:
  310.                 // GPU PCI vendor ID blocklist.
  311.                 //
  312.                 // Carries the existing score forward, then adds delta.
  313.                 uint delta = Check10_GpuVendorBlocklist();
  314.  
  315.                 score_register = g_score ^ XOR_MASK;
  316.                 score_register = score_register + delta;
  317.                 break;
  318.             }
  319.         }
  320.  
  321.         // LAB_140007060-style score store:
  322.         // re-mask and persist the updated score register.
  323.         g_score = score_register ^ XOR_MASK;
  324.  
  325.         // ── MurmurHash-style integrity hash ───────────────
  326.         //
  327.         // Important:
  328.         //   The integrity hash does NOT use HASH_SEED here.
  329.         //
  330.         //   HASH_SEED = 0xffaadf05 is only the initial mask value used
  331.         //   for g_integrity initialization:
  332.         //
  333.         //       DAT_140055f1c = 0xffaadf05
  334.         //
  335.         //   The actual per-round integrity XOR constant is:
  336.         //
  337.         //       0xd3de4abd
  338.         //
  339.         // Actual decompiled logic:
  340.         //
  341.         //     uVar4 = DAT_140055f1c
  342.         //             ^ 0xd3de4abd
  343.         //             ^ *(uint*)(&DAT_140044520 + lVar11);
  344.         //
  345.         //     uVar4 = (uVar4 << 0xd | uVar4 >> 0x13) * 0x5bd1e995;
  346.         //
  347.         //     DAT_140055f1c = uVar4 >> 0xf ^ uVar4 ^ 0xd3de4abd;
  348.         //
  349.         // The per-round step values are loaded from DAT_140044520,
  350.         // a third static array, not from the score table itself.
  351.         uint v = g_integrity ^ INTEGRITY_XOR ^ DAT_140044520[round];
  352.  
  353.         v = ROTL(v, 13) * 0x5BD1E995;
  354.  
  355.         g_integrity = (v >> 15) ^ v ^ INTEGRITY_XOR;
  356.  
  357.         check_round--;
  358.  
  359.     } while (check_round != 0);
  360.  
  361.     // ── Final kill gate ───────────────────────────────────
  362.  
  363.     // ┌──────┬──────────────────────────────────┬──────────────────────────────────────┬─────────────────────────┬────────────────────────────────────────────┐
  364.     // │  ID  │  Function                         │  Technique                           │  API / Instruction      │  Score behavior                            │
  365.     // ├──────┼──────────────────────────────────┼──────────────────────────────────────┼─────────────────────────┼────────────────────────────────────────────┤
  366.     // │  0   │  Check0_ProcessNameBlocklist      │  Running process name blocklist      │  CreateToolhelp32       │  5/2/1/0 + optional direct +99 injection   │
  367.     // │  1   │  Check1_CoreAndThreadCount        │  CPU logical core count + affinity   │  GetLogicalProcessorInfo│  7 / 2 / 1 / 0, additive path              │
  368.     // │  2   │  Check2_PhysicalMemory            │  Physical RAM size                   │  GlobalMemoryStatusEx   │  7 / 2 / 1 / 0, additive path              │
  369.     // │  3   │  Check3_DisplayAdapterAllowlist    │  Display adapter name hash allowlist │  EnumDisplayDevicesW    │  VM GPU found: no score; otherwise +7     │
  370.     // │  4   │  Check4_UsernameHostnameBlocklist │  Username / hostname blocklist       │  GetComputerNameW       │  Forces current_score + 7 on hit           │
  371.     // │      │                                  │                                      │  GetUserNameW           │                                            │
  372.     // │  5   │  Check5_SystemUptime              │  System uptime                       │  GetTickCount64         │  3 / 1 / 0, additive path                  │
  373.     // │  6   │  Check6_SleepAcceleration         │  Sleep acceleration detection        │  Sleep + GetTickCount64 │  Carries score, then adds 7 / 0            │
  374.     // │  7   │  Check7_RemoteSession             │  Remote / VM desktop session         │  GetSystemMetrics       │  7 / 0, does not carry score on this path  │
  375.     // │  8   │  Check8_RdtscHypervisorJitter     │  __rdtsc / __cpuid hypervisor timing     │  __rdtsc + __cpuid          │  Carries score, then adds 5 / 2 / 0        │
  376.     // │  9   │  Check9_CpuidBrandMismatch        │  __cpuid vendor vs brand mismatch      │  __cpuid leaf 0 + 0x8000  │  Carries score, then adds 5 / 0            │
  377.     // │ 10   │  Check10_GpuVendorBlocklist       │  GPU PCI vendor ID blocklist         │  EnumDisplayDevicesW    │  Carries score, then adds 5 / 0            │
  378.     // ├──────┴──────────────────────────────────┴──────────────────────────────────────┴─────────────────────────┴────────────────────────────────────────────┤
  379.     // │  KILL GATE:  accumulated_score >= 7  →  PoisonScores() + MessageBoxA spam loop + swi(3) / __debugbreak() crash                                      │
  380.     // │  CLEAN GATE: accumulated_score < 7   →  silent return → hidden window → C2 beacon → check.mentor-square80.click                                      │
  381.     // └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  382.  
  383.     int true_score = g_score ^ XOR_MASK;
  384.  
  385.     wprintf(L"[FINAL] true_score=%d threshold=7\n", true_score);
  386.  
  387.     if (true_score < 7) {
  388.         wprintf(L"All checks passed, resume execution...\n");
  389.         return;                // Clean path — resume execution.
  390.     } else {
  391.         wprintf(L"Checks failed, the sample evaded successfully...\n");
  392.     }
  393. }
  394.  
  395. // CHECK 0 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  396.  
  397. // ════════════════════════════════════════════════════════════════
  398. // HashLookup — FNV-1a variant on UTF-16LE strings
  399. // Used by: Check 0 (process names), Check 3 (window titles),
  400. //          Check 4 (username/hostname)
  401. //
  402. // Signature:
  403. //   bool HashLookup(
  404. //       const wchar_t *str,      // lowercased wide string
  405. //       int            char_count,
  406. //       const uint32_t *table,   // hash table pointer
  407. //       int            table_count
  408. //   )
  409. // ════════════════════════════════════════════════════════════════
  410.  
  411. bool HashLookup(const wchar_t *str,
  412.                 int char_count,
  413.                 const uint32_t *table,
  414.                 int table_count) {
  415.     const uint32_t FNV_SEED  = 0xEB1AF681;
  416.     const uint32_t FNV_PRIME = 0x01000193;
  417.  
  418.     uint32_t hash = FNV_SEED;
  419.  
  420.     // FNV-1a hash over UTF-16LE bytes.
  421.     //
  422.     // For each wide character:
  423.     //   lo_byte = str[i] & 0xFF
  424.     //   hi_byte = (str[i] >> 8) & 0xFF
  425.     //
  426.     // Equivalent byte order:
  427.     //   low byte first, then high byte.
  428.     for (int i = 0; i < char_count; i++) {
  429.         uint8_t lo = (uint8_t)(str[i] & 0xFF);
  430.         uint8_t hi = (uint8_t)((str[i] >> 8) & 0xFF);
  431.  
  432.         hash = ((lo ^ hash) * FNV_PRIME) ^ hi;
  433.         hash = hash * FNV_PRIME;
  434.     }
  435.  
  436.     // Linear search through the precomputed hash table.
  437.     for (int j = 0; j < table_count; j++) {
  438.         if (hash == table[j]) {
  439.             return true;
  440.         }
  441.     }
  442.  
  443.     return false;
  444. }
  445.  
  446. // ──────────────────────────────────────────────────────────
  447. // CHECK 0  |  Process Name Blocklist
  448. // Technique: Snapshot all running processes, lowercase each
  449. //            name, compute an FNV-like hash, and compare it
  450. //            against a table of 77 pre-hashed sandbox process
  451. //            names.
  452. // ──────────────────────────────────────────────────────────
  453.  
  454. uint Check0_ProcessNameBlocklist(void) {
  455.     #define PROC_BLOCKLIST_COUNT 77
  456.     // Backing process-name blocklist hash table.
  457.     // These are FNV-1a UTF-16LE variant hashes using seed 0xEB1AF681.
  458.     static const uint32_t proc_blocklist[PROC_BLOCKLIST_COUNT] = {
  459.         0xBA2A05BF, // x32dbg.exe
  460.         0xB9454E34, // x64dbg.exe
  461.         0xFD407966, // windbg.exe
  462.         0x854E2A78, // ollydbg.exe
  463.         0xD7A05A03, // ida.exe
  464.         0xC4AA0F91, // ida64.exe
  465.         0x14143F12, // idaq.exe
  466.         0x47FB2AB0, // idaq64.exe
  467.         0xC1BBDF5F, // h74xsp.exe
  468.         0x0745234D, // idat64.exe
  469.         0xF797E6CC, // radare2.exe
  470.         0x90ADBB1E, // ghidra.exe
  471.         0xE72659B7, // ghidrarun.exe
  472.         0x40C4C43F, // dnspy.exe
  473.         0xE9618405, // de4dot.exe
  474.         0x5C27A3CB, // dotpeek.exe
  475.         0x40C0D839, // dotpeek64.exe
  476.         0x2DC251BC, // cheatengine-x86_64.exe
  477.         0x80D108DA, // cheatengine.exe
  478.         0xE76ACA8F, // unknown
  479.         0xDC30D8CF, // scylla.exe
  480.         0x41CEC4CA, // scylla_x64.exe
  481.         0x30032056, // scylla_x86.exe
  482.         0xDB68AC3F, // die.exe
  483.         0xC81E3552, // pestudio.exe
  484.         0x51A2E803, // pe-bear.exe
  485.         0xCF43F3B4, // hiew.exe
  486.         0xE49EAD55, // hiew32.exe
  487.         0x64D4223C, // apimonitor-x64.exe
  488.         0x4CD9A220, // apimonitor-x86.exe
  489.         0x3F20CF87, // regshot.exe
  490.         0x851C9BF2, // iamn0f.exe
  491.         0x45A901D5, // wireshark.exe
  492.         0x0279E8DB, // fiddler.exe
  493.         0x5ABF9252, // j-obdk.exe
  494.         0x91D75626, // httpdebugger.exe
  495.         0xD48B4CF5, // unknown
  496.         0x1278F6CC, // burpsuite.exe
  497.         0x59CE92B9, // pyw.exe
  498.         0x023F59E6, // mitmproxy.exe
  499.         0xC9590D6E, // 1-7oyt.exe
  500.         0x97510723, // proxifier.exe
  501.         0x415AFE92, // smartsniff.exe
  502.         0xA4EF56F1, // rawcap.exe
  503.         0x60203BFC, // networkminer.exe
  504.         0xDA59FCD1, // dumpcap.exe
  505.         0x068FDF61, // fakenet.exe
  506.         0x6A7F9AC9, // xenservice.exe
  507.         0xA3C3EA38, // unknown
  508.         0xE5A5D8A3, // unknown
  509.         0xCB69584F, // procmon64.exe
  510.         0x7A16F9CC, // procexp.exe
  511.         0x3F72412E, // procexp64.exe
  512.         0x2324C535, // tcpview.exe
  513.         0x30773BF2, // autoruns.exe
  514.         0x4E0861CD, // filemon.exe
  515.         0x7E11166B, // regmon.exe
  516.         0x0AA6028A, // processhacker.exe
  517.         0x0535AC50, // sysanalyzer.exe
  518.         0x0536BF1F, // 52n1oo.exe
  519.         0x343B0086, // ozu0jc.exe
  520.         0x0E6EA0C8, // immunitydebugger.exe
  521.         0x33ECDC2A, // unknown
  522.         0x1E071347, // lordpe.exe
  523.         0x8D8648DE, // protection_id.exe
  524.         0x8C963DD7, // peid.exe
  525.         0x1E08DE85, // joeboxcontrol.exe
  526.         0x3B9C56E7, // joeboxserver.exe
  527.         0x2A00D3A1, // vboxservice.exe
  528.         0x292AEC72, // vboxtray.exe
  529.         0x96B59713, // vmtoolsd.exe
  530.         0xDB40F9D3, // vmwaretray.exe
  531.         0xD890E6CC, // vmwareuser.exe
  532.         0x93760CA8, // vmsrvc.exe
  533.         0xBA27E6E5, // vmusrvc.exe
  534.         0x5F7A2BB5, // prl_tools.exe
  535.         0xEAA39666  // prl_cc.exe
  536.     };
  537.     bool  found_badproc = false;
  538.     int   total_procs   = 0;
  539.  
  540.     // Open a process snapshot (Type=2 = TH32CS_SNAPPROCESS)
  541.     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  542.  
  543.     if (snap != INVALID_HANDLE_VALUE) {
  544.         PROCESSENTRY32W entry = {0};
  545.         entry.dwSize = sizeof(PROCESSENTRY32W);
  546.  
  547.         if (Process32FirstW(snap, &entry)) {
  548.             do {
  549.                 total_procs++;
  550.  
  551.                 // Lowercase the process name as a wide-character string
  552.                 wchar_t name_lower[MAX_PATH] = {0};
  553.                 wcsncpy_s(name_lower, MAX_PATH, entry.szExeFile, _TRUNCATE);
  554.                 _wcslwr_s(name_lower, MAX_PATH);
  555.  
  556.                 // Perform an FNV-style hash lookup against the 77-entry table.
  557.                 if (HashLookup(name_lower, (int)wcslen(name_lower), proc_blocklist, PROC_BLOCKLIST_COUNT)) {
  558.                     wprintf(L"[CHECK-0] blocked process found: %ls\n", name_lower);
  559.                     found_badproc = true;
  560.                 }
  561.  
  562.                 entry.dwSize = sizeof(PROCESSENTRY32W);
  563.             } while (Process32NextW(snap, &entry));
  564.         }
  565.         CloseHandle(snap);
  566.     }
  567.  
  568.     (void)found_badproc;
  569.     int ret_score;
  570.     std::string meaning;
  571.  
  572.     // Return a score based on the total number of running processes
  573.     if (total_procs < 0x33) {
  574.         ret_score = 5;          // < 51 processes → very suspicious
  575.         meaning = "very suspicious: fewer than 51 running processes";
  576.     } else if (total_procs <= 0x4b) {
  577.         ret_score = 2;          // 51–75 processes → mildly suspicious
  578.         meaning = "mildly suspicious: 51 to 75 running processes";
  579.     } else if (total_procs <= 100) {
  580.         ret_score = 1;          // 76–100 processes → slightly suspicious
  581.         meaning = "slightly suspicious: 76 to 100 running processes";
  582.     } else {
  583.         ret_score = 0;          // > 100 processes → normal
  584.         meaning = "normal: more than 100 running processes";
  585.     }
  586.  
  587.     if (found_badproc) {
  588.         ret_score += 99;
  589.     }
  590.  
  591.     printf("[CHECK0] total_processes=%d return=%u meaning=%s\n",
  592.            total_procs,
  593.            ret_score,
  594.            meaning.c_str());
  595.  
  596.     return ret_score;
  597. }
  598.  
  599.  
  600. // CHECK 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  601.  
  602. // ──────────────────────────────────────────────────────────
  603. // CHECK 1  |  CPU Core Count via Processor Information
  604. // Technique: Calls GetLogicalProcessorInformation to count
  605. //            physical CPU packages and logical core bits.
  606. //            Sandboxes often expose only 1–2 vCPUs.
  607. // ──────────────────────────────────────────────────────────
  608.  
  609. uint Check1_CoreAndThreadCount() {
  610.  
  611.     SYSTEM_LOGICAL_PROCESSOR_INFORMATION buf[48];
  612.     DWORD bytes_written = 0x600;
  613.  
  614.     BOOL ok = GetLogicalProcessorInformation(buf, &bytes_written);
  615.  
  616.     if (!ok) {
  617.         wprintf(L"[CHECK-1] GetLogicalProcessorInformation failed. GetLastError() = %lu\n", GetLastError());
  618.         wprintf(L"[CHECK-1] Returning score = 7\n");
  619.         return 0;
  620.     }
  621.  
  622.     uint core_count = 0;
  623.     uint logical_count = 0;
  624.  
  625.     int entry_count = bytes_written >> 5;
  626.  
  627.     for (int i = 0; i < entry_count; i++) {
  628.  
  629.         ULONG_PTR mask = buf[i].ProcessorMask;
  630.         LOGICAL_PROCESSOR_RELATIONSHIP rel = buf[i].Relationship;
  631.  
  632.         // Only RelationProcessorCore entries are used.
  633.         // Each RelationProcessorCore entry represents one physical CPU core.
  634.         if (rel != RelationProcessorCore)
  635.             continue;
  636.  
  637.         core_count++;
  638.  
  639.         // Count set bits in ProcessorMask.
  640.         // Each set bit represents one logical processor / hardware thread
  641.         // belonging to this physical core.
  642.         for (ULONG_PTR m = mask; m != 0; m >>= 1)
  643.             logical_count += static_cast<uint>(m & 1);
  644.     }
  645.  
  646.     uint score;
  647.  
  648.     if (core_count <= 1) {
  649.         score = 7;
  650.     }
  651.     else if (core_count == 2) {
  652.         if (logical_count < 3)
  653.             score = 2;
  654.         else if (logical_count > 4)
  655.             score = 0;
  656.         else
  657.             score = 1;
  658.     }
  659.     else {
  660.         score = 0;
  661.     }
  662.  
  663.     wprintf(L"[CHECK-1] physical core count  = %u\n", core_count);
  664.     wprintf(L"[CHECK-1] logical thread count = %u\n", logical_count);
  665.     wprintf(L"[CHECK-1] returning score      = %u\n", score);
  666.  
  667.     return score;
  668. }
  669.  
  670.  
  671. // CHECK 2 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  672.  
  673. // ──────────────────────────────────────────────────────────
  674. // CHECK 2  |  Physical Memory Size
  675. // Technique: Reads total installed RAM. Sandbox VMs are
  676. //            commonly configured with 512MB–2GB.
  677. //            Real machines typically have 8GB+.
  678. // ──────────────────────────────────────────────────────────
  679.  
  680. uint Check2_PhysicalMemory(void) {
  681.     MEMORYSTATUSEX mem = {0};
  682.  
  683.     // Set the structure size before calling GlobalMemoryStatusEx.
  684.     // This field is required by the Windows API.
  685.     mem.dwLength = sizeof(mem);
  686.  
  687.     // Query the total amount of physical memory installed on the system.
  688.     // If the API call fails, return 0 because the check could not be evaluated.
  689.     if (!GlobalMemoryStatusEx(&mem)) {
  690.         wprintf(L"[CHECK-2] GlobalMemoryStatusEx failed. Returning score: 0\n");
  691.         return 0;
  692.     }
  693.  
  694.     // Convert total physical memory from bytes to megabytes.
  695.     // Shifting right by 20 is equivalent to dividing by 1024 * 1024.
  696.     uint64_t ram_mb = mem.ullTotalPhys >> 20;
  697.  
  698.     uint score = 0;
  699.  
  700.     // Low-memory systems are often associated with sandbox or VM environments.
  701.     // Higher scores indicate stronger suspicion.
  702.     if (ram_mb <= 0x600) {
  703.         score = 7;   // <= 1536 MB: likely sandbox
  704.     } else if (ram_mb <= 0xE00) {
  705.         score = 2;   // <= 3584 MB: suspicious
  706.     } else if (ram_mb <= 0x1600) {
  707.         score = 1;   // <= 5632 MB: borderline
  708.     } else {
  709.         score = 0;   // > 5632 MB: likely real machine
  710.     }
  711.  
  712.     // Print debug information about the calculated RAM value and final score.
  713.     wprintf(L"[CHECK-2] Total physical memory: %llu MB\n",
  714.             (unsigned long long)ram_mb);
  715.  
  716.     wprintf(L"[CHECK-2] Physical memory check score: %u\n",
  717.             score);
  718.  
  719.     return score;
  720. }
  721.  
  722. // CHECK 3 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  723.  
  724. // ──────────────────────────────────────────────────────────
  725. // CHECK 3  |  Display Adapter Name Whitelist / Expected GPU Detection
  726. // API: EnumDisplayDevicesW from user32.dll
  727. // Technique: Enumerate display adapters. For each active adapter,
  728. //            lowercase DeviceString and test all substrings of
  729. //            length 5..14 against a 5-entry FNV UTF-16LE hash table.
  730. //
  731. // Important semantic note:
  732. //   Despite looking like VM GPU detection, the scoring logic shows that
  733. //   the hash table acts as an allowlist / whitelist.
  734. //
  735. // Return semantics, matching the pseudocode:
  736. //   1 -> expected / whitelisted adapter substring found
  737. //   0 -> no whitelisted adapter substring found
  738. //
  739. // Scoring is handled by AntiSandboxSuite():
  740. //   return 1 -> score unchanged
  741. //   return 0 -> score += 7
  742. //
  743. // Therefore:
  744. //   match     -> environment looks acceptable for this check
  745. //   no match  -> suspicious / sandbox score increased
  746. // ──────────────────────────────────────────────────────────
  747.  
  748. static bool Check3_ScanAdapterName(
  749.     const wchar_t* adapter_name_lower,
  750.     const uint32_t* allowlist,
  751.     int allowlist_count,
  752.     DWORD adapter_index
  753. ) {
  754.     if (adapter_name_lower == nullptr || adapter_name_lower[0] == L'\0') {
  755.         return false;
  756.     }
  757.  
  758.     int name_len = (int)wcslen(adapter_name_lower);
  759.  
  760.     for (int sub_len = 5; sub_len <= 14 && sub_len <= name_len; sub_len++) {
  761.         for (int start = 0; start + sub_len <= name_len; start++) {
  762.             wprintf(
  763.                 L"[CHECK-3]   Checking substring len=%d start=%d substring=\"%.*ls\"\n",
  764.                 sub_len,
  765.                 start,
  766.                 sub_len,
  767.                 &adapter_name_lower[start]
  768.             );
  769.  
  770.             if (HashLookup(
  771.                     &adapter_name_lower[start],
  772.                     sub_len,
  773.                     allowlist,
  774.                     allowlist_count))
  775.             {
  776.                 wprintf(L"[CHECK-3] Whitelisted GPU adapter substring hash matched\n");
  777.                 wprintf(
  778.                     L"[CHECK-3] adapter_index=%lu substring=\"%.*ls\" full_device_string=\"%ls\"\n",
  779.                     adapter_index,
  780.                     sub_len,
  781.                     &adapter_name_lower[start],
  782.                     adapter_name_lower
  783.                 );
  784.  
  785.                 return true;
  786.             }
  787.         }
  788.     }
  789.  
  790.     return false;
  791. }
  792.  
  793. uint Check3_DisplayAdapterAllowlist(void) {
  794.     wprintf(L"[CHECK-3] Enumerating display adapters with EnumDisplayDevicesW()...\n");
  795.  
  796.     static const uint32_t gpu_name_allowlist[5] = {
  797.         0x5D9F8AA4u,
  798.         0xB46022F2u,
  799.         0xA77EEF5Bu,
  800.         0xD31A9699u,
  801.         0x7346A4CCu
  802.     };
  803.  
  804.     int whitelisted_gpu_detected = 0;
  805.  
  806.     for (DWORD dev_idx = 0; ; dev_idx++) {
  807.         DISPLAY_DEVICEW dd;
  808.         ZeroMemory(&dd, sizeof(dd));
  809.  
  810.         // In the binary this value is 0x348, which matches sizeof(DISPLAY_DEVICEW).
  811.         dd.cb = sizeof(dd);
  812.  
  813.         if (!EnumDisplayDevicesW(NULL, dev_idx, &dd, 0)) {
  814.             break;
  815.         }
  816.  
  817.         wprintf(L"[CHECK-3] Adapter index %lu found.\n", dev_idx);
  818.         wprintf(L"[CHECK-3]   DeviceString: %ls\n", dd.DeviceString);
  819.         wprintf(L"[CHECK-3]   StateFlags  : 0x%08X\n", dd.StateFlags);
  820.  
  821.         // DISPLAY_DEVICE_ACTIVE / ATTACHED_TO_DESKTOP is bit 0 in the
  822.         // pseudocode. Only active display adapters are processed.
  823.         if ((dd.StateFlags & 0x1) == 0) {
  824.             wprintf(L"[CHECK-3]   Skipping adapter: not active.\n");
  825.             continue;
  826.         }
  827.  
  828.         wchar_t lower[128] = {0};
  829.         wcslower(dd.DeviceString, lower, _countof(lower));
  830.  
  831.         if (Check3_ScanAdapterName(
  832.                 lower,
  833.                 gpu_name_allowlist,
  834.                 _countof(gpu_name_allowlist),
  835.                 dev_idx))
  836.         {
  837.             whitelisted_gpu_detected = 1;
  838.             break;
  839.         }
  840.     }
  841.  
  842.     if (whitelisted_gpu_detected) {
  843.         wprintf(L"[CHECK-3] Returning 1: whitelisted GPU adapter name detected; score unchanged.\n");
  844.         return 1;
  845.     }
  846.  
  847.     wprintf(L"[CHECK-3] Returning 0: no whitelisted GPU adapter name detected; caller will add +7.\n");
  848.     return 0;
  849. }
  850.  
  851. // CHECK 4 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  852.  
  853. // ──────────────────────────────────────────────────────────
  854. // CHECK 4  |  Username & Hostname String Blocklist
  855. // Technique: Reads computer name and username, lowercases,
  856. //            computes FNV-style hash of each, compares
  857. //            against blocklist of 20 known sandbox values.
  858. //            Confirmed hits: vmware, sandbox, cuckoo,
  859. //            malware, sample, analysis, john doe
  860. // ──────────────────────────────────────────────────────────
  861.  
  862. uint32_t Check4_UsernameHostnameBlocklist(void) {
  863.     static const uint32_t name_blocklist[20] = {
  864.         0x7ba43f80, // ?
  865.         0x966ea0aa, // ?
  866.         0x6d2ab6d0, // malware
  867.         0x6ba382ef, // maltest
  868.         0x854d8bec, // johndoe
  869.         0xf8a8714c, // sandbox
  870.         0x0ef62eba, // virus
  871.         0x767a179c, // john doe
  872.         0xb9184372, // ?
  873.         0x5a7eb0b4, // ?
  874.         0x7f1c2975, // ?
  875.         0x4490d09d, // ?
  876.         0x1c9436cc, // george
  877.         0x69c2d508, // ?
  878.         0xf6c2b2af, // test
  879.         0x20ab9bf7, // sample
  880.         0xa7e472e3, // analysis
  881.         0x2410b3a7, // cuckoo
  882.         0xac7d9203, // vmware
  883.         0x17518351  // ?
  884.     };
  885.  
  886.     wchar_t computername[MAX_COMPUTERNAME_LENGTH + 1] = {0};
  887.     DWORD cn_size = MAX_COMPUTERNAME_LENGTH + 1;
  888.  
  889.     wchar_t username[UNLEN + 1] = {0};
  890.     DWORD un_size = UNLEN + 1;
  891.  
  892.     uint32_t result = 0;
  893.  
  894.     BOOL got_computername = GetComputerNameW(computername, &cn_size);
  895.     BOOL got_username = GetUserNameW(username, &un_size);
  896.  
  897.     wprintf(L"[CHECK-4] ComputerName: %ls (%ls)\n",
  898.             got_computername ? computername : L"<failed>",
  899.             got_computername ? L"OK" : L"FAILED");
  900.  
  901.     wprintf(L"[CHECK-4] Username: %ls (%ls)\n",
  902.             got_username ? username : L"<failed>",
  903.             got_username ? L"OK" : L"FAILED");
  904.  
  905.     if (got_computername) {
  906.         wchar_t computername_lower[MAX_COMPUTERNAME_LENGTH + 1] = {0};
  907.         wcslower(computername, computername_lower, _countof(computername_lower));
  908.         if (HashLookup(computername_lower, (int)wcslen(computername_lower), name_blocklist, 20)) {
  909.             wprintf(L"[CHECK-4] Match on ComputerName: %ls\n", computername);
  910.             result = 7;
  911.         }
  912.     }
  913.  
  914.     if (got_username) {
  915.         wchar_t username_lower[UNLEN + 1] = {0};
  916.         wcslower(username, username_lower, _countof(username_lower));
  917.         if (HashLookup(username_lower, (int)wcslen(username_lower), name_blocklist, 20)){
  918.             wprintf(L"[CHECK-4] Match on Username: %ls\n", username);
  919.             result = 7;
  920.         }
  921.     }
  922.  
  923.     wprintf(L"[CHECK-4] Result: %u\n", result);
  924.     return result;
  925. }
  926.  
  927. // CHECK 5 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  928.  
  929. // ──────────────────────────────────────────────────────────
  930. // CHECK 5  |  System Uptime Check
  931. // Technique: A freshly-booted sandbox VM typically has very
  932. //            low uptime. Real machines are usually up for
  933. //            hours or days.
  934. // ──────────────────────────────────────────────────────────
  935.  
  936. uint Check5_SystemUptime() {
  937.     // Get system uptime in milliseconds since boot.
  938.     uint64_t uptime_ms = GetTickCount64();
  939.  
  940.     uint result;
  941.     const wchar_t* meaning;
  942.  
  943.     if (uptime_ms < 0x1D4C1) {
  944.         result = 3;  // < ~2 min -> freshly booted VM
  945.         meaning = L"freshly booted VM";
  946.     }
  947.     else if (uptime_ms <= 600000) {
  948.         result = 1;  // < ~10 min -> suspicious
  949.         meaning = L"suspicious low uptime";
  950.     }
  951.     else {
  952.         result = 0;  // > 10 min -> plausible real machine
  953.         meaning = L"plausible real machine";
  954.     }
  955.  
  956.     wprintf(L"[CHECK-5] Uptime: %llu ms | Result: %u | Meaning: %ls\n",
  957.             uptime_ms, result, meaning);
  958.  
  959.     return result;
  960. }
  961.  
  962. // CHECK 6 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  963.  
  964. // ──────────────────────────────────────────────────────────
  965. // CHECK 6  |  Sleep Skew / Time Acceleration Detection
  966. // Technique: Many sandboxes patch Sleep() to return
  967. //            immediately (or after a few ms) to speed up
  968. //            analysis. This measures real elapsed time
  969. //            across a known Sleep(1000) call.
  970. // ──────────────────────────────────────────────────────────
  971.  
  972. uint Check6_SleepAcceleration(void) {
  973.     wprintf(L"[CHECK-6] Sleep requested: 1000 ms\n");
  974.  
  975.     uint64_t t_before = GetTickCount64();
  976.     Sleep(1000);
  977.     uint64_t t_after = GetTickCount64();
  978.     uint64_t elapsed = t_after - t_before;
  979.     uint result = elapsed < 400 ? 7 : 0;
  980.  
  981.     wprintf(L"[CHECK-6] Elapsed: %llu ms\n",
  982.             (unsigned long long)elapsed);
  983.  
  984.     wprintf(L"[CHECK-6] Result: %u (%ls)\n",
  985.         result,
  986.         result == 7
  987.             ? L"sandbox suspected: Sleep was accelerated or skipped"
  988.             : L"normal timing: Sleep duration looks realistic");
  989.  
  990.     return result;
  991. }
  992.  
  993. // CHECK 7 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  994.  
  995. // ──────────────────────────────────────────────────────────
  996. // CHECK 7  |  Remote Desktop / VM Session Detection
  997. // Technique: SM_REMOTESESSION (index 0x1000) returns non-zero
  998. //            if the calling process is running under a
  999. //            Terminal Services / Remote Desktop session.
  1000. //            VMs accessed via RDP, most sandbox hypervisors
  1001. //            and remote analysis environments always set this.
  1002. // ──────────────────────────────────────────────────────────
  1003.  
  1004. uint Check7_RemoteSession() {
  1005.     // user32!GetSystemMetrics(SM_REMOTESESSION = 0x1000)
  1006.     int result = GetSystemMetrics(SM_REMOTESESSION);
  1007.  
  1008.     if (result != 0) {
  1009.         wprintf(L"[CHECK-7] GetSystemMetrics(SM_REMOTESESSION) = %d (remote session)\n", result);
  1010.         return 7;
  1011.     } else {
  1012.         wprintf(L"[CHECK-7] GetSystemMetrics(SM_REMOTESESSION) = %d (local session)\n", result);
  1013.         return 0;
  1014.     }
  1015. }
  1016.  
  1017.  
  1018. // CHECK 8 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1019.  
  1020. // ──────────────────────────────────────────────────────────
  1021. // CHECK-8  |  __rdtsc / __cpuid Timing Side-Channel
  1022. // Technique: __cpuid is a serialising instruction — it flushes
  1023. //            the CPU pipeline. On bare metal, executing
  1024. //            __cpuid takes ~100–400 cycles. Under a hypervisor
  1025. //            it causes a VM-exit which costs 1000–5000+ cycles.
  1026. //            The malware measures this 7 times, sorts the
  1027. //            samples (bubble sort), and inspects the MEDIAN
  1028. //            to filter outlier noise.
  1029. // ──────────────────────────────────────────────────────────
  1030.  
  1031. uint Check8_RdtscHypervisorJitter() {
  1032.     unsigned long long samples[7];
  1033.  
  1034.     // Warmup: execute __cpuid twice before measuring to reduce first-run noise.
  1035.     cpuid(0);
  1036.     cpuid(0);
  1037.  
  1038.     // Take 7 timing samples.
  1039.     for (int i = 0; i < 7; i++) {
  1040.         unsigned long long t1 = __rdtsc();      // Timestamp before __cpuid.
  1041.         cpuid(0);                               // Serializing instruction; may trigger VM-exit.
  1042.         unsigned long long t2 = __rdtsc();      // Timestamp after __cpuid.
  1043.  
  1044.         samples[i] = t2 - t1;       // __cpuid latency in CPU cycles.
  1045.     }
  1046.  
  1047.     wprintf(
  1048.         L"[CHECK-8] Samples: %llu, %llu, %llu, %llu, %llu, %llu, %llu cycles\n",
  1049.         samples[0], samples[1], samples[2], samples[3], samples[4], samples[5], samples[6]
  1050.     );
  1051.  
  1052.     // Sort samples in ascending order.
  1053.     // The median is used to reduce the impact of outliers caused by interrupts,
  1054.     // scheduling, or temporary CPU noise.
  1055.     for (int i = 1; i < 7; i++)
  1056.         for (int j = i; j < 7; j++)
  1057.             if (samples[j] < samples[i-1])
  1058.                 std::swap(samples[i-1], samples[j]);
  1059.  
  1060.     // With 7 samples, the median is the value at index 3 after sorting.
  1061.     unsigned long long median_cycles = samples[3];
  1062.  
  1063.     wprintf(
  1064.         L"[CHECK-8] Median __cpuid latency: %llu cycles\n", median_cycles
  1065.     );
  1066.  
  1067.     if (median_cycles <= 0x5DC) {
  1068.         wprintf(L"[CHECK-8] Result: 0 - likely bare metal (<=1500 cycles)\n");
  1069.         return 0;
  1070.     }
  1071.     else if (median_cycles <= 0xBB8) {
  1072.         wprintf(L"[CHECK-8] Result: 2 - suspicious timing overhead (1501-3000 cycles)\n");
  1073.         return 2;
  1074.     }
  1075.     else {
  1076.         wprintf(L"[CHECK-8] Result: 5 - strong hypervisor/VM-exit indication (>=3001 cycles)\n");
  1077.         return 5;
  1078.     }
  1079. }
  1080.  
  1081. // CHECK 9 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1082.  
  1083. // ──────────────────────────────────────────────────────────
  1084. // CHECK-9  |  __cpuid Vendor / Brand String Mismatch
  1085. // Technique: Hypervisors sometimes misconfigure the CPU
  1086. //            vendor leaf (leaf 0) and the brand string
  1087. //            leaves (0x80000002-4). This check catches the
  1088. //            specific case where the two sources contradict
  1089. //            each other — a configuration only possible in
  1090. //            a virtual machine.
  1091. //
  1092. //  Leaf 0  EBX values:
  1093. //    0x68747541 = "Auth" → full vendor = "AuthenticAMD"
  1094. //    0x756e6547 = "Genu" → full vendor = "GenuineIntel"
  1095. //
  1096. //  Brand string leaves 0x80000002-4:
  1097. //    48-byte ASCII string like:
  1098. //    "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz"
  1099. // ──────────────────────────────────────────────────────────
  1100.  
  1101. uint Check9_CpuidBrandMismatch() {
  1102.     wprintf(L"[CHECK-9] Reading CPU vendor information from __cpuid leaf 0...\n");
  1103.  
  1104.     // Step 1: Read CPU vendor from leaf 0.
  1105.     // This leaf exposes the CPU vendor identifier. We only check EBX here,
  1106.     // because the original logic uses the first 4 bytes to distinguish
  1107.     // between "Auth" for AMD and "Genu" for Intel.
  1108.     cpuid_result_t r = cpuid(0);
  1109.     uint32_t vendor_ebx  = r.ebx;   // "Auth" or "Genu"
  1110.  
  1111.     wprintf(L"[CHECK-9] Leaf 0 EBX value: 0x%08X\n", vendor_ebx);
  1112.  
  1113.     // Only analyse AMD and Intel — skip unknown vendors.
  1114.     bool is_amd   = (vendor_ebx == 0x68747541);  // "Auth" -> AuthenticAMD
  1115.     bool is_intel = (vendor_ebx == 0x756e6547);  // "Genu" -> GenuineIntel
  1116.  
  1117.     if (is_amd) {
  1118.         wprintf(L"[CHECK-9] Vendor leaf indicates AMD-compatible CPU vendor.\n");
  1119.     } else if (is_intel) {
  1120.         wprintf(L"[CHECK-9] Vendor leaf indicates Intel-compatible CPU vendor.\n");
  1121.     } else {
  1122.         wprintf(L"[CHECK-9] Unknown or unsupported CPU vendor. Skipping mismatch check.\n");
  1123.         wprintf(L"[CHECK-9] Result: 0 - no judgment.\n");
  1124.         return 0;   // unknown vendor -> no judgment
  1125.     }
  1126.  
  1127.     wprintf(L"[CHECK-9] Reading CPU brand string from extended __cpuid leaves 0x80000002-0x80000004...\n");
  1128.  
  1129.     // Step 2: Read 48-byte CPU brand string from leaves 0x80000002-0x80000004.
  1130.     // The brand string is a human-readable CPU model string, for example:
  1131.     // "Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz".
  1132.     char brand[49] = {0};
  1133.  
  1134.     cpuid_result_t r2 = cpuid(0x80000002);
  1135.     cpuid_result_t r3 = cpuid(0x80000003);
  1136.     cpuid_result_t r4 = cpuid(0x80000004);
  1137.     static_assert(sizeof(cpuid_result_t) == 16, "cpuid_result_t must be 16 bytes");
  1138.     memcpy(brand + 0x00, &r2, sizeof r2);
  1139.     memcpy(brand + 0x10, &r3, sizeof r3);
  1140.     memcpy(brand + 0x20, &r4, sizeof r4);
  1141.     brand[48] = '\0';
  1142.  
  1143.     wprintf(L"[CHECK-9] CPU brand string: %S\n", brand);
  1144.  
  1145.     // Step 3: Scan brand string for manufacturer keywords.
  1146.     // The goal is to detect whether the brand string explicitly mentions
  1147.     // "Intel" or "AMD", independently from what __cpuid leaf 0 reports.
  1148.     bool brand_says_intel = false;
  1149.     bool brand_says_amd   = false;
  1150.  
  1151.     wprintf(L"[CHECK-9] Scanning brand string for manufacturer keywords: Intel / AMD...\n");
  1152.  
  1153.     size_t brand_len = strlen(brand);
  1154.  
  1155.     for (size_t i = 0; i < brand_len; i++) {
  1156.         char c = (char)tolower((unsigned char)brand[i]);
  1157.  
  1158.         if (i + 4 < brand_len &&
  1159.             c == 'i' &&
  1160.             tolower((unsigned char)brand[i + 1]) == 'n' &&
  1161.             tolower((unsigned char)brand[i + 2]) == 't' &&
  1162.             tolower((unsigned char)brand[i + 3]) == 'e' &&
  1163.             tolower((unsigned char)brand[i + 4]) == 'l')
  1164.         {
  1165.             brand_says_intel = true;
  1166.             wprintf(L"[CHECK-9] Brand string contains keyword: Intel\n");
  1167.         }
  1168.  
  1169.         if (i + 2 < brand_len &&
  1170.             c == 'a' &&
  1171.             tolower((unsigned char)brand[i + 1]) == 'm' &&
  1172.             tolower((unsigned char)brand[i + 2]) == 'd')
  1173.         {
  1174.             brand_says_amd = true;
  1175.             wprintf(L"[CHECK-9] Brand string contains keyword: AMD\n");
  1176.         }
  1177.     }
  1178.  
  1179.     wprintf(
  1180.         L"[CHECK-9] Summary: vendor_leaf=%ls, brand_mentions_intel=%ls, brand_mentions_amd=%ls\n",
  1181.         is_amd ? L"AMD" : L"Intel",
  1182.         brand_says_intel ? L"true" : L"false",
  1183.         brand_says_amd ? L"true" : L"false"
  1184.     );
  1185.  
  1186.     // Step 4: Cross-check — vendor leaf must agree with brand string.
  1187.     //
  1188.     // Contradiction cases:
  1189.     //   - AMD vendor leaf + Intel brand string
  1190.     //   - Intel vendor leaf + AMD brand string
  1191.     //
  1192.     // This can indicate a virtualized or misconfigured environment where
  1193.     // the __cpuid vendor and the CPU brand string were not configured consistently.
  1194.  
  1195.     if (is_amd && brand_says_intel && !brand_says_amd) {
  1196.         wprintf(L"[CHECK-9] Mismatch detected: __cpuid vendor says AMD, but brand string says Intel.\n");
  1197.         wprintf(L"[CHECK-9] Result: 5 - suspicious __cpuid vendor/brand mismatch.\n");
  1198.         return 5;
  1199.     }
  1200.  
  1201.     if (is_intel && brand_says_amd && !brand_says_intel) {
  1202.         wprintf(L"[CHECK-9] Mismatch detected: __cpuid vendor says Intel, but brand string says AMD.\n");
  1203.         wprintf(L"[CHECK-9] Result: 5 - suspicious __cpuid vendor/brand mismatch.\n");
  1204.         return 5;
  1205.     }
  1206.  
  1207.     wprintf(L"[CHECK-9] No __cpuid vendor/brand mismatch detected.\n");
  1208.     wprintf(L"[CHECK-9] Result: 0 - vendor and brand appear consistent.\n");
  1209.  
  1210.     return 0;   // vendor and brand agree -> real hardware / no mismatch detected
  1211. }
  1212.  
  1213.  
  1214. // ══════════════════════════════════════════════════════════════
  1215. //  CHECK 10  |  GPU PCI Vendor ID Enumeration + Blocklist
  1216. //  API:       EnumDisplayDevicesW  (user32.dll)
  1217. //  Weight:    309
  1218. //
  1219. //  Technique: Enumerate every display adapter registered in
  1220. //             Windows. For each adapter that is attached to
  1221. //             the active desktop, extract the PCI Vendor ID
  1222. //             from the Windows Device Instance Path string:
  1223. //
  1224. //               "PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD&REV_00"
  1225. //                         ^^^^
  1226. //                     4 hex digits = PCI Vendor ID
  1227. //
  1228. //             Compare the parsed vendor ID against a table of
  1229. //             6 known VM/hypervisor GPU vendors (stored XOR-
  1230. //             obfuscated in the binary with mask 0x884CBE0A).
  1231. //
  1232. //  Blocklisted GPU vendors:
  1233. //    0x1234  QEMU / BOCHS Standard VGA Controller
  1234. //    0x15AD  VMware SVGA II Adapter
  1235. //    0x80EE  VirtualBox Graphics Adapter
  1236. //    0x1B36  QEMU VirtIO GPU  (Red Hat)
  1237. //    0x1AF4  Red Hat VirtIO Device
  1238. //    0x1AE0  Google Compute Engine Virtual GPU
  1239. // ══════════════════════════════════════════════════════════════
  1240.  
  1241. uint Check10_GpuVendorBlocklist() {
  1242.  
  1243.     // Blocklist stored XOR-obfuscated in the binary.
  1244.     // Each entry is decoded at runtime with:
  1245.     //
  1246.     //     decoded_vendor_id = stored_value ^ 0x884CBE0A
  1247.     //
  1248.     // The decoded values correspond to known GPU vendors commonly
  1249.     // associated with virtual machines or hypervisors.
  1250.     uint32_t blocklist_raw[6] = {
  1251.         0x884CAC3E,   // -> 0x1234  QEMU / BOCHS
  1252.         0x884CABA7,   // -> 0x15AD  VMware
  1253.         0x884C3EE4,   // -> 0x80EE  VirtualBox
  1254.         0x884CA53C,   // -> 0x1B36  QEMU VirtIO GPU
  1255.         0x884CA4FE,   // -> 0x1AF4  Red Hat VirtIO
  1256.         0x884CA4EA,   // -> 0x1AE0  Google Compute Engine
  1257.     };
  1258.  
  1259.     wprintf(L"[CHECK-10] Starting GPU PCI Vendor ID enumeration check.\n");
  1260.     wprintf(L"[CHECK-10] Enumerating display adapters using EnumDisplayDevicesW().\n");
  1261.  
  1262.     DISPLAY_DEVICEW dd = {0};
  1263.     dd.cb = sizeof(dd);   // DISPLAY_DEVICEW structure size
  1264.  
  1265.     // Enumerate all display adapters registered by Windows.
  1266.     //
  1267.     // EnumDisplayDevicesW(NULL, iDevNum, &dd, 0) returns FALSE
  1268.     // when there are no more display adapters to enumerate.
  1269.     for (DWORD iDevNum = 0; EnumDisplayDevicesW(NULL, iDevNum, &dd, 0); iDevNum++) {
  1270.  
  1271.         wprintf(L"[CHECK-10] Adapter index %lu found.\n", iDevNum);
  1272.         wprintf(L"[CHECK-10]   DeviceName : %ls\n", dd.DeviceName);
  1273.         wprintf(L"[CHECK-10]   DeviceID   : %ls\n", dd.DeviceID);
  1274.         wprintf(L"[CHECK-10]   StateFlags : 0x%08X\n", dd.StateFlags);
  1275.  
  1276.         // Only process adapters attached to the active desktop.
  1277.         //
  1278.         // DISPLAY_DEVICE_ATTACHED_TO_DESKTOP is bit 0.
  1279.         // Also require DeviceName to be non-empty.
  1280.         if (!(dd.StateFlags & 0x1) || dd.DeviceName[0] == L'\0') {
  1281.             wprintf(L"[CHECK-10]   Skipping adapter: not attached to desktop or empty DeviceName.\n");
  1282.             goto checktennextadapter;
  1283.         }
  1284.  
  1285.         wprintf(L"[CHECK-10]   Adapter is attached to the desktop. Processing DeviceID.\n");
  1286.  
  1287.         {
  1288.             // DeviceID normally contains a PnP instance path such as:
  1289.             //
  1290.             //     PCI\\VEN_15AD&DEV_0405&SUBSYS_040515AD&REV_00\\...
  1291.             //
  1292.             // The code scans this string looking for the marker "VEN_".
  1293.             // The hexadecimal digits following "VEN_" are parsed as the
  1294.             // PCI Vendor ID.
  1295.             wchar_t* dev_id  = dd.DeviceID;
  1296.             size_t   id_len  = wcslen(dev_id);
  1297.  
  1298.             wprintf(L"[CHECK-10]   DeviceID length: %zd characters.\n", id_len);
  1299.             wprintf(L"[CHECK-10]   Scanning DeviceID for VEN_ marker.\n");
  1300.  
  1301.             // Scan for the substring "VEN_" in a case-insensitive way.
  1302.             // Each candidate character is converted to uppercase before
  1303.             // comparison.
  1304.             for (int pos = 0; pos < id_len - 3; pos++) {
  1305.  
  1306.                 wchar_t c0 = toupper_w(dev_id[pos]);
  1307.                 wchar_t c1 = toupper_w(dev_id[pos+1]);
  1308.                 wchar_t c2 = toupper_w(dev_id[pos+2]);
  1309.                 wchar_t c3 = toupper_w(dev_id[pos+3]);
  1310.  
  1311.                 if (c0 == L'V' && c1 == L'E' && c2 == L'N' && c3 == L'_') {
  1312.  
  1313.                     wprintf(L"[CHECK-10]   VEN_ marker found at position %d.\n", pos);
  1314.  
  1315.                     // Parse hexadecimal digits immediately after "VEN_".
  1316.                     //
  1317.                     // Parsing stops when one of the following is reached:
  1318.                     //   - end of string
  1319.                     //   - '&'
  1320.                     //   - a non-hexadecimal character
  1321.                     //
  1322.                     // The resulting value is the parsed PCI Vendor ID.
  1323.                     uint32_t vendor_id = 0;
  1324.                     int      hex_pos   = pos + 4;
  1325.  
  1326.                     while (true) {
  1327.                         wchar_t ch = dev_id[hex_pos];
  1328.  
  1329.                         if (ch == L'\0' || ch == L'&') {
  1330.                             wprintf(L"[CHECK-10]   End of VEN field reached.\n");
  1331.                             break;
  1332.                         }
  1333.  
  1334.                         uint32_t digit;
  1335.  
  1336.                         if (ch >= L'0' && ch <= L'9')
  1337.                             digit = ch - L'0';
  1338.                         else if (ch >= L'A' && ch <= L'F')
  1339.                             digit = ch - L'A' + 10;
  1340.                         else if (ch >= L'a' && ch <= L'f')
  1341.                             digit = ch - L'a' + 10;
  1342.                         else {
  1343.                             wprintf(L"[CHECK-10]   Non-hex character encountered while parsing VEN field: '%lc'.\n", ch);
  1344.                             break;
  1345.                         }
  1346.  
  1347.                         vendor_id = (vendor_id << 4) | digit;
  1348.                         hex_pos++;
  1349.                     }
  1350.  
  1351.                     wprintf(L"[CHECK-10]   Parsed PCI Vendor ID: 0x%04X.\n", vendor_id);
  1352.                     wprintf(L"[CHECK-10]   Comparing parsed Vendor ID against VM GPU vendor blocklist.\n");
  1353.  
  1354.                     // Decode each obfuscated blocklist entry and compare it
  1355.                     // with the parsed Vendor ID.
  1356.                     for (int j = 0; j < 6; j++) {
  1357.                         uint32_t expected = blocklist_raw[j] ^ 0x884CBE0A;
  1358.  
  1359.                         wprintf(
  1360.                             L"[CHECK-10]     Blocklist entry %d: raw=0x%08X decoded=0x%04X.\n",
  1361.                             j,
  1362.                             blocklist_raw[j],
  1363.                             expected
  1364.                         );
  1365.  
  1366.                         if (vendor_id == expected) {
  1367.                             // A known VM/hypervisor GPU Vendor ID was found.
  1368.                             //
  1369.                             // Return value 5 means:
  1370.                             //   this check positively identified a blocklisted
  1371.                             //   GPU vendor associated with a virtualized
  1372.                             //   environment.
  1373.                             wprintf(L"[CHECK-10]   MATCH FOUND: Vendor ID 0x%04X is blocklisted.\n", vendor_id);
  1374.                             wprintf(L"[CHECK-10]   Returning 5: VM/hypervisor GPU vendor detected.\n");
  1375.                             return 5;
  1376.                         }
  1377.                     }
  1378.  
  1379.                     // A VEN_ marker was found and parsed, but the resulting
  1380.                     // Vendor ID did not match any blocklisted VM GPU vendor.
  1381.                     //
  1382.                     // The scan continues in case the DeviceID contains another
  1383.                     // VEN_ token later in the string.
  1384.                     wprintf(L"[CHECK-10]   No blocklist match for Vendor ID 0x%04X.\n", vendor_id);
  1385.                     wprintf(L"[CHECK-10]   Continuing scan for possible additional VEN_ markers.\n");
  1386.                 }
  1387.             }
  1388.         }
  1389.  
  1390. checktennextadapter:
  1391.         // Reset selected DISPLAY_DEVICEW fields before the next iteration.
  1392.         //
  1393.         // This preserves the original logic while making sure stale values
  1394.         // from the current adapter are not reused on the next call.
  1395.         wprintf(L"[CHECK-10] Moving to next display adapter.\n");
  1396.  
  1397.         ZeroMemory(&dd, sizeof(dd));
  1398.         dd.cb = sizeof(dd);
  1399.     }
  1400.  
  1401.     // No display adapter exposed a blocklisted VM/hypervisor GPU Vendor ID.
  1402.     //
  1403.     // Return value 0 means:
  1404.     //   this check did not find evidence of a known virtual GPU vendor.
  1405.     wprintf(L"[CHECK-10] Enumeration completed.\n");
  1406.     wprintf(L"[CHECK-10] No blocklisted GPU PCI Vendor ID was found.\n");
  1407.     wprintf(L"[CHECK-10] Returning 0: no VM/hypervisor GPU vendor detected by this check.\n");
  1408.  
  1409.     return 0;
  1410. }
  1411.  
  1412. int main() {
  1413.     AntiSandboxSuite();
  1414.     std::wprintf(L"Press ENTER to continue...");
  1415.     std::getwchar();
  1416.     std::wprintf(L"Continuing...\n");
  1417.     return 0;
  1418. }
Add Comment
Please, Sign In to add comment