[Studying] Analyzing njRAT v0.8d

First Post:

Last Update:

Word Count:
3.5k

Read Time:
21 min

Introduction

This article is part of my series: Inside Different Generations of RATs and part of the njRAT Family.

If you are interested in the full series, please refer to the linked pages above.

This article presents an analysis of njRAT v0.8, and documents my observation during reverse engineering.

Key Takeaways

  • njRAT v0.8 is NOT a direct evolution of v0.7 — it appears to be partially reworked by a different author
  • Introduces a custom obfuscator (MON) targeting .NET structure rather than strings
  • Several advertised features (Remote Desktop, Chat, DoS) are incomplete or non-functional
  • Plugin-based architecture becomes more explicit (e.g., screenshot, password extraction)

njRAT v0.8

njRAT v0.8 was released in 2015. It is one of the variants of the njRAT Family.

In my previous article of [analyzing njRAT v0.10], I mentioned that version 0.10 adopted several features from v0.8. However, I originally skipped this version and went straight to [njRAT v0.9], assuming it was just a minor update by the same author — which turned out to be incorrect.

Yeah... this one is on me.

While analyzing v0.8, I finally got a much clearer picture of the evolution between v0.8 and v0.9. Both versions were actually developed by Naseer2012, while v0.7 was created by njq8.

That difference explains a lot.

So I decided to go back and properly document v0.8 as part of my ongoing study.

Experimental Environment

To safely conduct malware analysis, the environment should be isolated using virtual machines. Under no circumstances should malware be executed on a personal or production system.

Tool Description
ExeInfo PE Executable analysis tool used to detect packers, compilers, and basic file properties
Detect It Easy (DIE) File identification tool used to detect packers, protectors, and compiler signatures
Wireshark Network protocol analyzer used for packet capture and traffic analysis
de4dot De-obfuscation tool for .NET PE
RegShot Registry differentiating tool
Process Monitor Process activity monitor
Process Explorer Process and child processes viewer
TCPView Network connection viewer
dnSpy .NET debugger and assembly editor
pestudio PE static analysis tool

Device IP Address Description
Windows 7 x64 (VM) 192.168.85.5 Victim machine used for executing both the controller and payload
Windows 10 x64 (VM) 192.168.85.3 Analysis machine used for reverse engineering

The two virtual machines were configured within an isolated internal network to prevent unintended external communication. If you want to know how to set up your experimental environment, please view:

Usage

Note: To distinguish the terms “server”, “client”, “controller” and “payload”, please refer to this section.

The graphical user interface of the controller application is shown below:

Payload Deployment

Use the provided builder to generate the payload.

Configuring the payload

It can be observed that njRAT v0.8 does NOT provide options (or checkboxes) for persistence (such as copying itself to another directory or modifying the registry).

However, after further investigation, the payload automatically performs the installation process for persistence. This will be demonstrated in the reverse engineering section.

Before deploying the payload, I set up Regshot, TCP View, Process Monitor and Process Explorer.

After deploying the payload, the compromised machine will appear in the controller application once the connection is established:

I noticed that njRAT v0.8 is capable of retrieving the country’s (or region’s) flag of the infected machine.

In previous versions, this was not possible because the controller retrieved the flag based on the IP address. However, in our isolated lab environment, this would not work.

So how does njRAT v0.8 achieve this?

The answer is: it directly retrieves the region information from the Windows system.

To demonstrate this, I manually changed the region in “Region and Language” in the Control Panel. As a result, the flag of the compromised machine changed accordingly (e.g., Argentina).

Change the region and language in the Control Panel

Features Overview

It can be observed that the UI of the controller application was slightly changed:

Controller application

For example, in the previous versions, njRAT v0.7 involves multiple management functionalities in a single panel. However, they are separated in this version.

File Manager

File Manager

Process Manager

Process Manager

Remote Shell

This feature confirmed my assumption in the njRAT v0.10 analysis.

The remote shell functionality appears to originate from version 0.8, as it properly redirects stdin, stdout, and stderr for the console.

As a result, commands such as nslookup work correctly.

Remote Shell

Monitor

In version 0.8, several features appear to be broken, such as the remote desktop functionality:

Remote Desktop, but broken

Keylogger

The keylogger output is also corrupted:

Keylogger with corrupted data

Chatting

This feature is also defective — the dialog does not appear on the compromised machine:

Malfunction chatting

DoS Attack

njRAT v0.8 provides several Denial-of-Service methods. I will analyze them in the reverse engineering section.

Denial-of-Service

Protocol Analysis

The communication protocol of njRAT v0.8 is almost identical to that of version 0.7.

Data is transmitted in plain text, with some fields encoded using Base64, while others are hashed for validation.

