easyhook source code analysis II - injection

A method of injection EasyHook.

Function prototype

// EasyHook naming more interesting, is represented by Rh Remote Hook, this is a sub-function remote hook ---- injection process, it represents the front of the macro function is derived. 
RhInjectLibrary EASYHOOK_NT_EXPORT ( 
        ULONG InTargetPID, 
            ULONG InWakeUpTID, // if the current function is invoked by RhCreateAndInject over, this indicates that the main thread ID.
                             // After the process can be woken up RhWakeUpProcess otherwise pass 0. 
        ULONG InInjectionOptions, 
        WCHAR * InLibraryPath_x86, 
        WCHAR * InLibraryPath_x64, 
        PVOID InPassThruBuffer, 
        ULONG InPassThruSize)

Details of

       the front part is a function of a number of different initialization parameters and variables for checking the different X86 and X64 are prepared. After checking whether the cross WOW64 injection is then exit with an error. In addition, the code needs to use PROCESS_ALL_ACCESS permission to call OpenProcess. After the above checks behind is the real injection process. To understand the process must be injected REMOTE_INFO need to understand the structure and its position in relation to inject code target process address space.

REMOTE_INFO structure
#define WRAP_ULONG64(Decl)\
union\
{\
    ULONG64 UNUSED;\
    Decl;\
}\
typedef struct _REMOTE_INFO_
{
    // will be the same for all processes
    WRAP_ULONG64(wchar_t* UserLibrary); // fixed 0
    WRAP_ULONG64(wchar_t* EasyHookPath); // fixed 8
    WRAP_ULONG64(wchar_t* PATH); // fixed 16
    WRAP_ULONG64(char* EasyHookEntry); // fixed 24
    WRAP_ULONG64(void* RemoteEntryPoint); // fixed 32
    WRAP_ULONG64(void* LoadLibraryW); // fixed; 40
    WRAP_ULONG64(void* FreeLibrary); // fixed; 48
    WRAP_ULONG64(void* GetProcAddress); // fixed; 56
    WRAP_ULONG64(void* VirtualFree); // fixed; 64
    WRAP_ULONG64(void* VirtualProtect); // fixed; 72
    WRAP_ULONG64(void* ExitThread); // fixed; 80
    WRAP_ULONG64(void* GetLastError); // fixed; 88

    BOOL            IsManaged;     // 指示是否为托管代码
    HRemoteSignal HANDLE; // for indicating whether successful injection 
    DWORD hostprocess; 
    DWORD Size; 
    BYTE * the UserData;       // custom parameter 
    DWORD UserDataSize;   // custom size parameter 
    ULONG WakeUpThreadID; 
} REMOTE_INFO, * LPREMOTE_INFO;

  The foregoing structure is the function pointer, 8-byte aligned in. Further, these function pointers are obtained by GetRemoveFuncAddress function. After completion of the filling remote function address, the size of compiled code according to the code pattern is determined by the tail assembly code GetInjectionSize function, after a single application "code size structure size + + REMOTE_INFO string 'HookCompleteInjection'- X86 in the target process or '_HookCompleteInjection'-X64 + length + length + the current working directory of the current position of the module length + the target DLL path length. "

Details of the target process memory usage

 

 

 

We can see this prototype and implement code injection compliant thread function, and its argument is a pointer REMOGE_INFO structure. After the application memory in the target process and write data (containing the code), we should think of ways to execute our code, or as a remote thread function, there are two ways:

Use the remote thread function the way inject
introduce remote thread injection refer to my article before http://blog.csdn.net/qq_18218335/article/details/75246816

method of injecting a thread hijack
X64
http://blog.csdn.net/ qq_18218335 / Article this article was / the Details 75,308,957 /
the Win32-HTTP: //blog.csdn.net/qq_18218335/article/details/75268251
thinking of here before we introduce is the same, but the method used in EasyHook more stable, consider the more thoughtful.

 

 

EasyHook achieve thread hijacking

Function prototype

