Process Dynamic Interception Injection API HOOK

Posted by ac1dsp3ctrum on Sat, 25 May 2019 23:55:16 +0200

Recently, I met a problem in my work. I need to judge the running state of the program through the program interface. At first, I thought it was very simple. It was not just a form control to get Button's status and Text. It happened that I did it last year. As the project delivery time approached, I was ready to start solving the problem. Suddenly, the software author was smart and changed the pattern.

As you can see from the figure, the text and other information on the button here is not manipulated through Button information. Meditation guess is to write information directly through the Windows API, so which API can write it. It's not difficult to find that, in fact, it's DrawText, the way to write text in the window program. At the same time, it's implicitly remembered that SetWindows Text also has this function. Since there is a guess, let's take a look at it and test it.

Before we start to work, we need to rationalize our thinking.

  1. Since the program calls the DrawText method, if I want to get the text, I must intercept the method.
  2. The interception method is not to use the famous Hook function on Windows.
  3. Speaking of hook functions, this is divided into hook window messages and hook API functions. For this article, of course, the latter. There are two main links in realizing API HOOK:
    • How to inject code into the target address space
    • How to Call Your Code
  4. A little inquiry into the data shows that the hook window function seems to be complicated. If you want to study the details, please Refer to this article.
  5. For further information, I found that the library was developed by Microsoft itself: Detours (which I will provide, of course). Download links ) Detours is used to intercept API functions in Win32 binary code. It replaces the first few bytes of the objective function with a JMP instruction, so that the control calls the implemented Detours function directly. The function call of the original function is retained through a trampoline function.
  6. So far, this library has been used. There should be no problem with the documentation. But the document is injected into DetourCreateProcessWithDll by creating a process, which does not match my current application scenario. The client program has been running, and I need to get dynamic injection. So the problem arises. How to solve it?
BOOL WINAPI DetourCreateProcessWithDll(LPCSTR lpApplicationName,
                                        LPSTR lpCommandLine,
                                        ...);
  1. If we can raise questions, we can basically solve them. Otherwise, through the tireless efforts of Baidu and Google, we finally found that the original Detours 1.5 version has a method of DetourContinue Process WithDll, which is dynamic injection, and then we can start to work. (As for the principle of Detours, this article will not go on to elaborate, please query information, this paper is to solve practical problems.)
BOOL WINAPI DetourContinueProcessWithDll(HANDLE hProcess, LPCSTR lpDllName);

After the above steps, there has been a complete idea, the following main combination of practice, code practice. This article mainly carries on the time from the following aspects:

  • First of all, to intercept functions DrawText and SetWindows Text, first implement your own functions (encapsulated by DLL)
  • Then, we dynamically inject our DLL file specifying the process
  • Finally, take it out and slip away. (For simplicity, this article only prints relevant information into DebugView)

Implementing Hook Windows Text Dll

First, according to the Detours programming specification, it is necessary to register with the method DetourFunctionWithTrampoline when loading Hook Windows TextDll. When uninstalling, it is uninstalled by DetourRemove.

The department is mainly divided into the following steps:

  1. Method declarations and implementations requiring Hook
  2. Installation and unloading injection methods

Hook method declaration and Implementation

First, we need to state our approach.

BOOL WINAPI MySetWindowTextA( HWND hWnd, LPCTSTR lpString ); 
BOOL WINAPI MySetWindowTextW( HWND hWnd, LPCWSTR lpString ); 

int WINAPI MyDrawTextA( HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat );
int WINAPI MyDrawTextW( HDC hDC, LPCWSTR lpString, int nCount, LPRECT lpRect, UINT uFormat );

//This method mainly uses Real_Set Windows TextA to save the address of the original function Set Windows TextA, which is convenient for later calls.
DETOUR_TRAMPOLINE( BOOL WINAPI Real_SetWindowTextA( HWND a0, LPCTSTR a1 ), SetWindowTextA );
DETOUR_TRAMPOLINE( BOOL WINAPI Real_SetWindowTextW( HWND a0, LPCWSTR a1 ), SetWindowTextW );

DETOUR_TRAMPOLINE( int WINAPI Real_DrawTextA( HDC a0, LPCTSTR a1, int a2, LPRECT a3, UINT a4 ), DrawTextA );
DETOUR_TRAMPOLINE( int WINAPI Real_DrawTextW( HDC a0, LPCWSTR a1, int a2, LPRECT a3, UINT a4 ), DrawTextW );

//Code implementation, limited to length, lists only MySet Windows TextA
BOOL WINAPI MySetWindowTextA( HWND hWnd, LPCTSTR lpString )
{
#ifdef _DEBUG
    char strMsg[ 1024 ]={0};
    wsprintf( strMsg, "SetWindowTextA : %s. size = %ld\n", lpString, strlen(lpString) );
    OutputDebugString( strMsg );
#endif

    return Real_SetWindowTextA( hWnd, lpString );
}

Installation and Unloading of Hook Method

BOOL APIENTRY DllMain( HANDLE hModule, 
    DWORD  ul_reason_for_call, 
    LPVOID lpReserved )
{
    if( DLL_PROCESS_ATTACH == ul_reason_for_call )//dll loading
    {
        InstallProbes();
    }
    else if( DLL_PROCESS_DETACH == ul_reason_for_call )//dll uninstall
    {
        UninstallProbes();
    }
    else;

    return TRUE;
}

