{
	"id": "29607252-53db-4468-8506-d3859f3d9c42",
	"created_at": "2026-04-06T01:32:20.18176Z",
	"updated_at": "2026-04-10T03:27:18.662326Z",
	"deleted_at": null,
	"sha1_hash": "8089ac6f92203d92659f87fa6c016b01c192bb93",
	"title": "Malware Mitigation when Direct System Calls are Used",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 86732,
	"plain_text": "Malware Mitigation when Direct System Calls are Used\r\nBy sharon\r\nPublished: 2018-11-27 · Archived: 2026-04-06 01:19:50 UTC\r\nIn 2018 we have seen an increase in the malicious use of direct system calls in order to evade security product\r\nhooks.\r\nThese hooks are used to monitor API calls which may hint to malicious activity.\r\nDirect system calls evasion method utilizes reading system call numbers from ntdll.dll, putting the appropriate\r\nsystem call number on the EAX register, putting the function parameters on the stack and entering the kernel\r\ndirectly by using sysenter or int 0x2e commands. This way – the function from ntdll.dll isn’t called and the hook\r\nis useless at detecting the malicious activity. This photo shows how most malware perform direct systems calls:\r\nMalware Mitigation Directly executing functions of the Kernel without calling functions from\r\nntdll.dll\r\nFigure 1 – Directly executing functions of the Kernel without calling functions from ntdll.dll\r\nThe direct system calls are usually used to silently inject malicious code into other processes. In 32-bit systems, it\r\nis possible to monitor system calls in the kernel by hooking the SSDT.\r\nHowever in Windows Vista and up (64 bits only). It is not possible because of the patch guard mechanism.\r\nIn this article, we will show some ways to mitigate these malware – showing that even without hooks, we can find\r\ntraces of the malicious activity and how to spot their code injection.\r\nMalware analyzed\r\nWe will cover 8 different malware:\r\nLockPoS\r\nSHA256: 093fcbf6bd7e264201ce5405f83101cefa5588a56c5bab11cfdbabbab3ed8a28\r\nFlokibot\r\nSHA256: 5e1967db286d886b87d1ec655559b9af694fc6e002fea3a6c7fd3c6b0b49ea6e\r\nTrickbot\r\nSHA256: 1c81272ffc28b29a82d8313bd74d1c6030c2af1ba4b165c44dc8ea6376679d9f\r\nFormbook\r\nSHA256: c2bbec7eb5efc46c21d5950bb625c02ee96f565d2b8202733e784e6210679db9\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 1 of 8\n\nOsiris\r\nSHA256: e7d3181ef643d77bb33fe328d1ea58f512b4f27c8e6ed71935a2e7548f2facc0\r\nNeurevt\r\nSHA256: 634a93d42b6d2d7d302377aa8f1fb4b57ab923544836c83642c059826e9969d2\r\nFastcash\r\nSHA256: 820ca1903a30516263d630c7c08f2b95f7b65dffceb21129c51c9e21cf9551c6\r\nCoinMiner\r\nSHA256: 8b309c799d53ac759a8b304e068e2530958d7f1577d20775b589e17e533d132a\r\nDue to the lack of hooks, we cannot trace the functions called from ntdll.dll. But what can we trace? What can we\r\nget our hands on? Let’s collect and examine evidence and then make conclusions how we can mitigate these\r\nmalware.\r\nWe recall that for code injection in the context of the system calls method, we need to perform two tasks:\r\nReading ntdll.dll for the system call numbers\r\nPerforming the functions (resulting in remote thread/queuing APC/process creation)\r\nThe two tables below summarize the techniques of each task with their evidence, pros, and cons. Later, we will\r\nelaborate on each method.\r\nReading ntdll.dll\r\nMethods Pros Cons Evidence Malware\r\nDual load – Calling\r\nNtMapViewOfSection\r\nwith a section\r\ncontaining a fresh\r\ncopy of ntdll.dll from\r\nthe disk\r\nLooks like\r\nntdll.dll was\r\nloaded in a\r\ntypical way by\r\nthe Windows\r\nloader\r\nCan be\r\nspotted in\r\nloaded\r\nmodules\r\nCan be traced by a\r\nhook on\r\nNtMapViewOfSection\r\nLockPoS,\r\nFlokibot,\r\nOsiris,\r\nCoinMiner\r\nReading from the disk\r\n– using NtReadFile\r\nand manually\r\nmapping a fresh copy\r\nof ntdll.dll from the\r\ndisk\r\nThe mapping\r\nis\r\ndone manually,\r\nhence we can’t\r\nfind ntdll.dll in\r\nloaded\r\nmodules using\r\na debugger\r\nReading\r\nntdll.dll\r\nfrom the\r\ndisk is\r\nsuspicious\r\nsince\r\nevery\r\nprocess\r\nCan be traced by a\r\nhook on NtReadFile\r\nTrickbot,\r\nFormbook,\r\nFashcash\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 2 of 8\n\nhas it\r\nloaded\r\nalready\r\nReading the existing\r\nntdll.dll that is\r\nalready loaded to the\r\nprocess\r\nUndetected by\r\nhooks, leaves\r\nno evidence\r\nFails if\r\nthe\r\nfunctions\r\nare\r\nhooked\r\nby a\r\nsecurity\r\nproduct\r\nnone Neurevt\r\nPerforming the functions\r\nMethods Pros Cons Evidence Malware\r\nProcess\r\ncreation\r\n(hollowing)\r\nCreating\r\nlegitimate\r\nWindows\r\nprocesses that\r\ncan perform\r\nmalicious\r\nactivities\r\nwithout being\r\nblocked. Low\r\naccuracy in\r\nidentifying\r\nwhich process\r\nhad injection\r\ninto it\r\nCan be\r\nmonitored\r\neasily in the\r\nkernel\r\nProcess creation via the\r\nkernel\r\nFlokibot,\r\nTrickbot,\r\nNeurevt,\r\nCoinMiner,\r\nOsiris\r\nRemote\r\nthread\r\nWithout\r\ncreating a\r\nprocess,\r\nexecute code\r\nfrom the\r\noriginal\r\nWindows\r\nprocesses\r\nCan be\r\nmonitored\r\neasily in the\r\nkernel.\r\nGreater\r\naccuracy in\r\nidentifying\r\nwhich\r\nprocess has\r\ninjection into\r\nRemote thread creation via\r\nthe kernel\r\nLockPoS\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 3 of 8\n\nQueuing\r\nuser APC\r\nNo process or\r\nthread\r\ncreation –\r\nstealthiest\r\nmethod.\r\nLow accuracy\r\nin identifying\r\nwhich process\r\nhad injection\r\ninto it\r\nHarder to\r\nimplement.\r\nNeed to find\r\nan alertable\r\nthread\r\nThe malware has a handle to\r\na thread to\r\nTHREAD_SET_CONTEXT\r\nrights\r\nFormbook\r\nReading ntdll.dll\r\nWe know that the malware needs to obtain the system calls numbers. The system calls numbers exist in ntdll.dll, at\r\nthe “mov eax,SYSCALL_NUMBER” command at the corresponding function. These numbers are changing from\r\none operating system version to another (including service packs), and therefore can’t be hardcoded in the\r\nmalware. This dll is loaded to every process in the system. Hence – loading another copy of it might be\r\nsuspicious.\r\nIt is not a good idea to use the originally loaded ntdll.dll, because If these functions are hooked by a security\r\nproduct, it will not be able to find them easily since the function prologue is changed and the “mov” command is\r\nno longer in the same location.\r\nA snippet of NtMapViewOfSection API call from Windows 7 32-bit (syscall number 0xa8) and Windows 10 64-\r\nbit (0x28), shows system call numbers are different from one operating system to another.\r\nMalware Mitigation NtMapViewOfSection in ntdll.dl Windows 7 32-bit\r\nFigure 2 – NtMapViewOfSection in ntdll.dl, Windows 7 32-bit\r\nMalware Mitigation NtMapViewOfSection in ntdll.dll Windows 10 64-bit\r\nFigure 3 – NtMapViewOfSection in ntdll.dll, Windows 10 64-bit\r\nIn the table above, we showed 3 different techniques to achieve this:\r\nDual load – Using NtMapViewOfSection to map a fresh copy from the disk of ntdll.dll\r\nReading from the disk a fresh copy of ntdll.dll using NtReadFile\r\nReading the existing ntdll.dll\r\nLet’s analyze each method in the context of this malware:\r\nThe first method is to call NtMapViewOfSection with a section that contains a fresh copy of ntdll.dll. This section\r\nobject is created using NtCreateSection and uses a file handle to ntdll.dll which can be obtained using\r\nNtCreateFile.\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 4 of 8\n\nThis way, we will see another loaded module of ntdll.dll in the list of loaded modules.\r\nMalware Mitigation Osiris loaded modules after it loaded a fresh copy of ntdll.dll\r\nFigure 4 – A screenshot from Osiris loaded modules, after it loaded a fresh copy of ntdll.dll\r\nCoinMiner also uses this technique:\r\nMalware Mitigation call to NtMapViewOfSection with the section handle 0x98 of ntdll.dll\r\nFigure 5 – The call to NtMapViewOfSection with the section handle 0x98 of ntdll.dll. The section handle is\r\nplaced on the stack in the top-right image.\r\nThe same result can be achieved using a memory-mapped file, as LockPoS \u0026 Flokibot do:\r\nMalware Mitigation LockPoS mapping ntdll.dll as a memory mapped file using MapViewOfFile\r\nFigure 6 – LockPoS mapping ntdll.dll as a memory mapped file using MapViewOfFile\r\nInside MapViewOfFile, there is a call to NtMapViewOfSection. Essentially – calling MapViewOfFile is\r\nsubsequently calling NtMapViewOfSection.\r\nMalware Mitigation Inside MapViewOfFile we can see the file mapped using\r\nNtMapViewOfSection\r\nFigure 7 – from kernelbase.dll – Inside MapViewOfFile we can see the file mapped using\r\nNtMapViewOfSection\r\nMalware Mitigation Process Monitor shows reading of ntdll.dll by the malware\r\nFigure 8 – Process Monitor shows reading of ntdll.dll by the malware\r\nThe second method is reading ntdll.dll from the disk using ReadFile or NtReadFile. Thanks to SysInternals\r\nProcess Monitor we can easily spot this activity:\r\nTrickbot reads the whole file multiple times, each time for a different system call number.\r\nFormbook reads the whole file once for a few system calls.\r\nFastcash reads just a specific part of the file. It looks for NtQueryInformationProcess function – it can be used to\r\ndetect debuggers. This seems to be the stealthiest way. If we thought we could recognize this behavior solely\r\nbased on some signature (like, offset 0 and length = size of ntdll.dll) we were wrong!\r\nNote the 2nd read of ntdll.dll, marked in yellow – it was not initiated by Formbook, but by the kernel as a result of\r\npage fault (Paging I/O flag indicates this). So, this call is not relevant to our analysis of the malware.\r\nThe third and least effective method, which is implemented only by Neurevt, is reading from ntdll.dll that is\r\nalready loaded into the process memory.\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 5 of 8\n\nOne of the functions that Neurevt looks for is NtCreateSection. On this function, we don’t have a hook, so it will\r\nsuccessfully copy its syscall number (not only that but also the next instructions of that function) to another\r\nmemory region:\r\nMalware Mitigation Neurevt looks for NtCreateSection. The \"cmp eax,B8\" highlighted in grey is\r\na search for the “mov” instruction which has the syscall number\r\nFigure 9 – Neurevt looks for NtCreateSection. The “cmp eax,B8” highlighted in grey is a search for the “mov”\r\ninstruction which has the syscall number\r\nMalware Mitigation The NtCreateSection function in the new memory region, copied by the\r\nNeurevt. 0x54 is the syscall number of NtCreateSection\r\nFigure 10 – The NtCreateSection function in the new memory region, copied by the Neurevt. 0x54 is the syscall\r\nnumber of NtCreateSection\r\nBut on NtUnmapViewOfSection we do have a hook, so the checks that Neurevt performs in order to see if it can\r\ncopy the function will fail and therefore it won’t be able to retrieve its syscall number.\r\nThis is what the hooked UnmapViewOfSection looks like:\r\nMalware Mitigation NtUnmapViewOfSection hooked by Cyberbit EDR agent\r\nFigure 11 – NtUnmapViewOfSection\r\nMalware Mitigation The cmp eax,B8 check fails an the function will not be copied\r\nFigure 12 – The “cmp eax,B8” check fails and the function won’t be copied\r\nNeurevt has another mechanism – to call the APIs if the copy fails. But in this case, it will be intercepted by the\r\nhooks.\r\nThis method has one advantage – if the API wasn’t hooked by the security product, the malware will be able to\r\nuse the system calls without reading ntdll.dll from the disk – and we will not leave any evidence.\r\nPerforming the functions (resulting in remote thread/queuing APC/process creation)\r\nThere are many syscall numbers which can be extracted. We will focus on the ones related to code\r\ninjection/process hollowing – as powerful mechanisms that are favored by malware authors. The functions\r\nperformed after extracting ntdll.dll can perform code injection. This results, as mentioned in the table above, in a\r\nremote thread creation, queuing an APC to a remote process or process creation.\r\nFirst case – process creation\r\nFlokibot, Trickbot, Neurevt, CoinMiner, and Osiris – all create a suspended process, replace its code with another\r\ncode (process hollowing) and then resume its main thread. They use the direct system calls to perform it.\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 6 of 8\n\nFlokibot, Neurevt, CoinMiner, and Osiris – all chose a legitimate Windows process for hollowing. Flokibot and\r\nNeurevet chose explorer.exe. CoinMiner chooses notepad.exe. Osiris (which uses a combination of process\r\nhollowing and process Doppelgänging) chooses wermgr.exe. Neurevt, as part of its unpacking process, created\r\nitself as a suspended process and then unpacked its code into its child (in addition to the injection to explorer.exe).\r\nTrickbot created itself as a suspended process and injected its code into it (as did Neurevt).\r\nWe can learn that the suspended process will be a legitimate windows process or the malware itself or a web\r\nbrowser (Firefox/Chrome/Edge/Internet Explorer, etc.)\r\nThis is logical because Windows processes look legitimate to the user and web browsers and can send network\r\npackets, so it won’t look suspicious. As for the malware itself – it also doesn’t look suspicious, as it is common to\r\nspawn child processes of your own process (example: chrome.exe does it) and the malware knows that its\r\nexecutable definitely exists.\r\nSecond case – thread creation\r\nIf a process was not created, a remote thread might be created.\r\nLockPoS, which we have written about extensively (How Cyberbit Researchers Discovered a New Silent\r\nLockPoS Malware Injection Technique, Jan 2018)_, creates a remote thread in a remote process. Not surprisingly\r\n– in explorer.exe (malware’s absolute favorite).\r\nThird case – user APC queued\r\nFormbook, the most complex malware on the list, injects code into explorer.exe using an APC. But to inject an\r\nAPC, a process must obtain a handle to that remote thread with THREAD_SET_CONTEXT access rights. This\r\nhandle is a handle to a thread which does not belong to a child process of the malware.\r\nOne malware which was not covered here is Fastcash. This malware didn’t perform code injection. However, we\r\nlearned from this malware that malware might not read ntdll.dll entirely, Rather only a part of it.\r\nMalware Mitigation\r\nWe have two pieces of evidence:\r\nThe reading of ntdll.dll which splits into 3 cases. The 3rd case (reading from the existing ntdll.dll) is not relevant\r\nbecause we are expected to place hooks on the calls related to code injection.\r\nThis leaves us left with 2 cases – 2 methods. We can intercept the reading of ntdll.dll in two ways:\r\n1. Place a hook on NtMapViewOfSection on a process trying to map ntdll.dll to its own memory\r\n2. Monitor reading of at least one byte from ntdll.dll via the kernel. The reading must not be a result of page\r\nfault\r\nThe second evidence is one of the three below:\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 7 of 8\n\n1. Process creation – particularly of the malware itself/Windows process/web browser\r\n2. Thread creation in a remote process\r\n3. Queuing an APC in a remote process (not a child process)\r\nAll three cases can be monitored via the kernel without hooks.\r\nFor the first one, we have to monitor process creation and check its properties (the malware itself, Windows\r\nprocess, web browser)\r\nFor the second one, we have to monitor remote thread creation.\r\nFor the last one, we have to monitor handles obtained to a remote thread with THREAD_SET_CONTEXT access\r\nrights. That thread belongs to a process which is not a child process of the malware.\r\nThe mitigation can be done by combining the two pieces of evidence.\r\nIf a process is seen reading ntdll.dll as described above and performs one of the 3 cases from the second set of\r\nevidence (process creation, thread creation in a remote process or queuing an APC in a remote process) right\r\nafterward, we can mark it as suspicious. The process that has the injection into can be known from the second\r\nevidence.\r\nTwo remarks:\r\nWe do not know with 100% certainty which process had the injection into it. Let’s explain each case:\r\n1. Remote thread – We can be almost 100% sure about the targeted process. We just miss the allocation and\r\nwrite into that process, because they were done using system calls.\r\n2. Process creation – We cannot be sure about it since the malware might create other processes without\r\ninjecting into them (such as executing malicious commands via Windows command prompt)\r\n3. Queuing APC – We can also not be sure about it since having a handle to a thread doesn’t guarantee that\r\nan APC was queued successfully.\r\nWe can remove the condition of Windows process/web browser/the malware itself from evidence two; case\r\none. This will lead to fewer false-negatives and probably also lead to more false-positives\r\nHod Gavriel is a Malware Analyst at Cyberbit.\r\nWatch FREE webcast to learn How to Prevent the Next Financial Cyberattack with Next-Gen Technology\r\nSource: https://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nhttps://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://www.cyberbit.com/blog/endpoint-security/malware-mitigation-when-direct-system-calls-are-used/"
	],
	"report_names": [
		"malware-mitigation-when-direct-system-calls-are-used"
	],
	"threat_actors": [
		{
			"id": "679e335a-38a4-4db9-8fdf-a48c17a1f5e6",
			"created_at": "2023-01-06T13:46:38.820429Z",
			"updated_at": "2026-04-10T02:00:03.112131Z",
			"deleted_at": null,
			"main_name": "FASTCash",
			"aliases": [],
			"source_name": "MISPGALAXY:FASTCash",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775439140,
	"ts_updated_at": 1775791638,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/8089ac6f92203d92659f87fa6c016b01c192bb93.pdf",
		"text": "https://archive.orkl.eu/8089ac6f92203d92659f87fa6c016b01c192bb93.txt",
		"img": "https://archive.orkl.eu/8089ac6f92203d92659f87fa6c016b01c192bb93.jpg"
	}
}