Additionally, the screenshot stream appears to be encoded using Base64. This assumption will be verified in the reverse engineering section.

Overall, the protocol design remains simple and largely unencrypted, relying on Base64 encoding rather than proper encryption.

Reverse Engineering

Payload

RegShot

Process Monitor

Process Explorer

Process Explorer — Properties

TCP View

Open the payload using ExeInfo PE and DIE (Detect It Easy).

ExeInfo PE

DIE

DIE — Entropy

Open the payload using dnSpy:

dnSpy

w.A.main

a.WL() — the "main function" of the payload

Installation

The INS() function is responsible for the installation

INS() — installer

After analyzing the code, my assumption from the previous section was confirmed.

The installation procedure is forced — attackers cannot disable this feature manually.

I summarized the installation procedure as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
flowchart TD
A([START: INS called]) --> B{Current path\n== target path?}

B -- Yes, already installed --> G
B -- No, need to relocate --> C

C{Registry key\n'us' is empty?}
C -- Yes --> D{Running from\na drive root?\ne.g. C:\\}
D -- Yes --> E[STV 'US' = '!']
D -- No --> F[STV 'US' = '@']
E --> G2
F --> G2
C -- No --> G2

G2[Set env var\nSEE_MASK_NOZONECHECKS=1]
G2 --> H{Target EXE\nalready exists?}
H -- Yes --> I[Delete existing EXE]
H -- No --> J
I --> J[Copy self to\ntarget directory]
J --> K[Launch new copy]
K --> L([EndApp — exit\ncurrent instance])

G[Add firewall exception\nvia netsh]
G --> M[Write to\nHKCU Run key]
M --> N[Write to\nHKLM Run key]
N --> O[Copy EXE to\nStartup folder]
O --> P[Sleep 1000ms]
P --> Q([END])

style A fill:#4a1b8c,color:#e0ccff,stroke:#7c3aed
style L fill:#7c1d1d,color:#fecaca,stroke:#dc2626
style Q fill:#1a3a1a,color:#bbf7d0,stroke:#16a34a
style I fill:#7c3a1d,color:#fed7aa,stroke:#ea580c
style K fill:#1d3a6b,color:#bfdbfe,stroke:#2563eb
style M fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed
style N fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed
style O fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed

Mutex

Create mutex to ensure only a single payload instance is running:

Create mutex

USB Spreading

The USB spreading functionality is similar to that of njRAT Green Edition or njRAT Lime Edition.

More precisely, it is likely that those editions are based on (or derived from) this version, since njRAT v0.8 (2015) predates the Green Edition (2016) and Lime Edition (2017).

USB Spreading

Create *.lnk file

I summarized the USB spreading procedure as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
flowchart TD
A([START: usb called]) --> B[Reset thread\ncall clean]
B --> C[Set Off = false]
C --> D[Enumerate all drives\nDriveInfo.GetDrives]

D --> E{Drive already\nin tracking list?}
E -- No --> F[Create new DRV entry\nadd to tracking list]
E -- Yes --> G[Load existing DRV entry]
F --> H
G --> H

H{Off flag\nset?}
H -- Yes --> Z([END — exit loop])
H -- No --> I

I{Drive ready AND\nhas free space AND\nRemovable or CDRom?}
I -- No --> S
I -- Yes --> J

J{Payload already\nexists on drive?}
J -- No --> K[Copy self to drive root\nas ExeName]
K --> L[Set payload\nattribute Hidden]
L --> M
J -- Yes --> M

M[Enumerate all files\nin drive root]
M --> N{File is .lnk\nor is payload itself?}
N -- Yes, skip --> M
N -- No --> O

O{File already\nin drv.Files?}
O -- No --> P{drv.Files\ncount < 20?}
P -- No --> M
P -- Yes --> Q[Create .lnk shortcut\nwith matching icon]
Q --> R[Add to drv.Files\nset original Hidden]
R --> M

O -- Yes, known file --> T{File attribute\nstill Hidden?}
T -- No --> U[Re-set Hidden]
U --> V
T -- Yes --> V

V{.lnk file\nexists?}
V -- No --> W[Re-create .lnk]
W --> M
V -- Yes --> X{.lnk content\nchanged?}
X -- No --> M
X -- Yes --> Y[Re-create .lnk]
Y --> M

S[Sleep 3000ms] --> D

style A fill:#4a1b8c,color:#e0ccff,stroke:#7c3aed
style Z fill:#7c1d1d,color:#fecaca,stroke:#dc2626
style K fill:#7c3a1d,color:#fed7aa,stroke:#ea580c
style L fill:#7c3a1d,color:#fed7aa,stroke:#ea580c
style Q fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed
style R fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed
style W fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed
style Y fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed

Keylogger

The function WRK() is responsible for the keylogger feature:

WRK() function

Windows APIs for keylogger

Protect Process (BSOD)

Similar to njRAT v0.7, it uses NtSetInformationProcess() with ProcessInformationClass set to 29, marking the process as critical.

As a result, terminating the payload will trigger a BSOD.

I will explain the underlying mechanism in a future post.

NtSetInformationProcess

Message Handler

The Ind() function is responsible for handling incoming messages:

Ind() function

I summarized the C2 commands as follows:

SYS://MALWARE.ANALYSIS
METHOD: Ind() — v0.8d C2 Dispatcher

njRAT v0.8d — C2 Command Handler

// Ind() dispatcher  |  array[0] = command opcode
Category Opcode Sub-command / Params Description
Connection P Ping / heartbeat — responds with "P"
inf Send victim info — hostname, username, OS, locale, webcam, version, active window, plugin list
Remote Shell rss Start remote shell — spawn hidden cmd.exe with stdin / stdout / stderr redirected
rs array[1] = command (Base64) Send command to shell stdin via WriteLine
rsc Close remote shell — kill cmd.exe process, set Pro = null
Keylogger kl Send keylog buffer — returns kq.Logs encoded via ENB()
Profile
Registry
prof array[1] = "~" Set value in Software\RG subkey — STV(array[2], array[3])
prof array[1] = "!" Set value AND send back current value via getvalue
Registry
Editor
RG array[1] = "~" Enumerate subkeys and values at path — send full listing with type and value
RG array[1] = "!" Set registry value — array[3]=name, array[4]=value, array[5]=kind
RG array[1] = "@" Delete registry value — array[3]=name
RG array[1] = "#" Create registry subkey — array[3]=name
RG array[1] = "$" Delete registry subtree — array[3]=name
Remote
Execute
rn array[2] = Base64 payload Execute embedded payload — decompress, write to %temp% with array[1] extension, Process.Start
rn array[2] = URL (http) Download and execute from URL — WebClient.DownloadData, write to temp, Process.Start
Update up array[1] = Base64 EXE Self-update via embedded EXE — decompress, write to temp, start with "UP:PID", poll registry "di" key, call UNS() if confirmed
up array[1] = URL (http) Self-update via URL download — same flow as embedded variant
Control un array[1] = "~" Uninstall — calls UNS()
un array[1] = "!" Kill process — pr(0) then EndApp
un array[1] = "@" Restart — pr(0), Process.Start(LO.FullName), EndApp
Screenshot CAP array[1]=width, array[2]=height Capture screen — GetThumbnailImage to dims, JPEG encode, send via Send(byte[])
Plugin inv array[1]=plugin ID, array[2]=osk, array[3]=payload Load plugin (from registry cache or packet), set h/p/osk, call start(), wait for Off flag
ret array[1]=plugin ID, array[2]=payload (opt) Load plugin, call GT() to get result, send back via "ret" opcode

Some of the features are not actually used. One of the examples is the remote desktop.

In the version 0.7, screenshot images appear in the ListView of the controller application, this is what we call “Thumbnails“. However, in the version 0.8, thumbnails are replaced by countries’ flag.

Wireshark confirmed my point:

In truth, the remote desktop feature is implemented via plugins. It will be demonstrated in soon.

Plugins

Compared to the previous article, I also analyzed the provided plugins of njRAT v0.8.

njRAT v0.8 uses Assembly.Load() to load plugins into memory:

Assembly.Load()

Password Extractor

Open pw.dll using dnSpy:

Chrome dumper

Note that this Chrome credential dumping method is no longer effective in the latest versions of Chrome.

On the other hand, it demonstrates how credentials from various applications (such as social media) can be extracted:

Skype

MSN

Overall, I summarized the procedure as the following flow chart:

graph TD %% Core Modules Main[njRAT Password Module] --> Registry[Windows Registry] Main --> FileSystem[File System] Main --> SystemAPI[Windows APIs] %% Registry Registry -->|OpenSubKey| DUC[No-IP DUC: CID/CKey] Registry -->|OpenSubKey| ooVoo[ooVoo: User/Pass] Registry -->|OpenSubKey| Yahoo[Yahoo Profiles] Registry -->|OpenSubKey| Paltalk[Paltalk: pwd] %% File System FileSystem -->|Read XML| FileZilla[FileZilla: recentservers.xml] FileSystem -->|Read Config| DynDNS[DynDNS: config.dyndns] FileSystem -->|Read Binary| Opera[Opera: wand.dat] FileSystem -->|GetDirectories| Skype[Skype AppData] %% System APIs SystemAPI -->|CredEnumerateW| MSN[MSN/Hotmail Credentials] SystemAPI -->|GetVolumeInfo| HWID[Hardware ID Generation] %% Processing and output DUC & ooVoo & Yahoo & Paltalk & FileZilla & DynDNS & Opera & Skype & MSN --> Process[Base64 Encoding & Formatting] Process --> FinalList[p.OL: List of Strings] style FinalList fill:#f96,stroke:#333,stroke-width:2px style Main fill:#69f,stroke:#333,stroke-width:2px