EASYHOOK_NT_EXPORT RhCreateStealthRemoteThread(
            ULONG InTargetPID,
            LPTHREAD_START_ROUTINE InRemoteRoutine,
            PVOID InRemoteParam,
            HANDLE* OutRemoteThread)

 

 

Using the data structure

typedef struct _STEALTH_CONTEXT_
{
    union
    {
        struct
        {
            /*00*/ WRAP_ULONG64(PVOID      CreateThread);           
            /*08*/ WRAP_ULONG64(PVOID      RemoteThreadStart);
            /*16*/ WRAP_ULONG64(PVOID      RemoteThreadParam);
            /*24*/ WRAP_ULONG64(PVOID      WaitForSingleObject);
            /*32*/ WRAP_ULONG64(HANDLE     hCompletionEvent);
            /*40*/ WRAP_ULONG64(PVOID      CloseHandle);
            /*48*/ union
            {
                WRAP_ULONG64(HANDLE     hRemoteThread);
                WRAP_ULONG64(HANDLE     hSyncEvent);
            };
            /*56*/ WRAP_ULONG64(PVOID      SetEvent);
        };

        ULONGLONG       __Unused__[8];
    };

    ULONGLONG           Rax;
    ULONGLONG           Rcx;
    ULONGLONG           Rdx;
    ULONGLONG           Rbp;
    ULONGLONG           Rsp;
    ULONGLONG           Rsi;
    ULONGLONG           Rdi;
    ULONGLONG           Rbx;
    ULONGLONG           Rip;
    ULONGLONG           RFlags;
    ULONGLONG           R8;
    ULONGLONG           R9;
    ULONGLONG           R10;
    ULONGLONG           R11;
    ULONGLONG           R12;
    ULONGLONG           R13;
    ULONGLONG           R14;
    ULONGLONG           R15;
}STEALTH_CONTEXT, *PSTEALTH_CONTEXT;

 

Hijack a thread common practice, find a child thread activity of the target process, then suspend his view, after the success obtained by its ThreadContext GetThreadContext, and then write the application code, set RIP or EIP in the target process, and then resume thread execution . : Different EasyHook that the following
benefits after <1> before resuming execution thread on the preservation of the value of the register ThreadContext, rather than restoring the thread runs through the code to register values stored in the stack, to do so It is reliable and stable. When implemented in hijacking the thread introduce themselves to achieve, I found that the value of ThreadContext after a pause to get the thread and thread execution after recovery before the first instruction is executed, the portion of the value of the register will change, although later operating results correct, there is no case target process crashes happen, but we should be ThreadContext preservation and restoration in accordance with EasyHook way.
<2> code that is executed after hijacking to create a new thread to complete our task in the new thread, EasyHook the injection of code called StealthRemoteThread reasons here, our goal through the process of an existing thread creates a new thread to perform the injection process, in order to hide the purpose of injecting behavior.
<3> in the main thread to wait for the completion of the injection, and determine whether to inject success, which I was not aware of it.

 

EBX / RBX is STEALTH_CONTEXT structure pointer 
StealthStub_ASM_x86

 

public StealthStub_ASM_x86@0

StealthStub_ASM_x86@0 PROC

; Create thread...
    push        0
    push        0
    push        dword ptr [ebx + 16]        ; save stealth context
    push        dword ptr [ebx + 8]         ; RemoteThreadStart
    push        0
    push        0
    call        dword ptr [ebx + 0]         ; CreateThread(0, NULL, RemoteThreadStart, RemoteThreadParam, 0, NULL);


; signal thread creation...
    push        dword ptr [ebx + 48]        
    mov         dword ptr [ebx + 48], eax
    call        dword ptr [ebx + 56]        ; SetEvent(hSyncEvent);

; wait for completion
    push        -1
    push        dword ptr [ebx + 32]
    call        dword ptr [ebx + 24]        ; WaitForSingleObject(hCompletionEvent, INFINITE)

