{
	"id": "5ba1bff5-77b6-4236-913b-97a9c030d800",
	"created_at": "2026-04-06T00:15:19.567157Z",
	"updated_at": "2026-04-10T13:12:15.814422Z",
	"deleted_at": null,
	"sha1_hash": "31709d64827fb968451922a231cf95a05f719b58",
	"title": "GitHub - knight0x07/NailaoLoader-Hiding-Execution-Flow: NailaoLoader: Hiding Execution Flow via Patching",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 257094,
	"plain_text": "GitHub - knight0x07/NailaoLoader-Hiding-Execution-Flow:\r\nNailaoLoader: Hiding Execution Flow via Patching\r\nBy knight0x07\r\nArchived: 2026-04-05 17:04:45 UTC\r\nNailaoLoader: Hiding Execution Flow via Patching\r\nBackground\r\nThe threat actors were seen using Windows Management Instrumentation (WMI) to transfer the following three\r\nfiles (and execute usysdiag.exe) to each machine by executing a script that targeted a list of local IP addresses:\r\nusysdiag.exe\r\nsensapi.dll\r\nusysdiag.exe.dat\r\nAnalysis\r\nThe usysdiag.exe (Huorong Sysdiag Helper - Huorong Internet Security) which is a valid signed executable by\r\n\"Beijing Huorong Network Technology Co. Ltd.\" - Chinese endpoint security solutions provider is initially\r\nexecuted.\r\nThe usysdiag.exe calls a function which calls LoadLibraryA() to load \"sensapi.dll\" in its virtual address space\r\nand then calls GetProcAddress() function to fetch address to the IsNetworkAlive() function exported by\r\n\"sensapi.dll\".\r\nhttps://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nPage 1 of 6\n\nBut in our case, the NailaoLoader \"sensapi.dll\" is been side-loaded instead of the legitimate DLL as it is present\r\nalongside the \"usysdiag.exe\" in the same directory and then once the NailaoLoader DLL is loaded the DllMain()\r\nfunction of the malicious DLL is called.\r\nThe NailaoLoader's DllMain() function initially calls GetModuleHandleW() with lpModuleName = NULL\r\nwhich retrieves the handle (image base address - eg. 0x00007FF69CD50000) of the calling process\r\n\"usysdiag.exe\" and then it verifies a bytes sequence at a specific offset in the** .text section** of ussysdiag.exe.\r\nFollowing is the byte sequence which is verified:\r\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x10 compares byte: 0x8B\r\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x11 compares byte: 0xC8\r\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x12 compares byte: 0xFF\r\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x13 compares byte: 0x15\r\nhttps://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nPage 2 of 6\n\nThe NailaoLoader in this case checks for the following intructions mov ecx, eax ; call qword ptr (8B C8 FF\r\n15) at the given offset and interestingly the offset into the usysdiag's .text section is basically in the same initial\r\nfunction itself which called the LoadLibraryA() to load \"sensapi.dll\".\r\nIf the byte sequence does not match the DllMain() returns and the NailaoLoader does not execute the malicious\r\ncode. Therefore the following executable \"usysdiag.exe\" is required in order to execute the malicious code of the\r\nNailaoLoader.\r\nFurther if the bytes match then it performs following actions:\r\ncalls the ret_virtual_protect_addr() function which returns the address of VirtualProtect() function by\r\nfirstly allocating a 100 byte buffer and then performing cus_memcpy() to copy the string \"VirtualProtect\"\r\nbyte by byte into the allocated buffer.\r\nhttps://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nPage 3 of 6\n\nThen it calls GetModuleHandleA() where lpModuleName = kernel32 to get the handle to kernel32.dll and then\r\ncalls GetProcAddress() with the handle to kernel32.dll and lpProcName as VirtualProtect to get the address of\r\nVirtualProtect() and returns the address to it.\r\nhttps://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nPage 4 of 6\n\nThen it calls the VirtualProtect() function to change the page protection to PAGE_READWRITE of the\r\nmemory region in usysdiag.exe's .text section which consists of the initial function which called\r\nLoadLibraryA() to to load \"sensapi.dll\"\r\nNOW in order to execute the load_decrypt_exec_locker_func() function which is the main function which\r\nreads the encrypted usysdiag.exe.dat file from the disk, decrypts it using a XOR key and then maps the\r\ndecrypted NailaoLocker binary in memory and transfers the control flow to the binary's entrypoint.\r\nThe NailaoLoader patches the following instructions which are just after the call to LoadLibraryA(\"sensapi.dll\" )\r\nin the initial function that we say was called by usysdiag.exe when executed.\r\nmov rbx, rax\r\ntest rax,rax\r\nPatched to:\r\nmov rax,sensapi.7FFA8D1E1DF0 [address of load_decrypt_exec_locker_func() function]\r\ncall rax\r\nIf we compare both the unpatched and patched versions of the initial function called by usysdiag.exe calling the\r\nLoadLibraryA() to load Sensapi.dll we can clearly see the difference:\r\nDisasembled code of NailaoLoader patching the instructions:\r\nhttps://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nPage 5 of 6\n\nSo now whenever the LoadLibraryA() function trying to load the \"sensapi.dll\" ;) returns the next instruction\r\ncalled would be the patched instructions which will move the address of load_decrypt_exec_locker_func()\r\nfunction into rax and then call rax i.e call the load_decrypt_exec_locker_func() function\r\nThis technique helps in hiding the execution flow of the Loader as the load-decrypt-execute function is not\r\ncalled from the malicious NailaoLoader DLL itself.\r\nThen it again calls VirtualProtect() and sets the page protection of the same memory region of the .text section to\r\nPAGE_EXECUTE_READ (back to the old page protection) and then returns from the DllMain() of the\r\nsensapi.dll.\r\nFurther when the LoadLibraryA(\"sensapi.dll\") function called by usysdiag.exe returns back it then executes the\r\npatched instructions and calls the load_decrypt_exec_locker_func() which then further executes the\r\nNailaoLocker!\r\nmov rax,sensapi.7FFA8D1E1DF0 [addr of load_decrypt_exec_locker_func() function]\r\ncall rax\r\nThis is how the NailaoLoader hides the Execution Flow via Patching Instructions in order to run the load-decrypt-execute function which further executes the NailaoLocker.\r\nCampaign Reference: https://www.orangecyberdefense.com/global/blog/cert-news/meet-nailaolocker-a-ransomware-distributed-in-europe-by-shadowpad-and-plugx-backdoors\r\nSource: https://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nhttps://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow\r\nPage 6 of 6\n\n\"usysdiag.exe\" Following is the and then it verifies byte sequence a bytes sequence which is verified: at a specific offset in the** .text section** of ussysdiag.exe.\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x10 compares byte: 0x8B\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x11 compares byte: 0xC8\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x12 compares byte: 0xFF\nAt \u003cusysdiag_image_base_address\u003e + 0x1008F + 0x13 compares byte: 0x15\n  Page 2 of 6",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://github.com/knight0x07/NailaoLoader-Hiding-Execution-Flow"
	],
	"report_names": [
		"NailaoLoader-Hiding-Execution-Flow"
	],
	"threat_actors": [],
	"ts_created_at": 1775434519,
	"ts_updated_at": 1775826735,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/31709d64827fb968451922a231cf95a05f719b58.pdf",
		"text": "https://archive.orkl.eu/31709d64827fb968451922a231cf95a05f719b58.txt",
		"img": "https://archive.orkl.eu/31709d64827fb968451922a231cf95a05f719b58.jpg"
	}
}