DDoS

After further investigation, I believed that the DDoS feature is FAKE.

At the time of writing, I could not find any corresponding C2 commands in either the message hander or the provided plugins.

Additionally, Wireshark confirmed that the DDoS command does not include any of the parameters I provided.

Screenshot

Open sc2.dll using dnSpy. I found that the screenshot feature is implemented via plugin. It confirmed my assumption in the section protocol analysis: The image of screenshot is encoded via Base64:

The "scPK" command

Controller Application

Compared to the njRAT v0.9, the controller application is not packed:

The "scPK" command

While configuring the payload, I was curious about what the “Random Stub” option actually does:

Random Stub

Therefore, I enabled this option, and saw the panel as below:

After opening the payload in dnSpy, I realized that it is simply an obfuscation mechanism:

This can be renamed using de4dot:

Again, I was curious: how does this simple obfuscator work?

The author implemented a class MON to achieve it:

1
सभअलc艾尔吉伊艾艾豆儿艾艾儿提尔尔屁屁提开艾尔सशदअबघखरउऑढढमखकनऐऋफऔ豆吾屁德\u0902诶थव贝i吾艾ञहचउझछव娜छऐ\u0902p

The obfuscation procedure is:

  1. Rename all Type, Property, etc. to one or two combination of random Unicode char, Sanskrit, Chinese and Latin.
  2. It forces the types of Property, Event, and Field to be object, which breaks the type information
  3. It randomly shuffles the order of members in each collection, making structural analysis more difficult
  4. It shifts all the instruction offsets within each method body by -1, interfering with analysis tools that rely on offsets

The obfuscation procedure can be summarized as follows:

flowchart TD A([START: randz called\nwith file path]) --> B[Remove quotes\nfrom path] B --> C{.il backup\nalready exists?} C -- Yes --> D[Delete existing .il] C -- No --> E D --> E[Rename original file\nto filename.il] E --> F[Read assembly\nvia Mono.Cecil] F --> G[Iterate all Modules] G --> H[Iterate all Types] H --> I[Rename Type\nwith RN] I --> J[Iterate Properties\nrename + retype to object] J --> K[Shuffle Properties\norder via RandArray] K --> L[Iterate Events\nrename + retype to object] L --> M[Shuffle Events\norder via RandArray] M --> N[Iterate Fields\nrename + retype to object] N --> O[Shuffle Fields\norder via RandArray] O --> P[Iterate Methods\nskip constructors\ncontaining dot] P --> Q[Rename Method\nwith RN] Q --> R[Shift all instruction\noffsets by -1] R --> S[Rename all\nParameters with RN] S --> T[Shuffle Methods\norder via RandArray] T --> U{More Types?} U -- Yes --> H U -- No --> V{More Modules?} V -- Yes --> G V -- No --> W[Write modified\nassembly back to\noriginal path] W --> X[Delete .il backup] X --> Y([END]) Z([RN — name generator]) Z --> Z1[Pick 1-2 chars\nfrom mixed Unicode pool\nHindi + Chinese + Latin] Z1 --> Z2{Already used?} Z2 -- Yes --> Z1 Z2 -- No --> Z3[Add to used list\nreturn name] style A fill:#4a1b8c,color:#e0ccff,stroke:#7c3aed style Y fill:#1a3a1a,color:#bbf7d0,stroke:#16a34a style Z fill:#1d3a6b,color:#bfdbfe,stroke:#2563eb style Z3 fill:#1d3a6b,color:#bfdbfe,stroke:#2563eb style D fill:#7c3a1d,color:#fed7aa,stroke:#ea580c style R fill:#7c3a1d,color:#fed7aa,stroke:#ea580c style W fill:#2d1a4a,color:#ddd6fe,stroke:#7c3aed

Conclusion

This article presents an analysis of njRAT v0.8.

From my perspective, this version seems to reference v0.7 rather than directly building on top of it. While the overall architecture and features are similar, there are noticeable differences that suggest a partial redesign.

Interestingly, several features in this version are either broken or incomplete, which makes it feel somewhat transitional.

In version 0.9, the anti-analysis technique was significantly improved, not only the payload, but also the controller application.

This version turned out to be more important than I initially thought.

If you have any comments or suggestions, feel free to leave them below!

THANKS FOR READING