{
	"id": "cb5eb43e-c649-4cf5-b2d4-4c73f95545ad",
	"created_at": "2026-04-06T02:11:46.448929Z",
	"updated_at": "2026-04-10T13:12:21.641336Z",
	"deleted_at": null,
	"sha1_hash": "ce165f616826e23aadc6cee93ee27dc9c4fd3260",
	"title": "FinSpy VM Unpacking Tutorial Part 3: Devirtualization — Möbius Strip Reverse Engineering",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 44036,
	"plain_text": "FinSpy VM Unpacking Tutorial Part 3: Devirtualization — Möbius\r\nStrip Reverse Engineering\r\nBy Rolf Rolles\r\nPublished: 2018-02-21 · Archived: 2026-04-06 01:29:35 UTC\r\n1. Overview\r\nThis is the third and final part in my series on statically unpacking the FinSpy VM. After having deobfuscated the\r\nx86 implementation of FinSpy in part one and after having analyzed the VM and written a disassembler for the\r\nbytecode format for the particular sample in question in part two, we are now tasked with reconstructing x86 code\r\ncorresponding to the pre-virtualized code. As before, the files are in the GitHub repository here. Of note:\r\nThe IDB with the devirtualized code added, and mostly analyzed: here\r\nThe devirtualization program: here\r\nFinSpy VM bytecode disassembly listings with various simplifications: here\r\nBinary files for the devirtualized code: here\r\nBecause the FinSpy VM is a weak and ineffective software protection, unpacking it is not very difficult. Over half\r\nof the VM instructions in the bytecode program for the sample we're analyzing already contain raw x86 machine\r\ncode. It turns out that FinSpy only truly virtualizes a handful of x86 instruction types, and that simple pattern-matching is effective in reconstructing the original code. It's perhaps the easiest software protection I know of\r\nworthy of being called a \"virtualization obfuscator\" -- and that makes it good practice for the difficult ones. \r\nAs with part one, I am attempting to write this in a walk-through tutorial style. We start from first principles in\r\ninspecting the FinSpy VM bytecode disassembly listing, and show all of the steps (along with the code I wrote)\r\nalong the way. Hopefully I've managed to capture the process of observation, evaluation of design considerations,\r\nactions taken including mis-steps, and above all, the iterative, trial-and-error nature of the affair.\r\n2. Organizational Overview\r\nThis part ended up being longer and more difficult to write than expected. In fact, writing this document as a\r\ntutorial was considerably more difficult than doing the work in the first place. I hope the effort pays off in terms of\r\neducational value. Because I personally don't enjoy huge documents whose sections aren't well-segregated from\r\none another, I have decided to split the devirtualization process, and also this document, into a number of\r\n\"phases\", each in its own individual blog entry. All entries are currently online and available for reading; links to\r\nthem are immediately below. Each individual part links to the code and binary artifacts used in that phase.\r\nHere are the contents of each phase in brief, along with links to the individual parts:\r\n1. Part #3, Phase #1: analyzing and deobfuscating FinSpy VM bytecode programs. In part two, we analyzed\r\nthe FinSpy VM and its instruction set, and ultimately wrote a disassembler for FinSpy VM programs. This\r\nhttps://www.msreverseengineering.com/blog/2018/2/21/finspy-vm-unpacking-tutorial-part-3-devirtualization\r\nPage 1 of 2\n\nphase begins by reviewing the FinSpy VM instruction set. Next, we work from first principles in analyzing\r\nthe FinSpy VM bytecode program. First we add a useful feature to our FinSpy VM disassembler. Next, we\r\nanalyze a set of VM instructions -- group #2, in the parlance -- used by the VM bytecode program for\r\nobfuscation purposes. We determine several patterns, and write code to detect and simplify instances of\r\nthose patterns within FinSpy VM bytecode programs. After one more small tweak to the disassembler, the\r\nFinSpy VM bytecode program is now ready to be devirtualized back into x86 machine code.\r\n2. Part #3, Phase #2: initial devirtualization. The previous phase left the FinSpy VM bytecode program in a\r\nsuitable form for devirtualization. This phase begins by discussing the theory behind devirtualizing the\r\nindividual FinSpy VM instructions. Next, we write a tool to devirtualize FinSpy VM bytecode programs --\r\nthis tool takes as input the disassembly of a FinSpy VM program, and produces as output a blob of x86\r\nmachine code that no longer relies upon the VM. After producing output, we then inspect our initial results\r\nin devirtualization to determine a few deficiencies and remaining tasks before our devirtualization can be\r\nconsidered complete. This phase ends by fixing one of the issues revealed by inspecting the devirtualized\r\noutput.\r\n3. Part #3, Phase #3: devirtualizing function calls. The devirtualization generated by the previous stage still\r\nhas a few deficiencies that need to be fixed before the output is fully usable. As it turns out, all of these\r\ndeficiencies relate to how the FinSpy VM deals with function calls and function pointers. We discuss the\r\nsource of the difficulties and determine what needs to be done to fix the issues. Then we discuss a few\r\nstrategies that we might employ to solve the problems, including a somewhat sophisticated one involving\r\nx86 emulation. We settle for something less than that. \r\n4. Part #3, Phase #4: second devirtualization. The previous phase collected information about virtualized\r\nfunctions in preparation for a second attempt at devirtualization. This phase incorporates that information\r\ninto the devirtualization process, and then re-inspects the devirtualized code for defects. After fixing one\r\nmore remaining major issue and two small ones, we now have our complete devirtualized blob of x86\r\nmachine code for the FinSpy VM program in our sample. We trick IDA into loading our devirtualized code,\r\nand now our devirtualization journey is complete. \r\nAfter phase #4 was complete, I next completely analyzed the devirtualized FinSpy dropper binary. I may publish\r\nsome techniques I saw that weren't widely documented, and I may publish a complete analysis of the dropper.\r\nHowever, that writing remains to be done, and is not part of this blog series on devirtualization.\r\nEnjoy.\r\nSource: https://www.msreverseengineering.com/blog/2018/2/21/finspy-vm-unpacking-tutorial-part-3-devirtualization\r\nhttps://www.msreverseengineering.com/blog/2018/2/21/finspy-vm-unpacking-tutorial-part-3-devirtualization\r\nPage 2 of 2",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://www.msreverseengineering.com/blog/2018/2/21/finspy-vm-unpacking-tutorial-part-3-devirtualization"
	],
	"report_names": [
		"finspy-vm-unpacking-tutorial-part-3-devirtualization"
	],
	"threat_actors": [],
	"ts_created_at": 1775441506,
	"ts_updated_at": 1775826741,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/ce165f616826e23aadc6cee93ee27dc9c4fd3260.pdf",
		"text": "https://archive.orkl.eu/ce165f616826e23aadc6cee93ee27dc9c4fd3260.txt",
		"img": "https://archive.orkl.eu/ce165f616826e23aadc6cee93ee27dc9c4fd3260.jpg"
	}
}