; close handle
    push        dword ptr [ebx + 32]        
    call        dword ptr [ebx + 40]        ; CloseHandle(hCompletionEvent);

; close handle
    push        dword ptr [ebx + 48]        
    call        dword ptr [ebx + 40]        ; CloseHandle(hSyncEvent);


; restore context
    mov         eax, [ebx + 64 + 8 * 0]
    mov         ecx, [ebx + 64 + 8 * 1]
    mov         edx, [ebx + 64 + 8 * 2]
    mov         ebp, [ebx + 64 + 8 * 3]
    mov         esp, [ebx + 64 + 8 * 4]
    mov         esi, [ebx + 64 + 8 * 5]
    mov         edi, [ebx + 64 + 8 * 6]
    push        dword ptr[ebx + 64 + 8 * 9] ; push EFlags   
    push        dword ptr[ebx + 64 + 8 * 8] ; save old EIP
    mov         ebx, [ebx + 64 + 8 * 7]

    add         esp, 4
    popfd

; continue execution...
    jmp         dword ptr [esp - 8] 

; outro signature, to automatically determine code size
    db 78h
    db 56h
    db 34h
    db 12h
StealthStub_ASM_x86@0 ENDP

  Notes played very clear, the injected thread first created, the remote thread that is injected before the introduction of the parameters or the original parameters. Then thread handle stored in the structure, the triggering event, after waiting for the main thread to create a new thread to get a handle after closing two events. Before returning the original EIP, restore all kinds of registers, and then jump directly to the original EIP continues to run.

 

Injection_ASM_x86

public Injection_ASM_x86@0
Injection_ASM_x86@0 PROC
; no registers to save, because this is the thread main function
; save first param (address of hook injection information)

    mov esi, dword ptr [esp + 4]

; call LoadLibraryW(Inject->EasyHookPath);
    push dword ptr [esi + 8]

    call dword ptr [esi + 40] ; LoadLibraryW@4
    mov ebp, eax
    test eax, eax
    je HookInject_FAILURE_A

; call GetProcAddress(eax, Inject->EasyHookEntry);
    push dword ptr [esi + 24]
    push ebp
    call dword ptr [esi + 56] ; GetProcAddress@8
    test eax, eax
    je HookInject_FAILURE_B

; call EasyHookEntry(Inject);
    push esi
    call eax
    push eax ; save error code

; call FreeLibrary(ebp)
    push ebp
    call dword ptr [esi + 48] ; FreeLibrary@4
    test eax, eax
    je HookInject_FAILURE_C
    jmp HookInject_EXIT

HookInject_FAILURE_A:
    call dword ptr [esi + 88] ; GetLastError
    or eax, 40000000h
    jmp HookInject_FAILURE_E
HookInject_FAILURE_B:
    call dword ptr [esi + 88] ; GetLastError
    or eax, 10000000h
    jmp HookInject_FAILURE_E    
HookInject_FAILURE_C:
    call dword ptr [esi + 88] ; GetLastError
    or eax, 30000000h
    jmp HookInject_FAILURE_E    
HookInject_FAILURE_E:
    push eax ; save error value

HookInject_EXIT:

    push 0
    push 0
    push 0; // shadow space for executable stack part...

; call VirtualProtect(Outro, 4, PAGE_EXECUTE_READWRITE, &OldProtect)
    lea ebx, dword ptr [esp + 8] ; we'll write to shadow space
    push ebx
    push 40h
    push 12
    push ebx
    call dword ptr [esi + 72] ; VirtualProtect@16
    test eax, eax

    jne HookInject_EXECUTABLE

    ; failed to make stack executable
        call dword ptr [esi + 88] ; GetLastError
        or eax, 20000000h
        add esp, 16
        ret

HookInject_EXECUTABLE:
; save outro to executable stack
    mov dword ptr [esp],     0448BD3FFh     ; call ebx [VirtualFree()]
    mov dword ptr [esp + 4], 05C8B0C24h     ; mov eax, [esp + 12]
    mov dword ptr [esp + 8], 0E3FF1024h     ; mov ebx, [esp + 16]
                                            ; jmp ebx [exit thread]

