InfoStealer
VIPKeyLogger - ENCRYPTED.ps1
- Author
- Moise Medici
- Updated
- 09 May 2026 · Completed
- Difficulty
- Easy
- Platform
- Capabilities
- Tags
Question 5: What is the content of the executable file coming from decodedAssemblyCode, stage3.exe?
Opening the file in PEStudio, it shows that it is a 32 bit .NET dll file. This information can be found in file → type (dynamic-link-library, 32-bit, console) and file → signature (Microsoft Linker 6.0 | Microsoft Visual C# / Basic .NET | Microsoft.NET). Also in version → original-file-name the value is MAFFIA.dll which will be the name used to refer to stage3.exe from now on. It is however interesting to note that the dll does not have any export function. Also, the import section is very telling of the fact that even though it is possible to see the source code of the file, since it is a .NET sample, the file is very likely going to be highly obfuscated. For example, look at the name of some of the imports:
VsCTMEAZAwaByTfv99pAFOjTEjsEMqHowever, the ProcessHollowing method being looked for is present:
ProcessHollowing,MAFFIA,-,TypeDef,-,mscoree.dllProbably the best bet will be to start from there.
To learn more about Process Hollowing, see: Process Hollowing.
One last note on PEStudio before moving to DnSpy is that the resources section shows five resources, but for some reason they cannot be dumped.
Opening the file with DnSpy, it can be seen that the first 1200 lines are functions that seem to be doing something, and after that there are many functions that are simple one-liners like:
internal static bool ktdrUh2DgZ4T5U1uxA(object A_0){ return string.IsNullOrWhiteSpace(A_0);}To rename a method like the one above in DnSpy, right click on the method name → Edit Method → change the name and hit Ok. Below is a table of original names and the renamed values:
| Original Name | Renamed As |
|---|---|
ktdrUh2DgZ4T5U1uxA | IsNullOrSpace |
W2HEw349qsdIS4TUTm | getMessage |
UqEU2y6ynvXPlC1x3Z | threeArgsToString |
binehgeHaUcMx6t7Gn | writeLine |
DGxZtXkpXdC6gbr9PT | twoArgsToString |
FvyKYnpJKBcsAJNhZm | getTrue |
rCeRE09SP4S5cTVIN5 | getFalse |
rBeV47R2G3wie1X9h0 | bitToInt32 |
KjZod9XFg3Fs16xR7A | getTypeFromHandle |
uWLcneQm415baO9D41 | getSizeOf |
GxrQqHq6jg2DThbaO3 | getSizeOfPointer |
rp5uOUjFXfbQ0aJrkF | runFirstArgWithParams |
T6Zw0vfFPo8U9aWlw8 | runFirstArgWithParamsAgain |
CD2GHZKtN6uyx5EsSL | runFirstArgWithProcAndAddress |
VeTSlDmW28qwQWE4da | runFirstArgWithFileLikeSpec |
pmL31aEAp2LlEyGpSP | areArgsEqual |
gIbPvD5jdSsqOhRShq | bitToInt16 |
RerTj7HXu08etebJti | blockCopy |
jnr3NuP0olT7P8U9Ea | bitToByte |
mHxkTnxG5HdTrNGBeW | runFirstObjArgWithObjArg3 |
xQuIuGcVixoLGyO7Qr | runFirstObjArgWithObjArg3Again |
dIkvBiVbvV3a2SZvul | runArgWithThread |
APmBKqoHyWMipx6mlT | getProcessId |
sya6PiM3jPl8nTcRr5 | hasObjExited |
NQ6s74UjhWMOlRPonp | killObj |
There are certain functions like the one below that cannot be understood right now, so they are not renamed.
internal static void OKpwg4bNTeiOxQcvoi(IntPtr \u0020, object A_1, IntPtr \u0020, int \u0020, bool \u0020){ ProcessHollowing.zh5JlmwHj(\u0020, A_1, \u0020, \u0020, \u0020);}Once all the possible renames are done, the next step will be to review from top to bottom what can be understood and further renamed.
The first few lines can already be changed by replacing the variable names with the Windows API that they are importing. What the lines are accomplishing is to associate GetProcAddress to the variable VyeTQhEU2 and LoadLibraryW with bt9AFPBi0. So they were renamed with the library imported.
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, EntryPoint = "GetProcAddress", SetLastError = true)]private static extern IntPtr VyeTQhEU2(IntPtr \u0020, string \u0020);
// Token: 0x06000002 RID: 2[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", SetLastError = true)]private static extern IntPtr bt9AFPBi0(string \u0020);The first method, FvA7fKsRq, can now be analyzed. In the signature you will likely see \u0020-like characters. This is a way of making the code harder to read, by renaming variables with characters that DnSpy cannot represent. However, DnSpy keeps track of which \u0020 is which one. So simply right clicking on the method name → Edit Method → Params → from there the two arguments have been renamed as arg1 and arg2.
The function itself does not do much other than dynamically loading a Windows API (whose name is in arg2) coming from the DLL in arg1. The last line:
return Marshal.GetDelegateForFunctionPointer<iKuLIR7vohr2be8Mon>(procAddress);Is a way of making the loaded address pointer a C#-like callable. The value iKuLIR7vohr2be8Mon is an obfuscated type, a generic un-obfuscated way of defining this type of function is:
private static T GetFunction<T>(string dll, object funcName) where T : DelegateWhere the Delegate is just a function pointer 6 7.
The function can be renamed as DynamicLoadLibrary. In this function, the catch block is leading to another part of the program that is by itself very much obfuscated, so for now it will be ignored in order to try and get a higher-level understanding of what is happening without digging a rabbit hole.
Moving to the Execute function, one thing that can be immediately noticed is a lot of cases:
case 0: goto IL_AB;case 1: flag = !flag2; goto IL_72;case 2: goto IL_0E;case 3:case 4: goto IL_129;This is a common strategy of obfuscated code to throw off the analysis. The trick here is to see what is called instead of focusing on the code. Specifically, in the whole method, the functions called are:
ProcessHollowing.IsNullOrSpace(targetProcessPath), which is not interesting, just checking for spaces.ProcessHollowing.JMlKL8nG0HoSf7bZwh(targetProcessPath, payload), which is not yet known.ProcessHollowing.writeLine(ProcessHollowing.threeArgsToSting(ProcessHollowing.LvR4Mc8Tes71ySOtio(332), num2 + 1, ProcessHollowing.getMessage(ex)));this is writing debug statements, so only a logging functionality.ProcessHollowing.getFalse(), just returningfalse.ProcessHollowing.LvR4Mc8Tes71ySOtio(120), there are multiple instances of this function being called, always as an exception. Might be interesting as some malicious code might be hidden in purposefully created exceptions.
From these, it can be understood that what needs to be focused on is JMlKL8nG0HoSf7bZwh, and eventually later on LvR4Mc8Tes71ySOtio, but at least the whole looping has been avoided, which is probably just retrying the execution of the main routines in JMlKL8nG0HoSf7bZwh.
The content of JMlKL8nG0HoSf7bZwh is:
internal static bool JMlKL8nG0HoSf7bZwh(object A_0, object A_1) { return ProcessHollowing.PZ7sKk8k1(A_0, A_1); }At this point, looking at PZ7sKk8k1 reveals a number of other obfuscated calls that are just getting too deep in obfuscation. Continuing with the deobfuscation effort is just going to lead to a huge investment of time that is probably better used in another part of the analysis. That was kind of a waste of time, but it happens and the important point is to realize it as quickly as possible.