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 ----