Analysis of Equation Group’s nls_933w.dll: Revealing Core Tactics and Technical Mindset
Analysis of Equation
Group’s nls_933w.dll Revealing Core Tactics and Technical Mindset
Though
over a decade old, nls_933w.dll reflects not just complicated technical
mastery but firmware resource related and still very big challenges for analysts
today
“To understand the
immeasurable, the mind must be extraordinarily quiet, still.”
— Jiddu Krishnamurti
By Seeker(李标明) China
Independent Malware Analyst & Researcher
2025.6 – 2025.8
Summary
This report
documents my firsthand experience, observations, and technical findings in
order to capture the underlying mindset and intent behind the firmware
research. It combines narrative context with in-depth malware analysis,
organized as follows:
- Prologue
- Sample Choice
- First Impression
- What is nls_933w.dll
- Technical Analysis
- The Core Anti-Analysis Policies for Malware
- Limitations & Unresolved Questions
- Conclusion
- References
- Epilogue
Prologue: The Temple and the Kernel and the E-book
Recently, I
published my new e-book called “The Path of Clarity”. To be honest, I
had no plan for that. I’m happy for the decision, and the process also encouraged
me to go forward.
On my e-book I
wrote down and shared my feeling: “To truly understand an adversary, you must
rise to — or beyond — their depth. Because only depth reveals intent.”, which
is the key when I deeper dive into nation-state-level malware research. And
something to research malware without any fear, but to understand deeply what
they are.
I’m very special
thanks to Jiddu Krishnamurti; he is a great figure. I did research on how he
saw the world with spiritual vision and insight more than 12 years ago. One of
the important things is to observe the world with one's own eyes, not from a
conclusion. I learned a lot from him and beyond my pass with understanding and
the real action, not only malware research. Thanks again, no other words.
I did not go to
the temple frequently; it seems that my brain has already adapted to the kernel
knowledge and mindset.
At the same time,
I continue to learn more about the malware of history, and I start to notice
low-level threats based on firmware, but without any knowledge for that.
Strongly driven by curiosity, I take weeks to learn about “Persistence Storage”
and build the basic mindset. After that, I tried to look for malware related to
the firmware sample to do analysis. That’s why I come back again and beyond
limited knowledge. and it is an important and a good sample that makes me learn
more and satisfies my strong curiosity.
Sample choice: nls_933w.dll
First of all, many
thanks to Kaspersky for sharing the IOC about the malware sample from its
report and their contribution, which gave me the chance to study more and do
research. And also, including the IOC about Turla’s Uroboros, I did analysis
before. I appreciated it.
Yeah, I tracked back to the
history and learned from media that in 2015, the NSA-linked equation group
developed an HDD reprogramming module. This is a very deep persistence
mechanism and extremely difficult to detect.
And I looked for some stuff about
the event and malware’s report, but without the detail of nls_933w.dll, I was
very curious about what happened inside, so here I go.
nls_933w.dll
md5: 11fb08b9126cdb4668b3f5135cf7a6c5
WIN32M.sys
md5: 2b444ac5209a8b4140dd6b747a996653
First impression: sophisticated and shocking
To be honest, this is my first
time touching such a shocking thing; it is very sophisticated, not only about
software malware but also related to hardware. I realize nls_933w.dll should be
a rare and good sample to do research on, but there is a very limited analysis
report online, so I have to start from scratch and learn “Persistence Storage”
first, as I did before, and it’s a pity I have a limited source without the
compromised firmware, but it did not stop me from going forward.
What is nls_933w.dll?
nls_933w.dll is an advanced
malware that can issue custom IOCTLs requests to the embedded driver WIN32.sys,
which embedded a binary drive-level malware that used to interact with the hard
drive from the kernel level and can send custom ATA commands to compromised
firmware on the victim machine. It does persist very deeply and makes it hard
to detect.
Figure 1: Embedded a binary driver-level
malware.
It embedded a binary
drive-level malware named WIN32M that can be dumped manually from nls_933w.dll,
which is a classic situation in malware where a dropped driver is embedded as a
binary blob (short for binary large object) as follows.
Figure 2: Dump an embedded binary driver
malware.
The embedded driver name called WIN32M in the nls_933w.dll can be decoded
by the Custom XOR algorithm; the constant name is unk_1002F1B4 for user-mode
evasion. And the
suffix “.sys” also did like that, but it will be decoded on other sub-branches
of logic like sub_10003470.
Figure 3: the process of decoding
unk_1002F1B4.
Technical Analysis
About the version of WIN32M.sys
After I installed WIN32M.sys manually and analyzed it
with IOCTLs like 0x870021C0 (custom IOCTLs). Diving deeper into the logic to
debug and finally get the result “3.0.0.0”, this strongly indicates that the
driver returned a version string; the IOCTLs like 0x870021C0 are version
query commands.
Figure 4: return the version
by sending custom IOCTLs like 0x870021C0.
Possible supporting multiple drive categories
Combining static and automatic analysis, it seems that
nls_933w.dll shows it supporting multiple drive “classes” for vendors; they are
strings about “SAMSUNG, ST, Maxtor STM, Maxtor, and WDC WD,” which can be
decoded by the custom XOR algorithm too. But here I have limited
resources to confirm them.
Figure 5: supporting multiple drives in plugin
version 3
The process
of understanding how to interact with the kernel
nls_933w.dll
embedded a driver, which seems to have been designed to drop to a certain
directory of the system. But the logic doesn’t work well on Windows XP or
Windows 7. To be honest, at the beginning, I was not sure about this; it seemed
that I missed something.
It uses GetSystemDirectoryA to
get the correct directory “c:\windows\system32” but concatenates the invalid
strings. Both the constant “Source” and the “Str” design from the .data segment
don’t even plan to decode. But at the beginning, I couldn't even understand it
completely.
Figure 6: invalid path
from concatenating the constant “Source” and the “Str”.
It designs a serial function like FindResourceA,
LoadResource, and CreateFileA to write the embedded driver to an existing path.
Because the invalid path is passed as an address parameter to the function
CreateFileA, the address is “0012F410,” including the invalid value “0012F52C”
and the logic design doesn’t work well.
it will explain more detail on the part of “The
core anti-analysis policies for malware”.
Figure 7: Design an invalid path passed
as an address parameter.
Custom XOR Algorithm Obfuscated NtDll.dll
The strings “NtDll.dll” are deobfuscated by the Custom
XOR algorithm. And then it uses LoadLibraryA to load it.
Figure 8: automatically
deobfuscated the ntdll.dll and loaded it.
The API NtLoadDriver
for loading WIN32M.sys
The API
“NtLoadDriver” also was designed stealthily to load the embedded driver and
prevents static detection of strings, bypassing SC and SCM detection.
The API NtLoadDriver loads a
driver by the value of the registry that is the key
"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\WIN32M"
mapped to C:\Windows\System32\drivers\WIN32M.sys, and then the embedded driver
is loaded into kernel memory and executed. But here the path is invalid and
can't work.
Figure 9: the NtLoadDriver for loading
the embedded WIN32M.sys.
I continue to debug nls_933w.dll
without enough knowledge about the flowchart of “DeviceIoControl” that
interacts with the kernel. Because of dropping failures, I have to frequently
change the status of “EFLAGS” to observe what’s happening. I reserve and share
the process of analysis and learning here. To be honest, it looks a bit blind,
and it failed, but it is my real analysis process.
Without
installing WIN32M.sys to understand DeviceIoControl
When I did static analysis
I noted “DeviceIoControl” as an import function, it
is a Windows API used by user-mode applications
to send I/O control codes (IOCTLs) to kernel-mode drivers or devices.
Malware often abuses DeviceIoControl to use
custom or undocumented IOCTLs to control malicious drivers.
nls_933w.dll supports custom
IOCTLs such as 0x870021C0, 0x870021C4, 0x870021C8, 0x870021CC, 0x870021D0 and
0x870021D4.
Taking 0x870021D0 as an example,
stepping into the function sub_10007090 and finding the strings “WDC WD” which
is the drive “class” for the vendor, it shows after “call sub_1000A6B0”
executes.
Figure 10: Supporting drive class “WDC WD”.
To continue to take a deep
dive into sub_1000A1D0, and then observe the “call sub_1000A140” and find the
special device object “\\.\Global\WIN32M”, the sensitive strings were decoded
by the same as “Custom XOR Algorithm”.
Figure 11: user-mode symbolic link that refers to a kernel-mode device
object.
The function CreateFileW is
not to create a new device object but to open a handle to an already existing
device; the call finally failed, but it can help me to learn more about what’s
happened.
Figure 12: Open a handle to a device object.
From the above, I understood a
certain driver isn’t present, and EAX is an invalid device handle value. It
seems that I need to install an embedded driver.
Figure 13: sending
custom IOCTLs code like “870021D0”
From the many times it failed
to interact with the kernel, the process of analysis became clear. I copied the
driver WIN32M.sys to the driver directory “c:\windows\system32\drivers” and
installed WIN32M.sys manually and tried enough times to keep moving. I have to
save large numbers of virtual machine snapshots.
Installed the driver
WIN32M.sys and analysis
I
installed WIN32M.sys manually and debugged it again in kernel mode and found
implemented IRP_MJ_CODE, such as CREATE (00), CLOSE (02), READ (03), WRITE
(04), DEVICE_CONTROL (0E), and CLEANUP (12).
They are all dispatched to the
same function at f79f258c, which is the custom central dispatch routine
that internally switches based on the IRP major function. And many entries are
"nt!IopInvalidDeviceRequest" is the default handler used by the OS,
but the driver does not implement a dispatch routine for a given IRP major
code.
Figure 14: implemented many IRP_MJ_CODE.
To continue to dive deep into
the custom central dispatch routine with “uf f79f258c” and find custom IOCTLs
like “870021D0” and jump to its branch and handle the request with
“IofCompleteRequest”.
Figure 15: dispatch routine
for custom IOCTLs.
I’m eager to try my best to
dive deep into different logic branches to learn what’s happened, but to be
honest, I moved it very slowly. Sometimes I confirmed it on Windows XP and
sometimes did it on Windows 7 to check them again and again. And they all
failed to send custom IOCTLs through “DeviceIoControl”.
To debug the
custom “0x870021CC” IOCTLs from the central dispatch routine. It successfully
executes “deviceIoControl”. the output buffer as follows:
Figure 16: Custom IOCTLs like
870021CC request.
0x0012F048 = F0 01 00 00 → 0x1F0
0x0012F04C = F6 03
00 00 → 0x3F6
0x0012F0AC = 70 01
00 00 → 0x170
0x0012F0B0 = 76 03
00 00 → 0x376
This maps to ATA
primary and secondary ports, which refer to the two traditional I/O interfaces
used to connect PATA (Parallel ATA) devices.ATA is an older standard for connecting storage
devices.
Important
milestones finding
The above process is helping me be ready for knowledge
about structure. ATA_PASS_THROUGH_EX.
A big change happened: after more than sixty days of
analysis and debug practice, I started to understand I need to create a simple
wrapper DLL to load nls_933w.dll. it will explain more detail on the part of “The
core anti-analysis policies for malware”.
multiple times trying to
observe the custom IOCTLs like “0x870021D0” and successfully executes
“deviceIoControl”. It sends the standard IDENTIFY DEVICE ATA command 0xEC
multiple times, and finally the output buffer returns information about an ATA
device which includes things like the serial number and module number as
follows:
Figure 17: standard IDENTIFY
DEVICE ATA command 0xEC.
Figure 18: device information
from the output buffer.
When setting a breakpoint with
“bp f7a0a58c” (Figure 14) and sending custom IOCTLs
like “870021D0” in user mode, it will touch the breakpoint. Which indicates
DeviceIoControl interacts with the driver WIN32M.sys in kernel mode. And
WIN32M.sys was designed to handle IRP. And the ATA IDENTIFY DEVICE
storage-specific IOCTLs, which strongly indicate interaction with physical disk
devices.
Figure 19: touching
breakpoint on windbg.
As mentioned above, it sends the
custom IOCTLs like “0x870021D0” and successfully executes “deviceIoControl”,
which returns the strings like module numbers, and it looks like the original
scrambled string "MVawerV riutlaI EDH ra drDvi e" changed to
"VMware Virtual IDE Hard Drive" by byte-swapping (neighbor-swapping)
with even positions to odd positions in memory.
Why does it look
"scrambled"? because PC memory is little-endian, but ATA devices
store ASCII strings in big-endian word order. It's not intended as obfuscation;
it's just a hardware communication convention that persisted.
It further indicates that malware
does really have the ability to send ATA commands. Obviously, it is very
advanced malware. Here is new knowledge. When
reversing firmware or ATA data like IDENTIFY DEVICE responses, incorrect
endianness interpretation will scramble strings and values.
Figure 20: not an
obfuscation algorithm but a communication convention.
To go forward and notice
that it uses the function sub_10027190 to compare one of the vendor likes, “WDC
WD” with the module name “VMware Virtual IDE Hard Drive”, which indicates that
it uses DeviceIoControl to send the standard IDENTIFY DEVICE ATA command 0xEC
to get the module name of the target device and detect the environments and
decide to alter its behavior. If it does not match, it will exit. This
also gives high confidence that it has the ability to send ATA commands to
special firmware vendors, like the one shown in Figure 5 above, but strictly speaking can’t confirm
them without devices.
Figure 21: detecting the firmware
environment to see if it does match.
It’s a great pity that I can’t
do further research without the firmware of compromised machines; it is said to
be the most complicated part, and I don't think I'll have the chance to analyze
such a device. So I have to stop here.
The
core anti-analysis policies for malware
1. The hidden functionality name for anti-analysis policy
nls_933w.dll
does not support named exports like the address “100013F4”,
and most DLL loaders only support named exports, not ordinals
directly. It can’t be loaded and executed with the default loader of the
system, like “DLLLoader32_*.exe”.
In order to run
normally, analysts have to create a simple wrapper DLL that exports a named
stub that calls the ordinal function with parameter 1.
The policy aims to
protect malware itself and makes more challenges for analysts. Because the
first thing analysts need to be aware of is this anti-analysis technique or
policy, and they need to have the ability to do static analysis and also have
good skills for debugging.
This strongly implies
the DLL “nls_933w.dll” is meant to be stealthy—likely not designed for public
use, but rather for internal loader logic or malware frameworks. But I hadn’t
found that loader.
Figure 22: the hidden
functionality without name export.
2.
The policy of the Custom XOR algorithm: clever
thinking and design for evasion and anti-static analysis; the fine-grained
algorithm is full of advanced skill
Taking the drive
category like “WDC WD” shown in the function sub_10007550 executed as an
example.
Figure 23: “WDC WD” show after sub_10007550
execute
Deep dive into the
function sub_10007550 and its sub-branch; it calls the same
function, sub_1000A6B0, and finally executes the deobfuscation function
sub_10005850, which is designed by the Custom XOR Algorithm.
Figure 24: Custom
XOR Algorithm for the Name Drive Categories.
The thinking of design
is to set the constant value obfuscated from the .data segment and then use the
Custom XOR Algorithm to deobfuscate. One of the constant values is used for
controlling the loop and setting the initial value; the other of the drive
category constant values is obfuscated and needs to be deobfuscated.
Figure 25:controlling flag for loop and
obfuscation constant for “WDC WD”.
How to think and
design for deobfuscation?
The controlling
constant value “7E000701h” is in hex format in the .data segment.
1.
“7E” as the initial value, and the character is “~”.
2.
“07” as the length of the loop.
3.
“01” as the start of the loop.
And both
“4B658A5h” and “95EC7Eh” need to do deobfuscation with the Custom XOR
Algorithm.
Figure 26: the thinking of
design for the Custom XOR Algorithm.
The important
thing is malware itself using a custom XOR algorithm to hide their sensitive
information for anti-static analysis in many places.
3.
Decoy-style
for anti-analysis technique: Supplying intentionally malformed parameters to
legit API functions
This is a common but APT-style and full
of decoy-style anti-analysis technique. The malware is intentionally passing
invalid parameters pollution to CreateFileA, and it makes noise in both static
and dynamic analysis, which also makes it harder for analysts to do analysis,
and analysts might waste time tracing it. They are designed to mislead
disassemblers, significantly increasing the manual analysis burden. In other
words, they also know how the other analysts think and do.
And the intent is not to drop and load automatically
but only to require manual loading or injection by an operator with a loader.
In a word, it's designed for manual deployment, not
automatic self-spreading and loading. The anti-analysis tricks are to hide
intent and frustrate analysts and stealthy operation only.
Figure 27: The
invalid parameters Pollution for anti-analysis technique.
Limitations
& Unresolved Questions
1.
Very limited analysis without any compromised firmware; can't learn about
what’s happened inside in firmware.
2.
Haven't completely confirmed other hidden functionality; other designs or
intentions may be missed.
3.
Unknown which loader to be used.
Conclusion
In
this report, it’s related only to some primary but limited characteristics. I
also shared my process of doing research blindly at the beginning, and just
helping me go further, it varies from person to person; don’t copy them.
I
did not pursue full tracing of all in this report, to be honest. It took me too
much time and more than 2 months. It is complex malware, and without completely
understanding it, I especially have to face the reality without the compromised
firmware.
From the process of analysis, the malware nls_933w.dll uses multiple-layer
policies for anti-analysis techniques, the hidden functionality name, the custom XOR algorithm, and the decoy style for
misleading, which implies the Equation Group took the protection of
malware very seriously and was very full of tricks and skills.
I
hold deep respect the Equation group for the depth of technique, both in design
and thinking, which gave another different core tactic and technical mindset to
the world. obviously, they are very good for algorithms and also know how
analysts think, and they did it on a fine-grained algorithm that is full of
advanced skill. Which means that analysts have to be calm in the process of
analysis and observe carefully.
What’s truly
remarkable is that this mindset and technical capability were already present
over a decade ago. I now do analysis very limited on my own way and a huge
unknown to me. A constant
reminder: stay humble, learn endlessly, seek without ceasing.
It
is the art of both patience and time for anti-depth.
References
[1].
https://threatpost.com/inside-nls_933w-dll-the-equation-apt-persistence-module/111128/
[2]. https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2018/03/08064459/
Equation_group_questions_and_answers.pdf
[3].
https://www.securityartwork.es/2015/06/09/analisis-de-nls_933w-dll-i/
Epilogue: What the Firmware Research Taught Me
1.
Don’t hurry up to make the last conclusion when you’re not sure; different
paths imply different logic and intention.
2.
Sometimes it’s normal without any progress; researching and resting need to
balance. To be honest, they also belong to you.
3.
To understand what it is that is the real one path you want to.
4.
The process of thinking becomes clear, which makes people happy by nature.
5.
Faced with the depth of malicious code, sometimes you have to face the
frustration without any process. Calm down; it’s normal, and life is not just
being happy.
6.
There is no real concentration without inner peace.
7. On the process of research, I have learned
and mastered how to sing “Om Ah Hum Vajra Guru Padma Siddhi Hum”, it is known
as the Vajra Guru Mantra.
8. At a quiet moment of inner reflection, I
came to a profound realization: to truly analyze malware from the top-elite state-level
APTs, one must adopt a scientist's attitude—meticulous, objective, and deeply
committed—in the digital world.
Annotation: In all the
sentences I wrote and used the word “you or your or yourself” in, it talked to
me or “the malware sample itself, especially in my poem I did”, not the reader.
I must clarify my motivation.
About me
Malware Analysis Space
All content is provided strictly for educational and defensive purposes.
seeker-lee
PDF format malware analysis report for my
malware analysis space and my ebook.
clibm079 GitHub Pages.
Specifically designed to showcase research
topics for my Malware Analysis Space.
MalwareBazaar
Follow me
📄 Copyright Notice
© Seeker
(李标明), 2025. All rights
reserved.
This
document may be freely shared for non-commercial purposes, provided that it
remains unmodified and proper attribution is given to the author.
Comments
Post a Comment