{
	"id": "399efc8d-232f-4521-87cb-35199d7eec87",
	"created_at": "2026-04-06T00:19:30.366438Z",
	"updated_at": "2026-04-10T13:11:43.6123Z",
	"deleted_at": null,
	"sha1_hash": "4a15903769a5f28fc3718b08cb4b7c7450626b76",
	"title": "MalwareAnalysisReports/WikiLoader/WikiLoader notepad.md at main · VenzoV/MalwareAnalysisReports",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1612450,
	"plain_text": "MalwareAnalysisReports/WikiLoader/WikiLoader notepad.md at\r\nmain · VenzoV/MalwareAnalysisReports\r\nBy VenzoV\r\nArchived: 2026-04-05 19:26:23 UTC\r\nSample Information\r\nI stumbled upon this sample checking the following post:\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 1 of 22\n\nThe following hash is for the malicious .dll \"MimeTools.dll\"\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 2 of 22\n\nSHA256\r\n67283e154b86612e325030e5a5f7995a6fe552d20655283ea5de8b53ff405f69\r\nFollowing the hashes for the .zip file which contains the .dll.\r\nSHA256\r\nbef04e3b2b81f2dee39c42ab9be781f3db0059ec722aeee3b5434c2e63512a68\r\nZIP file contents and \"notepad\"\r\nThe zip file initially contains files that mimic the notepad++ file structure and files. The end game is for the\r\n\"notepad.exe\" to side load the malicious .dll \"mimetools.dll\"\r\nMalicious zip file contents:\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 3 of 22\n\nLegitimate notepade++.exe folder structure.\r\nThe malicious .dll is located also in the same path notepad++ contains the file. This is of course so that the\r\nsideloading can work. The path for the .dll:\r\nplugins\\mimeTools\\\r\nComparing hashes on VT or any other site like unpac.me, we can observe that they are different and one of them\r\nis also detected by many engines.\r\nStatic Analysis\r\nUsing IDA or any decompiler won't really help initially. The sample uses a lot of control flow obfuscation\r\nessentially changing calls for jmp instructions. This will confuse IDA and we don't get clear disassembly view nor\r\ndecompiler. So, for the moment we will go with dynamic analysis with x64 dbg.\r\nInformation Gathering\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 4 of 22\n\nSome information based on OSINT and other resources just to get ahead and have some hits on what to look for. I\r\nwas able to find only one report from proofpoint with some useful information. Also online sandboxes gives us\r\nsome hints of behaviors to expect.\r\nWe will expect PE parsing to occur:\r\nI also observed from IDA some potential stack strings, I ran FLOSS to try and extract some. Following the results,\r\nwhich we will user to help our analysis. Although it may not be useful in the end.\r\nFLOSS Stack Strings\r\nFLOSS decoded 1 strings\r\n !\"#$%\u0026'()*+,-./0123456789:;\u003c=\u003e?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\nFLOSS extracted 67 stackstrings\r\nnXm21XOk}Z\r\nnXm21XOk}Z\r\nVirtualAlloc\r\nCryptStringToBinaryA\r\nCryptStringToBinaryA\r\nex28mBHjNU7\r\nex28mBHjNU7\r\nCryptStringToBinaryA\r\n8mBHjNU7\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nex28mBHjNU7\r\nLoadLibraryA\r\nZBAA\r\nVirtu\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 5 of 22\n\nVirtualAlloc\r\nnXm21XOk}Z\r\nVirtualAlloc\r\nVirtualAlloc\r\n8mBHjNU7\r\nVirtualFreeA\r\nVirtualFreeA\r\n8mBHjNU7\r\nVirtualFreeA\r\nVirtualAlloc\r\nex28mBHjNU7\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nLoadLibraryA\r\nnXm21XOk}Z\r\nVirtualFreeA\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nLoadLibraryA\r\nCryptStringToBinaryA\r\nLoadLibraryA\r\n8mBHjNU7\r\nVirtu\r\nLoadLibraryA\r\nVirtualFreeA\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nnXm21XOk}Z\r\nVirtualFreeA\r\nVirtu\r\n8mBHjNU7\r\nVirtu\r\nex28mBHjNU7\r\nCryptStringToBinaryA\r\nLoadLibraryA\r\nLoadLibraryA\r\n8mBHjNU7\r\nLoadLibraryA\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\n8mBHjNU7\r\nVirtualAlloc\r\nex28mBHjNU7\r\nCryptStringToBinaryA\r\nex28mBHjNU7\r\nnXm21XOk}Z\r\nHjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nnXm21XOk}Z\r\nCryptStringToBinaryA\r\nVirtualAlloc\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 6 of 22\n\nVirtu\r\nVirtualFreeA\r\nCreateThread\r\nVirtualFreeA\r\nLoadLibraryA\r\nVirtualFreeA\r\nDynamic Analysis\r\nGetting Kernel32.dll \u0026 GetProcAddress\r\nFirst thing the malware gets a handle to the PEB from the TIB, and then sets up relevant offsets to access the\r\nLIST_ENTRY InMemoryOrderModuleList structure. As per microsoft docs it is a double linked list that contains\r\nthe loaded modules for the process. This will be used the malware to go though the dlls loaded. Ntdll.dll is the first\r\n.dll of the process, followed by kernel32.dll.\r\nOffset 0x60 is passed to RAX regiser\r\nRAX is used with gs: special register to obtain the PEB location.\r\nOffset 0x18 is passed to RBX\r\nOffset RBX+RAX is used to access PEB_LDR_DATA struct and saved to RBX\r\nOffset 0x20 is passed to RAX\r\nOffset RBX+RAX now leads to LIST_ENTRY InMemoryOrderModuleList\r\nThe first of the list InMemoryOrderModuleList is now saved in r12 register, this will be used to check for\r\nend of list. Since it is a doubled link list, if the code goes through all the modules, it will eventually end up\r\nat the start.\r\nLastly it gets the 0x40 offset to add to the InMemoryOrderModuleList. This is for the Dllbase address.\r\nThe objective of this first loop is to find kernel32.dll. By walking the PEB, we expect to find the following\r\nmodule order:\r\nExecuting Process\r\nntdll.dll\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 7 of 22\n\nkernel32.dll\r\nSo, the code will have to go through 3 forward pointers. The way the malware checks if it has the right dll\r\nreference is through series of jumps. A certain offset is added to the Dllbase address, this will be a letter in the\r\nname, then it is compared to a letter. The malware will check these letters for the kernel32.dll\r\n4b-\u003e'K'\r\n45-\u003e'E'\r\n4c-\u003e'L'\r\n32-\u003e'2'\r\n4c-\u003e'L'\r\n4c-\u003e'L'\r\nNow that the malware has the base address to Kernel32.dll, it will go through all the functions to search for\r\nGetProcAddress. It parses the PE header of kernel32.dll to reach the export table.\r\n1. Offset 3C to reach PE header pointer with value F8.\r\n2. Offset 88 to reach the export table, this value is calculated with: RVA of export table - F8. In our case\r\n0x180-F8=0x88\r\n3. Offset 0x20 to finally reach the exported functions names table.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 8 of 22\n\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 9 of 22\n\nTo check it performs some calculations to build a hex value which correspond to the first 8 bytes of\r\n\"GetProcAddress\" keeping in mind endianness. It then loops through all the functions in kernel32.dll to perform\r\ncompare.\r\n1. Calculates the first 8 bytes in hex for GetProcAddress string (GetProcA)\r\n2. Loops through all the functions and compares the 8 bytes to r9\r\nNext, the malware need to retrieve the function address. To to this it will make use of the ordinal name stored in\r\nRCX. The ordinal will be searched withing the AddressOfFunctions struct to get the value.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 10 of 22\n\nSo all this PE parsing is to retrieve finally GetProcAddress and use this API to load others.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 11 of 22\n\nString builder and Control flow manipulation\r\nNext the malware builds the string \"VirtualAlloc\" and proceeds to user GetProcAddress to fetch the function from\r\nkernel32.dll. This is done through jmp instructions, where the function needed is loaded into the appropriate\r\nregistry.\r\nThe string is built by using the lower registry al which contains 1 byte. It simply adds two hex values to generate a\r\nchar. At the end the null byte is used as terminator.\r\nImage\r\nThe block of code after this is responsible for the following:\r\nSets up the arguments for GetProcAddress in RCX and RDX in this case the module kernel32.dll and API\r\nto fetch VirtualAlloc\r\nReturn address following the jmp is saved to rax which is pushed to the stack, this makes it so that when\r\nthe jmp returns the return address will be on the stack. This points simply to the code following the jmp rax\r\ninstruction. This is repeated throughout the next jmp instructions to other API.\r\nFinally it jmp to the GetProcAddress API with the args mentioned above returning the address to\r\nVirutalAlloc from kernel32.dll\r\nNext thing of course, the VirtualAlloc is called to allocate some memory space with\r\nPAGE_EXECUTE_READWRITE.\r\nAllocated memory buffer:\r\nImage\r\nThrought the entire sample, all API strings to be fetched or DLLs to be loaded are built in a similar fashion as\r\nshown above. All calls to any API are changed with JMP instructions where the return value is pushed onto the\r\nstack before execution, this is so following the ret from the JMP, the code can continue.\r\nThread manipulation\r\nUsing the same string creation as for VirtualAlloc() and same usage of GetProcAddress(), the malware proceeds to\r\ncall GetCurrentThreadID() API.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 12 of 22\n\nFollowing OpenThread() call.\r\nFinally a new thread is created by calling CreateThread(). The arguments passed:\r\nPointer to start address in R8\r\nPointer to parameter passed to thread in R9, in this case seems to be the ID handle to that thread.\r\nImage\r\nThe malware will then follow by suspending the main thread and leave execution to the newly created one. For\r\ndebugging purposes, the debugger was acting unpredictably maybe because of race conditions and threads\r\ncolliding. Once the code reached the new thread start, I manually killed the main thread this seems to have worked\r\nbut not 100% sure if it is just a fluke.\r\nDecrypting Shellcode\r\nShellcode is decrypted from the certificate.pem file. Malware uses CreateFileA() to open the file and then\r\nReadFile() to read the contents. It is then decrypted using the key, with AES in CBC mode.\r\nkey: HjGwPX65nXm21XOkX4nMqex28mBHjNU7\r\nThe decryption functions are not custome, and uses the bcrypt library which is loaded after file is read.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 13 of 22\n\nFor ReadFile the two arguments correspond to:\r\nFile Handle\r\nMemory buffer to store data that is read.\r\nNow to proceed with decryption part some setup is necessary.\r\nCrypt32.dll is loaded\r\nCryptStringToBinaryA is called on the data of the certificate.pem file saved in the memory.\r\nCryptStringToBinaryA is used to convert a formatted string into an array of bytes. The flags supplied is in R8 and\r\nis 0x1 which means format of the string to be converted is base64. R9 contains the buffer that will receive the\r\noutput of the function.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 14 of 22\n\nThe result is the following:\r\nNext setup phase, the malware builds string that will be used later.\r\nObjectLength\r\nBlockLength\r\nChainingMode\r\nChainingModeCBC\r\nbcrypt.dll module is loaded and proceeds to get addresses for these functions and the jmps to them.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 15 of 22\n\nBCryptOpenAlgorithmProvider\r\nBCryptGetProperty\r\nBCryptSetProperty\r\nBCryptGenerateSymmetricKey\r\nBCryptDecrypt\r\nBCryptDestroyKey\r\nBCryptCloseAlgorithmProvider\r\nFirst call of the chain is of course BCryptOpenAlgorithmProvider with \"AES\" supplied as argument. All these\r\nfunctions are from Microsoft DLL so can be searched on official docs for more information.\r\nSecond call makes use of the initial strings saved at the first setup stage. It calls on BCryptGetProperty for:\r\nObjectLength\r\nBlockLength -\u003e blocks of 4 bytes\r\nChainingMode\r\nChainingModeCBC\r\nAfter the first one, some memory is allocated. After the second, lstrlen is called to get size of the payload that\r\nneeds to be decrypted but reads only until first null byte so 17 bytes. Also, VirtuaAlloc() is called again and the\r\nfirst 10 bytes of the initial payload are written to it.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 16 of 22\n\nlstrlenw() is called to count \"ChainingModeCBC\" string. This is necessary for the next call, which is\r\nBCryptSetProperty(). The arguements relevant are:\r\nszProperty -\u003e \"A pointer to a null-terminated Unicode string that contains the name of the property to set.\"\r\nIn this case \"ChainingMode\"\r\npbInput -\u003e \"The address of a buffer that contains the new property value. The cbInput parameter contains\r\nthe size of this buffer.\" In our case this would be \"ChainingModeCBC\" with size 15 calculated in the step\r\nbefore.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 17 of 22\n\nNext call is to get size of the key:\r\nsymmetric key is generated with BCryptGenerateSymmetricKey(). The fifth arguement is the actual key and is\r\npassed as reference onto the stack.\r\nNow, the interesting part BCryptDecrypt() is called. This is responsible for decrypting the shellcode. Let's look at\r\nthe arguments in the debugger. We can use the Microsoft documentation.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 18 of 22\n\nFirst 4 args are passed in: RDX,RCX,R8,R9 The rest are passed as reference onto the stack. The arguments we are\r\ninterested in are:\r\npbInput The address of a buffer that contains the ciphertext to be decrypted. Second argument.\r\npbIV The address of a buffer that contains the initialization vector (IV) to use during decryption. Fifth\r\nargument.\r\npbOutput The address of a buffer to receive the plaintext produced by this function. Seventh argument.\r\nSecond Argument (RCX) contains the certificate.pem data that was read into memory before.\r\nThe Fifth argument has the IV. This is on the stack, and it is 10 bytes according the the sixth argument.\r\nThe pbOutput is null in this case, so no output found.\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 19 of 22\n\nFollowing this VirtualAlloc is used to allocate a memory buffer and BCryptDecrypt() is called once again, this\r\ntime the pbOutput is a pointer to the newly allocated memory. In this case the output can be observed, and we can\r\nsee the decrypted shellcode.\r\nTo make memory section which will contain the payload executable, the malware calls on VirtualProtect() onto\r\nthat section. The call is used to give the section of memory 0x20-\u003e PAGE_EXECUTE_READ. Finally, the\r\nmalware can jump to the memory location and resume execution!\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 20 of 22\n\nWith this we have finally made it to the first decrypted shellcode!\r\nSince this was pretty long I will continue with a part 2 soon!\r\nReferences\r\nhttps://bazaar.abuse.ch/sample/bef04e3b2b81f2dee39c42ab9be781f3db0059ec722aeee3b5434c2e63512a68/\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 21 of 22\n\nhttps://www.unpac.me/results/612d6d2c-c45d-47ba-a2bb-a218ec753d3f\r\nhttps://twitter.com/Cryptolaemus1/status/1747394506331160736\r\nhttps://www.proofpoint.com/us/blog/threat-insight/out-sandbox-wikiloader-digs-sophisticated-evasion\r\nhttps://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm\r\nhttps://mohamed-fakroud.gitbook.io/red-teamings-dojo/shellcoding/leveraging-from-pe-parsing-technique-to-write-x86-shellcode\r\nSource: https://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nhttps://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md\r\nPage 22 of 22",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://github.com/VenzoV/MalwareAnalysisReports/blob/main/WikiLoader/WikiLoader%20notepad.md"
	],
	"report_names": [
		"WikiLoader%20notepad.md"
	],
	"threat_actors": [],
	"ts_created_at": 1775434770,
	"ts_updated_at": 1775826703,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/4a15903769a5f28fc3718b08cb4b7c7450626b76.pdf",
		"text": "https://archive.orkl.eu/4a15903769a5f28fc3718b08cb4b7c7450626b76.txt",
		"img": "https://archive.orkl.eu/4a15903769a5f28fc3718b08cb4b7c7450626b76.jpg"
	}
}