Interception injection is performed through the method DetourFunctionWithTrampoline, which is prototyped as follows:

BOOL  WINAPI DetourFunctionWithTrampoline(PBYTE pbTrampoline,
                                          PBYTE pbDetour);

This function has two parameters, pbTrampoline and a pointer to the pbDetour function. Why Target did not do the objective function
It is a parameter because it has been coded into the pbTrampoline function (DETOUR_TRAMPOLINE is coded above).

BOOL InstallProbes()
{
    DetourFunctionWithTrampoline( (PBYTE)Real_SetWindowTextA, (PBYTE)MySetWindowTextA );
    DetourFunctionWithTrampoline( (PBYTE)Real_SetWindowTextW, (PBYTE)MySetWindowTextW );

    DetourFunctionWithTrampoline( (PBYTE)Real_DrawTextA, (PBYTE)MyDrawTextA );
    DetourFunctionWithTrampoline( (PBYTE)Real_DrawTextW, (PBYTE)MyDrawTextW );

    OutputDebugString("InstallProbesA ok.\n");
    return TRUE;
}

BOOL UninstallProbes()
{
    DetourRemove( (PBYTE)Real_SetWindowTextA, (PBYTE)MySetWindowTextA );
    DetourRemove( (PBYTE)Real_SetWindowTextW, (PBYTE)MySetWindowTextW );

    OutputDebugString("UNInstallProbesB ok.\n");
    return TRUE;
}

So far, the method of intercepting injection is completed. The next section is how to do dynamic injection.

Dynamic injection of Hook Windows TextDll

Since dynamic injection is required, first look at the use of the dynamic injection method DetourContinueProcessWithDll

//Injecting a dynamic link library into a new process
BOOL WINAPI DetourContinueProcessWithDllA(HANDLE hProcess, LPCSTR lpDllName)

There are two parameters in this method, which is clear at first glance.

  • hProcess: The original process handle to be injected
  • lpDllName: The Dell path that needs to be injected, in this article, Hook Windows TextDll.Dll

Then the process handle should be acquired first, and the process handle can be acquired by the following methods:

Getting process ID by process name

DWORD   GetProcessIdFromProcessName(std::string processname)
{
    DWORD dwRet = 0;
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap != INVALID_HANDLE_VALUE)
    {
        BOOL bMore = ::Process32First(hProcessSnap, &pe32);
        while (bMore)
        {
            if (boost::iequals(pe32.szExeFile, processname))
            {
                dwRet = pe32.th32ProcessID;
                 break;
            }
            bMore = ::Process32Next(hProcessSnap, &pe32);
        }
        ::CloseHandle(hProcessSnap);
    }
    return dwRet;
}

Call the test:

std::string str1 = "WireCut.EXE";
DWORD dwProcessId = GetProcessIdFromProcessName(str1);
std::cout << dwProcessId << std::endl; //

If you get the process, go to the next section and get the handle.

Getting process handles through process ID

The OpenProcess function is used to open an existing process object and return a handle to the process.

HANDLE OpenProcess(
        DWORD dwDesiredAccess, 
        BOOL bInheritHandle, 
        DWORD dwProcessId
);

Parameters:

  • dwDesiredAccess: The process access rights you want to have
    • PROCESS_ALL_ACCESS: All permissions available
    • PROCESS_CREATE_PROCESS: You need to create a process
    • PROCESS_CREATE_THREAD: You need to create a thread
    • PROCESS_DUP_HANDLE: Reuse DuplicateHandle Handle
    • PROCESS_QUERY_INFORMATION: Permission to obtain process information, such as its exit code, priority
    • PROCESS_SET_INFORMATION: Setting permissions for certain information, such as process priority
    • PROCESS_SET_QUOTA: Permissions to set memory limits, using SetProcess WorkingSetSize
    • PROCESS_SUSPEND_RESUME: Permissions to suspend or restore processes
    • PROCES_TERMINATE: The right to terminate a process, using TerminateProcess
    • PROCESS_VM_OPERATION: Permission to manipulate process memory space (available VirtualProtectEx and WriteProcessMemory)
    • PROCESS_VM_READ: Permission to read process memory space, using ReadProcessMemory
    • PROCESS_VM_WRITE: Permission to read process memory space, using WriteProcessMemory
    • SYNCHRONIZE: Waiting for process termination
  • bInheritHandle: Indicates whether the resulting process handle can be inherited
  • dwProcessId: PID of the process being opened

Return type:

  • If successful, the return value is a handle to the specified process.
  • If it fails, the return value is NULL, and GetLastError() can be called to get the error code.
HANDLE GetProcessHandle(DWORD nID)
{
    //All permissions available to PROCESS_ALL_ACCESS
    return OpenProcess(PROCESS_ALL_ACCESS, FALSE, nID);
}
DWORD dwProcessId = GetProcessIdFromProcessName("WireCut.EXE");
if (dwProcessId != 0)
{
    bRet = DetourContinueProcessWithDllW(GetProcessHandle(dwProcessId), szDllFilePath); 
}

This completes all the work, and the project code and library files are provided later.

Links: https://pan.baidu.com/s/1c09LWg9zo5NIVwR2htJYZA
Extraction code: f0kt

Welcome to pay attention to exchange and common progress

Blog address: wqliceman.top

Topics: PHP Windows Google Programming