PowerPoint 簡報
Download
Report
Transcript PowerPoint 簡報
電腦攻擊與防禦
The Attack and Defense of Computers
Dr. 許 富 皓
Rootkit
for Windows by
[Bryce Cogswell et al. ]
Categories of Rootkits – Windows
User-mode Rootkits
Kernel-mode Rootkits
User-mode Rootkits
Windows API [wikipedia]
The Windows API, informally WinAPI,
is the name given by Microsoft to the core
set of application programming interfaces
available in the Microsoft Windows
operating systems.
It is designed for use by C/C++ programs
and is the most direct way to interact with a
Windows system for software applications.
Windows API [developerfusion]
As you know, windows can do lots of things.
Manage hardware, run programs, display icons.
Much of these functions are carried out by DLL
files.
DLLs (Dynamic Linked Libraries) store functions,
so other programs can access them.
The advantage of using DLLs is that the same file can
be accessed at the same time by different programs.
The functions stored in the windows DLLs are
called Windows API.
Native API[wikipedia]
Lower level access to a Windows system,
mostly required for device drivers, is
provided by the Native API in current
versions of Windows.
Windows Library Files -user32.dll [answers.com]
user32.dll is a DLL that implements
the Windows User API Client Library.
It is a core file for several versions of the
Microsoft Windows operating system. If
this file is damaged or deleted, the operating
system will not work.
Windows Library Files -ntdll.dll [answers.com]
The Native API (with capitalized N) is
the publicly mostly undocumented
application programming interface used
internally by the Windows NT family of
operating systems produced by Microsoft.
Most of them are in ntdll.dll and
ntoskrnl.exe (and it's variants).
User-mode Rootkits – Utilizing
Windows APIs
A user-mode rootkit might intercept all calls to the
Windows FindFirstFile/FindNextFile
APIs, which are used by file system exploration
utilities, including Explorer and the command
prompt, to enumerate the contents of file system
directories.
When an application performs a directory listing
that would otherwise return results that contain
entries identifying the files associated with the
rootkit, the rootkit intercepts and modifies the
output to remove the entries.
API Hooking [craigheffner]
In Windows, all applications must communicate
with the kernel through API functions; as such,
these functions are critical to even the simplest
Windows application.
Thus, the ability to intercept, monitor, and modify
a program's API calls, commonly called API
hooking, effectively gives one full control over
that process.
This can be useful for a multitude of reasons,
including debugging, reverse engineering, and
hacking.
Intercept API Calls
While there are several methods which can
be used to intercept, monitor, and modify a
program's API calls, one of them is DLL
redirection.
DLL Redirection [craigheffner]
Since an executable imports API functions
from DLL files, DLL redirection allows us
to tell a program that the DLLs it needs are
located in a different directory than the
originals; in this way we can create a DLL
with the same name as the original, which
exports the same function names as the
original, but each function may contain
whatever code we like.
User-mode Rootkits – Utilizing
Windows Native APIs
More sophisticated user-mode rootkits
intercept file system, Registry, and process
enumeration functions of the Native
API.
This prevents their detection by scanners
that compare the results of a Windows API
enumeration with that returned by a
Native API enumeration.
Registry [Microsoft]
A central hierarchical database used in Microsoft
Windows 9x, Windows CE, Windows NT, and
Windows 2000 used to store information
necessary to configure
the system for one or more users.
applications.
hardware devices.
Registry data is stored in binary files.
Information Contained in the
Registry [Microsoft]
The Registry contains information that Windows
continually references during operation, such as
profiles for each user.
the applications installed on the computer.
the types of documents that each can create.
property sheet settings for folders and application icons.
what hardware exists on the system and the ports that
are being used.
Description of the Registry [Microsoft]
The Registry replaces most of the textbased .ini files used in Windows 3.x and
MS-DOS configuration files, such as the
Autoexec.bat and Config.sys.
Although the Registry is common to several
Windows operating systems, there are some
differences among them.
What Does the Registry Look Like -[Tim Smith] ?
The Registry is stored on your hard disk in several
files but the only way to look at it and make
changes is to use the regedit program.
To access this, click on the Start Button and then on
the Run option.
Type regedit into the box that appears and press
Enter.
This will launch regedit and you will now have your
first sight of the Registry.
Organization of Registry [Tim Smith]
The Registry is organized much like the files on a
disk and will look familiar if you have ever used
the Folders view in Windows Explorer.
In the Registry, however, these folders are called
keys.
To open a key, simply click on the small plus (+)
symbol next to it. You will then see that each key
contains either more keys - called subkeys - or values.
Key Overview [Tim Smith]
The keys are organized logically but there
are thousands of them, which can be
daunting the first time you sneak a peek at
the Registry.
To simplify things, be aware that there are
five root keys and these are the basic
building blocks of the Registry.
Predefined Keys [Microsoft]
What follows is the predefined keys that are
used by the system.
HKEY_CURRENT_USER
HKEY_USERS
HKEY_LOCAL_MACHINE
HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
(abbr. HKCU)
(abbr. HKU)
(abbr. HKLM)
(abbr. HKCR)
(abbr. HKCC)
• The maximum size of a key name is 255 characters.
Key Value [wikipedia]
Each of the predefined keys is divided into
subkeys, which may contain further subkeys, and
so on.
Any key may contain values. These values can be:
String Value
Binary Value (0 and 1's)
DWORD Value, a 32 bit unsigned integer (numbers
between 0 and 4,294,967,295 [232 – 1])
Multi-String value
Expandable String Value
Key Hierarchy [wikipedia]
Each key has a default value, which is in effect a
value with the same name as the key.
Registry keys and values are specified with a
syntax similar to Windows' filenames, using
backslashes to indicate levels of hierarchy.
E.g.
HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows refers to the subkey "Windows" of the
subkey "Microsoft" of the subkey "Software" of
the HKEY_LOCAL_MACHINE key.
Example (1)
HKCU has subkeys and values. By pressing the
+ before the HKCU you can see its subkeys.
Example (2)
Key Specifying Applications to Run
When a User Logs in [wikipedia]
HKLM\Software\Microsoft\Windows\Cu
rrentVersion\Run (and the HKCU equivalent)
specifies applications to run whenever a user logs
in.
These can include desirable programs, such as
printer monitoring programs or frequently-used
tools, but a lot of malware uses this registry key to
ensure it is automatically run.
This key is a good place to start looking for
evidence of malware if you think your computer
has been infected.
Example
Spyware and Registry [Tim Smith]
Spyware often installs values in the Registry to make sure
that it's launched to monitor your computer when Windows
starts up.
When looking for advice on how to remove these programs you
may be told to edit the Registry.
Always make sure that the advice is coming from a trustworthy
source such as Registry Guide for Windows or Systweak.com.
Sometimes the spyware also installs a small program to
monitor the Registry and replace keys that you delete, so
you should use software such as Spybot Search and
Destroy to clean your computer entirely.
Kernel-mode Rootkits
Kernel-mode Rootkits
Kernel-mode rootkits can be even more powerful
since, not only can they intercept the Native
API in kernel-mode, but they can also directly
manipulate kernel-mode data structures.
A common technique for hiding the presence of a
malware process is to remove the process from
the kernel's list of active processes. Since process
management APIs rely on the contents of the list,
the malware process will not display in process
management tools like Task Manager or
Process Explorer.
Rootkit Techniques by [Ivo Ivanov]
Techniques Involved
Step 1: Injecting techniques
Step 2: Interception Mechanisms
Injecting Techniques [Ivo Ivanov]
Injecting Techniques
Method 1: Registry
Method 2: Global Windows Hooks
Other Methods(omitted in this lecture)
Injecting DLL by using
CreateRemoteThread() API function
Implanting through BHO add-ins
MS Office add-ins
Inject by Registry
Inject a DLL into Processes
In order to inject a DLL into processes that link with
USER32.DLL, you simply can add the DLL name to
the value of the following registry key:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
Its value contains a single DLL name or group of DLLs
separated either by comma or spaces.
According to MSDN documentation, all DLLs
specified by the value of that key are loaded by each
Windows-based application running within the
current logon session.
Invoke Registry Editor
Select the Appropriate Key
Edit the Selected Key
Load USER32-Related DLLs
It is interesting that the actual loading of these
DLLs occurs as a part of USER32's initialization.
USER32 reads the value of mentioned registry
key and calls LoadLibrary() for these DLLs
in its DllMain code.
Restrictions:
However this trick applies only to applications that use
USER32.DLL.
Another restriction is that this built-in mechanism is
supported only by NT and 2K operating systems.
Shortcomings
In order to activate/deactivate the injection process you
have to reboot Windows.
The DLL you want to inject will be mapped only into these
processes that use USER32.DLL, thus you cannot expect
to get your hook injected into console applications, since
they usually don't import functions from USER32.DLL.
On the other hand you don't have any control over the
injection process. It means that it is implanted into every
single GUI application, regardless you want it or not. It is
a redundant overhead especially if you intend to hook few
applications only.
Inject by Hooks [Chris Cummings ]
What Are Hooks?
Put shortly, a hook is a function you can create as part of a
dll or your application to monitor the 'goings on' inside
the windows operating system.
The idea is to write a function that is called every time a
certain event in windows occurs –
for example when a user presses a key on the keyboard or moves
the mouse.
Hooks were provided by Microsoft primarily to help
program writers with the debugging of their applications,
but they can be put to use in many different ways –
for example, write hidden key logging program to find out other
users’ passwords to the internet!
Types of Hooks
There are 2 types of hooks - global or local.
A local hook is one that monitors things happening
only for a specific program (or thread).
A global hook monitors the entire system (all threads).
Both types of hooks are set up in the same way,
the main difference being that for a local hook, the
function to be called can be within the program it
is monitoring, but with a global hook the function
must be stored and loaded from a separate dll.
Hook-related Functions
The SetWindowsHookEx
Function
SetWindowsHookEx is the function provided by Microsoft to
install a hook. It accepts the following arguments:
SetWindowsHookEx returns a handle (i.e. an identifier) for the
current hook, so you can use UnhookWindowsHookEx to remove
the hook later on.
SetWindowsHookEx Example
[Michel Leunen]
// Hood Function (Callback Procedure) Declaration
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);
// Global variables
HHOOK HookHandle;
HINSTANCE DllInstance;
bool IsInRect=false;
bool InstallMouseHook()
{
HookHandle=SetWindowsHookEx(WH_MOUSE,
reinterpret_cast<HOOKPROC>(MouseProc),DllInstance,0);
if (HookHandle==NULL)
return false;
else
return true;
}
//-------------------------------------------------------------------bool RemoveMouseHook()
{ if(UnhookWindowsHookEx(HookHandle)==0)
return false;
else
return true;
}
Types of Hooks used in idHook
Parameter of a Hook Function
Types of Hooks appearing in a hook
function.
The Hook Function
The hook function is the procedure to be called by windows when the
event we specify happens. A hook for any event always takes the same
form, but the values passed to it by windows can mean different things.
For example if the hook is type WH_KEYBOARD, windows will pass
information to it relating to which key was pressed. Your hook procedure
should accept the following arguments:
A hook function returns a value of type longword. What you should set it
to depends on the type of hook, or you can just set it to the value that
CallNextHookEx returns.
Hook Function Example
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code<0)
{
return CallNextHookEx(HookHandle,code,wParam,lParam);
}
//Define a rectangle
POINT MousePos;
RECT UpperRightCorner=Rect(Screen->Width-2,0,Screen->Width,2);
//Get the mouse position
GetCursorPos(&MousePos);
//Check if the mouse is in the rectangle
if((PtInRect(&UpperRightCorner,MousePos))&&(IsInRect==false))
{
//if the mouse is in the rectangle, launch the screensaver
IsInRect=true;
SendMessage(GetDesktopWindow(),WM_SYSCOMMAND,SC_SCREENSAVE,0);
}
else IsInRect=false;
//Call the next hook in the chain
return CallNextHookEx(HookHandle,code,wParam,lParam);
}
The CallNextHookEx Function
This function is to do with hook chains. When a hook is installed for a
certain event, there may be others like it already installed –
for example 2 programs at once might be trying to log keyboard input.
When you install a hook with SetWindowsHookEx it adds your
hook procedure to the front of a list of hook procedures.
CallNextHookEx simply calls the next procedure in the list.
When your hook procedure is finished, it can run CallNextHookEx,
and then return the value it gets from it or a different one depending on
the type of hook.
CallNextHookEx takes exactly the same form as a hook procedure
plus one extra –
the handle returned by SetWindowsHookEx identifying the hook.
The other values you pass to it should be the values your hook procedure
was called with. How you should use it depends on the type of hook
The UnhookWindowsHookEx
Function
This function simply removes your hook.
The only argument you pass to it is the hook
handle returned by SetWindowsHookEx.
Global Windows Hooks
Global Hooks
The global hook is slightly more
complicated than a local hook.
To create a global hook you need 2 steps,
to make the executable file that executes
SetWindowsHookEx()
to make a Dll to contain the hook procedure.
System-wide Hooks
A system-wide hook is registered just once when
SetWindowsHookEx() is executed.
If no error occurs a handle to the hook is returned.
The returned value is required at the end of the custom
hook function when a call to CallNextHookEx()
has to be made.
After a successful call to
SetWindowsHookEx() , the operating system
injects the DLL automatically (but not necessary
immediately) into all processes that meet the
requirements for this particular hook.
Processes that use hook functions with the same type as this particular hook.
Global Variables vs. Shared Data
Once an application installs a system-wide hook, the operating system
maps the DLL into the address space in each of its client processes.
Therefore global variables within the DLL will be per-process and cannot
be shared among the processes that have loaded the hook DLL. All
variables that contain shared data must be placed in a shared data section.
A Global Hook Is Loaded by Multiple Processes
That Don't Share the Same Address Space
For instance hook handle sg_hGetMsgHook, that is obtained by
SetWindowsHookEx() and is used as parameter in
CallNextHookEx() must be used virtually in all address spaces. It
means that its value must be shared among hooked processes as well as
the Hook Server application. In order to make this variable "visible" to
all processes we should store it in the shared data section.
Define Variables Shared by All
Processes
Example
Installing a mouse hook.
Interception Mechanisms [Ivo Ivanov]
Interception Mechanisms
Injecting a DLL into the address space of an
external process is a key element of a spying
system. It provides an excellent opportunity to
have a control over process's thread activities.
However it is not sufficient to have the DLL
injected if you want to intercept API function calls
within the process.
In terms of the level where the hook is applied,
there are two mechanisms for API spying –
Kernel level spying,
User level spying.
The Module Relationships and Their Dependencies on
Windows 2K and Interception Points
NT Kernel Level Hooking
NT Kernel Level Hooking
There are several methods for achieving hooking
of NT system services in kernel mode.
The most popular interception mechanism was
originally demonstrated by Mark Russinovich and
Bryce Cogswell in their article "Windows NT SystemCall Hooking".
• Their basic idea is to inject an interception mechanism for
monitoring NT system calls just bellow the user mode. This
technique is very powerful and provides an extremely flexible
method for hooking the point that all user-mode threads pass
through before they are serviced by the OS kernel.
However, all these hooking strategies, remain out
of the scope of this course.
Win32 User Level Hooking
Win32 User Level Hooking
Proxy DLL (Trojan DLL)
Spying by altering of the Import Address Table
Other Approaches (not covered in this lecture)
Code overwriting
Spying by a debugger
Windows sub-classing
Function Forwarder [Jeffrey Richter]
Define a Function Forwarder
Defined in a stub DLL (A stub DLL refers to an
entire DLL of unimplemented functions.)
// Function forwarders to functions in DllWork
#pragma comment(linker, "/ export:SomeFunc=DllWork.SomeFunc")
This pragma tells the linker that the stub DLL should export
a function called SomeFunc, but that the actual
implementation for the function is in a function SomeFunc
contained in the DllWork.dll. You'll have to have one
pragma line for each function exported by your
DllWork.dll for a program to call your functions correctly.
Stub DLL and the Real DLL
stub DLL
The DLL
used by an
application
#pragma comment(linker, "/export:SomeFunc=DllWork.SomeFunc")
BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID p)
{
:
ShutdownLibrary();
:
return(TRUE);
}
The real
DLL
__declspec(dllexport) void ShutdownLibrary();
__declspec(dllexport) void SomeFunc();
void SomeFunc()
{ MessageBox(NULL, "Doing something", "SomeFunc in DllWork", MB_OK); }
LoadLibrary
loads the stub
DLL, then the
OS loader
automatically
loads your real
DLL.
void ShutdownLibrary()
{ // Notify the worker threads to shutdown
SetEvent(g_hEventTerminate); }
BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID fImpLoad)
{ int nThread;
:
}
_declspec
[wikipedia]
the _declspec keyword is a strange new
keyword that is not part of the ANSI C standard,
but that most compilers will understand anyway.
_declspec allows a variety of non-standard options
to be specified, that will affect the way a program
runs. specifically, there are two _declspec
identifiers that we want to discuss:
_declspec(dllexport)
_declspec(dllimport)
Dllexport
[wikipedia]
When writing a DLL, we need to use the
dllexport keyword to denote functions
that are going to be available to other
programs.
Functions without this keyword will only be
available for use from inside the library
itself.
DllMain
[wikipedia]
When Windows links a DLL to a program,
Windows calls the libraries' DllMain
function. This means that every DLL needs
to have a DllMain function.
Function Forwarder Example
Function Forwarder
A function forwarder is an entry in a DLL's export table that redirects
a function call to another function in another DLL.
For example, if you run the Visual C++® DumpBin utility on the
Windows NT Kernel32.dll, you'll see a part of the output that looks
like this:
C:\winnt\system32>DumpBin -Exports Kernel32.dll
(some output omitted)
360 167 HeapAlloc (forwarded to NTDLL.RtlAllocateHeap)
361 168 HeapCompact (000128D9)
362 169 HeapCreate (000126EF)
363 16A HeapCreateTagsW (0001279E)
364 16B HeapDestroy (00012750)
365 16C HeapExtend (00012773)
366 16D HeapFree (forwarded to NTDLL.RtlFreeHeap)
367 16E HeapLock (000128ED)
368 16F HeapQueryTagW (000127B8)
369 170 HeapReAlloc (forwarded to NTDLL.RtlReAllocateHeap)
370 171 HeapSize (forwarded to NTDLL.RtlSizeHeap)
(remainder of output omitted)
How a Function Forwarder Forwards the
Execution of a Function to Another Function
This output of the previous slide shows four forwarded functions. Whenever your
application calls HeapAlloc, HeapFree, HeapReAlloc, or HeapSize, your
executable is dynamically linked with Kernel32.dll. When you invoke your
executable, the loader loads Kernel32.dll and sees that there are forwarded
functions that are actually contained inside NTDLL.dll, so the loader also loads
the NTDLL.dll module. When your executable calls HeapAlloc, it is actually
calling the RtlAllocateHeap function inside NTDLL.dll. The HeapAlloc
function does not actually exist anywhere in the system!
If you call
GetProcAddress(GetModuleHandle("Kernel32"), "HeapAlloc");
GetProcAddress looks in Kernel32's export table, sees that HeapAlloc is
a forwarded function, and calls GetProcAddress recursively looking for
RtlAllocateHeap inside NTDLL.dll's export table.
Proxy DLL (Trojan DLL) Rootkits
An easy way for hacking API is just to replace a DLL with
one that has the same name and exports all the symbols of the
original one. This technique can be effortlessly implemented
using function forwarders.
A function forwarder basically is an entry in the DLL's export
section that delegates a function call to another DLL's function.
You can accomplish this task by simply using #pragma
comment:
#pragma comment(linker, "/export:DoSomething=DllImpl.ActuallyDoSomething")
However, if you decide to employ this method, you should take
the responsibility of providing compatibilities with newer
versions of the original library.
Portable Executable File Format [Ashkbiz Danehkar ]
Portable Executable File Format
The Portable Executable file format was defined
to provide the best way for the Windows
Operating System to execute code and also to
store the essential data which is needed to run a
program, for example constant data, variable data,
import library links, and resource data.
It consists of
MS-DOS file information,
Windows NT file information,
Section Headers,
and Section images.
Portable Executable File Format
Structure (1)
Portable Executable File Format
Structure Types
MS-DOS Information
IMAGE_DOS_HEADER
MS-DOS Stub Program
Windows NT Information
(IMAGE_NT_HEADERS)
Signature
IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER32
IMAGE_DATA_DIRECTORY[16]
IMAGE_SECTION_HEADER[0]
:
Sections Information
IMAGE_SECTION_HEADER[n]
SECTION[0]
:
SECTION[n]
Using Ordinal Numbers to Identify
DLL Procedures
In addition to a name, all DLL procedures
can be identified by an ordinal number that
specifies the procedure in the DLL.
Some DLLs do not include the names of
their procedures and require you to use
ordinal numbers when declaring the
procedures they contain.
MS-DOS Information
offset
value
e_lfanew is the offset
which refers to the
position of the Windows
NT data.
Structure IMAGE_NT_HEADERS
typedef struct _IMAGE_NT_HEADERS
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS,
*PIMAGE_NT_HEADERS;[1][2]
Access the MS-DOS header and the
Windows NT Header
If you assume that the pMem pointer relates the start point of the
memory space for a selected portable executable file, you can retrieve
the MS-DOS header and also the Windows NT header by the
following lines,
IMAGE_DOS_HEADER
image_dos_header;
IMAGE_NT_HEADERS
image_nt_headers;
PCHAR pMem;
…
memcpy(&image_dos_header, pMem, sizeof(IMAGE_DOS_HEADER));
memcpy(&image_nt_headers, pMem+image_dos_header.e_lfanew,
sizeof(IMAGE_NT_HEADERS));
Signature and IMAGE_FILE_HEADER of
Windows NT Information
Structure
IMAGE_OPTIONAL_HEADER
typedef struct _IMAGE_OPTIONAL_HEADER
{
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD AddressOfEntryPoint;
:
A pointer to the first IMAGE_DATA_DIRECTORY
DWORD ImageBase;
structure in the data directory.
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER;
AddressOfEntryPoint and
ImageBase
AddressOfEntryPoint
A pointer to the entry point function,
relative to the image base address.
ImageBase
The preferred address of the first byte of the
image when it is loaded in memory.
This value is a multiple of 64K bytes.
• The default value for DLLs is 0x10000000.
• The default value for applications is 0x00400000.
Remarks
The actual structure in Winnt.h is named
IMAGE_OPTIONAL_HEADER32 and
IMAGE_OPTIONAL_HEADER is defined
as IMAGE_OPTIONAL_HEADER32.
However, if _WIN64 is defined, then
IMAGE_OPTIONAL_HEADER is defined
as IMAGE_OPTIONAL_HEADER64.
IMAGE_OPTIONAL_HEADER32 of
Windows NT Information
IMAGE_DATA_DIRECTORY
Represents the data directory.
typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY,
*PIMAGE_DATA_DIRECTORY;
Members
VirtualAddress
• The relative virtual address of the table.
Size
• The size of the table, in bytes.
points to a
IMAGE_IMPORT_DESCRIPTOR
structure
List of the Data Directories
IMAGE_DATA_DIRECTORY of Windows
NT Information
points to a IMAGE_IMPORT_DESCRIPTOR structure.
Each DLL file has a corresponding IMAGE_IMPORT_DESCRIPTOR structure.
type
Example [stinson]
If we have a PE that imports two modules, we'll have:
one IMAGE_DATA_DIRECTORY structure for the Import
Symbols (i.e. the Import Table)
let's say that struct's VirtualAddress is 0xc7d8 -- recall this is
an RVA.
then 0xc7d8 is where the first IMAGE_IMPORT_DESCRIPTOR
lives
since we are importing two modules, there will be 3
IMAGE_IMPORT_DESCRIPTOR structs in our array (which starts
at 0xc7d8, recall)
• --> the 3rd IMAGE_IMPORT_DESCRIPTOR is all zeroes
then the Size field above (for this IMAGE_DATA_DIRECTORY)
will be 3 * sizeof( IMAGE_IMPORT_DESCRIPTOR )
IMAGE_SECTION_HEADER
IMAGE_SECTION_HEADER
Represents the image section header format.
typedef struct _IMAGE_SECTION_HEADER
{
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union
{
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER,
*PIMAGE_SECTION_HEADER;
The address of the first byte
of the section when loaded
into memory, relative to the
image base.
Section Names
IMAGE_SECTION_HEADER Array
of Section Information
SECTION Array of Section
Information
Import Address Table
Within a PE file, there's an array of data structures, one
per imported DLL.
Each of these structures gives the name of the imported
DLL and points to an array of function pointers.
The array of function pointers is known as the import
address table (IAT). Each imported API has its own
reserved spot in the IAT where the address of the imported
function is written by the Windows loader. This last point
is particularly important: once a module is loaded, the IAT
contains the address that is invoked when calling imported
APIs.
Imported API Calls and the IAT (1)
The beauty of the IAT is that there's just one place in a PE
file where an imported API's address is stored. No matter
how many source files you scatter calls to a given API
through, all the calls go through the same function pointer
in the IAT.
Let's examine what the call to an imported API looks like.
CALL 0x0040100C
•••
CALL 0x0040100C
•••
0x0040100C:
JMP DWORD PTR [0x00405030]
• Here, 0x405030 is an entry within the IAT.
Imported API Calls and the IAT (2)
The CALL in previous slide transfers
control to a small stub. The stub is a JMP to
the address whose value is at 0x405030
The Imports Section [Matt Pietrek]
Section .idata
Section .idata contains information
about Import Address Table.
This part of the PE structure is particularly
very crucial for building a spy program
based on altering IAT.
Location of Section .idata in a
PE File
IMAGE_IMPORT_DESCRIPTOR
Structure
The anchor of the imports data is the
IMAGE_IMPORT_DESCRIPTOR structure.
There's one
IMAGE_IMPORT_DESCRIPTOR for each
imported executable (e.g. DLL file).
The end of the
IMAGE_IMPORT_DESCRIPTOR array is
indicated by an entry with fields all set to 0.
Structure
IMAGE_IMPORT_DESCRIPTOR
typedef struct
_IMAGE_IMPORT_DESCRIPTOR
{
DWORD
OriginalFirstThunk;
DWORD
TimeDateStamp;
DWORD
ForwarderChain;
DWORD
Name;
DWORD
FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR,
*PIMAGE_IMPORT_DESCRIPTOR;
Members of Structure
IMAGE_IMPORT_DESCRIPTOR
An Executable Importing Some APIs
from USER32.DLL
IMAGE_THUNK_DATA
Both arrays have elements of type IMAGE_THUNK_DATA, which is a
pointer-sized union. Each IMAGE_THUNK_DATA element corresponds
to one imported function from the executable. The ends of both arrays
are indicated by an IMAGE_THUNK_DATA element with a value of zero.
Structure IMAGE_THUNK_DATA
typedef struct _IMAGE_IMPORT_BY_NAME
{
WORD
Hint;
BYTE
Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
typedef struct _IMAGE_THUNK_DATA
{
union {
PDWORD
Function;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA;
Interpretation of Fields of Structure
IMAGE_THUNK_DATA
The IMAGE_THUNK_DATA union is a DWORD
with these interpretations:
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
//
//
//
//
DWORD ForwarderString; //
Memory address of the imported function
Ordinal value of imported API
RVA to an IMAGE_IMPORT_BY_NAME with
the imported API name
RVA to a forwarder string
IMAGE_THUNK_DATA Structures
of the IAT
The IMAGE_THUNK_DATA structures within
the IAT lead a dual-purpose life.
In the executable file, they contain either the ordinal of
the imported API or an RVA to an
IMAGE_IMPORT_BY_NAME structure.
• The IMAGE_IMPORT_BY_NAME structure is just a WORD,
followed by a string naming the imported API. The WORD
value is a hint to the loader as to what the ordinal of the
imported API might be.
When the loader brings in the executable, it overwrites
each IAT entry with the actual address of the imported
function.
Binding
When an executable is bound (via the bind program,
for instance), the IMAGE_THUNK_DATA structures
in the IAT are overwritten with the actual address of
the imported function.
The executable file on disk has the actual in-memory
addresses of APIs in other DLLs in its IAT. When
loading a bound executable, the Windows loader can
bypass the step of looking up each imported API and
writing it to the IAT. The correct address is already
there.
Spying by Altering of the Import
Address Table
Here are the logical steps of a replacing cycle:
Locate the import section from the IAT of each loaded by the
process DLL module as well as the process itself
Find the IMAGE_IMPORT_DESCRIPTOR chunk of the DLL that
exports that function. Practically speaking, usually we search this
entry by the name of the DLL
Locate the IMAGE_THUNK_DATA which holds the original
address of the imported function
Replace the function address with the user supplied one
By changing the address of the imported function inside
the IAT, we ensure that all calls to the hooked function
will be re-routed to the function interceptor.
Case Study 1
A Simple Rootkit
A simple script put in Perl’s string context, compiled and
named netstat.exe may be an example of a trivial rootkit.
A real system netstat could be named oldnetstat.exe. The
principle of operation of the new netstat is that
• once the command line will call the real netstat (now
oldnetstat.exe), it will be directed to a temporary text file.
• Then the rootkit searches that file for any information about the listening
port to remove it (according to the procedure predefined in the rootkit
code).
• After modification, the result is displayed on the screen and the old file is
removed. This principle is both simple and efficient and provides an
interesting possibility – it may be used to spoof output data acting from
any other tool available through the command line – for example, tlist,
or dir.
There are many programs of this type available on the Web.
Some of them did not display, for example, information on
listening ports such as 666, 27374, 12345, 31337 – i.e. wellknown Trojan horse ports.
Case Study 2
A Windows Rootkit Example -Rootki
The idea of a first enhanced rootkit,
Rootki , for the Windows environment
was born in due time. The originator was
Greg Hoglund.
Rootki 0.40 – Existing Form and
Activating Methods
This rootkit has been designed as a kernel mode
driver that runs with system privileges right at the
core of the system kernel.
Given this fact, it has access to all resources of the
operating system, thus having a broad field of action.
In order to install it one requires the administrator’s
permissions whilst simple net start/net stop
commands are sufficient to activate/disactivate it
respectively.
Rootki 0.40 – Hiding Approach
Once the rootkit has been loaded, the hacker can hide
directories and files on the victim’s disk.
This method is efficient provided that the object to be
hidden has a name prefixed with _root_ – for example,
_root_directory_name.
How does this work?
• The rootkit, by patching the kernel, intercepts all system calls for
the listing of the disk content and all objects beginning with the
sequence _root_ – are hidden from display.
• The same applies to the searching process – all files and directories
with the above sequence of characters are hidden from the search.
Rootki 0.40 – Hiding Processes
This rootkit feature can also be used to hide
processes running as well as to do the same
with the system registry entries, by prefixing
all keys and entries with _root_ .
This enables the hacker to install, for example,
services which will become a backdoor, thus being
as invisible for the system administrator as
services or registry entries or processes running in
the system memory.
Rootki 0.40 – Key Logger
The rootkit can also intercept all key strokes typed at
the system console. This may be carried out by
hooking into the keyboard driver and issuing the
‘sniffkeys’ command.
Case Study 3
A Famous Rootkit Case
The word "rootkit" came to public awareness
in the 2005 Sony CD copy protection scandal,
in which Sony BMG music CDs
surreptitiously placed a rootkit on Microsoft
Windows PCs when the CD was played on
the computer. Sony provided no mention of
this in the CD or its packaging, referring only
to security rights management measures.
Protect Systems against Rootkits
Guarding against the Rootkit –
Checking from Other Hosts
“vulnerability” of a rootkit: objects are only
hidden from the environment of the compromised
machine and they can easily be seen from another
computer.
Mapping a Network Drive remotely from another
machine (or using net use command) is a
means to see everything, which has been hidden
for a local user. This is because the remote
machine is using a clean kernel to view the files
and directories on the compromised machine,
avoiding the rootkits filtration process.
Guarding against the Rootkit –
Renaming Check Utilities
A rootkit, however, cannot affect processes that have _root_ in their
names. In other words, when a system administrator, is analyzing the
system log using regedit.exe, he cannot see hidden entries, but just
by changing its name to _root_regedit.exe, it will be enough for
him to see all of them as well as hidden keys and registry entries. This is
true for all programs – for example, Task Manager.
Appendix (could be omitted)
List the Names of ALL Import
Functions of a PE File (1) [Iczelion]
Verify that the file is a valid PE
From the DOS header, go to the PE header
Obtain the address of the data directory in
OptionalHeader
Go to the 2nd member of the data directory. Extract the
value of VirtualAddress
Use that value to go to the first
IMAGE_IMPORT_DESCRIPTOR structure
Check the value of OriginalFirstThunk. If it's not
zero, follow the RVA in OriginalFirstThunk to the
RVA array. If OriginalFirstThunk is zero, use the
value in FirstThunk instead. Some linkers generate PE
files with 0 in OriginalFirstThunk. This is considered
a bug. Just to be on the safe side, we check the value in
OriginalFirstThunk first.
List the Names of ALL Import
Functions of a PE File (2) [Iczelion]
For each member in the array, we check the value of the member
against IMAGE_ORDINAL_FLAG32. If the most significant bit of the
member is 1, then the function is exported by ordinal and we can
extract the ordinal number from the low word of the member.
If the most significant bit of the member is 0, use the value in the
member as the RVA into the IMAGE_IMPORT_BY_NAME, skip Hint,
and you're at the name of the function.
Skip to the next array member, and retrieve the names until the end of
the array is reached (it's null -terminated). Now we are done extracting
the names of the functions imported from a DLL. We go to the next
DLL.
Skip to the next IMAGE_IMPORT_DESCRIPTOR and process it. Do
that until the end of the array is reached
(IMAGE_IMPORT_DESCRIPTOR array is terminated by a member
with all zeroes in its fields).