|
API monitoring unleashed - I
Tech Forum - Dr. Nitin Paranjpe
Often,
applications need to be monitored. Say, you need to know how an
application is performing and need to keep a track on the resource
usage. This is quite possible if you are the author of the application
or have control over the source tree. What if the application is
a third-party application? How can you plan to monitor a complete
black box? This article will target this area and show you various
ways in which this can be achieved. Even though the APIs used in
this article are documented in the Microsoft developer network (MSDN),
you wont find a correct code snippet that shows you how to
use them. And when you read the documentation, you will yourself
notice that it is quite complicated to understand what has been
written, forget about knowing how to use it! Thats why I would
like to say that whatever appears in the form of program code from
now on is all undocumented stuff. Like all undocumented
code, this code is not supported by the vendor and can change across
versions.
The need
Before we even get started with understanding how to achieve the
above, first let us try and understand properly why one would require
to do this, or, what the business benefit would be.
Even when I learned this technique, I was working on a project that
required me to monitor a particular set of APIs. Although my scope
was the entire system and monitoring each and every application
running, we will limit the scope of this article to monitoring just
one running application. Here are a few reasons why one would require
doing so:
1. To log how the application is using
a particular API. Now, this API could also be your written function
exposed by some custom COM component or library dll. In a normal
case, you will have to write the entire logging code inside your
dll. But with the API monitoring system, you can actually keep this
logging code generic and keep the monitoring specific to a running
process.
2. To change the behaviour of a particular
API. Say you need to disable all calls to a particular API. For
example, user32.dll has an exported API called OpenFile, in order
for you to disable a particular application from using this API,
you can override the OpenFile API and write your own code (in case
of disabling it, returning a failure value to the caller). In this
way, two things are highlighted, first, you can disable any such
API from any dll, and second, you can override the default behaviour
of a particular API and provide your own functionality.
The method of
injecting code
Before we start of with understanding how to inject
code into a running process, let us try and understand how applications
work and how process memory is organised. If this explanation gets
too complicated, you can skip this part and jump to the next section,
Writing the code.
| Import Table |
| Internal Shared Memory |
| Process Stack and Heap |
| Export Table |
The above diagram gives a brief idea of
how memory is allocated for any running process in the Windows environment
Every process has sections in memory as
seen in the diagram. When the process starts, the global/shared
data is loaded in the Internal Shared Memory area. It
has been called Internal because the data is global
within the process but not outside the process boundary. After this,
the Import Table and the Export Table are
populated for this process. This information comes from the binary
of the process. Basically, the Import Table and the Export Table
have the following meaning:
- Import Table: The Import Table lists
all the dlls that the process will be using. From now on, I will
refer to dlls as Modules. It not only lists all the
modules that the process will use, but also pointers to functions.
Keep this point in mind, because we will be digging into this
Import Table and changing the function pointer to
our function. Although the mechanism to do so sounds pretty simple
and straightforward, when you take a look at the code you will
notice that it is in fact not the case.
- Export Table: The Export Table lists
all the functions and the respective function pointers that the
running process will export. This is true if the process itself
is a dynamic link library.
- Process Stack and Heap are locations
that are used by the process dynamically. It is on the stack that
all function pointer tables and the respective variables required
for an entering function are stored.
The method that we will use to inject a
particular code will run in the following manner. Later, I will
show you the respective code for the steps given below:
1. Develop a dll that will contain an exported
function for the API, which you require to monitor/override.
2. Get a process id to a running process,
which you need to modify.
3. Open the process with respect to the
process id using the OpenProcess API.
4. Create a remote thread inside the running
process to control the LoadLibrary functionality of the running
process.
5. Execute this thread to ensure that the
running process loads your dll into its memory.
6. Once your dll is loaded, and when the
dll is attaching, get a pointer to the import list inside the running
process. This part is a bit tricky and will require an understanding
of how to iterate an Import Table list. To make life easier, there
are helper APIs available, which allow you to do this. This will
be explained in detail when explaining the code.
7. Now after getting a pointer to the import
function and a pointer to the function itself, replace this pointer
with a pointer to your function from the dll.
After following the above steps, you will
observe that any call to the particular API will result in your
function being called. In this way you can monitor or override any
API from any Module, provided that you know the name of the Module.
A little clarity
Now that I have explained the steps involved, lets go ahead
and write the code to achieve this. Before we write the code, we
need to make the following things very clear:
1. This code injection system
will only work in the context of the currently logged-in user. This
clearly means that it is not possible to monitor an application
running under a different user context. This also means that you
cannot monitor or inject code into any application over a network
and running under a different user context.
2. This system uses the OpenProcess API
with the OPEN_ALL_ACCESS bit, which means that when you open the
process, you need to be the administrator of the machine or the
creator of the process. Also, you cannot open a process that has
been created with a completely different security descriptor. There
is a way to handle this situation but it might not work in all circumstances.
There is something called opening a process with debug privileges,
read up on it in MSDN.
So, what are the pieces that we need to
code? For API Monitoring consider the following pieces of code:
1. Shared Module
This is the dll that will export our function for the corresponding
API.
2. Injector Application
This is the executable, or the utility that we will write for code
injection. This application basically accepts a process id of the
application we need to monitor and injects our shared module inside
the process space of that application.
3. [Optional] A device driver for new process
creation/destruction notificationThis is an Optional component,
which I will explain but wont provide the source code. This
is a device driver, which compiles with Microsoft Windows DDK using
Microsoft Visual C++ 6.0 Compiler. This driver basically fires an
event whenever a process has been created or destroyed.
This driver is important if you write a
system-level injection code and need to know when a process is created
or destroyed. I wrote a device driver for this (kernel-mode) because
I couldnt find an alternative for this in user-mode. Not even
using Hooks! The actual code for demonstrating all that has been
discussed above will be published next week.
(Note: Please Add "imagehlp.lib"
to your library linking list.)
Your feedback,
suggestions, requests for covering specific topics or issues are
welcome. Please send feedback to techforum@mediline.co.in
 |
About the Author:Dr
Nitin Paranjape is the Chairman and MD of Maestros (Mediline).
He is a consultant with many organisations, covering appropriate
technology utilisation, business application of relevant technology,
application architecture and audit as well as knowledge transfer.
He has authored more than 650 articles on various technology-related
subjects. He can be contacted at nitin@mediline.co.in |
|