Home > Articles
Nailuj Rootkit Analysis
Malware analysis: Nailuj sys file
Last Update: 12th March 2007
Lately a lot of malwares are using rootkit techniques. Private
and antivirus companies are trying to develop tools against malwares
but, despite the fact that most of the techniques are well documented
around the net, only a few companies are getting positive results.
This particular malware is a perfect example because when it came
out only a few tools were able to recognize its nasty operations.
Don't know what you think but that's sound a little bit strange
The malware, named Nailuj by some antivirus companies, is composed
of 3 files: VideoAti0.exe, VideoAti0.dll and VideoAti0.sys. I won't
talk about all the files, but will focus my attention on only one,
the sys file. This malware represents a nice target for those who
want to approach a malware for the very first time because it uses
well-known techniques, such as hiding files and hooking functions.
Nothing hard once you have dealt with them at least once. In addition,
the sys file is compiled in debug mode and every operation performed
by the malware is documented inside the code. Yes, every time it
does something it reveals its success or failure, printing out a
comment using DbgPrint function. This is really useful because you
know what it will do before starting to analyze the code, not so
bad. Do you want something more for your first malware analysis?
Most of the analysis can be done with a disassembler but support
from a kernel mode debugger comes in handy. To run the driver, I
have used Kernel-Mode Driver Manager by Four-F. Using this software,
you don't have to deal with the malware's other files. DbgView is
useful also for its comments. You'll find the malware here: http://www.rku.xell.ru/samples/allinone.rar
Approaching the malware
The file to analyze is a .sys file so it's easy to approach the
target. One of the first thing to look at is the DriverEntry routine
which has this common declaration:
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING
pRegistryPath) DriverEntry routine is used mainly for initialization
purposes and it has some useful information inside, like callback
How to locate the function? One of the functions inside DriverEntry
routine is IoCreateDevice, the function is surely called because
if you want to control a device you have to create a device.
Besides that, most of the time, a sys file doesn't have many functions
declared inside and this is helpful for us. Knowing this information,
it is not so difficult to locate the DriverEntry routine
inside the disassembler's dead list, from all the functions you
have to extract the ones with two parameters and a call to IoCreateDevice
inside and then it's almost done.
Here is a little sum-up of the Nailuj DriverEntry routine:
As you can see, everything depends on the operations performed
inside the box named 'General initialization' because if something
goes wrong with the ‘initialization,' the sys file exits.
During initialization, the malware retrieves some information that
it will use in the following parts. Before analyzing the core of
the file, it's good to have a look at what it really needs.
All my investigations are done on a WinXP machine, but the malware
should run fine on
both Win2K and WinXP systems. Inside the malware there are some
kernel related operations and due to this fact it needs to confirm
the installed OS; it uses this code to determine the OS:
00011557 mov eax, ds:NtBuildNumber
0001155C and [ebp+var_4], 0
00011560 cmp word ptr [eax], 893h ; 0x893 = 2195. Win2K = 2195,
WinXP = 2600
00011565 jnz short _11578__WinXP
Every time the malware needs to access OS specific kernel structures,
you'll find the snippet
above repeated. That's why I said it should run fine on both OSs.
Before starting, the malware needs to get the address of the PsLoadedModuleList
variable. The content of the variable points to the head of a list
that is used by the kernel; the list (a double linked list) contains
information about the loaded drivers on the system. Every elements
of the list represents a module and the structure of each element
is defined as:
typedef struct _MODULE_ENTRY
LIST_ENTRY le_mod; // +00
BYTE unknown; // +08
DWORD base; // +18
DWORD driver_start; // +1C
DWORD unknown_1; // +20
UNICODE_STRING driver_Path; // +24
UNICODE_STRING driver_Name; // +28
} MODULE_ENTRY, *PMODULE_ENTRY;
To locate PsLoadedModuleList the malware uses this piece of code:
00011578 mov eax, 0FFDFF034h
0001157D mov eax, [eax]
0001157F mov eax, [eax+70h]
00011582 mov [ebp+var_4], eax ; eax = value stored inside PsLoadedModuleList
PsLoadedModuleList variable is OS dependent, so before this code
you'll surely see the OS
check mentioned before. This is an old technique exposed by Opcode.
Next the malware needs the ntoskrnl module. It checks for it by
000115FF mov eax, _1436C__PsLoadedModuleList_Variable
00011604 mov edi, ds:wcsncpy
0001160A mov ebx, [eax] ; Takes the 1° element of PsLoadedModuleList
0001160C mov esi, ebx ; esi points to MODULE_ENTRY structure of
the first PsLoadedModuleList element
0001160E cmp dword ptr [esi+20h], 0
00011612 jz short loc_1165F
00011614 push dword ptr [esi+28h] ; +28: driver_Name
00011646 lea eax, [ebp+var_208] ; Unicode path of the current module
of the list
0001164C push offset unk_115AA ; wchar_t *: unicode of "krnl"
00011651 push eax ; wchar_t *: unicode of current module
00011652 call ds:wcsstr ; Search the first occurrence of "krnl"
in the current module name
00011658 add esp, 18h
0001165B test eax, eax ; Is ntoskrnl the current module?
0001165D jnz short _11667__ntoskrnl_module_found
0001165F mov esi, [esi] ; +00: next module
00011661 cmp esi, ebx
00011663 jz short loc_11687 ; It stops when it reaches the first
element for the second time
00011665 jmp short loc_1160E ; Otherwise it jumps checking the next
Nothing hard, a simple routine that scans the entire list looking
for a module with "krnl" word
inside; I'm pretty sure it's looking for ntoskrnl module. If the
module isn’t found, the check
routine ends when the first element is found for the second time.
It's a double linked list! If the
module is found in the list, the malware can proceed with the rest
of the DriverEntry's instructions and create the device with the
"VideoAti0" service name.
In the next parts of the article I'll talk about what happens after
the driver initialization process.
The malware performs various tasks and I'm going to discuss them
- Hide registry keys
- IRP_MJ_CREATE, IRP_MJ_DIRECTORY_CONTROL modification
- Auto start when Windows starts
- Hide itself
Hide registry keys
As I stated in the beginning, the programmer calls DbgPrint many
times. I believe the author
used this function during the development process and maybe forgot
to remove them all from the final (debug) release, just a thought.
They are, however, really useful because in few seconds you can
really understand what the malware does. The debug strings are not
encrypted, and looking at them got my attention, particularly to
one titled: "start register hook.\n". I'll start with
000107B4 push ebx
000107B5 push edi
000107B6 push offset aStartRegisterH ; "start register hook.\n"
000107BB call DbgPrint ; Print the message
That sentence taken alone doesn't help too much, but it surely identifies
a piece of code of some interest because of the DbgPrint call. Before
inspecting the code I decided to look at the output produced by
Hm, what about CmEnumerateKey?
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
IN PVOID KeyInformation,
IN ULONG Length,
IN PULONG ResultLength)
According to the CmEnumerateKey documentation, the function returns
the name of the
Index-the entry of the open specified key. Hm, the printed string
could be connected with that.
Some lines below the DbgPrint function there is an interesting piece
000107DD call _1279A__CR0_Disable_WP ; Disable Write Protection
000107E2 mov edi, _unknown
000107E8 mov al, 68h
000107EA stosb ; Patch a byte
000107EB lea eax, sub_112D4
000107F1 stosd ; Patch a dword
000107F2 mov al, 0C3h
000107F4 stosb ; Patch a byte
000107F5 call _127A8__CR0_Enable_WP ; Enable Write Protection
The code is used to patch 6 bytes starting from the address stored
inside “_unknown” which is an address that points somewhere.
At the moment I don't know what it's patching but
CmEnumerateKey could be the right answer. On my machine the new
68 D4 42 D2 F9 push F9D242D4
Address 0xF9D242D4 is inside the driver space, so it's pretty clear
that the malware redirects
something to a function inside the sys file. This is the classical
situation of an API hook trick. The initial bytes of the hooked
function are patched with a jump to another function. Using this
method, the hooked function is called, but you don't know if it
will be executed.
Some questions may arise at this point: Is it possible to patch
one or more kernel bytes? Yes, but you have to enable write protection
using a well documented trick which involves the Control Register
0. In the snippet above there are two calls and I renamed them as
CR0_Enable_WP and CR0_Disable_WP because this is what they do:
0001279B mov eax, cr0
0001279E and eax,
000127A3 mov cr0, eax
000127A8 mov eax, cr0
000127AB or eax, 10000h
000127B0 mov cr0, eax
There are two more things we need to understand: What does it patch,
and what is the function at offset 112D4.
To be sure it patches the initial bytes of CmEnumerateKey function,
I put a breakpoint on one of the addresses printed by DebugView,
0x8056A6C4. The debugger breaks and I'm now sure the malware hooks
CmEnumerateKey. As often happens, the new function calls the old
function and then performs some operations over the data returned
(by the old function). That's how a hook normally works and this
hook is no exception. In fact, the function is called at the beginning
of new_CmEnumerateKey. After this call, there is a check over the
process name that calls CmEnumerateKey. Let’s look at the
00011354 push offset aFhs_exe
00011359 push eax ; process name
0001135A call edi ; _stricmp
0001135C pop ecx
0001135D test eax, eax
0001135F pop ecx
00011360 jnz short _1136D__not_fhs_exe
00011362 lea eax, [ebp+var_24]
00011365 push eax
00011366 push offset aProcessnameS ; "ProcessName:%S\n":
parameter for the next
0001136B jmp short loc_11387
It does a string compare with "fhs.exe". If the strings
are equal, the malware prints the debug
string and quit from the procedure. It also does a compare with
the string: "knlsc13.exe".
Fhs (Find Hidden Service) and knlsc (Kernel SC) processes are rootkit
detectors. Scanning the Registry, fhs and knlsc are able to identify
VideoAti0 driver as hidden service. The idea of the malware's programmer
is to fool fhs and knlsc by not hiding VideoAti0 registry key. That's
why nothing happens when one of two process is found.
Now, let's see what happens when the process name is not fhs.exe
and not even knlsc13.exe.
The malware hides two registry keys: "LEGACY_VIDEOATI0"
and "VideoAti0". I'm pretty sure the author read the good
paper by HolyFather, the one about invisibility on Nt!
00011396 cmp edi, offset
0001139C jz short loc_11408
0001139E test edi, edi
000113A0 jz short loc_11408
000113A2 push dword ptr [edi-8] ; To hide: "LEGACY_VIDEOATI0"
000113A5 call ds:wcslen
000113AB push eax
000113AC lea eax, [ebx+10h]
000113AF push eax ; The name of the current key
000113B0 push dword ptr [edi-8] ; Key to hide
000113B3 call ds:_wcsnicmp ; Is the string to hide?
000113B9 add esp, 10h
000113BC test eax, eax
000113BE jz short _113C5__HideReg ; Jump if it has to hide a key
000113C0 mov edi, [edi+4] ; Get the next string to check (LEGACY_VideoAti0
000113C3 jmp short _11396__Check_Key_To_Hide
000113C5 push dword ptr [edi-8]
000113C8 push offset aFoundHideregS ; "Found HideReg:%S\n"
000113CD call DbgPrint ; Print "Find HideReg:" followed
name of the reg key to hide
000113D2 pop ecx
000113D3 inc esi ; Add 1 to the index of the subkey, subkey at esi
index will be hided!!!
000113D4 pop ecx
000113D5 mov dword_14374, 1
000113DF push [ebp+arg_14]
000113E2 push [ebp+arg_10]
000113E5 push ebx
000113E6 push [ebp+arg_8]
000113E9 push esi ; Index of the next key to return
000113EA push [ebp+arg_0]
000113ED call CmEnumerateKey ; Get the name of the key at the specified
As you can see, at the end of the snippet is a call to CmEnumerateKey,
the original bytes of the function are restored at the beginning
of new_CmEnumerateKey and then patched again at the end of new_CmEnumerateKey.
By doing this, the author avoids an infinite loop.
The snippet is used to pass over the subkey for concealment. Pretty
IRP_MJ_CREATE, IRP_MJ_DIRECTORY_CONTROL modification
"Hook Ntfs IRP_MJ_CREATE failed\n" is another entry inside
DbgView output, another good
starting point. The malware attempts to hook IRP_MJ_CREATE and
IRP_MJ_DIRECTORY_CONTROL. This is done inside the call at offset
0x10BBE. Inside, you'll see the same operations performed two times,
because the malware manages both Ntfs and Fastfat files systems.
This is the scheme called for a Ntfs file system (the one on my
1. Try to get NTFS device object address
2. Try to set IRP_MJ_CREATE hook
3. Try to set IRP_MJ_DIRECTORY_CONTROL hook
For those with FastFat, the process is the same. To locate the system,
the device object uses the function ObReferenceObjectByName. The
most interesting things are point’s 2 and 3, above. The hook
is done the same way; the old pointer is replaced by a new one.
In this case there are two hooks and there will be two functions
An IRP_MJ_CREATE request is sent when a new file or directory is
created or in general when a file/device/directory is opened. The
new function simply checks if the name of a new/open file/directory
contains one of these words: Fastfat.sys, Ntfs.sys, fastfat.sys,
If the string contains one of these the malware blocks the operation:
00010FB3 mov ecx, [ebp+Irp]
; Received IRP
00010FB6 mov esi, STATUS_ACCESS_DENIED
00010FBB xor dl, dl
00010FBD and dword ptr [ecx+1Ch], 0
00010FC1 mov [ecx+18h], esi ; Irp.IoStatus = STATUS_ACCESS_DENIED
00010FC4 call ds:IofCompleteRequest ; Returns the Irp to the I/O
00010FCA mov eax, esi
00010FCC jmp short loc_10FA5
The IRP_MJ_CREATE dispatch routine changes the Irp before returning
the Irp to the I/O Manager (calling IoCompleteRequest). The I/O
Manager will receive a modified Irp! The I/O operation can't be
performed because Iostatus field (it holds the status of the I/O
operation) is forced to STATUS_ACCESS_DENIED. That's how the malware
blocks every operation which involves the names listed above. If
you want to see the trick in action you have only to create a new
directory naming it ntfs.sys, that's the result:
Access denied! Sorry, it's in Italian, but I think you can understand
it, I bet you have already
seen this box before...
An IRP_MJ_DIRECTORY_CONTROL request is sent when a list of directories
and files is requested; the new dispatch routine is used to hide
files on our system. It tries to hide three files: VideoAti0.dll,
VideoAti0.exe and VideoAti0.sys unlinking a specific structure from
a list. We'll see later what is in the list and how the unlinking
000110D6 mov ecx, [ebp+Irp]
000110D9 push ecx
000110DA push edx
000110DB mov eax, [ecx+60h] ; +60: Current Stack Location
000110DE mov esi, [ecx+3Ch] ; +3C: UserBuffer
000110E1 cmp byte ptr [eax+1], IRP_MN_QUERY_DIRECTORY ; [eax+1]
= MinorFunction = IRP_MN_QUERY_DIRECTORY
000110E5 mov edi, [eax+0Ch] ; edi = 3, FileBothDirectoryInformation
000110E8 jz short loc_110F1 ; Jump!
000110EA call ebx ; Something goes wrong, call the dispatch function
000110EC jmp loc_11287 ; and jump to the end of the call...
000110F1 call ebx ; Old IRP_MJ_DIRECTORY_CONTROL function
000110F3 test eax, eax ; Test the result
000110F5 mov [ebp+var_8], eax
000110F8 jl loc_11287
The malware gets the address of the Current Stack Location. The
stack location contains useful information about the user buffer
data. The malware needs this information because it has to know
the MinorFunction, IRP_MN_QUERY_DIRECTORY in this case.
Next it takes the address of the UserBuffer which is used to store
information about the contents of the directory. The buffer is filled
by the original dispatch function with the requested information
which is stored inside the Irp structure:
- Major function: IRP_MJ_DIRECTORY_CONTROL
- Minor function: IRP_MN_QUERY_DIRECTORY: a directory query request
At this point the buffer contains some data which the malware can
play with. Let's see what it
0001114D mov eax, [esi]
; esi -> current structure inside UserBuffer
0001114F mov edi, [ebp+0Ch]
00011152 mov [ebp+arg_0], eax ; Save the offset of the next structure
of the buffer
00011155 mov ecx, 82h
0001115A xor eax, eax
0001115C lea ebx, [esi+5Eh] ; +5E: Filename
0001115F rep stosd ; Copy the file name
00011161 mov eax, [esi+3Ch] ; +3C: Length of the filename in unicode
00011164 shr eax, 1
00011166 push eax ; size_t
00011167 push ebx ; wchar_t *: filename of the current entry inside
00011168 push [ebp+0Ch] ; wchar_t *: buffer
0001116B call ds:wcsncpy ; copy the filename into the buffer
00011171 mov edi, dword_14384 ; list of the files to hide
00011177 add esp, 0Ch
0001117A cmp edi, offset dword_14380
00011180 jz loc_11258 ; Jump if it has checked the
filenames of the files to hide
00011186 push [ebp+0Ch] ; wchar_t *: current file of the directory
00011189 push dword ptr [edi-8] ; wchar_t *: current filename to
0001118C call ds:_wcsicmp ; Compare the two filenames
00011192 pop ecx
00011193 test eax, eax
00011195 pop ecx
00011196 jz short _1119D__File_To_Hide ; Jump if it needs to hide
00011198 mov edi, [edi+4] ; Moves on the next filename to check
0001119B jmp short _1117A__NextFile ; and jump above ...
00011258 mov [ebp+var_4], esi ; Save the pointer to the current
structures, necessary for the unlinking
0001125B mov eax, [esi] ; Get the offset of the next structure
0001125D test eax, eax
0001125F jz short loc_11263
00011261 add esi, eax ; Move the pointer to the next structure
00011263 cmp [ebp+arg_0], 0 ; Is there another structure?
00011267 jz short loc_1127B ; No: quit
00011269 jmp loc_1114D ; Yes: jump up and check
First of all, it wants to know if it needs to hide one or more files.
The files it needs to hide are:
VideoAti0.sys, VideoAti0.exe and VideoAti0.dll. To make this determination,
the malware scans the buffer filled by the old IRP_MJ_DIRECTORY_CONTROL
dispatch call. The buffer is filled with a sequence of structures,
each containing information about a filename. The characteristics
of the single structure depend on the FileInformationsClass parameter,
in this case FileBothDirectoryInformation. Of all the fields inside
the structure, only three are used by the snippet above: offset
to the next structure, file name and length of the file name. It
doesn't need anything else.
The malware does a compare between strings of the “filename
to hide” and “the current filename” inside the
UserBuffer. If the filenames are the same, the next step is the
hiding of its component file, otherwise it simply checks the next
Once it has done all the checks on the current entry, it moves to
the next entry inside UserBuffer. Before moving to the next entry,
the malware saves the pointer to the current structure because it
will come in handy for a possible structure unlink. How does the
unlink work? Suppose it opens a directory containing one of the
file to be hidden (named “To_Hide”) and other files
(named A, B, and C); suppose that the structures are linked in this
“To_Hide” is linked inside the structure and at the
moment is visible, a field inside A tells me
where “To_Hide” is and a field inside “To_Hide”
tells me where B is. To hide the file the malware simply unlinks
“To_Hide’s” structure from the rest of the chain.
By doing this operation, the
system will only see A, B, C. To_Hide won't be shown.
This is possible because the first field of every structure is named
NextEntryOffset and it
represents the offset of the next instruction. Doing:
A.NextEntryOffset = A.NextEntryOffset + To_Hide.NextEntryOffset
you'll obtain the situation described above. Now take a look at
the snippet that is called when a file needs to be hidden:
000111B6 mov edx, [ebp+var_4]
; edx -> "A", the structure that preceeds the structure
000111B9 test edx, edx
000111BB jz short loc_111E2
000111BD mov ecx, [edx] ; ecx = A.NextEntryOffset, from "A"
to "To_Hide" offset
000111BF test ecx, ecx
000111C1 jz short loc_111E2
000111C3 test esi, esi ; esi -> "To_Hide", the structure
000111C5 jz loc_1125B
000111CB mov eax, [esi] ; eax = To_Hide.NextEntryOffset, from "To_Hide"
to "B" offset
000111CD test eax, eax
000111CF jz short loc_111E2
000111D1 add ecx, eax ; Important operation: A.NextEntryOffset +
000111D3 push offset aMoveNextentryo
000111D8 mov [edx], ecx ; Store the result in A.NextEntryOffset,
the unlink has done!
Auto start when Windows starts
Speaking in general, one of the features of a malware is the ability
to run every time Windows
starts. There are several ways to make it run at start-up, the most
frequently used methods
involve using the Windows registry; which is what this malware uses.
Again, starting from the debugview output I arrive here:
0001047C push FALSE ; Remove:
0001047E push offset NotifyRoutine ; NotifyRoutine
00010483 call PsSetCreateProcessNotifyRoutine
PsSetCreateProcessNotifyRoutine is used to add/remove a driver callback
routine to a list of
routines called when a process is created or deleted. The routine
is added and it's located at
00010937 push eax ; *PEPROCESS
00010938 push [ebp+_ProcessId] ; ProcessId to convert into EPROCESS
0001093B call ds:PsLookupProcessByProcessId ; Return a pointer to
00010941 test eax, eax
00010943 jl short loc_10992
00010945 push esi
00010946 push [ebp+Object]
00010949 call sub_12788 ; Get ImageFileName field from EPROCESS
0001094E mov ecx, [ebp+Object]
00010951 mov esi, eax
00010953 call ds:ObfDereferenceObject
00010959 push offset aUserinit_exe ; "userinit.exe"
0001095E push esi ; char *: ImageFileName of the new process
0001095F call ds:_stricmp ; Compare the strings
00010965 pop ecx
00010966 pop ecx
00010967 pop esi
00010968 test eax, eax
0001096A jnz short loc_10992 ; Jump if not equal
0001096C push 1085A ; "VideoAti0.exe"
00010971 push 10876 ; "ATICardInit"
00010976 push 1088E ;
0001097B call sub_1280C ; Registry related operations inside
That last push should make the bells go off! It is the path to the
“Run” folder, the place for the
programs Windows will start every time it is started. A quick glance
inside the call confirms what I was suspecting. A new key named
ATICardInit was created with value "VideoAti0.exe". (In
article I haven't analyze the exe file but, as you can guess, it
loads both the dll and the sys file so
the malware will be in running mode until your antivirus (or you)
This key will be created every time the process "userinit.exe"
is launched. If you are not sure
what userinit.exe is, I suggest you to run it from the command line.
Yes! How many times have
you used it without knowing anything about its filename? Ahah!
To sum up, the malware adds a new callback routine via PsSetCreateProcessNotifyRoutine
every time the process userinit.exe is created the malware adds
a new key inside the Windows
registry, used to start the malware when Windows starts.
In the last part of the article, I’ll show you how the malware
tries to hide its sys file. At the
beginning of the article, I said that the malware needs two pieces
of information before starting,
one of them is PsLoadedModuleList symbol and the time to use it
it's finally arrived.
PsLoadedModuleList stores the address of the head of a list. This
list (a double linked list)
contains information about all loaded drivers. The malware takes
the address of the first element
of the list and start checking all the loaded modules. Why? Let's
000117D4 mov eax, [esi+28h] ; +28: name of the current module
000117DB movzx ecx, word ptr [esi+24h] ; +24: length in bytes of
the module path (it's a unicode string)
000117DF shr ecx, 1 ; Get the real length of the module path
000117E1 push ecx ; size_t
000117E2 push eax ; wchar_t *: name of the current module
000117E3 lea eax, [ebp+var_208] ; Buffer
000117E9 push eax ; wchar_t *
000117EA call ds:wcsncpy ; Copy the path into another buffer
000117F0 lea eax, [ebp+var_208]
000117F6 push eax ; wchar_t *
000117F7 call ds:_wcslwr ; Tolower(module path)
000117FD lea eax, [ebp+var_208] ; Module path in lowercase
00011803 push offset aVideoati0 ; Unicode of "videoati0"
00011808 push eax ; wchar_t *
00011809 call ds:wcsstr ; Locate the first occurrence of "videoati0"
in the current module name
0001180F add esp, 18h
00011812 test eax, eax
00011814 jnz short loc_1181E ; Jump if module was found
00011816 mov esi, [esi] ; Next module entry
00011818 cmp esi, edi ; Are all entries checked?
0001181A jz short loc_11828 ; Yes: jump down...
0001181C jmp short _117C1__Next_Module_List_Entry
0001181E mov eax, [esi] ; Unlink!
00011820 mov esi, [esi+4] ; Unlink!
00011823 mov [eax+4], esi ; Unlink!
00011826 mov [esi], eax ; Unlink!
It looks for a module named "videoati0" (itself) and then
it unlinks the module from the list. The
idea behind this unlinking is the same as we saw where the malware
tries to hide its file names,
but there's a little difference.
The first field of the MODULE_ENTRY structure is a LIST_ENTRY structure
which has two fields,
named flink and blink (forward and backward link). The fields are
pointers. The first one is the
pointer to the next structure and the other field is the pointer
to the previous structure. Because
it's a double linked list, two pointers are necessary:
The instructions in range 1181E/11826 do the unlink, the result
0001181E mov eax, [esi]
; esi points to To_Hide, the structure to hide. eax points to B
00011820 mov esi, [esi+4] ; After the instruction esi points to
00011823 mov [eax+4], esi ; B.blink = A
00011826 mov [esi], eax ; A.flink = B
In the end
For any kind of comments/criticism/suggestions, feel free to contact
me at zaironcrk[at]hotmail[dot]com
Many thanks to ZaiRoN for submitting this excellent Article. The
Article is also available in PDF format from here - Malware
analysis: Nailuj sys file PDF ( 230Kb ).