Part 2 Practical Guides on Windows API hooking
Author: http://www.kk-wuti.blogspot.com
Copyright: http://kk-wuti.blogspot.com
TABLE OF CONTENTS Disclaimer .......................................................................................................................... 3 About this guide ................................................................................................................ 3 Pre-exquisite ...................................................................................................................... 3 Method II – Running codes in remote process space..................................................... 4 Section I – Allocate memory, create and run a thread in remote application................. 4 Step 1 – Create a DLL to be loaded by remote process.............................................. 4 Step2: - Inject codes into remote process ................................................................... 5 Section II – Inside the remote process .......................................................................... 10 Summary ....................................................................................................................... 10
2
Copyright: http://kk-wuti.blogspot.com
Disclaimer THIS INFORMATION IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS INFORMATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
About this guide There are tones of information available regarding windows hooking. As such, this guide will not go into details of what is Windows Hooking or what it can do for you. This guide will instead directly jump into the many ways of Windows Hooking and for each method; a working source codes example with a step by step guide is given. While the author will paint a picture on where and how a particular method could be used, it is entirely up to the reader to assess and make his or her own judgment on how a particular method presented here could best be used with regard to the actual needs.
Pre-exquisite Familiar with Ms Visual C++ 6.0 tools
3
Copyright: http://kk-wuti.blogspot.com
Method II – Running codes in remote process space This is a more advanced method of hacking Windows application. This guide and explanation is divided into two main sections.
Section I – Allocate memory, create and run a thread in remote application Briefly, the codes or function to be run in the remote process are residing in a normal Win32 DLL. In order to force any application to run the codes inside the Win32 DLL that we have written, first we need to force the application we want to hack to load our Win32 DLL in a separate thread in its (the remote process) own address space. We know that whenever a DLL is loaded by Windows, DllMain is entered and the codes in DLL_PROCESS_ATTACH is executed. So it is inside here that we lay our codes to be run by the remote process. All this doesn’t sound complicated, and here is a step by step guide in doing it.
Step 1 – Create a DLL to be loaded by remote process Create a simple WIN32 DLL; let’s call it SimpleDll.dll (You can create it through the VC++ 6 project wizard). You DllMain function should look similar to the following: CODE: BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { //HERE is where we are going to lay our function to be run by the remote process
} break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
4
Copyright: http://kk-wuti.blogspot.com
Step2: - Inject codes into remote process Create an application to “inject” or copy your codes into the remote application you are targeting. This can be any MFC or even non-windows application. Create a function inside the application to do the following codes injection. This is the core part of the method and as such, there are a few important concepts and twists need to be aware of. A bottom up explanation is these: As mentioned in the beginning, we need to force the remote application to load our DLL. The remote application has its own list of tasks to do. So we don’t interfere with its main thread. To achieve our purpose, we instead ask the remote application to spawn a new thread. This new thread will do nothing other than loading the DLL (SimpleDll.dll) that we have created earlier. The function we use to achieve this is CreateRemoteThread Windows API. CODE: HMODULE hKernel32 = GetModuleHandle(__TEXT("kernel32")); if (hUser32 == NULL) return 0; hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"LoadLibraryA"), pLibRemote, 0, NULL );
From MSDN: HANDLE WINAPI CreateRemoteThread( __in HANDLE hProcess, __in LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in LPVOID lpParameter, __in DWORD dwCreationFlags, __out LPDWORD lpThreadId );
hProcess A handle to the process in which the thread is to be created.
So we need to pass the process handle of the remote process. It is easy to get that handle. All we need is to get the process ID of the remote process. Assuming we want to target a Window application, all we need is the following lines of codes to get the remote process handle: -
5
Copyright: http://kk-wuti.blogspot.com
CODE: int PID; //hWnd is your windows application handler ::GetWindowThreadProcessId( hWnd, (DWORD*)&PID );
//Getting the remote process handler hProcess = ::OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, PID);
if (hProcess == NULL) return 0;
lpStartAddress According to MSDN, this represents the starting address of the thread in the remote process. ::GetProcAddress(hKernel32,"LoadLibraryA") If you are aware of, since we are calling this function in our application, the address returned may not be in the right context when this value is passed in to the remote application or process. Fortunately, all windows processes somehow always load the kernel32.dll at the same address. This is so for all the function exported functions from kernel32.dll. Consequently there is no issue in passing our LoadLibrary’s function address to the remote process as the remote process will be interpreting this value correctly as we want it to.
lpParameter From MSDN: This is a pointer to a variable to be passed to the thread function. Firstly, what data need to be passed into the remote thread? As if creating a thread in a normal application, it is the parameter to the function of the remote process that needs to be passed in. The standard thread function looks like this: CODE: ThreadFunc (LPVOID lpParam)
6
Copyright: http://kk-wuti.blogspot.com
The thread function that we want the remote process to execute should have the same signature as the normal thread function. And the remote thread function that we want to execute here is non-other than the LoadLibrary function, and this function’s signature is similar to that of thread function. HMODULE WINAPI LoadLibrary( __in LPCTSTR lpFileName );
So in this case, the LoadLibrary’s input parameter is treated as the parameter given to the remote thread function, similar to the lpParam in ThreadFunc. To summarize, we will be using CreateRemoteThread function to create a worker thread in the remote process, passing in the address of the “thread procedure” (LoadLibrary) and the data/value of “thread parameter” (which is the path of the DLL to be used by LoadLibrary). In other words, LoadLibrary (lpFileName) is now a mock of the actual ThreadFunc(lpParam).
Caution: As mentioned before, the same address value in different processes refers to different thing. Since the address of LoadLibrary is the same in both our application and in the remote, there is nothing to worry. However, this is not the case for the “thread parameter” or path of the DLL that we pass into the remote process!!
Since the pointer value of our application could be pointing to different things when it is interpreted in the remote process, it is thus obvious that the value of this pointer pointing to should be some value allocated in the remote space rather than value allocated in our application’s address space. So the question is how do we allocate memory in the remote address? This is achieved through Windows API VirtualAllocEx. CODE: TCHAR szLibPath[]="c:\\SimpleDll.dll";
//Allocate memory in the remote space void * pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE ); if( pLibRemote == NULL ) return false;
//Write the data into the memory allocated ::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL);
7
Copyright: http://kk-wuti.blogspot.com
Up to this point, the function to inject codes into the remote process should look similar to the following: -
8
Copyright: http://kk-wuti.blogspot.com
CODE: //hWnd is the Windows handler of the remote process window int WriteCodesIntoRemoteProcess (HWND hWnd) {
int PID; hKernel32 = GetModuleHandle(__TEXT("kernel32")); if (hKernel32 == NULL) return 0; ::GetWindowThreadProcessId( hWnd, (DWORD*)&PID );
//Getting the remote process handler hProcess = ::OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, PID);
if (hProcess == NULL) return 0; __try {
// 1. Allocate memory in the remote process for my dll name path // 2. Write szLibPath to the allocated memory TCHAR szLibPath[]="c:\\SimpleDll.dll";
//Allocate memory in the remote space void * pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE ); if( pLibRemote == NULL ) return false;
//Write the data into the memory allocated ::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL);
//Create the remote thread and begin execution hThread = ::CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) ::GetProcAddress(hUser32,"LoadLibraryA"), pLibRemote, 0, NULL ); WaitForSingleObject(hThread, INFINITE); GetExitCodeThread(hThread, (PDWORD) &nSuccess); if ( nSuccess ) ::MessageBeep(MB_OK); } __finally { if( !nSuccess )// failed? -> deallocate memory VirtualFreeEx( hProcess, pDataRemote, 0, MEM_RELEASE ); if ( hThread != 0 ) CloseHandle(hThread); } CloseHandle( hProcess ); return nSuccess;
}
9
Copyright: http://kk-wuti.blogspot.com
Section II – Inside the remote process Summary Now that our DLL is loaded by the remote thread, the codes inside the SimpleDll.dll should be running by the remote process and within the remote process space. We can do whatever we want to do just like any other thread created by the remote process. While thread created directly by the remote process may have for instance a Windows object inside the remote process directly passed in from the main thread, here we are not able to do that. The parameter we passed in to the thread that we have just created using CreateRemoteThread is just the path to the SimpleDll.dll.
To be continued in Practical guides to Windows API hooking Part 3.
10