{
	"id": "108bbc0f-5492-4b5f-aa53-0df5cd9ccba8",
	"created_at": "2026-04-06T01:28:53.18506Z",
	"updated_at": "2026-04-10T13:13:07.987062Z",
	"deleted_at": null,
	"sha1_hash": "40a795ef942a1901f33b950137dbc42f02f218eb",
	"title": "Raspberry Robin: Anti-Evasion How-To \u0026 Exploit Analysis",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 228863,
	"plain_text": "Raspberry Robin: Anti-Evasion How-To \u0026 Exploit Analysis\r\nBy elism@checkpoint.com\r\nPublished: 2023-04-18 · Archived: 2026-04-06 00:09:07 UTC\r\nResearch by: Shavit Yosef\r\nIntroduction\r\nDuring the last year, Raspberry Robin has evolved to be one of the most distributed malware currently active.\r\nDuring this time, it is likely to be used by many actors to distribute their own malware such as IcedID, Clop\r\nransomware and more.\r\nOver time, malware has continued to evolve and escalate the game of cat and mouse. Raspberry Robin, however,\r\nhas taken this game to the next level with a great number of unique tricks and evasions.\r\nIn this research, we examine Raspberry Robin as an example of identifying and evading different evasions. We\r\ndiscovered some unique and innovative methods and analyzed the two exploits used by Raspberry Robin to gain\r\nhigher privileges showing that it also has capabilities in the exploiting area. \r\nAnti-debugging and other evasions can be exhausting, and even more when it comes to such obfuscation methods\r\nand volume of methods as Raspberry Robin implements.  This research aims to show plenty of methods with\r\nexplanations of how they work and how to evade those evasions.\r\nRaspberry Robin Overview\r\nRaspberry Robin belongs to the rapidly growing club of malware that really doesn’t want to be run on any VM. It\r\nadded various evasions in many stages which makes the debugging of this malware a living hell.\r\nRaspberry Robin emerged, last May and was first analyzed by Red Canary. Raspberry Robin was technically well-documented by previous work, see for example avast’s report.\r\nThe malware has several entry vectors which lead to the main sample. The most prevalent one is via an LNK\r\ndisguised as a thumb drive or a network share which launches  msiexec.exe  that downloads the main component.\r\nThis main component is packed with 14 different layers (some of them are identical), while some of them contain\r\nevasions and some of them do not. Those stages are stored in memory in a custom format being unpacked and run\r\nwithout the headers section. This makes it difficult to unpack Raspberry Robin layers statically into a standalone\r\nPE file. Understanding the custom format and how the loading of each stage works is doable but can be\r\nexhausting and it probably will be simpler to just analyze the stages dynamically.\r\nThe code in all of the stages is heavily obfuscated, including the main payload itself. What makes the obfuscation\r\neven harder, is that Raspberry Robin functions expect an argument that is being used to decrypt all the variables\r\nand constants the functions need. The initial argument is hardcoded and then every function gets an argument\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 1 of 38\n\nbased on the initial one. This is also important for the flow as the malware uses those variables in order to decide\r\nwhich block of logic to run now. That means, that jumping to a function without knowing the real argument will\r\nresult in faults.\r\nRaspberry Robin has different ways of how it behaves in case of detecting it is not running on a real victim’s\r\ncomputer:\r\nTerminate the process\r\nEnters infinite loop\r\nCause a crash – such as in the fifth stage where it uses a different RC4 key to decrypt the next stage in case\r\nthe malware detects it is not running on a physical machine.\r\nThe most diabolical one – Raspberry Robin decrypts a fake new stage instead the real one, which results in\r\nanother 3 layers of packers until a fake loader. This loader was mentioned in several blogposts and it loads\r\nsamples from a domain hardcoded inside. The fake loader can be monitored by detecting that the malware\r\ntries to query  HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Media\\Active . Many fake payloads were seen in\r\nthe wild such as BroAssist and other .NET backdoors.\r\nFigure 1 – Raspberry Robin’s fake payload\r\nEvasions walkthrough\r\nAs we mentioned, Raspberry Robin has various evasion tricks, we decided to take some of them and elaborate on\r\nwhat those evasions try to check and how to avoid them not only for Raspberry Robin but also for other malware\r\nwhich can use those evasions as well. We recommend reading our evasions and anti-debug encyclopedias for\r\ninformation about many other techniques.\r\nFirst, we highly recommend using several anti-anti-debug libraries to skip many of the evasions with ease. A good\r\nexample of one would be ScyllaHide. ScyllaHide is an open-source anti-Anti-Debug library for user-mode\r\nevasions. It supports various debuggers such as x64dbg, IDA debugger and OllyDbg. If you desire a kernel-mode\r\nplugin then you have titanhide which hooks Nt* kernel functions using SSDT table hooks.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 2 of 38\n\nFigure 2 – ScyllaHide’s options bar\r\nAnother thing recommended before starting to work on malware these days is checking if your working machine\r\ncan be detected easily by malware. For this case, there are many solutions such as our own tool –\r\n InviZzzible which helping assessing the virtual environment against a variety of evasions and al-khaser project.\r\nRaspberry Robin evasions\r\nPEB checks – Anti Debug\r\nSpecial flags in system tables, which dwell in process memory and which an operation system sets, can be used to\r\nindicate that the process is being debugged. Raspberry Robin checks some of them.\r\nFirst, it inspects two flags in the PEB (Process Environment Block) which is a structure that holds data about the\r\ncurrent process. Those flags are:\r\nBeingDebugged  – Indicates whether the specified process is currently being debugged and can be also\r\nqueried with the API  IsDebuggerPresent\r\nNtGlobalFlag  – A flag that is 0 by default but if a process was created by a debugger then the following\r\nflags will be set:\r\nFLG_HEAP_ENABLE_TAIL_CHECK (0x10)\r\nFLG_HEAP_ENABLE_FREE_CHECK (0x20)\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 3 of 38\n\nFLG_HEAP_VALIDATE_PARAMETERS (0x40)\r\nExample code:\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nmov eax, fs:[30h] ; get PEB, in 64-bit it is in gs:[60h]\r\ncmp byte ptr [eax+2], 0 ; checking BeingDebugged flag\r\njne being_debugged\r\nmov al, [eax+68h] ; checking NtGlobalFlag\r\nand al, 70h\r\ncmp al, 70h ; (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK |\r\nFLG_HEAP_VALIDATE_PARAMETERS)\r\njz being_debugged\r\nmov eax, fs:[30h] ; get PEB, in 64-bit it is in gs:[60h] cmp byte ptr [eax+2], 0 ; checking BeingDebugged flag jne\r\nbeing_debugged mov al, [eax+68h] ; checking NtGlobalFlag and al, 70h cmp al, 70h ;\r\n(FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK |\r\nFLG_HEAP_VALIDATE_PARAMETERS) jz being_debugged\r\nmov eax, fs:[30h] ; get PEB, in 64-bit it is in gs:[60h]\r\ncmp byte ptr [eax+2], 0 ; checking BeingDebugged flag\r\njne being_debugged\r\nmov al, [eax+68h] ; checking NtGlobalFlag\r\nand al, 70h\r\ncmp al, 70h ; (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS\r\njz being_debugged\r\nMitigations\r\nUsing ScyllaHide or any other Anti-Debug plugin setting those flags to 0. You can also edit those bytes yourself\r\nwhen you start debugging.\r\nKUSER_SHARED_DATA check – Anti Debug\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 4 of 38\n\nIt also inspects a flag in  KUSER_SHARED_DATA  – a structure that provides a quick mechanism to obtain frequently\r\nneeded global data from the kernel without involving user-kernel mode switching using system calls or interrupts.\r\nThe structure is mapped at a fixed, hardcoded address on both kernel and user sides  0x7FFE0000  on 32-bit\r\nand  0xFFFFF78000000000  on 64-bit. The flag that is checked is  KdDebuggerEnabled  that returns TRUE if a\r\nkernel debugger is connected/enabled.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure address\r\ncmp byte ptr [eax+0x2d4], 0 ; checking KdDebuggerEnabled flag\r\njne being_debugged\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure address cmp byte ptr [eax+0x2d4], 0 ; checking\r\nKdDebuggerEnabled flag jne being_debugged\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure address\r\ncmp byte ptr [eax+0x2d4], 0 ; checking KdDebuggerEnabled flag\r\njne being_debugged\r\nMitigations\r\nYou can patch it manually, also a draft code for patching  kdcom.dll  (kernel debugger) so it will not be detected\r\nwas issued here.\r\nUser name and Computer name check – Anti VM\r\nUsual hosts have meaningful and non-standard usernames/computer names. Particular virtual environments assign\r\nsome predefined names to default users as well as computer names.\r\nRaspberry Robin checks the username and computer name against a list of known default names given by VMs\r\nand analysts. Usually, those values are queried by  GetComputerNameA  and  GetUserNameA  but Raspberry Robin\r\nqueries them differently. It finds the environment variables through  PEB -\u003e\r\nProcessParameters(RTL_USER_PROCESS_PARAMETERS) -\u003e Environment  and then loops through the environment\r\nblock looking for the USERNAME and COMPUTERNAME variables.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 5 of 38\n\nEnlighterJS 3 Syntax Highlighter\r\n.data\r\nusername db 255 dup(0) ; buffer to hold the username\r\nmsg db \"Username: \", 0\r\n.code\r\nstart:\r\n; Get a pointer to the PEB\r\nmov eax, fs:[30h]\r\n; Get a pointer to the ProcessParameters member of the PEB\r\nmov eax, [eax + 0x10]\r\n; Get a pointer to the Environment member of the ProcessParameters structure\r\nmov eax, [eax + 0x48]\r\n; Loop through the environment block looking for the USERNAME variable\r\nnext_var:\r\n; Read a null-terminated string from the environment block\r\nlodsb\r\n; Check if the string is empty (i.e., end of environment block)\r\ntest al, al\r\njz end_env\r\n; Check if the string starts with \"USERNAME=\"\r\ncmp byte ptr [esi], 'U'\r\njne next_var\r\ncmp dword ptr [esi+1], 'SER'\r\njne next_var\r\ncmp dword ptr [esi+5], 'NAME'\r\njne next_var\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 6 of 38\n\ncmp byte ptr [esi+9], '='\r\njne next_var\r\n; Copy the value of the USERNAME variable into the username buffer\r\nmov edi, offset username\r\nlea esi, [esi+10]\r\nrep movsb\r\nend_env:\r\n; Print the username to the console\r\ninvoke StdOut, offset msg\r\ninvoke StdOut, offset username\r\n; Exit the program\r\ninvoke ExitProcess, 0\r\n; Define the Win32 API functions\r\nStdOut equ \u003cGetStdHandle, WriteFile\u003e\r\nextern StdOut :PROC\r\nExitProcess equ \u003cGetProcAddress, GetModuleHandle, 0\u003e\r\nextern ExitProcess :PROC\r\n.data username db 255 dup(0) ; buffer to hold the username msg db \"Username: \", 0 .code start: ; Get a pointer to\r\nthe PEB mov eax, fs:[30h] ; Get a pointer to the ProcessParameters member of the PEB mov eax, [eax + 0x10] ;\r\nGet a pointer to the Environment member of the ProcessParameters structure mov eax, [eax + 0x48] ; Loop\r\nthrough the environment block looking for the USERNAME variable next_var: ; Read a null-terminated string\r\nfrom the environment block lodsb ; Check if the string is empty (i.e., end of environment block) test al, al jz\r\nend_env ; Check if the string starts with \"USERNAME=\" cmp byte ptr [esi], 'U' jne next_var cmp dword ptr\r\n[esi+1], 'SER' jne next_var cmp dword ptr [esi+5], 'NAME' jne next_var cmp byte ptr [esi+9], '=' jne next_var ;\r\nCopy the value of the USERNAME variable into the username buffer mov edi, offset username lea esi, [esi+10]\r\nrep movsb end_env: ; Print the username to the console invoke StdOut, offset msg invoke StdOut, offset username\r\n; Exit the program invoke ExitProcess, 0 ; Define the Win32 API functions StdOut equ \u003cGetStdHandle,\r\nWriteFile\u003e extern StdOut :PROC ExitProcess equ \u003cGetProcAddress, GetModuleHandle, 0\u003e extern ExitProcess\r\n:PROC\r\n.data\r\nusername db 255 dup(0) ; buffer to hold the username\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 7 of 38\n\nmsg db \"Username: \", 0\r\n.code\r\nstart:\r\n ; Get a pointer to the PEB\r\n mov eax, fs:[30h]\r\n ; Get a pointer to the ProcessParameters member of the PEB\r\n mov eax, [eax + 0x10]\r\n ; Get a pointer to the Environment member of the ProcessParameters structure\r\n mov eax, [eax + 0x48]\r\n ; Loop through the environment block looking for the USERNAME variable\r\nnext_var:\r\n ; Read a null-terminated string from the environment block\r\n lodsb\r\n ; Check if the string is empty (i.e., end of environment block)\r\n test al, al\r\n jz end_env\r\n ; Check if the string starts with \"USERNAME=\"\r\n cmp byte ptr [esi], 'U'\r\n jne next_var\r\n cmp dword ptr [esi+1], 'SER'\r\n jne next_var\r\n cmp dword ptr [esi+5], 'NAME'\r\n jne next_var\r\n cmp byte ptr [esi+9], '='\r\n jne next_var\r\n ; Copy the value of the USERNAME variable into the username buffer\r\n mov edi, offset username\r\n lea esi, [esi+10]\r\n rep movsb\r\nend_env:\r\n ; Print the username to the console\r\n invoke StdOut, offset msg\r\n invoke StdOut, offset username\r\n ; Exit the program\r\n invoke ExitProcess, 0\r\n; Define the Win32 API functions\r\nStdOut equ \u003cGetStdHandle, WriteFile\u003e\r\n extern StdOut :PROC\r\nExitProcess equ \u003cGetProcAddress, GetModuleHandle, 0\u003e\r\n extern ExitProcess :PROC\r\nMitigations\r\nChange the Computer name and User name of your machine to non-suspicious values.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 8 of 38\n\nProcess name \u0026 full path – Anti Debug and Anti VM\r\nSome Virtual environments launch executables from specific paths. Moreover, some debuggers have default\r\nnames for their DLL loaders such as x64dbg’s DLL loader.\r\nRaspberry Robin queries for the name of the process’ main module as well as its full path.\r\nFigure 3 – BaseDllName check\r\nTo circumvent this evasion, use  rundll32.exe  to debug Raspberry Robin even if you use x64dbg.\r\nNumber of active CPUs – Anti-Sandbox\r\nAnalysis environments usually have a Low CPU core count. This information can be queried from the PEB\r\n( NumberOfProcessors ) or using the  GetSystemInfo  API.\r\nRaspberry Robin checks if there are fewer than 2 active processors. It does that by\r\nquerying  ActiveProcessorCount  from the  KUSER_SHARED_DATA  structure.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure fixed address\r\ncmp byte ptr [eax+0x3c0], 2 ; checking ActiveProcessorCount\r\njb being_debugged\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 9 of 38\n\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure fixed address cmp byte ptr [eax+0x3c0], 2 ; checking\r\nActiveProcessorCount jb being_debugged\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure fixed address\r\ncmp byte ptr [eax+0x3c0], 2 ; checking ActiveProcessorCount\r\njb being_debugged\r\nMitigations\r\nTo circumvent this evasion in an analysis environment, configure the virtual machine to Assign two or more cores\r\nfor Virtual Machine. As an alternative solution, patch/hook  NtCreateThread  to assign a specific core for each\r\nnew thread.\r\nFor example on VMware Workstation Pro:\r\nFigure 4 – Number of processors in VMWare\r\nMemory pages – Anti Sandbox\r\nToo little memory on a system in the current day and age might mean a low-performance system that the authors\r\nare not interested in, or are suspecting to be an analysis environment.\r\nRaspberry Robin checks for how many physical memory pages are available in the system by\r\nquerying  NumberOfPhysicalPages  from the  KUSER_SHARED_DATA  which is unique as most malware checks this\r\nby calling the API  GetMemoryStatusEx .\r\nOnce met with too few pages, a flag is raised. The threshold is 204800 pages (800 MB) which is pretty low for\r\npersonal computers. The standard amount for this kind of computer is around 8-16 GB of RAM.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure address\r\ncmp byte ptr [eax+0x2e8], 0x32000 ; checking NumberOfPhysicalPages\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 10 of 38\n\njbe being_debugged\r\nmov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure address cmp byte ptr [eax+0x2e8], 0x32000 ;\r\nchecking NumberOfPhysicalPages jbe being_debugged\r\n mov eax, 0x7ffe0000 ; KUSER_SHARED_DATA structure address\r\n cmp byte ptr [eax+0x2e8], 0x32000 ; checking NumberOfPhysicalPages\r\n jbe being_debugged\r\nFigure 5 – Number of physical pages check\r\nPatch  NumberOfPhysicalPages  in  KUSER_SHARED_DATA  or just assign more RAM for your VM.\r\nMac address Check – Anti VM\r\nVendors of different virtual environments hard-code some values as MAC addresses for their products — due to\r\nthis fact such environments may be detected via checking the properties of appropriate objects.\r\nRaspberry Robin not only compares its own adapter info with a list of blacklisted addresses but also compares the\r\naddresses in its ARP table. It does that by first populating it with  GetBestRoute  API and then getting the table\r\ninfo using  GetIpNetTable .\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 11 of 38\n\nint check_mac_vendor(char * mac_vendor) {\r\nunsigned long alist_size = 0, ret;\r\nret = GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, \u0026alist_size);\r\nif (ret == ERROR_BUFFER_OVERFLOW) {\r\nIP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT,\r\nalist_size);\r\nvoid * palist_free = palist;\r\nif (palist) {\r\nGetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, \u0026alist_size);\r\nchar mac[6]={0};\r\nwhile (palist){\r\nif (palist-\u003ePhysicalAddressLength == 0x6) {\r\nmemcpy(mac, palist-\u003ePhysicalAddress, 0x6);\r\nif (!memcmp(mac_vendor, mac, 3)) { /* First 3 bytes are the same */\r\nLocalFree(palist_free);\r\nreturn TRUE;\r\n}\r\n}\r\npalist = palist-\u003eNext;\r\n}\r\nLocalFree(palist_free);\r\n}\r\n}\r\nreturn FALSE;\r\n}\r\nint check_mac_vendor(char * mac_vendor) { unsigned long alist_size = 0, ret; ret =\r\nGetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, \u0026alist_size); if (ret == ERROR_BUFFER_OVERFLOW) {\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 12 of 38\n\nIP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT,\r\nalist_size); void * palist_free = palist; if (palist) { GetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, \u0026alist_size);\r\nchar mac[6]={0}; while (palist){ if (palist-\u003ePhysicalAddressLength == 0x6) { memcpy(mac, palist-\r\n\u003ePhysicalAddress, 0x6); if (!memcmp(mac_vendor, mac, 3)) { /* First 3 bytes are the same */\r\nLocalFree(palist_free); return TRUE; } } palist = palist-\u003eNext; } LocalFree(palist_free); } } return FALSE; }\r\nint check_mac_vendor(char * mac_vendor) {\r\n unsigned long alist_size = 0, ret;\r\n ret = GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, \u0026alist_size);\r\n if (ret == ERROR_BUFFER_OVERFLOW) {\r\n IP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT,\r\n \r\n void * palist_free = palist;\r\n if (palist) {\r\n GetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, \u0026alist_size);\r\n char mac[6]={0};\r\n while (palist){\r\n if (palist-\u003ePhysicalAddressLength == 0x6) {\r\n memcpy(mac, palist-\u003ePhysicalAddress, 0x6);\r\n if (!memcmp(mac_vendor, mac, 3)) { /* First 3 bytes are the same */\r\n LocalFree(palist_free);\r\n return TRUE;\r\n }\r\n }\r\n palist = palist-\u003eNext;\r\n }\r\n LocalFree(palist_free);\r\n }\r\n }\r\n return FALSE;\r\n}\r\nMitigations\r\nChange your machine’s MAC address to a non-default address.\r\nCPUID checks – Anti VM\r\nThe CPUID instruction is an assembly instruction that returns processor identification and features information\r\nto the EBX, ECX and EDX registers based on the value in the EAX register.\r\nRaspberry Robin uses the CPUID instruction for several checks\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 13 of 38\n\nEAX = 0x40000000 – returns Vendor ID which Raspberry Robin compares to default IDs given in known\r\nvirtual machine’s such as  Microsoft hv  (Hyper-V) or  VMwareVMware  (VMware)\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\npush ebx\r\n; nullify output registers\r\nxor ebx, ebx\r\nxor ecx, ecx\r\nxor edx, edx\r\nmov eax, 0x40000000 ; call cpuid with argument in EAX\r\ncpuid\r\nmov edi, vendor_id ; store vendor_id ptr to destination\r\n; move string parts to destination\r\nmov eax, ebx\r\nstosd\r\nmov eax, ecx\r\nstosd\r\nmov eax, edx\r\nstosd\r\n; now check this against the different strings connected to VMs\r\npush ebx ; nullify output registers xor ebx, ebx xor ecx, ecx xor edx, edx mov eax, 0x40000000 ; call cpuid with\r\nargument in EAX cpuid mov edi, vendor_id ; store vendor_id ptr to destination ; move string parts to destination\r\nmov eax, ebx stosd mov eax, ecx stosd mov eax, edx stosd ; now check this against the different strings connected\r\nto VMs\r\n push ebx\r\n ; nullify output registers\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 14 of 38\n\nxor ebx, ebx\r\n xor ecx, ecx\r\n xor edx, edx\r\n \r\n mov eax, 0x40000000 ; call cpuid with argument in EAX\r\n cpuid\r\n \r\n mov edi, vendor_id ; store vendor_id ptr to destination\r\n \r\n ; move string parts to destination\r\n mov eax, ebx\r\n stosd\r\n mov eax, ecx\r\n stosd\r\n mov eax, edx\r\n stosd\r\n ; now check this against the different strings connected to VMs\r\nEAX = 0x1 – returns a set of feature flags These flags indicate the CPU’s capabilities and features, such as\r\nwhether it supports MMX, SSE, or AVX instructions, as well as information about the CPU’s stepping,\r\nmodel, and family. One of the flags is in the 31st bit in ECX which indicates whether the program is being\r\nrun in Hypervisor.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nxor ecx, ecx\r\nmov eax, 1 // sets EAX to 1\r\ncpuid\r\nbt ecx, 31 // set CF equal to 31st bit in ECX\r\nsetc al // set AL to the value of CF\r\ntest eax, eax\r\njne being_debugged\r\nxor ecx, ecx mov eax, 1 // sets EAX to 1 cpuid bt ecx, 31 // set CF equal to 31st bit in ECX setc al // set AL to the\r\nvalue of CF test eax, eax jne being_debugged\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 15 of 38\n\nxor ecx, ecx\r\n mov eax, 1 // sets EAX to 1\r\n cpuid\r\n bt ecx, 31 // set CF equal to 31st bit in ECX\r\n setc al // set AL to the value of CF\r\n test eax, eax\r\n jne being_debugged\r\nMitigations\r\nDifferent VM solutions have different ways of how to modify CPUID and CPU features. For example, in\r\nVMWare you can edit the configuration file (.vmx) and add the following lines:\r\ncpuid.40000000.ecx = “0000:0000:0000:0000:0000:0000:0000:0000”\r\ncpuid.40000000.edx = “0000:0000:0000:0000:0000:0000:0000:0000” – for the first (EAX = 0X40000000)\r\ncheck\r\ncpuid.1.ecx = ”0—:—-:—-:—-:—-:—-:—-:—-” – for the Hypervisor (EAX = 1) check\r\nPEB module enumeration – Anti Debug and Anti Sandbox\r\nUsing the PEB, a program can enumerate the modules loaded in its memory by iterating through the list of\r\nmodules stored in the PEB’s “Ldr” (Loader) data structure. Each module in the list contains information such as\r\nits base address, entry point, and size, which can be used to analyze the module and its contents.\r\nRaspberry Robin uses that technique to check if blacklisted applications that inject their module into processes\r\n(hashed) name exists in the hardcoded blacklist. Those modules can be sandbox-related or anti-anti-debug\r\nlibraries such as ScyllaHide.\r\nMitigations\r\nTo circumvent this evasion, the following options are available:\r\nDon’t load foreign modules into the process of the malware.\r\nLoad your foreign module of choice and then unlink it from the PEB, or use a different method to load it,\r\nsuch as manual mapping.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n// Unlinking specific module from PEB's InMemoryOrderModuleList\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 16 of 38\n\nbool unlink()\r\n{\r\nHMODULE mod = nullptr;\r\nif(!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |\r\nGET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,\r\nreinterpret_cast\u003cLPCSTR\u003e(\u0026unlink), \u0026mod))\r\n{\r\nreturn false;\r\n}\r\nwchar_t module_name[MAX_PATH];\r\nif(GetModuleFileNameW(mod, module_name, sizeof(module_name)))\r\n{\r\nreturn false;\r\n}\r\n#ifdef _WIN64 auto peb = reinterpret_cast\u003cPEB*\u003e(__readgsqword(0x60));\r\n#else auto peb = reinterpret_cast\u003cPEB*\u003e(__readfsdword(0x30));\r\n#endif for(auto peb_link = peb-\u003eLdr-\u003eInMemoryOrderModuleList.Flink; peb_link !=\r\n\u0026peb-\u003eLdr-\u003eInMemoryOrderModuleList; peb_link = peb_link-\u003eFlink)\r\n{\r\nif(::wcscmp(reinterpret_cast\u003cLDR_DATA_TABLE_ENTRY*\u003e(peb_link)-\u003eFullDllName.Buffer,\r\nmodule_name) == 0)\r\n{\r\npeb_link-\u003eFlink-\u003eBlink = peb_link-\u003eBlink;\r\npeb_link-\u003eBlink-\u003eFlink = peb_link-\u003eFlink;\r\nreturn true;\r\n}\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 17 of 38\n\n}\r\nreturn false;\r\n// Unlinking specific module from PEB's InMemoryOrderModuleList bool unlink() { HMODULE mod = nullptr;\r\nif(!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |\r\nGET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast\u003cLPCSTR\u003e(\u0026unlink),\r\n\u0026mod)) { return false; } wchar_t module_name[MAX_PATH]; if(GetModuleFileNameW(mod, module_name,\r\nsizeof(module_name))) { return false; } #ifdef _WIN64 auto peb = reinterpret_cast\u003cPEB*\u003e\r\n(__readgsqword(0x60)); #else auto peb = reinterpret_cast\u003cPEB*\u003e(__readfsdword(0x30)); #endif for(auto\r\npeb_link = peb-\u003eLdr-\u003eInMemoryOrderModuleList.Flink; peb_link != \u0026peb-\u003eLdr-\u003eInMemoryOrderModuleList;\r\npeb_link = peb_link-\u003eFlink) { if(::wcscmp(reinterpret_cast\u003cLDR_DATA_TABLE_ENTRY*\u003e(peb_link)-\r\n\u003eFullDllName.Buffer, module_name) == 0) { peb_link-\u003eFlink-\u003eBlink = peb_link-\u003eBlink; peb_link-\u003eBlink-\r\n\u003eFlink = peb_link-\u003eFlink; return true; } } return false;\r\n// Unlinking specific module from PEB's InMemoryOrderModuleList\r\nbool unlink()\r\n{\r\n HMODULE mod = nullptr;\r\n if(!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |\r\n GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,\r\n reinterpret_cast\u003cLPCSTR\u003e(\u0026unlink), \u0026mod))\r\n {\r\n return false;\r\n }\r\n wchar_t module_name[MAX_PATH];\r\n if(GetModuleFileNameW(mod, module_name, sizeof(module_name)))\r\n {\r\n return false;\r\n }\r\n #ifdef _WIN64 auto peb = reinterpret_cast\u003cPEB*\u003e(__readgsqword(0x60));\r\n #else auto peb = reinterpret_cast\u003cPEB*\u003e(__readfsdword(0x30));\r\n #endif for(auto peb_link = peb-\u003eLdr-\u003eInMemoryOrderModuleList.Flink; peb_link !=\r\n \u0026peb-\u003eLdr-\u003eInMemoryOrderModuleList; peb_link = peb_link-\u003eFlink)\r\n {\r\n if(::wcscmp(reinterpret_cast\u003cLDR_DATA_TABLE_ENTRY*\u003e(peb_link)-\u003eFullDllName.Buffer,\r\n module_name) == 0)\r\n {\r\n peb_link-\u003eFlink-\u003eBlink = peb_link-\u003eBlink;\r\n peb_link-\u003eBlink-\u003eFlink = peb_link-\u003eFlink;\r\n return true;\r\n }\r\n }\r\n return false;\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 18 of 38\n\nMulDiv – Anti Wine\r\nThe  MulDiv  API is being called with specific arguments ( MulDiv(1, 0x80000000, 0x80000000) ) which should\r\nlogically return 1 – however, due to a bug with the ancient implementation on Windows, it returns 2.\r\nRaspberry Robin uses this check in order to detect Wine which for some reason returns 0 (even the latest version,\r\nas of today). There are more known evasion methods to detect Wine like the good old check of searching for the\r\nexistence of one of Wine’s exclusive APIs such\r\nas  kernel32.dll!wine_get_unix_file_name  or  ntdll.dll!wine_get_host_version ).\r\nHere is an example of comparing the execution using the Windows implementation of  MulDiv , a logically\r\ncorrect implementation (which is not backward compatible), and Wine’s C++ implementation.\r\nFigure 6 – MulDiv implementations\r\nIf Using Wine, hook  MulDiv  to return 2 or modify the implementation as it works in Windows.\r\nDevices detections – Anti VM\r\nVirtual environments emulate hardware devices and display devices and leave specific traces in their descriptions\r\n– which may be queried and the conclusion about non-host OS made.\r\nRaspberry Robin tries to evade by checking the ProductID of  \\\\.\\PhysicalDrive0  and DeviceIDs of the display\r\ndevices and comparing them to a list of blacklisted known names.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nbool GetHDDVendorId(std::string\u0026 outVendorId) {\r\nHANDLE hDevice = CreateFileA(_T(\"\\\\\\\\.\\\\PhysicalDrive0\"),\r\n0,\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 19 of 38\n\nFILE_SHARE_READ | FILE_SHARE_WRITE,\r\n0,\r\nOPEN_EXISTING,\r\n0,\r\n0);\r\nSTORAGE_PROPERTY_QUERY storage_property_query = {};\r\nstorage_property_query.PropertyId = StorageDeviceProperty;\r\nstorage_property_query.QueryType = PropertyStandardQuery;\r\nSTORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {};\r\nDWORD BytesReturned = 0;\r\nDeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,\r\n\u0026storage_property_query, sizeof(storage_property_query),\r\n\u0026storage_descriptor_header, sizeof(storage_descriptor_header),\r\n\u0026BytesReturned);\r\nstd::vector\u003cchar\u003e buff(storage_descriptor_header.Size); //_STORAGE_DEVICE_DESCRIPTOR\r\nDeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,\r\n\u0026storage_property_query, sizeof(storage_property_query),\r\nbuff.data(), buff.size(), 0);\r\nSTORAGE_DEVICE_DESCRIPTOR* device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buff.data();\r\nif (device_descriptor-\u003eVendorIdOffset){\r\noutVendorId = \u0026buff[device_descriptor-\u003eVendorIdOffset];\r\n}\r\n}\r\nbool GetHDDVendorId(std::string\u0026 outVendorId) { HANDLE hDevice =\r\nCreateFileA(_T(\"\\\\\\\\.\\\\PhysicalDrive0\"), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,\r\nOPEN_EXISTING, 0, 0); STORAGE_PROPERTY_QUERY storage_property_query = {};\r\nstorage_property_query.PropertyId = StorageDeviceProperty; storage_property_query.QueryType =\r\nPropertyStandardQuery; STORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {}; DWORD\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 20 of 38\n\nBytesReturned = 0; DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,\r\n\u0026storage_property_query, sizeof(storage_property_query), \u0026storage_descriptor_header,\r\nsizeof(storage_descriptor_header), \u0026BytesReturned); std::vector\u003cchar\u003e buff(storage_descriptor_header.Size);\r\n//_STORAGE_DEVICE_DESCRIPTOR DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,\r\n\u0026storage_property_query, sizeof(storage_property_query), buff.data(), buff.size(), 0);\r\nSTORAGE_DEVICE_DESCRIPTOR* device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buff.data();\r\nif (device_descriptor-\u003eVendorIdOffset){ outVendorId = \u0026buff[device_descriptor-\u003eVendorIdOffset]; } }\r\nbool GetHDDVendorId(std::string\u0026 outVendorId) {\r\n HANDLE hDevice = CreateFileA(_T(\"\\\\\\\\.\\\\PhysicalDrive0\"),\r\n 0,\r\n FILE_SHARE_READ | FILE_SHARE_WRITE,\r\n 0,\r\n OPEN_EXISTING,\r\n 0,\r\n 0);\r\n \r\n STORAGE_PROPERTY_QUERY storage_property_query = {};\r\n storage_property_query.PropertyId = StorageDeviceProperty;\r\n storage_property_query.QueryType = PropertyStandardQuery;\r\n STORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {};\r\n DWORD BytesReturned = 0;\r\n \r\n DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,\r\n \u0026storage_property_query, sizeof(storage_property_query),\r\n \u0026storage_descriptor_header, sizeof(storage_descriptor_header),\r\n \u0026BytesReturned);\r\n \r\n \r\n std::vector\u003cchar\u003e buff(storage_descriptor_header.Size); //_STORAGE_DEVICE_DESCRIPTOR\r\n DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,\r\n \u0026storage_property_query, sizeof(storage_property_query),\r\n buff.data(), buff.size(), 0);\r\n \r\n \r\n STORAGE_DEVICE_DESCRIPTOR* device_descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buff.data();\r\n if (device_descriptor-\u003eVendorIdOffset){\r\n outVendorId = \u0026buff[device_descriptor-\u003eVendorIdOffset];\r\n }\r\n}\r\nMitigations\r\nTo circumvent this evasion you can hook  DeviceIoControl  or rename HDD so that it will not be detected by\r\nspecific strings\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 21 of 38\n\nFirmware tables – Anti VM\r\nThere are special memory areas used by OS that contain specific artifacts if OS is run under a virtual environment.\r\nThese memory areas may be dumped using different methods depending on the OS version.\r\nFirmware tables are retrieved via  SYSTEM_FIRMWARE_TABLE_INFORMATION  object. In our case, Raspberry Robin\r\nchecks if specific strings are present in Raw SMBIOS Firmware Table. It does that by\r\ncalling  NtQuerySystemInformation  with  SystemFirmwareTableInformation  argument (76).\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n// First, SYSTEM_FIRMWARE_TABLE_INFORMATION object is initialized in the following way:\r\nSYSTEM_FIRMWARE_TABLE_INFORMATION *sfti =\r\n(PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,\r\nLength);\r\nsfti-\u003eAction = SystemFirmwareTable_Get; // 1\r\nsfti-\u003eProviderSignature = 'RSMB'; // raw SMBIOS\r\nsfti-\u003eTableID = 0;\r\nsfti-\u003eTableBufferLength = Length;\r\n// Then initialized SYSTEM_FIRMWARE_TABLE_INFORMATION object is used as an argument for\r\n// the system information call in the following way in order to dump raw firmware table:\r\nNtQuerySystemInformation(\r\nSystemFirmwareTableInformation, // 76\r\nsfti,\r\nLength,\r\n\u0026Length);\r\n// First, SYSTEM_FIRMWARE_TABLE_INFORMATION object is initialized in the following way:\r\nSYSTEM_FIRMWARE_TABLE_INFORMATION *sfti =\r\n(PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 22 of 38\n\nLength); sfti-\u003eAction = SystemFirmwareTable_Get; // 1 sfti-\u003eProviderSignature = 'RSMB'; // raw SMBIOS sfti-\r\n\u003eTableID = 0; sfti-\u003eTableBufferLength = Length; // Then initialized\r\nSYSTEM_FIRMWARE_TABLE_INFORMATION object is used as an argument for // the system information\r\ncall in the following way in order to dump raw firmware table: NtQuerySystemInformation(\r\nSystemFirmwareTableInformation, // 76 sfti, Length, \u0026Length);\r\n// First, SYSTEM_FIRMWARE_TABLE_INFORMATION object is initialized in the following way:\r\nSYSTEM_FIRMWARE_TABLE_INFORMATION *sfti =\r\n (PSYSTEM_FIRMWARE_TABLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,\r\n Length);\r\nsfti-\u003eAction = SystemFirmwareTable_Get; // 1\r\nsfti-\u003eProviderSignature = 'RSMB'; // raw SMBIOS\r\nsfti-\u003eTableID = 0;\r\nsfti-\u003eTableBufferLength = Length;\r\n// Then initialized SYSTEM_FIRMWARE_TABLE_INFORMATION object is used as an argument for\r\n// the system information call in the following way in order to dump raw firmware table:\r\nNtQuerySystemInformation(\r\n SystemFirmwareTableInformation, // 76\r\n sfti,\r\n Length,\r\n \u0026Length);\r\nMitigations\r\nTo circumvent this evasion hook  NtQuerySystemInformation  for\r\nretrieving  SystemFirmwareTableInformation  class and parse the SFTI structure for provided field values.\r\nVectored Exception Filter – Anti Debug\r\nVectored exception handler (VEH) is a Windows operating system feature that allows programs to register a\r\ncallback function to handle certain types of exceptions that occur during program execution. It is an extension of\r\nStructured Exception Handling (SEH) and it is being called for unhandled exceptions regardless of the exception’s\r\nlocation.\r\nThen, every time those exceptions are triggered the exception handler will be called. But when a debugger is\r\nattached to a program, it becomes the exception handler for the program’s exceptions, which allows it to intercept\r\nand handle exceptions before they are handled by the program’s normal exception-handling mechanism. That\r\nmeans that if the program is running under a debugger, the custom filter won’t be called and the exception will be\r\npassed to the debugger.\r\nRaspberry Robin adds a handler using  RtlAddVectoredExceptionHandler  and then deliberately causes exceptions\r\n(such as in the above assembly instruction part) to verify if the control passed to the handler which means further\r\nthat the process running without a debugger.\r\nPlain text\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 23 of 38\n\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\ninclude 'win32ax.inc'\r\ninvoke RtlAddVectoredExceptionHandler, not_debugged\r\nint 3\r\njmp being_debugged\r\nnot_debugged:\r\n; continue the process...\r\nbeing_debugged:\r\ninvoke ExitProcess,0\r\ninclude 'win32ax.inc' invoke RtlAddVectoredExceptionHandler, not_debugged int 3 jmp being_debugged\r\nnot_debugged: ; continue the process... being_debugged: invoke ExitProcess,0\r\ninclude 'win32ax.inc'\r\ninvoke RtlAddVectoredExceptionHandler, not_debugged\r\nint 3\r\njmp being_debugged\r\nnot_debugged:\r\n ; continue the process...\r\nbeing_debugged:\r\n invoke ExitProcess,0\r\nMitigations\r\nTo circumvent this evasion, you can patch  KiUserExceptionDispatcher  which ScyllaHide implements. Also, you\r\ncan patch the opcodes which cause the exception.\r\nHardware breakpoints – Anti Debug\r\nHardware breakpoints are implemented using dedicated hardware resources within the CPU, including registers\r\nthat are used to store the parameters for the breakpoints. Debug Address Registers (DR0-DR3) are among those\r\nregisters and they are used to store the memory addresses or data values that the breakpoints are set on.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 24 of 38\n\nDR0-DR3 can be retrieved from the thread context structure. If they contain non-zero values, it may mean that the\r\nprocess is executed under a debugger and a hardware breakpoint was set.\r\nRaspberry Robin uses the fact of having the CONTEXT structure that is given as an argument to the exception\r\nhandler and inspects it. This is without using the API  GetThreadContext  which is more commonly used to query\r\nthe context structure.\r\nFigure 7 – Hardware breakpoints check\r\nTo circumvent this evasion you can hook  GetThreadContext  and modify debug registers or in our case as it\r\nhappens in VEH we can again hook  KiUserExceptionDispatcher .\r\nAssembly Instructions – Anti Debug\r\nThere are some techniques are intended to detect a debugger presence based on how debuggers behave when the\r\nCPU executes a certain instruction.\r\nRaspberry Robin uses some of them in different execution stages:\r\nPOPF and CPUID\r\nTo detect the use of a VM in a sandbox, malware could check the behavior of the CPU after the trap flag is set.\r\nThe trap flag is a flag bit in the processor’s flags register that is used for debugging purposes. When the Trap Flag\r\nis set, the processor enters a single-step mode, which causes it to execute only one instruction at a time and then\r\ngenerate a debug exception.\r\nFigure 8 – raising the POPF and CPUID exception\r\nIn our case, The  popf instruction pops the top value from the stack and loads it into the flags register. Based on\r\nthe value on the stack that has the Trap Flag bit set, the processor enters a single-step mode\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 25 of 38\n\n(SINGLE_STEP_EXCEPTION) after executing the next instruction.\r\nBut the next instruction is  cpuid  which behaves differently in VM. When in a physical machine, this exception\r\nstops the CPU execution to allow the contents of the registers and memory location to be examined by the\r\nexception handler after the cpuid  instruction which moves away the instruction pointer from the next bytes. In a\r\nVM, executing  cpuid  will result in a VM exit. During the VM exit the hypervisor will carry out its usual tasks of\r\nemulating the behaviors of the  cpuid  instruction which will make the Trap Flag be delayed and the code\r\nexecution will continue to the next instruction with the  C7 B2  bytes. This results in an exception because of an\r\nillegal instruction exception.\r\nThe vectored exception handler we mentioned above checks for this type of exception (Illegal instruction) and if it\r\nencounters such an exception, Raspberry Robin knows it runs under a VM.\r\nStack Segment Reigster\r\nThe trick relies on the fact that certain instructions cause all of the interrupts to be disabled while executing the\r\nnext instruction. This is happening using the following code:\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\npush ss\r\npop ss\r\npushf\r\ntest byte ptr [esp+1], 1\r\njnz being_debugged\r\npush ss pop ss pushf test byte ptr [esp+1], 1 jnz being_debugged\r\npush ss\r\npop ss\r\npushf\r\ntest byte ptr [esp+1], 1\r\njnz being_debugged\r\nThe register ss is called the Stack Segment and is a special-purpose register that stores the segment address of the\r\ncurrent stack.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 26 of 38\n\nLoading the ss (Stack segment) register clears interrupts to allow the next instruction to load the esp register\r\nwithout the risk of stack corruption. However, there is no requirement that the next instruction loads anything into\r\nthe esp register.\r\nIf a debugger is being used to single-step through the code, then the Trap Flag will be set in the EFLAGS image.\r\nThis is typically not visible because the Trap Flag will be cleared in the EFLAGS image after each debugger event\r\nis delivered. However, if the flags are saved to the stack using the pushf instruction before the debugger event is\r\ndelivered, then the Trap Flag will become visible. Therefore, comparing the flag on the stack can tell us whether\r\nthe sample is being debugged.\r\nINT 3\r\nINT3  is an interruption that is used as a software breakpoint. Without a debugger present, after getting to\r\nthe  INT3  instruction, the exception  EXCEPTION_BREAKPOINT (0x80000003) is generated and an exception handler\r\nwill be called. If the debugger is present, the control won’t be given to the exception handler but to the debugger\r\nitself.\r\nBesides the short form of INT3 instruction ( CC  opcode), there is also a long form of this instruction:  CD\r\n03  opcode.\r\nWhen the exception  EXCEPTION_BREAKPOINT  occurs, Windows decrements the  EIP  register to the assumed\r\nlocation of the  CC  opcode and passes the control to the exception handler. In the case of the long form of\r\nthe  INT3  instruction,  EIP  will point to the middle of the instruction (means to the  03  byte).\r\nTherefore,  EIP  should be edited in the exception handler if we want to continue execution after\r\nthe  INT3  instruction (otherwise we’ll most likely get an  EXCEPTION_ACCESS_VIOLATION  exception). If not, we\r\ncan neglect the instruction pointer modification.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nbool IsDebugged()\r\n{\r\n__try\r\n{\r\n__asm int 3;\r\nreturn true;\r\n}\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 27 of 38\n\n__except(EXCEPTION_EXECUTE_HANDLER)\r\n{\r\nreturn false;\r\n}\r\n}\r\nbool IsDebugged() { __try { __asm int 3; return true; } __except(EXCEPTION_EXECUTE_HANDLER) { return\r\nfalse; } }\r\nbool IsDebugged()\r\n{\r\n __try\r\n {\r\n __asm int 3;\r\n return true;\r\n }\r\n __except(EXCEPTION_EXECUTE_HANDLER)\r\n {\r\n return false;\r\n }\r\n}\r\nMitigations\r\nThe best way to mitigate all the following checks is to find those methods and patch them with NOP instructions.\r\nThread hiding – Anti Debug\r\nIn Windows 2000, a new class of thread information was added to the API  NtSetInformationThread  –\r\n  ThreadHideFromDebugger . It was added because when you attach a debugger to a remote process a new thread is\r\ncreated. If this was just a normal thread the debugger would be caught in an endless loop as it attempted to stop its\r\nown execution.\r\nSo behind the scenes when the debugging thread is created Windows calls NtSetInformationThread with the\r\nThreadHideFromDebugger flag set (1). This way the process can be debugged and a deadlock prevented.\r\nAllowing code execution to continue as normal.\r\nIf this flag is set for a thread, then that thread stops sending notifications about debug events. These events include\r\nbreakpoints and notifications about program completion. Due to this flag, the debugger cannot see the thread and\r\nis now unable to trap these events.\r\nRaspberry Robin enables the flag on newly spawned threads and then queries this information again using the\r\nAPI  NtQueryInformationThread  to check if the flag was set.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 28 of 38\n\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nbool HideFromDebugger(HANDLE hThread) {\r\nint status;\r\nint lHideThread = 1;\r\n// ThreadHideFromDebugger = 0x11\r\nstatus = NtSetInformationThread(hThread, ThreadHideFromDebugger, 0, 0);\r\n// check if the thread is hidden\r\nstatus = NtQueryInformationThread(hThread, ThreadHideFromDebugger, \u0026ThreadInformation, 1u, 0);\r\nreturn status\r\n}\r\nbool HideFromDebugger(HANDLE hThread) { int status; int lHideThread = 1; // ThreadHideFromDebugger =\r\n0x11 status = NtSetInformationThread(hThread, ThreadHideFromDebugger, 0, 0); // check if the thread is hidden\r\nstatus = NtQueryInformationThread(hThread, ThreadHideFromDebugger, \u0026ThreadInformation, 1u, 0); return\r\nstatus }\r\nbool HideFromDebugger(HANDLE hThread) {\r\n int status;\r\n int lHideThread = 1;\r\n // ThreadHideFromDebugger = 0x11\r\n status = NtSetInformationThread(hThread, ThreadHideFromDebugger, 0, 0);\r\n // check if the thread is hidden\r\n status = NtQueryInformationThread(hThread, ThreadHideFromDebugger, \u0026ThreadInformation, 1u, 0);\r\n \r\n return status\r\n}\r\nMitigations\r\nTo circumvent this evasion you need to hook  NtSetInformationThread . In our case, you also need to\r\nhook  NtQueryInformationThread .\r\nNtQueryInformationProcess flags – Anti Debug\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 29 of 38\n\nThe API  NtQueryInformationProcess  can retrieve a different kind of information from a process. It accepts\r\na  ProcessInformationClass  parameter which specifies the information you want to get and defines the output\r\ntype of the  ProcessInformation  parameter.\r\nRaspberry Robin uses this API in order to query the following values:\r\nProcessDebugPort (ProcessInformationClass = 7) – returns the port number of the debugger for the\r\nprocess.\r\nProcessDebugFlags (ProcessInformationClass = 0x1f) – returns the inverse value of the field\r\nNoDebugInherit which is a boolean value that determines whether child processes inherit the debugging\r\nprivileges of their parent process. This flag resides in the EPROCESS object.\r\nProcessDebugObjectHandle (ProcessInformationClass = 0x1e) – returns a handle to a kernel object that\r\ncan be used to attach a debugging object to a process, allowing a debugger to control the process and\r\naccess its memory and state. When a process is created, it does not have a debugging object associated with\r\nit by default. However, a debugger can use the API OpenProcess with the\r\nDEBUG_ONLY_THIS_PROCESS or DEBUG_PROCESS flags to create a ProcessDebugObjectHandle\r\nfor the process.\r\nFigure 11 – NtQueryInformationProcess flags checks\r\nMitigations\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 30 of 38\n\nTo circumvent this evasion hook  NtQueryInformationProcess  and set the following values in return buffers:\r\n0 (or any value except -1) in the case of  ProcessDebugPort\r\nA non-zero value in the case of  ProcessDebugFlags\r\n0 in the case of  ProcessDebugObjectHandle\r\nDbgBreakPoint patch – Anti Debug\r\nDbgBreakPoint  is the API called when a debugger attaches to a running process. It allows the debugger to gain\r\ncontrol because an exception is raised which it can intercept. The API has the following implementation:\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\ncc int3\r\nc3 ret\r\ncc int3 c3 ret\r\ncc int3\r\nc3 ret\r\nby replacing the first byte (int3 = 0xcc) with a ret (0xc3) instruction, the debugger won’t break in and the thread\r\nwill exit.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nvoid Patch_DbgBreakPoint()\r\n{\r\nHMODULE hNtdll = GetModuleHandleA(\"ntdll.dll\");\r\nif (!hNtdll)\r\nreturn;\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 31 of 38\n\nFARPROC pDbgBreakPoint = GetProcAddress(hNtdll, \"DbgBreakPoint\");\r\nif (!pDbgBreakPoint)\r\nreturn;\r\nDWORD dwOldProtect;\r\nif (!VirtualProtect(pDbgBreakPoint, 1, PAGE_EXECUTE_READWRITE, \u0026dwOldProtect))\r\nreturn;\r\n*(PBYTE)pDbgBreakPoint = (BYTE)0xC3; // ret\r\n}\r\nvoid Patch_DbgBreakPoint() { HMODULE hNtdll = GetModuleHandleA(\"ntdll.dll\"); if (!hNtdll) return;\r\nFARPROC pDbgBreakPoint = GetProcAddress(hNtdll, \"DbgBreakPoint\"); if (!pDbgBreakPoint) return; DWORD\r\ndwOldProtect; if (!VirtualProtect(pDbgBreakPoint, 1, PAGE_EXECUTE_READWRITE, \u0026dwOldProtect))\r\nreturn; *(PBYTE)pDbgBreakPoint = (BYTE)0xC3; // ret }\r\nvoid Patch_DbgBreakPoint()\r\n{\r\n HMODULE hNtdll = GetModuleHandleA(\"ntdll.dll\");\r\n if (!hNtdll)\r\n return;\r\n FARPROC pDbgBreakPoint = GetProcAddress(hNtdll, \"DbgBreakPoint\");\r\n if (!pDbgBreakPoint)\r\n return;\r\n DWORD dwOldProtect;\r\n if (!VirtualProtect(pDbgBreakPoint, 1, PAGE_EXECUTE_READWRITE, \u0026dwOldProtect))\r\n return;\r\n *(PBYTE)pDbgBreakPoint = (BYTE)0xC3; // ret\r\n}\r\nMitigations\r\nTo circumvent this evasion, you can patch  DbgBreakPoint  back after the modification (can be as part of\r\nhooking  VirtualProtect ).\r\nProcess Suspension detection – Anti Debug\r\nThis evasion depends on having the thread creation flag  THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE  (name\r\ngiven by researcher) that Microsoft added into 19H1. This flag makes the thread ignore\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 32 of 38\n\nany  PsSuspendProcess  API being called.\r\nRaspberry Robin uses this flag for a cool trick. It creates two threads with this flag, one of which keeps\r\nsuspending the other one until the suspend counter limit which is 127 is reached (suspend count is a signed 8-bit\r\nvalue).\r\nWhen you get to the limit, every call for  PsSuspendProcess  doesn’t increment the suspend counter and\r\nreturns  STATUS_SUSPEND_COUNT_EXCEEDED . But what happens if someone calls  NtResumeProcess ? It decrements\r\nthe suspend count! So when someone decides to suspend and resume the thread, they’ll actually leave the count in\r\na state it wasn’t previously in. Therefore, Raspberry Robin calls periodically to  NtSuspendThread  and if it\r\nsucceeds and increments the counter it means that the thread has been externally suspended and resumed and is\r\nbeing debugged.\r\nMitigations\r\nTo circumvent this evasion, you can hook  NtCreateThread  to omit\r\nthe  THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZEIt  flag.\r\nVBAWarnings check – Anti Sandbox\r\nYou all know the “Enable all macros” prompt in Office documents. It means the macros can be executed without\r\nany user interaction. This behavior is common for sandboxes.\r\nRaspberry Robin uses that in order to check if it is running on a sandbox checking the flag in the registry\r\nkeys  SOFTWARE\\Microsoft\\Office\\\u003cversion\u003e\\Word\\Security\\VBAWarnings  while the version is between  12.0 to\r\n19.0 .\r\nMitigations\r\nTo circumvent this evasion disable this flag or hook  SHGetValueW  or other registry query APIs such\r\nas RegQueryValueExA .\r\nMain stage tricks\r\nRaspberry Robin not only has numerous evasions but also some really nice tricks of how not being detected by\r\nsecurity solutions.\r\nIFEO Removal\r\nThis technique involves modifying the Image File Execution Options (IFEO) registry key, which is used by the\r\nWindows operating system to set debugging options for executable files. When an executable file is launched, the\r\noperating system checks the corresponding IFEO registry key for any specified debugging options. If the key\r\nexists, the operating system launches the specified debugger instead of the executable file.\r\nWhat Raspberry Robin does is remove the registry keys for the following files so they will run instead of the\r\ndebugger:\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 33 of 38\n\nrundll32.exe\r\nregsvr32.exe\r\ndllhost.exe\r\nmsiexec.exe\r\nodbcconf.exe\r\nregasm.exe\r\nregsvcs.exe\r\ninstallutil.exe\r\nexplorer.exe\r\nWindows Defender Exclusion List\r\nRaspberry Robin tries to evade Windows Defender by adding its processes and paths to its exclusion list by\r\nadding the values to the registry keys:  HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\r\nDefender\\Exclusions\\Paths  and  HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\r\nDefender\\Exclusions\\Processes .\r\nPersistence trick\r\nRaspberry Robin Creates a file with a random name and extension and puts it in  %TEMP%  directory. Then, the\r\nmalware writes the command  shell32.dll|ShellExec_RunDLLA|REGSVR32.EXE -U /s \"C:\\Windows\\Temp\r\n\u003cgenerated_file_name\u003e.”  to  RunOnce  or  RunOnceEx  key but with a twist.\r\nRaspberry Robin tries to evade security solutions by first generating a random registry key\r\ninside  HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion  and renaming the  RunOnce  key to this random\r\nname. Then add the new value to this random key before renaming it back to  RunOnce .\r\nNow that we’ve covered the various evasive tactics, let’s shift our focus to the exploits used by the malware.\r\nPrivilege escalation\r\nRaspberry Robin has several ways of how it elevates its privileges – two UAC methods, which were covered in\r\navast’s blogpost and two 1-days EoP exploits. Raspberry Robin only runs those methods only in case this malware\r\nreally needs them. Those checks including:\r\nProcess’s SID is S-1-5-32-544 ( DOMAIN_ALIAS_RID_ADMIN )\r\nThe integrity level is  0x3000  (high integrity) or  0x2000  (medium integrity)\r\nIf consent by user or admin is required –\r\n  HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\ConsentPromptBehaviorUser/Admin\r\nIf the time of the last input event (calling to  GetLastInput ) is in the last hour.\r\nBelow we drill down into the EoP exploits used by the malware.\r\nThe malware contains 2 exploits, which are embedded and encrypted by RC4 in memory. Each exploit is suitable\r\nfor different versions of windows which increases the odds of the malware elevating its privileges on a random\r\nvictim’s machine.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 34 of 38\n\nBoth exploits are 64-bit and packed with the same packer known as Donut Loader. The packer is injected as a\r\nshellcode with KernelCallbackTable injection as copied from the open-source POC. The exploits are injected\r\ninto  winver.exe  which is also added to the Windows Defender excluded list.\r\nThe exploits share code such as the encryption scheme of loops of math expressions for their strings (such as class\r\nnames and loaded modules).\r\nCVE-2020-1054\r\nCVE-2020-1054 is Win32k Elevation of Privilege Vulnerability reported by Check Point Research. The\r\nvulnerability is out of bounds write in win32k and it was used by different Exploit kits in the past. The exploit has\r\nsome similarities to open-source GitHub POCs but we haven’t found one that surely is the base for this\r\nimplementation. The exploit is only used by Raspberry Robin on Windows 7 systems where the revision number\r\nis not higher than  24552  (It gets the build number through the  BuildLabEx  registry key).\r\nVersioning Decisions\r\nDepends on the environment variable that Raspberry Robin’s main module sets. This variable means what kind of\r\nWindows 7 we have (with patches or not – for different offsets). The exploit uses this variable in order to decide 2\r\noffsets:\r\nxleft_offset  – 0x900 or 0x8c0\r\noob_offset  – 0x238 or 0x240\r\nHMValidateHandle Wrapper\r\nGets the HMValidateHandle from searching inside the IsMenu code for 0xe8 opcode. It searches in the first 0x20\r\nbytes for this opcode.\r\nFigure 12 - Untitled\r\nFigure 12 – Getting HMValidateHandle address\r\nPrivilege Elevation\r\nThe exploit uses a shellcode, different than the ones in GitHub doing the same replacing of the process’s token.\r\nThis shellcode is invoked by sending a message after the API SetBitmapBits\r\nNaming\r\nThe name of the window used in this exploit is “ #32272 ” so it can’t be used for hunting as it’s pretty known and\r\nused in many places.\r\nToken Swap\r\nIn the shellcode, it is pretty straightforward:\r\nWe search for the target process – using the target PID\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 35 of 38\n\nWe search for  SYSTEM  – using a PID of  4\r\nWe update our pointer to point at  SYSTEM ’s token\r\nFigure 13 - Untitled\r\nFigure 13 – Token swapping in kernel shellcode\r\nCVE-2021-1732\r\nCVE-2021-1732 is a win32k window object type confusion leading to an OOB (out-of-bounds) write. It was used\r\nas a 0-day in the wild by Bitter APT and written by Moses – also known as Exodus Intelligence. We found some\r\nresemble to an open source POC in both the flow and how to restore windows after changing the token but also\r\nsome differences.\r\nCVE-2021-1732 runs on Windows 10, with the targeted build number range being from  16353  to  19042 . For\r\nthe second exploit, it also checks if the package KB4601319 of the patch is present.\r\nVersioning Decisions\r\nQuerying OSBuildNumber from the PEB in order to decide on a number of offsets (some of them are 0x40\r\ndifferent from real offset due to the type of read primitive):\r\nBuild number greater than 19041\r\noffset_ActiveProcessLinks = 0x448\r\noffset_token = 0x4b8\r\noffset_UniqueProcessId = 0x400\r\nBuild number greater than 18362 but less than 19041\r\noffset_ActiveProcessLinks = 0x2f0\r\noffset_token = 0x360\r\noffset_UniqueProcessId = 0x2a8\r\nBuild number greater than 15063 but less than 18362\r\noffset_ActiveProcessLinks = 0x2e8\r\noffset_token = 0x358\r\noffset_UniqueProcessId = 0x2a0\r\nBuild number less than 15063\r\noffset_ActiveProcessLinks = 0x2f0\r\noffset_token = 0x358\r\noffset_UniqueProcessId = 0x2a8\r\nThis is pretty weird because the check inside RaspberryRobin is that the version is between  16353   to  19042\r\nHMValidateHandle Wrapper\r\nAs in CVE-2020-1054, it gets the HMValidateHandle from searching inside the IsMenu code for 0xe8 opcode. It\r\nsearches in the first 0x20 bytes for this opcode. This function is identical to the same function in the other exploit.\r\nNaming\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 36 of 38\n\nRegistering class with the name:  “helper_cls”  and then registering (spray handles) windows from this class\r\nwith the name:  “helper” .\r\nPrivilege Elevation\r\nThe exploit uses Arbitrary-Read \u0026 Arbitrary-Write exploit primitives, based on the vulnerability feature in\r\nconjunction with  GetMenuBarInfo  and Call  SetWindowLong  to write data to the address of the kernel-space\r\ndesktop heap.\r\nToken Swap\r\nScanning the  EPROCESS.ActiveProcessLinks  until finding the System process (PID = 4) and our process (equal\r\nour PID) using an Arbitrary-Read / Arbitrary-Write exploit primitives. It then writes the process’s token address\r\nto  wndMax.pExtraBytes  and then writes to this address the system token.\r\nFigure 14 – Token Swap using the exploit primitives\r\nNotable info\r\nThe exploit gets the ETHREAD structure using the\r\nAPI  NtQuerySystemInformation  with  SystemExtendedHandleInformation  and\r\niterates  SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX  for handles until getting the one with the thread ID as the running\r\nthread.\r\nConclusion\r\nAnti-debugging and other evasions can be pretty exhausting, and even more when it comes to such obfuscation\r\nmethods and volume of methods as Raspberry Robin implements. We showed plenty of methods with\r\nexplanations of how they work and how to evade those evasions. Raspberry Robin implemented other cool tricks\r\nand exploits showing that he also has capabilities in the exploiting area. Unfortunately, the world of evasions is\r\nonly getting harder and more creative, so buckle up and pray that somebody already encountered this evasion\r\nbefore you.\r\nCheck Point Customers remain protected against the threat described in this research.\r\nCheck Point Threat Emulation provides Comprehensive coverage of attack tactics, file-types, and operating\r\nsystems, and has developed and deployed a signatures named “Trojan.Wins.RaspberryRobin” to detect and\r\nprotect our customers against the malware described in this blog.\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 37 of 38\n\nSource: https://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nhttps://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/\r\nPage 38 of 38",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/"
	],
	"report_names": [
		"raspberry-robin-anti-evasion-how-to-exploit-analysis"
	],
	"threat_actors": [
		{
			"id": "d90307b6-14a9-4d0b-9156-89e453d6eb13",
			"created_at": "2022-10-25T16:07:23.773944Z",
			"updated_at": "2026-04-10T02:00:04.746188Z",
			"deleted_at": null,
			"main_name": "Lead",
			"aliases": [
				"Casper",
				"TG-3279"
			],
			"source_name": "ETDA:Lead",
			"tools": [
				"Agentemis",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"RbDoor",
				"RibDoor",
				"Winnti",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "655f7d0b-7ea6-4950-b272-969ab7c27a4b",
			"created_at": "2022-10-27T08:27:13.133291Z",
			"updated_at": "2026-04-10T02:00:05.315213Z",
			"deleted_at": null,
			"main_name": "BITTER",
			"aliases": [
				"T-APT-17"
			],
			"source_name": "MITRE:BITTER",
			"tools": [
				"ZxxZ"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "bf6cb670-bb69-473f-a220-97ac713fd081",
			"created_at": "2022-10-25T16:07:23.395205Z",
			"updated_at": "2026-04-10T02:00:04.578924Z",
			"deleted_at": null,
			"main_name": "Bitter",
			"aliases": [
				"G1002",
				"T-APT-17",
				"TA397"
			],
			"source_name": "ETDA:Bitter",
			"tools": [
				"Artra Downloader",
				"ArtraDownloader",
				"Bitter RAT",
				"BitterRAT",
				"Dracarys"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775438933,
	"ts_updated_at": 1775826787,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/40a795ef942a1901f33b950137dbc42f02f218eb.pdf",
		"text": "https://archive.orkl.eu/40a795ef942a1901f33b950137dbc42f02f218eb.txt",
		"img": "https://archive.orkl.eu/40a795ef942a1901f33b950137dbc42f02f218eb.jpg"
	}
}