{
	"id": "f64ae8fe-c664-49da-a662-89734e9f74b6",
	"created_at": "2026-04-06T00:22:22.787818Z",
	"updated_at": "2026-04-10T13:13:09.913207Z",
	"deleted_at": null,
	"sha1_hash": "d33f0a0e6e99b19f5df0f59fa0fcf7b6af858b60",
	"title": "Agent Tesla Analysis [Part 2: Deobfuscation]",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1155450,
	"plain_text": "Agent Tesla Analysis [Part 2: Deobfuscation]\r\nPublished: 2024-03-01 · Archived: 2026-04-05 17:45:23 UTC\r\nIntroduction\r\nIn the previous post we successfully unpacked Agent Tesla. We left off on a bit of a cliffhanger though, because after\r\nopening it in dnSpy it was apparent that it had control flow flattening applied. At first glance it doesn’t look too unreadable:\r\nFigure 1\r\nBut if we continue looking around other functions, we can see it gets ridiculous. Take a look at this one zg5QIGkJ for\r\nexample:\r\nFigure 2\r\nThis took me 20 seconds to scroll from the top of the function to the bottom because it contains a whopping 800 lines of\r\ncode!\r\nHow many lines of code do you think the function had originally before the flattening was applied? I’ll give you a little\r\nsneak peak of what our finished product will look like:\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 1 of 13\n\nFigure 3\r\nThat’s right, only 200 lines of code. The flattening made the code roughly 4x larger than it was before and more or less\r\ncompletely eliminated any readbility. Unless you want to spend 500 years debugging such vomit, we’re going need to find a\r\nway to deobfuscate this.\r\nFailed de4dot attempt\r\nFirst, let’s try throwing Agent Tesla into de4dot and see if it removes the control flow like it did for the assemblies\r\nmentioned in the first post.\r\n Figure 4\r\nFigure 5\r\nAs shown above, de4dot as it comes by default is completely powerless against this perform of control flow flattening. No\r\nchanges were made at all to the code. That means one thing: we are going to have to write something ourselves.\r\nAnalyzing the flattening\r\nWe first need to analyze the flattening to find a consistent pattern to detect. In order to do that, we will need to look at a\r\ncontrol flow graph of the IL code blocks directly. We will use my preferred tool IDA Pro to look at the control flow graph.\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 2 of 13\n\nThere is a dnSpy plugin for generating a control flow graph, but I personally prefer how IDA’s graph looks. We’re going to\r\nstart by navigating to the main function 8YpydOv4 in IDA.\r\nFigure 6\r\nLet’s break down what is happening here. In the first block, the integer 0 is pushed onto the stack and then stored in the\r\nlocal variable index 0. Then, we have an unconditional jump to block loc_FF which then begins a series of checks on the\r\ndispatcher variable. When a check passes successfully, it executes the original code and then controls the flow by setting the\r\nvariable to the next block to be executed. The number 5 here is the last check that is executed. We will refer to this final\r\ncheck as the loop condition because if this check fails, then it returns to the beginning of the loop. Otherwise, the\r\nfunction ends. It’s also worth noting that case 0 does nothing except set the next case to 1 , i.e there is no actual code here\r\nbeing executed.\r\nOur goal here is to connect each block to the next one in the flow, bypassing the parts that set the dispatcher variable. I feel\r\nlike I should clarify the terminology I’m going to be using. When I say ‘setter’ I mean groups of instructions like this which\r\nset the dispatcher variable:\r\n Figure 7\r\nWhen I refer to ‘cases’ I am talking about blocks like this which check the dispatcher variable:\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 3 of 13\n\nFigure 8\r\nLastly, before I conclude this section I think it is important to manually unflatten the function in something like notepad just\r\nso we have an idea what kind of output to expect. This was a tip that was mentioned by Georgy Kucherin in his presentation\r\nabout unflattening DoubleZero (which was way more complex and is totally worth a read!) and I found it to be very helpful.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\npublic static void 8YpydOv4()\r\n{\r\n ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11\r\n ServicePointManager.ServerCertificateValidationCallback = (RemoteCertificateValidationCallback)Delegate.Combine(ServicePo\r\n oBH.Z29wZduHO();\r\n Application.Run();\r\n}\r\nCreating the de4dot plugin\r\nThe first thing we will do is clone the de4dot repo. I am personally using this one here because it already has support for a\r\ncommonly used obfuscator called ConfuserEx, but it doesn’t matter which one you decide to use.\r\nLet’s open it in Visual Studio. The first step is to create the obfuscator by doing the following steps. First, creating a new\r\nfolder in this directory:\r\n Figure 9\r\nEvery deobfuscator in de4dot needs to have a DeobfuscatorInfo class. Here is what yours should look like:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n/*\r\n Copyright (C) 2011-2015 de4dot@gmail.com\r\n This file is part of de4dot.\r\n de4dot is free software: you can redistribute it and/or modify\r\n it under the terms of the GNU General Public License as published by\r\n the Free Software Foundation, either version 3 of the License, or\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 4 of 13\n\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59\n60\n61\n62\n63\n64\n65\n66\n67\n68\n69\n70\n71\n72\n73\n74\n75\n76\n (at your option) any later version.\n de4dot is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU General Public License for more details.\n You should have received a copy of the GNU General Public License\n along with de4dot. If not, see .\n*/\nusing System.Collections.Generic;\nusing de4dot.blocks.cflow;\nnamespace de4dot.code.deobfuscators.AgentTesla\n{\npublic class DeobfuscatorInfo : DeobfuscatorInfoBase\n{\npublic const string THE_NAME = \"AgentTesla Obfuscator\"; // Obfuscator name\npublic const string THE_TYPE = \"agt\"; // Obfuscator short name\nconst string DEFAULT_REGEX = @\"(^\u003c.*)|(^[a-zA-Z_\u003c{$][a-zA-Z_0-9\u003c\u003e{}$.`-]*$)\";\npublic DeobfuscatorInfo()\n: base(DEFAULT_REGEX) {\n}\npublic override string Name =\u003e THE_NAME;\npublic override string Type =\u003e THE_TYPE;\npublic override IDeobfuscator CreateDeobfuscator() =\u003e\nnew Deobfuscator(new Deobfuscator.Options {\nRenameResourcesInCode = false,\nValidNameRegex = validNameRegex.Get(),\n});\n}\npublic class Deobfuscator : DeobfuscatorBase\n{\ninternal class Options : OptionsBase\n{\n}\npublic override string Type =\u003e DeobfuscatorInfo.THE_TYPE;\npublic override string TypeLong =\u003e DeobfuscatorInfo.THE_NAME;\npublic override string Name =\u003e DeobfuscatorInfo.THE_NAME;\npublic override IEnumerable BlocksDeobfuscators\n{\nget\n{\nvar list = new List();\nreturn list;\n}\n}\ninternal Deobfuscator(Options options)\n: base(options) {\n}\nprotected override void ScanForObfuscator() {\n}\nprotected override int DetectInternal() {\nreturn 0;\n}\npublic override IEnumerable GetStringDecrypterMethods() =\u003e new List();\n}\n}\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\nPage 5 of 13\n\nI’ll explain a bit about this class the variable THE_NAME is the name that will show up in de4dot’s console output.\r\nTHE_TYPE is the short name for the deobfuscator. This one is particular important because we are going to manually specify\r\nthe de4dot deobfuscator in the command line arguments to use against Agent Tesla. You can either use same values I used or\r\nyour own, it’s up to you.\r\nRegarding this:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\npublic override IEnumerable\u003cIBlocksDeobfuscator\u003e BlocksDeobfuscators\r\n{\r\n get\r\n {\r\n var list = new List\u003cIBlocksDeobfuscator\u003e();\r\n return list;\r\n }\r\n}\r\nIt returns a list of IBlocksDeobfuscator ’s. Each IBlocksDeobfuscator is then eventually called on the basic blocks of\r\nevery function. Right now the list is empty, but we will be adding our own IBlocksDeobfuscator next.\r\nAt this point, your project structure should look like this:\r\n Figure 10\r\nWe also need to add the deobfuscator to the Program.cs file in de4dot.cui so it shows up in the actual application when\r\nit’s launched\r\nFigure 11\r\nNow, we are going to create a new class that implements the IBlocksDeobfuscator interface. I’m going to call it\r\nUnflattener .\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\nusing de4dot.blocks;\r\nusing de4dot.blocks.cflow;\r\nusing dnlib.DotNet;\r\nusing dnlib.DotNet.Emit;\r\nnamespace de4dot.code.deobfuscators.AgentTesla\r\n{\r\npublic class Unflattener : IBlocksDeobfuscator\r\n{\r\npublic bool ExecuteIfNotModified\r\n{\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 6 of 13\n\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\nget { return false; }\r\n}\r\npublic Deobfuscator Deobfuscator;\r\npublic Unflattener(Deobfuscator deobfuscator)\r\n{\r\nDeobfuscator = deobfuscator;\r\n}\r\npublic void DeobfuscateBegin(Blocks blocks)\r\n{\r\n}\r\npublic bool Deobfuscate(List\u003cBlock\u003e methodBlocks)\r\n{\r\n \r\n}\r\n}\r\n}\r\nThis is what the default is for this class. Make sure to go back and add the class to the list in the Deobfuscator.cs file like\r\nso:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\npublic override IEnumerable\u003cIBlocksDeobfuscator\u003e BlocksDeobfuscators\r\n{\r\n get\r\n {\r\n var list = new List\u003cIBlocksDeobfuscator\u003e();\r\n list.Add(new Unflattener(this));\r\n return list;\r\n }\r\n}\r\nWhat we need to do is implement the Deobfuscate() method. This method is going to get called on each method in the\r\ntarget binary. That list that’s being passed in is all the basic blocks of the method. We want to begin deobfuscation starting\r\nwith the first block of each method.\r\n Figure 12\r\nEach method begins with ldc.i4 and stloc . We can use that as a signature. However, I’m going to make new class\r\ncalled UnflattenerHelper to do the actual unflattening part, since I’d like to separate the logic.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\nusing de4dot.blocks;\r\nusing dnlib.DotNet.Emit;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nnamespace de4dot.code.deobfuscators.AgentTesla\r\n{\r\n public class UnflattenerHelper\r\n {\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 7 of 13\n\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n public UnflattenerHelper(Block block)\r\n {\r\n \r\n }\r\n }\r\n}\r\nGreat. I’m going to now edit our IBlocksDeobfuscator class to call this helper class passing in the first block of the\r\nmethod like so:\r\n1\r\n2\r\n3\r\n4\r\npublic bool Deobfuscate(List\u003cBlock\u003e methodBlocks)\r\n{\r\n UnflattenerHelper unflattenerHelper = new UnflattenerHelper(methodBlocks[0]);\r\n}\r\nNow, our unflattener helper should perform a check to make sure the first block matches the pattern we described earlier.\r\nThis is what I came up with:\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\npublic UnflattenerHelper(Block block)\r\n{\r\n if (block.Instructions.Count \u003c 2\r\n || block.Instructions[0].OpCode.Code != Code.Ldc_I4\r\n || block.Instructions[1].OpCode.Code != Code.Stloc)\r\n return;\r\n}\r\nThis should filter out any problematic functions.\r\nNext, we should go and save some of the variables we described in our plan. In de4dot, the Fallthrough member of a\r\nBlock corresponds to either an unconditional jump or the false condition of an if statement. The Targets member\r\ncorresponds to the true condition of an if statement. Finally, the Sources list contains any block that jumps to the block.\r\nUsing this knowledge, we will save the value of the first case that gets executed as well as create a global for the current\r\nblock (start block). Finally, we will store the loop condition. To do this, we will first get the fallthrough block of the start\r\nblock. We will then extract the second item in the sources list since the first item will be the start block. I’ve added some\r\nchecks to ensure that the start block exists in addition to making sure it has the expected count of sources.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\nprivate Block _startBlock;\r\nprivate Block _loopCondition;\r\nprivate int _initialCase;\r\npublic UnflattenerHelper(Block block)\r\n{\r\n if (block.Instructions.Count \u003c 2\r\n || block.Instructions[0].OpCode.Code != Code.Ldc_I4\r\n || block.Instructions[1].OpCode.Code != Code.Stloc)\r\n return;\r\n _initialCase = (int)block.Instructions[0].Operand;\r\n _startBlock = block;\r\n if (_startBlock.FallThrough == null\r\n || _startBlock.FallThrough.Sources == null\r\n || _startBlock.FallThrough.Sources.Count \u003c 2)\r\n return;\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 8 of 13\n\n21\r\n22\r\n _loopCondition = _startBlock.FallThrough.Sources[1];\r\n}\r\nNow that we’ve done all this, it’s time to explore the control flow graph and gather all the cases and setters. I will create a\r\nfunction ExploreControlFlow() which will iterate the entire control flow graph by checking each block’s Fallthrough\r\nand Target members and recursing through them.\r\nSomething very important here is the fact that I am keeping track of the visited blocks. Why? Well what happens if the\r\nmethod we are analyzing contains a loop? If we don’t filter blocks we’ve visited before, our code will enter an infinite\r\nrecursion when we’re exploring the blocks and ultimately cause a stack overflow.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\nHashSet\u003cBlock\u003e visitedBlocks = new HashSet\u003cBlock\u003e();\r\nvoid ExploreControlFlow(Block block)\r\n{\r\n if (visitedBlocks.Contains(block))\r\n return;\r\n visitedBlocks.Add(block);\r\n if (block.FallThrough != null)\r\n {\r\n ExploreControlFlow(block.FallThrough);\r\n }\r\n if (block.Targets != null)\r\n {\r\n foreach (Block targetBlock in block.Targets)\r\n ExploreControlFlow(targetBlock);\r\n }\r\n}\r\nSo now we have our code to explore the control flow. Next, we need to actually gather the relevant data. If you remember\r\nfrom earlier, we want to store all setters and cases . I’ve created two functions to check for either one.\r\nIsCaseStartBlock will check if the block is the beginning of a case by looking for the four instructions which load the\r\nlocal dispatcher variable and compare it against a hardcoded value, branching if they are not equal. If it does not match, it\r\nreturns -1 . Otherwise it returns the extracted case number\r\nIsCaseEndBlock will check if the block is the end of a case , i.e the part that sets the next case by modifying the local\r\ndispatcher variable. We look for only two instructions this time, the loading and storing of the next state to the dispatcher.\r\nNotice how in both this function and the previous one that I am making sure the operand is the same as the start block’s. The\r\nreason for this is to avoid any false positives by ensuring the local variable that is getting modified is the dispatcher variable\r\nfrom the original block. If it does not match, it returns -1 . Otherwise it returns the extracted case number\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\nint IsCaseEndBlock(Block block)\r\n{\r\n for (int i = 0; i \u003c block.Instructions.Count; i++)\r\n {\r\n if (block.Instructions[i].OpCode.Code == Code.Ldc_I4\r\n \u0026\u0026 block.Instructions[i + 1].OpCode.Code == Code.Stloc\r\n \u0026\u0026 block.Instructions[i + 1].Operand == _startBlock.Instructions[1].Operand)\r\n {\r\n return (int)block.Instructions[i].Operand;\r\n }\r\n }\r\n return -1;\r\n}\r\nint IsCaseStartBlock(Block block)\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 9 of 13\n\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n{\r\n for (int i = 0; i + 3 \u003c= block.Instructions.Count; i++)\r\n {\r\n if (block.Instructions[i].OpCode.Code == Code.Ldloc\r\n \u0026\u0026 block.Instructions[i].Operand == _startBlock.Instructions[1].Operand\r\n \u0026\u0026 block.Instructions[i + 1].OpCode.Code == Code.Ldc_I4\r\n \u0026\u0026 block.Instructions[i + 2].OpCode.Code == Code.Ceq\r\n \u0026\u0026 block.Instructions[i + 3].OpCode.Code == Code.Brfalse)\r\n {\r\n return (int)block.Instructions[i + 1].Operand;\r\n }\r\n }\r\n return -1;\r\n}\r\nNow, we will update our ExploreControlFlow to save all the cases and setters that we logged. To do, I’ve created\r\ntwo dictionaries that each store the dispatcher number and the matching case or setter block. Keep in mind that when we\r\nsave the case we are NOT including the case block itself, but the block it connects/falls through to.\r\nFigure 13\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\nDictionary\u003cint, Block\u003e _casesDict = new Dictionary\u003cint, Block\u003e();\r\nDictionary\u003cint, Block\u003e _settersDict = new Dictionary\u003cint, Block\u003e();\r\nvoid ExploreControlFlow(Block block)\r\n{\r\n if (visitedBlocks.Contains(block))\r\n return;\r\n visitedBlocks.Add(block);\r\n int StartBlockNum = IsCaseStartBlock(block);\r\n if (StartBlockNum != -1)\r\n {\r\n if(!_casesDict.ContainsKey(StartBlockNum))\r\n _casesDict.Add(StartBlockNum, block.FallThrough);\r\n }\r\n int nextCase = IsCaseEndBlock(block);\r\n if (nextCase != -1)\r\n {\r\n if (!_settersDict.ContainsKey(nextCase))\r\n _settersDict.Add(nextCase, block);\r\n }\r\n if (block.FallThrough != null)\r\n {\r\n ExploreControlFlow(block.FallThrough);\r\n }\r\n if (block.Targets != null)\r\n {\r\n foreach (Block targetBlock in block.Targets)\r\n ExploreControlFlow(targetBlock);\r\n }\r\n}\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 10 of 13\n\nAfter we’ve extracted all the data we need, it’s time to perform the unflattening procedure. We will make a function called\r\nUnflatten which returns a boolean. The reason it will return a boolean is because the way de4dot works is that it will\r\ncontinuously call the Deobfuscate function in the IBlocksDeobfuscator class we defined until it returns false . Why?\r\nWell, de4dot has built-in optimizers which will remove dead code amongst other things. So, we return true because\r\nmodifications were made. If there were no modifications made for any reason, we return false. If you want to see more, take\r\na look at the class BlocksCflowDeobfuscator.cs :\r\nFigure 14\r\nThe first thing we do is connect the starting block to the first case block with an unconditional jump (SetNewFallThrough).\r\nThen, we loop through all the setters and check if there is a corresponding case block for the setter. If so, we connect the\r\nblock. I’ve also implemented a function called CleanBlock() that will remove the leftover setter instructions from the\r\nblock.\r\nFigure 15\r\nWhen we are done unflattening, we also clean the same instructions in the start block.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\npublic bool Unflatten()\r\n{\r\n if (_casesDict.Count == 0)\r\n return false;\r\n Block firstCase = _casesDict[_initialCase];\r\n _startBlock.SetNewFallThrough(firstCase);\r\n foreach (var setter in _settersDict)\r\n {\r\n if (!_casesDict.ContainsKey(setter.Key))\r\n {\r\n Console.WriteLine(\"[!] Could not find next case for block in list!\");\r\n throw new Exception();\r\n }\r\n else\r\n {\r\n // Remove the code which sets the next case;\r\n CleanBlock(setter.Value);\r\n setter.Value.SetNewFallThrough(_casesDict[setter.Key]);\r\n }\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 11 of 13\n\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43\r\n44\r\n }\r\n _startBlock.Instructions[0] = new Instr(OpCodes.Nop.ToInstruction());\r\n _startBlock.Instructions[1] = new Instr(OpCodes.Nop.ToInstruction());\r\n return true;\r\n}\r\nvoid CleanBlock(Block block)\r\n{\r\n for (int i = 0; i \u003c block.Instructions.Count; i++)\r\n {\r\n if (block.Instructions[i].OpCode.Code == Code.Ldc_I4\r\n \u0026\u0026 block.Instructions[i + 1].OpCode.Code == Code.Stloc\r\n \u0026\u0026 block.Instructions[i + 1].Operand == _startBlock.Instructions[1].Operand)\r\n {\r\n block.Instructions[i] = new Instr(OpCodes.Nop.ToInstruction());\r\n block.Instructions[i + 1] = new Instr(OpCodes.Nop.ToInstruction());\r\n }\r\n }\r\n}\r\nNow, we need to go back to our Unflattener.cs class and add the call to the Unflatten() function\r\n1\r\n2\r\n3\r\n4\r\n5\r\npublic bool Deobfuscate(List\u003cBlock\u003e methodBlocks)\r\n{\r\n UnflattenerHelper unflattenerHelper = new UnflattenerHelper(methodBlocks[0]);\r\n return unflattenerHelper.Unflatten();\r\n}\r\nAnd that’s it. The final classes can be found here:\r\nDeobfuscator.cs Unflattener.cs UnflattenerHelper.cs\r\nNow it’s time for the fun part. Let’s launch de4dot with the following parameters:\r\nde4dot \u003cpath-to-your-payload-here\u003e -p \u003cshort-name-of-your-deobfuscator-here\u003e\r\nHere are some pictures of the results:\r\nFigure 16\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 12 of 13\n\nFigure 17\r\nI hope you enjoyed this post. My goal in the future is to gain more experience and work on more complex obfuscation\r\nschemes. The article below shows a much more difficult type of control flow obfuscation that necessitates a different\r\napproach. I would highly recommend reading it.\r\nLastly, I would like to thank Ch40zz for helping me understand some logic errors that I made.\r\nFurther reading:\r\nhttps://www.virusbulletin.com/uploads/pdf/conference/vb2022/papers/VB2022-Combating-control-flow-flattening-in-NET-malware.pdf\r\nSource: https://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nhttps://ryan-weil.github.io/posts/AGENT-TESLA-2/\r\nPage 13 of 13",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://ryan-weil.github.io/posts/AGENT-TESLA-2/"
	],
	"report_names": [
		"AGENT-TESLA-2"
	],
	"threat_actors": [],
	"ts_created_at": 1775434942,
	"ts_updated_at": 1775826789,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/d33f0a0e6e99b19f5df0f59fa0fcf7b6af858b60.pdf",
		"text": "https://archive.orkl.eu/d33f0a0e6e99b19f5df0f59fa0fcf7b6af858b60.txt",
		"img": "https://archive.orkl.eu/d33f0a0e6e99b19f5df0f59fa0fcf7b6af858b60.jpg"
	}
}