← Reports

InfoStealer

VIPKeyLogger - ENCRYPTED.ps1

Author
Moise Medici
Updated
09 May 2026 · Completed
Difficulty
Easy
Platform
Windows
Capabilities
Dropping Secondary PayloadsProcess Hollowing
Tags
ps1C#VIPKeyLogger

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:

VsCTMEAZAw
aByTfv99pA
FOjTEjsEMq

However, the ProcessHollowing method being looked for is present:

ProcessHollowing,MAFFIA,-,TypeDef,-,mscoree.dll

Probably 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:

MAFFIA.ProcessHollowing
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 NameRenamed As
ktdrUh2DgZ4T5U1uxAIsNullOrSpace
W2HEw349qsdIS4TUTmgetMessage
UqEU2y6ynvXPlC1x3ZthreeArgsToString
binehgeHaUcMx6t7GnwriteLine
DGxZtXkpXdC6gbr9PTtwoArgsToString
FvyKYnpJKBcsAJNhZmgetTrue
rCeRE09SP4S5cTVIN5getFalse
rBeV47R2G3wie1X9h0bitToInt32
KjZod9XFg3Fs16xR7AgetTypeFromHandle
uWLcneQm415baO9D41getSizeOf
GxrQqHq6jg2DThbaO3getSizeOfPointer
rp5uOUjFXfbQ0aJrkFrunFirstArgWithParams
T6Zw0vfFPo8U9aWlw8runFirstArgWithParamsAgain
CD2GHZKtN6uyx5EsSLrunFirstArgWithProcAndAddress
VeTSlDmW28qwQWE4darunFirstArgWithFileLikeSpec
pmL31aEAp2LlEyGpSPareArgsEqual
gIbPvD5jdSsqOhRShqbitToInt16
RerTj7HXu08etebJtiblockCopy
jnr3NuP0olT7P8U9EabitToByte
mHxkTnxG5HdTrNGBeWrunFirstObjArgWithObjArg3
xQuIuGcVixoLGyO7QrrunFirstObjArgWithObjArg3Again
dIkvBiVbvV3a2SZvulrunArgWithThread
APmBKqoHyWMipx6mlTgetProcessId
sya6PiM3jPl8nTcRr5hasObjExited
NQ6s74UjhWMOlRPonpkillObj

There are certain functions like the one below that cannot be understood right now, so they are not renamed.

MAFFIA.ProcessHollowing
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.

MAFFIA.ProcessHollowing
[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:

MAFFIA.ProcessHollowing
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 : Delegate

Where 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:

MAFFIA.ProcessHollowing
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 returning false.
  • 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:

MAFFIA.ProcessHollowing
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.