; save params for VirtualFree(Inject->RemoteEntryPoint, 0, MEM_RELEASE);
    mov ebx, [esi + 64] ; VirtualFree()
    push 08000h
    push 0
    push dword ptr [esi + 16]

    lea eax, dword ptr [esp + 12]
    jmp eax

; outro signature, to automatically determine code size
    db 78h
    db 56h
    db 34h
    db 12h

Injection_ASM_x86@0 ENDP

 

First, the remote thread function calls LoadLibraryW load the target DLL, then call GetProcAddress function to get provisions must be implemented in the DLL export function, call the function after the get and pass a user-specified parameters and parameter length. After the call is completed, the function call FreeLibrary function to release the target DLL. After a more powerful action, to open up three bytes of stack region, to modify the properties of the protective region is writable stack is performed, then the copy instruction, and executing the instruction function: release the memory required for injection, and then exit thread. This code would write very well, and memory to run over will be applied for the release of their own. And the code in the release of the memory stack area, and do not worry about freeing memory after execution will cause unauthorized access. Baton! ! !

X64 injected code and comments

public StealthStub_ASM_x64
    int 3
StealthStub_ASM_x64 PROC
    sub         rsp, 8 * 4

    mov         qword ptr[rsp + 40], 0
    mov         qword ptr[rsp + 32], 0
    mov         r9, qword ptr [rbx + 16]    ; RemoteThreadParam
    mov         r8, qword ptr [rbx + 8]     ; RemoteThreadStart
    mov         rdx, 0
    mov         rcx, 0
    call        qword ptr[rbx]              ; CreateThread
    cmp         rax, 0

; signal completion
    mov         rcx, qword ptr [rbx + 48]       
    mov         qword ptr [rbx + 48], rax
    call        qword ptr [rbx + 56]        ; SetEvent(hSyncEvent);

; wait for completion
    mov         rdx, -1
    mov         rcx, qword ptr [ebx + 32]
    call        qword ptr [ebx + 24]        ; WaitForSingleObject(hCompletionEvent, INFINITE)   

; close handle
    mov         rcx, qword ptr [rbx + 32]       
    call        qword ptr [rbx + 40]        ; CloseHandle(hCompletionEvent);

; close handle
    mov         rcx, qword ptr [rbx + 48]       
    call        qword ptr [rbx + 40]        ; CloseHandle(hSyncEvent);

; restore context
    mov         rax, [rbx + 64 + 8 * 0]
    mov         rcx, [rbx + 64 + 8 * 1]
    mov         rdx, [rbx + 64 + 8 * 2]
    mov         rbp, [rbx + 64 + 8 * 3]
    mov         rsp, [rbx + 64 + 8 * 4]
    mov         rsi, [rbx + 64 + 8 * 5]
    mov         rdi, [rbx + 64 + 8 * 6]
    mov         r8, [rbx + 64 + 8 * 10]
    mov         r9, [rbx + 64 + 8 * 11]
    mov         r10, [rbx + 64 + 8 * 12]
    mov         r11, [rbx + 64 + 8 * 13]
    mov         r12, [rbx + 64 + 8 * 14]
    mov         r13, [rbx + 64 + 8 * 15]
    mov         r14, [rbx + 64 + 8 * 16]
    mov         r15, [rbx + 64 + 8 * 17]
    push        qword ptr[rbx + 64 + 8 * 9] ; push EFlags   
    push        qword ptr[rbx + 64 + 8 * 8] ; save old EIP
    mov         rbx, [rbx + 64 + 8 * 7]

    add         rsp, 8
    popfq

; continue execution...
    jmp         qword ptr [rsp - 16]    

; outro signature, to automatically determine code size
    db 78h
    db 56h
    db 34h
    db 12h
StealthStub_ASM_x64 ENDP


public Injection_ASM_x64

