Rootkit List
Contact Us

Click Flag for Translation

Subscribe to the Antirootkit Newsletter
Enter Email Address


Home > Articles


Nailuj Rootkit Analysis

Malware analysis: Nailuj sys file

Last Update: 12th March 2007

Author: ZaiRoN


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 for me.

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 definitions.

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:

Figure 1

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[16]; // +08
DWORD base; // +18
DWORD driver_start; // +1C
DWORD unknown_1; // +20
UNICODE_STRING driver_Path; // +24
UNICODE_STRING driver_Name; // +28
// ...

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 variable

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 scanning sLoadedModuleList:

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 module

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 separately:
- Hide registry keys
- 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 this procedure:

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

Figure 2

Hm, what about CmEnumerateKey?

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 of code:

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 bytes are:

68 D4 42 D2 F9 push F9D242D4
C3 ret

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:

0001279A cli
0001279B mov eax, cr0
0001279E and eax,
000127A3 mov cr0, eax
000127A6 retn
000127A8 mov eax, cr0
000127AB or eax, 10000h
000127B0 mov cr0, eax
000127B3 sti
000127B4 retn

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

00011354 push offset aFhs_exe ; "fhs.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 dword_14398
0001139C jz short loc_11408
0001139E test edi, edi
000113A0 jz short loc_11408
000113A2 push dword ptr [edi-8] ; To hide: "LEGACY_VIDEOATI0" or "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 and VideoAti0)
000113C3 jmp short _11396__Check_Key_To_Hide
000113C5 _113C5__HideReg:
000113C5 push dword ptr [edi-8]
000113C8 push offset aFoundHideregS ; "Found HideReg:%S\n"
000113CD call DbgPrint ; Print "Find HideReg:" followed by the
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 index

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 easy trick.


"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 machine):

1. Try to get NTFS device object address
2. Try to set IRP_MJ_CREATE 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 to analyze.

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, ntfs.sys, tmp.hiv.

If the string contains one of these the malware blocks the operation:

00010FB3 mov ecx, [ebp+Irp] ; Received IRP
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 Manager
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:

Figure 3

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 works.

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:
- 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 UserBuffer
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 _1117A__NextFile:
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 check
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 a file
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 filename.

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

Figure 4

“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.

Figure 5

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 to unlink
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 to unlink
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 + To_Hide.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: callback added
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
offset 0x1092B:

00010937 push eax ; *PEPROCESS
00010938 push [ebp+_ProcessId] ; ProcessId to convert into EPROCESS pointer
0001093B call ds:PsLookupProcessByProcessId ; Return a pointer to EPROCESS structure
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 structure
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 this
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) catches it.)
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 and
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.

Hide itself

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

000117C1 _117C1__Next_Module_List_Entry:
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:

Figure 6

The instructions in range 1181E/11826 do the unlink, the result is:

Figure 7

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 A
00011823 mov [eax+4], esi ; B.blink = A
00011826 mov [esi], eax ; A.flink = B

That's all...
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 ).

©2005 Antirootkit.com