{
	"id": "65b77e8d-c3df-48f9-8dee-83ea51bcf473",
	"created_at": "2026-04-06T00:13:14.921345Z",
	"updated_at": "2026-04-10T13:12:01.923605Z",
	"deleted_at": null,
	"sha1_hash": "1482168c47ff947c5e2526a054e46b55e6a870ed",
	"title": "Applied Emulation - Decrypting Ursnif strings with Unicorn",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 703132,
	"plain_text": "Applied Emulation - Decrypting Ursnif strings with Unicorn\r\nBy map[name:Alessandro Strino]\r\nPublished: 2023-12-20 · Archived: 2026-04-05 13:55:17 UTC\r\nIntroduction\r\nHere we go with another chapter of the Applied Emulation series. In the previous blogpost we have talked about\r\nemulation and more specifically, the usage of Dumpulator to perform a quick triage. However, in this blogpost we\r\nare going to use a completely different approach, taking advantage of static analysis to understand and emulate\r\na string decryption algorithm through Unicorn. As always, I would like to give a practical example on a real\r\ncase scenario. Because of that, I have chosen to analyze an Ursnif sample.\r\nIt’s worth mentioning that this sample is part of weekly challenges related to the Zero2Auto course. However,\r\ninstead of automating the full code extraction, as requested by the challenge, I found even more interesting going\r\ninto unexplored paths applying emulation instead.\r\nUnicorn challenges\r\nUsing pure static analysis came with few limitations, in fact, Unicorn is a CPU emulator framework that does not\r\nhave built in capabilities to invoke syscall directly. Because of that we need to overcome at least a couple of\r\nchallenges before emulating our code:\r\nThe first and probably the most challenging constraint is that we are going to emulate CPU instructions,\r\ninstead of using direct calls to Windows API. Because of that, we need to choose our code carefully.\r\nAfter that we have identified a candidate algorithm, we still need to set up the emulation environment\r\nvariables (e.g., stack memory, registers, sections, start and end code, algorithm parameters, etc…).\r\nSpeaking about “good candidate function”, we could look for a custom and self-contained algorithm. Even if\r\nthose requirements seem to be strict constraints, an experienced reverser knows that most of the malware out there\r\nhave these components as a part of their decryption routine. However, let’s take a step back. Someone may\r\nwonder why we don’t use Dumpulator instead of Unicorn, considering all these limitations. The answer is\r\nstraightforward:\r\nDumpulator may not be usable if we are unable to run the code and set up the user mode space. Nevertheless,\r\nI strongly believe that it is more important to understand where and when a technique should be applied,\r\neven if it adds a few layers of complexity, rather than rushing for the easiest or the first solution that comes to our\r\nmind.\r\nUrsnif decryption routine\r\nhttps://viuleeenz.github.io/posts/2023/12/applied-emulation-decrypting-ursnif-strings-with-unicorn/\r\nPage 1 of 4\n\nDecryption routine is pretty straightforward to locate, it just follows the campaignDate variable that is going to be\r\nused as a decryption key for the .bss segment. Key generation algorithm has been already described here,\r\nhowever, I would like to focus on the decryption routine itself, in order to understand all requirements to emulate\r\nthis code.\r\nFigure 1: Decryption routine explained\r\nAt the first glance we could see that the function is self-contained and most of the operations are simple math\r\n(sub, add) that involves the key and the encrypted data. However, before proceeding it’s important to have a look\r\nat the following instruction:\r\n[ecx] is going to be the place to save the output of each decrypted byte. Nevertheless, ecx is also used as a\r\ncounter to get the next byte from the .bss segment:\r\nBecause of that we need to handle that situation very carefully. In fact, if we do not consider those instructions,\r\nwe are going to have an access memory violation with ecx pointing to an unallocated memory region. In order\r\nto solve this issue, there are two ways:\r\nAllocating memory at 0x000000. In this case, ecx will be used as a counter and as a pointer without any\r\nissue;\r\nOtherwise, we could use a hook on our emulator that is going to analyze each instruction and when we are\r\ndealing with [ecx], we could skip that instruction, redirecting the decrypted output towards another\r\nvariable.\r\nFor this blogpost we are going to use the first solution. Considering this post as an introduction of Unicorn, I don’t\r\nwant to overwhelm readers with a lot of content (setting up the environment will be quite challenging to follow).\r\nIt’s always good to master some basic concept before proceeding towards more advanced techniques\r\nSetting up emulation environment\r\nFirst of all we need to import the Unicorn module and also unicorn constants that we are going to use to interact\r\nwith registers. Then we could use the pefile module to load our bin file and take notes of .bss segment data..\r\nhttps://viuleeenz.github.io/posts/2023/12/applied-emulation-decrypting-ursnif-strings-with-unicorn/\r\nPage 2 of 4\n\nThen, we need to take the whole code that needs to be emulated. Before proceeding, it’s important to keep in mind\r\nthat emulation is quite slow and we need to use only the code needed for our purpose. For instance, prolog and\r\nepilogue instructions are not very useful for us, since we are emulating a single function without knowing any\r\ninformation about previous ESP and EBP values. Because of that we could skip them.\r\nTo get the proper bytes, with IDA, we could highlight the code that is going to be emulated and, using shift+e,\r\nspawn a popup with all selected bytes, ready to be pasted in our code.\r\nFigure 2: Select code to emulate\r\nNow things are starting to heat up, trying to settle our emulation. First of all, we need to instantiate Unicorn, then\r\nwe need to set up stack memory, ESP, data memory (using .bss content) and then fill the code section with our\r\nbytes. To make it easier to read, I have split the whole script in multiple parts, as follow:\r\nThrough this snippet of code, we have prepared everything related to the stack and data section. More precisely,\r\nwith uc.mem_map we have mapped a memory address with its size and protections ( rwx permissions have been\r\nchosen to avoid exceptions).\r\nIt’s worth mentioning that, what really makes those memories different is the ESP register that represents a pointer\r\nonto the stack. However, under the hood, there are no differences between the allocated memories.\r\nIn order to get the key, we have called a gen_key routine (omitted here, to focus on emulation setup) that is going\r\nto return a string containing the decryption key. Then, once we get the key, we have to store it on the stack,\r\nrespecting the little endian format. Along with key parameters we have also settled all other arguments needed for\r\nthe function, such as: encrypted data (edx), counter (ecx) as well as updated the esp according to the instruction\r\n[esp+0xC] that is going to take the key bytes.\r\nAs mentioned above, since that ecx will be either a pointer and a counter, we are going to create a memory space\r\nlocated to 0x00000000. In this case, when we are going to deal with mov [ecx] ,eax instruction, we will avoid any\r\nexception.\r\nTo conclude we need to setup memory for our code. This is a pretty straightforward task if have followed the\r\nprevious steps. Map the code address, fill it with null bytes and then write the bytes stored in the code variable.\r\nNow we have satisfied all setting requirements for our emulation and its time to test it out.\r\nhttps://viuleeenz.github.io/posts/2023/12/applied-emulation-decrypting-ursnif-strings-with-unicorn/\r\nPage 3 of 4\n\nTesting\r\nRunning our emulator against few Ursnif samples reveal that we have correctly settled all the environment\r\nvariables. As you can see from the image below, it has been possible to retrieve all encrypted strings. Some of\r\nthem could be very useful in order to do a step further in our analysis, since that we have references to\r\ninfrastructure, powershell script as well as network requests template.\r\nFigure 3: Decrypted strings\r\nConclusion \u0026 Further Development\r\nEmulation is a powerful tool, like a swiss knife, and it needs to be considered anytime we are dealing with\r\nmalware automation. I hope that some of you had fun reading this post and learnt something new, to the point that\r\nyou can’t wait to apply this technique to your own sample.\r\nI’m still planning to write some structured content about this topic. I don’t have a deadline for that but it is in my\r\nnew year resolution list! In the meantime, keep reversing!\r\nReferences\r\nSample analyzed:\r\nUnpacme\r\nMalwareBazaar\r\nUnicorn:\r\nUnicorn engine\r\nEmulator:\r\nursnif_string_decryptor.py\r\nSource: https://viuleeenz.github.io/posts/2023/12/applied-emulation-decrypting-ursnif-strings-with-unicorn/\r\nhttps://viuleeenz.github.io/posts/2023/12/applied-emulation-decrypting-ursnif-strings-with-unicorn/\r\nPage 4 of 4",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://viuleeenz.github.io/posts/2023/12/applied-emulation-decrypting-ursnif-strings-with-unicorn/"
	],
	"report_names": [
		"applied-emulation-decrypting-ursnif-strings-with-unicorn"
	],
	"threat_actors": [],
	"ts_created_at": 1775434394,
	"ts_updated_at": 1775826721,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/1482168c47ff947c5e2526a054e46b55e6a870ed.pdf",
		"text": "https://archive.orkl.eu/1482168c47ff947c5e2526a054e46b55e6a870ed.txt",
		"img": "https://archive.orkl.eu/1482168c47ff947c5e2526a054e46b55e6a870ed.jpg"
	}
}