Injection_ASM_x64 PROC
; no registers to save, because this is the thread main function
    mov         r14, rcx ; r14 当前存储的为LPREMOTE_INFO
    sub         rsp, 40  ; space for register parameter stack, should be 32 bytes... no idea why it only works with 40

; call LoadLibraryW(Inject->EasyHookPath);
    mov         rcx, qword ptr [r14 + 8]
    call        qword ptr [r14 + 40] ; LoadLibraryW
    mov         r13, rax
    test        rax, rax
    je          HookInject_FAILURE_A

; call GetProcAddress(hModule, Inject->EntryPoint)
    mov         rdx, qword ptr [r14 + 24] 
    mov         rcx, rax 
    call        qword ptr [r14 + 56] ; GetProcAddress 
    test        rax, rax
    je          HookInject_FAILURE_B

; call EasyHookEntry(Inject);
    mov         rcx, r14
    call        rax
    mov         r15, rax ; save error code to non-volatile register

; call FreeLibrary(hEasyHookLib)
    mov         rcx, r13
    call        qword ptr [r14 + 48] ; FreeLibrary
    test        rax, rax
    je          HookInject_FAILURE_C

    jmp         HookInject_EXIT

HookInject_FAILURE_A:
    call        qword ptr [r14 + 88] ; GetLastError
    or          rax, 40000000h
    jmp         HookInject_FAILURE_E
HookInject_FAILURE_B:
    call        qword ptr [r14 + 88] ; GetLastError
    or          rax, 10000000h
    jmp         HookInject_FAILURE_E    
HookInject_FAILURE_C:
    call        qword ptr [r14 + 88]; The GetLastError 
    or          rax, 30000000h
    JMP HookInject_FAILURE_E     
HookInject_FAILURE_E: 
    MOV R15, RAX; Save error value 

HookInject_EXIT: 

; Call the VirtualProtect (Outro, . 8 , PAGE_EXECUTE_READWRITE,, & OldProtect) 
; Outro herein is & OldProtect, i.e. the application at the beginning of this thread function of a local variable 
    RBX LEA, QWORD PTR [RSP + . 8 ]; Parameter Stack Register Writes INTO 
    MOV R9, RBX 
    MOV R8, 40H 
    MOV RDX, . 8 
    MOV RCX, RBX 
    Call QWORD PTR [R14 + 72 ]; the VirtualProtect 
    Test RAX, RAX

    JNE HookInject_EXECUTABLE 

    ; failed to the make Stack Executable 
        Call QWORD PTR [R14 + 88 ]; the GetLastError 
        or RAX, 01000000h 
        MOV RCX, RAX 
        Call QWORD PTR [R14 + 80 ]; the ExitThread 

HookInject_EXECUTABLE: 
; Save Outro to Executable Stack 
    MOV RBX, [R14 + 64 ]; the VirtualFree () 
    MOV RBP, [R14 + 80 ]; the ExitThread () 

    MOV RAX, 000D5FFCF8B49D3FFh 
        ; similar shellcode, will jump back to & OldProtect performed at three directives 
        ; R15 is a return value code EsayHookEntry
        ; Call RBX 
        ; MOV RCX, R15 
        ; Call RBP 

    MOV QWORD PTR [RSP + . 8 ], RAX 

; Save the params  for the VirtualFree (Inject-> RemoteEntryPoint, 0 , MEM_RELEASE); 
; here directly to the deleted parameter RemoteEntryPoint ..... 
    MOV R8, 8000h 
    MOV RDX, 0h 
    MOV RCX, QWORD PTR [R14 + 32 ] 

    LEA RAX, QWORD PTR [RSP + . 8 ] 
    Sub RSP, 48 
    JMP RAX 

; Outro Signature, to Automatically the Determine code size 
    DB 78H  
    db 56h
    DB 34h 
    DB 12h 

Injection_ASM_x64 ENDP

 

 

 

 

 

Quote

Analysis realize part of the function in EasyHook injection ----

Guess you like

Origin www.cnblogs.com/code1992/p/11580382.html