Hook Windows API (ZT)

xiaoxiao2021-03-06  44

Hook Windows API --------------------------------------------------------------------------------------------- -------------------------------- Source: XFOCUS.NET Category: Safety Date: 2004-3-10 12 : 30: 43

=========================== [hook Windows API] ========================================================================================================================================================

Sobeit

Author: holy_father Version: 1.1 Englishdate: 6.10.2002

===== [1. content] ======================================== =====

1. Content 2. Introduction 3. Hook Method 3.1 Running Hook 3.2 Running Hook 3.2.1 Using IAT Hook Processes 3.2.2 Remove the entry point hooks Processes 3.2.3 Save the original function 3.2.4 Hook other processes 3.2.4.1 DLL injection 3.2.4.2 independent code 3.2.4.3 Original modification 4. Concluding

===== [2. Introduction] ========================================= ============

This article is a method for hook API functions under OS Windows. All examples are valid in NT technology-based Windows version NT 4.0 and above (Windows NT 4.0, Windows 2000, Windows XP). It may be effective in other Windows systems. You should be more familiar with the process, the assembler, PE file structure, and some API functions, in order to understand the content in this article. Here, use "hooking API" term representation to completely modify the API. When calling the hooked API, our code can be executed immediately. I will write a complete hook process.

===== [3. Hook method] ======================================== =======

Generally speaking, our purpose is to replace the code in some functions in our code. These problems can sometimes be solved before the process is running. Most of these times can be done with our user-level processes, and the purpose can be the behavior of modifying the program. For example, the crack of the application is, for example, some programs will require the original disc while startup, we want to start it without the CD. If we modify the function of getting a drive type, we can let the program start from the hard drive. When we hook system processes (such as service) These can't do it or we don't plan to do so, or in this example we don't know which process is the goal. At this time we have to use dynamic hooks (hook at runtime). Examples of use have a technique of antifrobic software in rootkit or viruses. ===== [3.1 Before running] ======================================== ====

This modifies what we want to modify the physical module from the function (most of it is .exe or .dll). Here we have at least three possible practices. The first one may be the entry point of the function and then override it. This will be restricted by the size of the function, but we can dynamically load other modules (API LoadLibrary, so it should be enough. The kernel function (kernel32.dll) is generic because each process in Windows has a copy of this module. Another benefit is if we know which modules will be modified in a version, we can use direct pointers in some APIs such as LoadLibrarya. This is because the kernel module is fixed in the same Windows version in the same Windows version. We can also use dynamically loaded modules. Here, its initialization section will run immediately after loading in memory. We are not restricted in the initialization part of the new module. The second may be that the function replaced in the module is only the extension of the original function. Then we choose whether the 5 bytes that you want to modify are jump instructions or override IAT. If you change to jump instructions, you will change the instruction execution process to perform our code. If the IAT record modified function is called, our code can be executed after the call is completed. But the extension of the module is not so easy, because we must pay attention to the DLL head. The next is to modify the entire module. This means we create our module version, which can load the original module and call the original function, of course, we are not interested in this, but the important functions are updated. This approach is very inconvenient for some of the excessive export functions of some modules.

===== [3.2 Runtime hook] ========================================= ===

The hook is usually very special before running and is an interior or a specific application (or module). If we replace the function in kernel32.dll or ntdll.dll (only in the NT operating system), we can perfectly replace this function in all processes that will run. But it is very difficult to say that it is easy to do, because we not only consider accuracy and need to write a relatively perfect new function or new module, but the main problem is that the process to be run can be hooked (you want to hook all processes can only restart the computer ). Another problem is how to enter these files because the NT operating system protects them. A better solution is hooked when the process is running. This requires more knowledge, but the final result is quite good. The hook is hook only for the process that can be written into their memory. In order to write it yourself we use the API function WriteProcessMemory. Now let's start running our process. ===== [3.2.1 Use IAT hooks] ===================================

There are many possibilities here. First introduce how to use the method of rewriting the IAT hook function. Next this picture describes the structure of the PE file:

------------------------------ - Offset 0 | MS DOS Sign ("MZ") and DOS block | ------------------------------- | PE logo ("PE") | -------- ----------------------- | .Text | - Module Code | Program Code || | ------------ ------------------- | .data | - Initialized (global static) data | The initialized data || | ------ ----------------------- | .idata | Information and Data | Import Function | Import Table | | -------- ---------------------- |. Information and data for export functions | Export Table | | | ------ ----------------------- | Commissioning symbols | --------------------- ----------

Here is more important to us, the import address table (IAT) of the .idata section. This section contains the imported information and the address of the import function. One thing is very important that we must know how the PE file is created. When you indirectly call any API in the programming language (this means we call it with the name of the function, not with its address), the compiler does not directly connect the call to the module, but use the JMP instruction connection to call to IAT, IAT will be filled by the process loader when the process is transferred to the memory. This is why we can use the same binary code in two different versions of Windows, although modules may load to different addresses. The process loader will fill in the JMP instruction that directly jumps in the IAT in the program code. So we can find the specified function we want to hook in IAT, we can easily change the JMP instruction there and redirect the code to our address. Every call will execute our code each time. The disadvantage of this method is that there are often many functions to be hooked (for example if we want to change the program in the API of the Search File We have to modify the function FindfirstFile and FindNextFile, but we have to know that these functions have ANSI and WIDE versions. so we had to modify FindFirstFileA, FindFirstFileW, FindNextFileA and FileNextFileW of IAT address. but there are other similar functions such as FindFirstFileExA and its WIDE version FindFirstFileExW, it is also mentioned by the previous function call. we know FindFirstFileW call FindFirstFileExW But this is directly called instead of using IAT. For example, SHELLAPI's function ShgetDesktopFolder also calls FindfirstFILWW or FindFirstFILEXW). If we can get all of them, the result will be perfect. We can easily find IAT by using ImageDirectoryEntryTodata in Imagehlp.dll. PVOID ImageDirectoryEntryTodayTodayTodayTodata (In LPVOID BASE, IN BOOLEAN MAPPEDASIMAGE, IN USHORT DIRECTORYENTRY, OUT PULONG SIZE);

Here, the base parameter can be used by our program's instance (Instance by calling getModuleHandle):

Hinstance = getModuleHandlea (NULL);

DirectoryEntry We can use constant image_directory_entry_import.

#DEFINE Image_DIRECTORY_ENTRY_IMPORT 1

The result of the function is to point to the first IAT record pointer. All records of IAT are structures defined by image_import_descriptor. So the function result is the pointer to Image_import_Descriptor.

Typedef struct _image_thunk_data {UNITION {Pbyte Forwarderstring; pdword function; dword order_import_by_name addressofdata;};} image_thunk_data, * pimage_thunk_data;

typedef struct _IMAGE_IMPORT_DESCRIPTOR {union {DWORD Characteristics; PIMAGE_THUNK_DATA OriginalFirstThunk;}; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; PIMAGE_THUNK_DATA FirstThunk;} IMAGE_IMPORT_DESCRIPTOR, * PIMAGE_IMPORT_DESCRIPTOR;

Image_import_descriptor's NAME member variable is a pointer to the module name. If we want to hook a function such as from kernel32.dll, we look at the descriptor symbol belonging to the name kernel32.dll in the import table. We first call imageDirectoryEntryTodata and find the name "kernel32.dll" description symbol (possibly not only one description symbol is this name), and finally we find the function we want in all functions in this module record (function address GetProcAddress function is obtained). If we find the VirtualProtect function to change the protection properties of the memory page, then you can write to the code in these parts in memory. After rewriting the address, we must change the protection attribute back. We must first know the information about the page before calling VirtualProtect, which is implemented through VirtualQuery. We can join some tests to prevent certain functions fail (for example, if the first call VirtualProcTect failed, we can't continue). PCSTR pszHookModName = "kernel32.dll", pszSleepName = "Sleep"; HMODULE hKernel = GetModuleHandle (pszHookModName); PROC pfnNew = (PROC) 0x12345678, // where to store the new address pfnHookAPIAddr = GetProcAddress (hKernel, pszSleepName);

Ulong ulsize; pimage_import_descriptor pimportdesc = (pimage_import_descriptor) ImageDirectoryEntryTodata (Hkernel, true, image_directory_entry_import, & ulsize);

While (PimportDesc-> Name) {pstr pszmodname = (pstr) (PBYTE) Hkernel PimportDesc-> Name); if (stricmp (pszmodname, pszhookmodname) == 0) Break; PimportDesc ;}

PIMAGE_THUNK_DATA PTHUNK = (Pimage_thunk_data) (PBYTE) Hkernel PimportDesc-> firstthunk);

While (pthunk-> u1.function) {proc * ppfn = (proc *) & pthunk-> u1.function; bool bfound = (* ppfn == pfnhookiaddd);

IF (bFound) {memory_basic_information mbi; VirtualQuery (PPFN, & MBI, SIZEOF (Memory_basic_information)); VirtualProtect (Mbi.Baseaddress, Mbi.RegionSize, Page_Readwrite, & Mbi.Protect))

* ppfn = * pfnnew;

DWORD DWOLDPROTECT; VirtualProtect (Mbi.Baseaddress, Mbi.Regionsize, Mbi.Protect, & dwoldprotect); Break;} Pthunk ;}

The result of calling SLEEP (1000) as shown in example:

00407BD8: 68E8030000 Push 0000003E8H00407BDD: E812FAFFFFFFFFFFFFFFFFF

Sleep:; This is a jump to the IAT address 004075F4: FF25BCA14000 jmp dword ptr [00040A1BCh] the original table: 0040A1BC: 79 67 E8 77 00 00 00 00

New Table: 0040A1BC: 78 56 34 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

So finally jump to 0x12345678.

===== [3.2.2 Remove the entry point hook this process] ==================

Some bytes starting to rewrite the function entry point This method is quite simple. Like the address that changes IAT, we must first modify the page properties first. Here, the function we want to hook is 5 bytes. We used to use our dynamically assigned a Memory_basic_information structure. The start address of the function is also obtained with getProcAddress. We insert the jump instruction points to our code in this address. Next program calls SLEEP (5000) (so it will wait 5 seconds), then the SLEEP function is hooked and redirected to new_sleep, and finally call Sleep (5000) again. Because the new function new_sleep does not do and returns directly, the entire program only takes 5 seconds rather than 10 seconds.

.386p.model flat, stdcall

includelib lib / kernel32.libSleep PROTO: DWORDGetModuleHandleA PROTO: DWORDGetProcAddress PROTO: DWORD,: DWORDVirtualQuery PROTO: DWORD,: DWORD,: DWORDVirtualProtect PROTO: DWORD,: DWORD,: DWORD,: DWORDVirtualAlloc PROTO: DWORD,: DWORD,: DWORD ,: DWORDVIRTUALFREE PROTO: DWORD,: DWORD,: DWORDFLUSHINSTRUCACAE PROTO: DWORD,: DWORD,: DWORDGETCURRENTPROCESS ProtoExitProcess Proto: DWord

.DATA

KERNEL_NAME DB "kernel32.dll", 0Sleep_name DB "SLEEP", 0OLD_PROTECT DD?

Memory_basic_information_size EQU 28

Page_readwrite dd 000000004hpage_execute_readwrite dd 000000040HMEM_COMMIT DD 000001000HMEM_RELEASE DD 000008000H

.CODESTART: PUSH 5000CALL SLEP

Do_hook: push offset kernel_namecall getModuleHandleapush Offset Sleep_namepush EDCALL GETPROCADDRESSMOV EDI, EAX; Finally get a SLEEP address

Push Page_readwritepush Mem_Commitpush Memory_basic_information_sizepush 0Call Virtualaloctest ESI, ESJZ DO_SLEPMOV ESI, EAX; Assign Memory for MBI Structures

Push memory_basic_information_sizepush esipush edicall virtualquery; memory page information TEST EAX, Eaxjz Free_MEM

Call getcurrentprocesspush 5push Edipush EaxCall Flushinstructioncache; just to determine :)

Lea Eax, [ESI 014H] Push Eaxpush Page_execute_readwritelea Eax, [ESI 00ch] Push [ESI 00ch] Call VirtualProtect; We want to modify the protection properties so that you can write to the code test Eax, Eaxjz free_mem

MOV BYTE PTR [EDI], 0E9H; Writing Jump Command MOV Eax, Offset New_Sleepsub Eax, Edisub Eax, 5inc Edistosd; here is a jump address

Push Offset Old_Protectlea Eax, [ESI 014H] Push [EAX] Lea Eax, [ESI 00ch] Push [EAX] Push [ESI] Call VirtualProtect; Recovery Page Protection Attribute

Free_MEM: Push Mem_releasePush 0push EsicalL VirtualFree; Release Memory DO_SLEP: Push 5000Call SleEppush 0Call EXITPROCESSNEW_SLEP: RET 004hend Start

The result of the second call SLEEP is this:

004010A4: 6888130000 Push 000001388H004010A9: E80A000000 CALL SLEEP

Sleep:; Here is the address of jump to IAT 004010B8: FF2514204000 JMP DWORD PTR [000402014H]

Tabulka: 00402014: 79 67 E8 77 6C 7D E8 77

Kernel32.sleep: 77E86779: E937A95788 JMP 0004010B5H

New_sleep: 004010B5: C20400 RET 004H

===== [3.2.3 Save the original function] =====================================

More time we need not only the hook function. For example, maybe we don't want to replace a given function, just want to check its results, or maybe we just want to replace the original function when the function is used to be called. A preferred example has the previously mentioned by replacing the findXXXFile function to complete the hidden file. So if we want to hide the specified file and if you don't want to pay attention, you have to only call the original function that is not modified. This is very simple to use the modified IAT method. We can use getProcAddress to get its original address with getProcAddress, and then call it. But the way to modify the entry point will have a problem because the 5 bytes of the function entry point have been modified, so that we destroy the original function. So we have to save those instructions starting. This will use the following technique. We know that we have to modify the starting 5 bytes but don't know how many instructions are included in the inside and the length of the instruction. We have to start sufficient memory space for those instructions. 16 bytes should be enough, because there is no longer directive when the function starts, and it is very likely that there is no 16 bytes at all. The entire reserved memory is filled with 0x90 (0x90 = NOP). The next 5 bytes reserve jump instructions that will be filled thereon.

Old_hook: DB 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090HDB 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090HDB 0E9H, 000H, 000H, 000H, 000H, 000H

Now we have prepared a copy of the copy start. It is quite troublesome to get the code length of the command length, which is why we have to use the completed engine. It is written by Z0Mbie. The incoming parameter is the address we have to get the length of the command. Output parameters in Eax. LDE32, Length-Disassembler Engine, 32-bit, (x) 1999-2000 Z0Mbie; Special Edition for Revert Tool

Version 1.05

C_MEM1 equ 0001h; | C_MEM2 equ 0002h; | may be used simultaneouslyC_MEM4 equ 0004h; | C_DATA1 equ 0100h; | C_DATA2 equ 0200h; | may be used simultaneouslyC_DATA4 equ 0400h; | C_67 equ 0010h; used with C_PREFIXC_MEM67 equ 0020h; C_67 C_MEM2:? C_MEM4C_66 equ 1000h; used with C_PREFIXC_DATA66 equ 2000h; c_66 C_DATA2:?.? C_DATA4C_PREFIX equ 0008h; prefix take opcode againC_MODRM equ 4000h; MODxxxR / MC_DATAW0 equ 8000h; opc & 1 C_DATA66: C_DATA1

P386Model Flatlocals @@

.code

PUBLIC DISASM_MAINPUBLIC _DISASM_MAINPUBLIC @DISASM_MAINPUBLIC DISASM_MAIN

DISASM_MAIN: _DISASM_MAIN: @disasm_main: disasm_main:

__fastcall eax; __cdecl [ESP 4]

This is my first modification, it is just the declaration of this function Get_instr_len:

MOV ECX, [ESP 4]; ECX = OPCode PTR

XOR Edx, Edx; Sign xor Eax, EAX

@@ prefix: and dl, not c_prefix

MOV Al, [ECX] Inc ECX

OR EDX, TABLE_1 [EAX * 4]

TEST DL, C_PREFIXJNZ @@ prefix

CMP AL, 0F6HJE @@ TestCmp Al, 0F7HJE @@ TEST

CMP Al, 0CDHJE @@ int

CMP Al, 0FHJE @@ 0f @@ Cont: Test DH, C_DataW0 shr 8jnz @@ DataW0 @@ DataW0done: Test DH, C_MODRM SHR 8JNZ @@ modrm @@ exitmodrm: Test DL, C_MEM67JNZ @@ MEM67 @@mie7done: test dh , C_data66 shr 8jnz @@ data66 @@ data66done: MOV Eax, ECXSUB EAX, [ESP 4]

AND EDX, C_MEM1 C_MEM2 C_MEM4 C_DATA1 C_DATA2 C_DATA4ADD AL, DLADD AL, DH

Here is my second modification, only in the original version is Retn @@ EXIX: RET 00004H

@@ Test: OR DH, C_MODRM SHR 8TEST BYTE PTR [ECX], 00111000B; F6 / F7 - TestJNz @@ Contra, c_dataw0 shr 8jmp @@ Cont @@ int: or dh, c_data1 shr 8cmp byte PTR [ECX] 20hjne @@ Contor Dh, c_data4 shr 8jmp @@ Cont

@@ 0f: MOV Al, [ECX] Inc ECXOR EDX, TABLE_0F [EAX * 4]

CMP EDX, -1JNE @@ Cont

@@ error: MOV EAX, edxjmp @@ exit

@@ dataw0: xor dh, c_data66 shr 8test al, 00000001bjnz @@ dataw0donexor DH, (c_data66 c_data1) shr 8jmp @@ DataW0done

@@ MEM67: XOR DL, C_MEM2TEST DL, C_67JNZ @@mem67donexor DL, C_MEM4 C_MEM2JMP @@mem67done

@@ Data66: XOR DH, C_DATA2 SHR 8TEST DH, C_66 SHR 8JNZ @@ Data66Donexor DH, (C_Data4 C_Data2) Shr 8jmp @@ data66done

@@ modrm: MOV Al, [ECX] Inc ECX

MOV AH, Al; AH = MOD, Al = RM

And AX, 0C007HCMP AH, 0C0HJE @@ EXIXTMODRM

TEST DL, C_67JNZ @@ modrm16

@@ modrm32: CMP Al, 04HJNE @@ a

MOV Al, [ECX]; Sibinc ECXAND Al, 07H

@@ A: CMP AH, 40HJE @@mem1cmp Ah, 80HJE @@mie

CMP AX, 0005HJNE @@ EXITMODRM

@@ MEM4: OR DL, C_MEM4JMP @@ exitmodrm

@@MEM1: OR DL, C_MEM1JMP @@ EXITMODRM

@@ modRM16: CMP AX, 0006HJE @@MEM2CMP AH, 40HJE @@mem1cmp Ah, 80HJNE @@ EXITMODRM

@@ Mem2: OR DL, C_MEM2JMP @@ EXIXTMODRM

ENDP

.DATA

; 0F - Analysis in the code, no mark (not the flag (FLAG) must be 0); F6, F7 - - // (TTT = 000 - 3 bytes, otherwise 2 bytes ); CD - - // - (if CD 20 is 6 bytes, otherwise 2 bytes)

Table_1 Label DWORD; General Directive

dd C_MODRM; 00dd C_MODRM; 01dd C_MODRM; 02dd C_MODRM; 03dd C_DATAW0; 04dd C_DATAW0; 05dd 0; 06dd 0; 07dd C_MODRM; 08dd C_MODRM; 09dd C_MODRM; 0Add C_MODRM; 0Bdd C_DATAW0; 0Cdd C_DATAW0; 0Ddd 0; 0Edd 0; 0Fdd C_MODRM ; 10dd C_MODRM; 11dd C_MODRM; 12dd C_MODRM; 13dd C_DATAW0; 14dd C_DATAW0; 15dd 0; 16dd 0; 17dd C_MODRM; 18dd C_MODRM; 19dd C_MODRM; 1Add C_MODRM; 1Bdd C_DATAW0; 1Cdd C_DATAW0; 1Ddd 0; 1Edd 0; 1Fdd C_MODRM; 20dd C_MODRM; 21dd C_MODRM; 22dd C_MODRM; 23dd C_DATAW0; 24dd C_DATAW0; 25dd C_PREFIX; 26dd 0; 27dd C_MODRM; 28dd C_MODRM; 29dd C_MODRM; 2Add C_MODRM; 2Bdd C_DATAW0; 2Cdd C_DATAW0; 2Ddd C_PREFIX; 2Edd 0; 2Fdd C_MODRM; 30dd C_MODRM; 31dd C_MODRM; 32dd C_MODRM; 33dd C_DATAW0; 34dd C_DATAW0; 35dd C_PREFIX; 36dd 0; 37dd C_MODRM; 38dd C_MODRM; 39dd C_MODRM; 3Add C_MODRM; 3Bdd C_DATAW0; 3Cdd C_DATAW0; 3Ddd C_PREFIX; 3Edd 0; 3Fdd 0; 40dd 0; 41dd 0 42DD 0; 43DD 0; 45DD 0; 46DD 0; 47DD 0; 48DD 0; 49DD 0; 4Add 0; 4bdd 0; 4CDD 0; 4DDD 0; 4EDD 0; 4FDD 0; 50DD 0; 53DD 0; 54DD 0; 55DD 0; 56DD 0; 57DD 0; 58DD 0; 59DD 0; 5Add 0; 5Bdd 0; 5Cdd 0; 5Ddd 0; 5Edd 0; 5Fdd 0; 60dd 0; 61dd C_MODRM; 62dd C_MODRM; 63dd C_PREFIX; 64dd C_PREFIX; 65dd C_PREFIX c_66; 66dd C_PREFIX c_67; 67dd C_DATA66; 68dd C_MODRM C_DATA66 ; 69dd C_DATA1; 6Add C_MODRM C_DATA1; 6Bdd 0; 6Cdd 0; 6Ddd 0; 6Edd 0; 6Fdd C_DATA1; 70dd C_DATA1; 71dd C_DATA1; 72dd C_DATA1; 73dd C_DATA1; 74dd C_DATA1; 75dd C_DATA1; 76dd C_DATA1; 77dd C_DATA1; 78dd C_DATA1 79DD C_DATA1; 7ADD C_DATA1; 7BDD C_DATA1; 7DDD C_DATA1; 7 -DD C_DATA1; 7FDD C_MODRM C_DATA1; 80DD C_MODRM C_DATA66;

81dd C_MODRM C_DATA1; 82dd C_MODRM C_DATA1; 83dd C_MODRM; 84dd C_MODRM; 85dd C_MODRM; 86dd C_MODRM; 87dd C_MODRM; 88dd C_MODRM; 89dd C_MODRM; 8Add C_MODRM; 8Bdd C_MODRM; 8Cdd C_MODRM; 8Ddd C_MODRM; 8Edd C_MODRM; 8Fdd 0; 90dd 0; 91DD 0; 92DD 0; 93DD 0; 94DD 0; 95DD 0; 96DD 0; 99DD 0; 98DD 0; 9AdD 0; 9BDD 0; 9CDD 0; 9DDD 0; 9EDD 0; 9FDD C_MEM67; A0DD C_MEM67; A1DD C_MEM67; A2DD C_MEM67; A3DD 0; A4DD 0; A5DD 0; A6DD 0; A7DD C_DATA1; A8DD C_DATA66; A9DD 0; AADD 0; AEDD 0; AFDD 0; AEDD 0; AFDD C_DATA1; B0DD C_DATA1; B1dd C_DATA1; B2dd C_DATA1; B3dd C_DATA1; B4dd C_DATA1; B5dd C_DATA1; B6dd C_DATA1; B7dd C_DATA66; B8dd C_DATA66; B9dd C_DATA66; bAdd C_DATA66; BBdd C_DATA66; BCdd C_DATA66; BDdd C_DATA66; BEdd C_DATA66; BFdd C_MODRM C_DATA1; C0dd C_MODRM C_DATA1; C1DD C_DATA2; C2DD 0; C3DD C_MODRM; C4DD C_MODRM; C5DD C_MODRM C_DATA1; C6DD C_MODRM C_DATA66; C7DD C_DATA2 C_DATA1; C8DD 0; C9DD C_D ATA2; CAdd 0; CBdd 0; CCdd 0; CDdd 0; CEdd 0; CFdd C_MODRM; D0dd C_MODRM; D1dd C_MODRM; D2dd C_MODRM; D3dd C_DATA1; D4dd C_DATA1; D5dd 0; D6dd 0; D7dd C_MODRM; D8dd C_MODRM; D9dd C_MODRM; DAdd C_MODRM; DBdd C_MODRM; DCdd C_MODRM; dDdd C_MODRM; DEdd C_MODRM; DFdd C_DATA1; E0dd C_DATA1; E1dd C_DATA1; E2dd C_DATA1; E3dd C_DATA1; E4dd C_DATA1; E5dd C_DATA1; E6dd C_DATA1; E7dd C_DATA66; E8dd C_DATA66; E9dd C_DATA66 C_MEM2; EBDD 0; ECDD 0; ECDD 0; EEDD 0; EFDD C_PREFIX; F0DD 0; F1DD C_PREFIX; F2DD C_PREFIX; F3DD 0; F4DD 0; F5DD 0; F6DD 0; F7DD 0; F8DD 0; F9DD 0; FADD 0 FBDD 0; FCDD 0; FDDD C_MODRM; FEDD C_MODRM; FF

Table_0f label dword; 0f is the prefix instruction

DD C_MODRM; 00DD C_MODRM; 01DD C_MODRM; 02DD C_MODRM; 03DD -1; 04DD -1; 05DD 0; 06DD -1; 07DD 0; 08DD 0; 09DD 0; 0Add 0; 0BDD -1; 0CDD -1; 0DDD -1 0EDD -1; 0FDD -1; 10DD -1; 11DD -1; 12DD -1; 15DD -1; 16DD -1; 17DD -1; 18DD -1; 19DD -1; 1ADD -1; 1BDD -1; 1CDD -1; 1DDD -1; 1EDD -1; 1FDD-1; 20DD -1; 23DD -1; 24DD -1; 25DD-1; 26DD -1 27DD -1; 28DD -1; 29DD -1; 2ADD -1; 2BDD -1; 2CDD -1; 2DDD -1; 2EDD -1; 2FDD -1; 30DD -1; 31DD-1; 32DD -1; 33DD -1; 34DD -1; 35DD -1; 36DD -1; 37DD -1; 38DD -1; 39DD -1; 3ADD -1; 3DDD -1; 3dd -1; 3FDD -1 40DD -1; 41DD -1; 42DD -1; 43DD -1; 44DD -1; 45DD -1; 46DD -1; 47DD -1; 48DD -1; 49DD -1; 4ADD -1; 4BDD -1; 4CDD -1; 4DDD -1; 4EDD -1; 4FDD -1; 50DD -1; 51DD -1; 52DD -1; 53DD -1; 54DD -1; 55DD -1; 56DD -1; 57DD-1; 58DD -1 59DD -1; 5ADD -1; 5BDD -1; 5CDD -1; 5DDD -1; 5EDD -1; 5FDD -1; 60DD -1; 61DD-1 62DD -1; 63DD -1; 64DD -1; 65DD -1; 66DD -1; 67DD -1; 68DD -1; 69DD -1; 6ADD -1; 6DDD -1; 6edd -1; 6FDD -1; 70DD -1; 71DD -1; 72DD -1; 73DD -1; 74DD -1; 75DD -1; 76DD -1; 77DD -1; 78DD -1; 79DD -1; 7ADD-1 ; 7Bdd -1; 7Cdd -1; 7Ddd -1; 7Edd -1; 7Fdd C_DATA66; 80dd C_DATA66; 81dd C_DATA66; 82dd C_DATA66; 83dd C_DATA66; 84dd C_DATA66; 85dd C_DATA66; 86dd C_DATA66; 87dd C_DATA66; 88dd C_DATA66; 89dd C_DATA66; 8Add C_DATA66; 8Bdd C_DATA66; 8Cdd C_DATA66; 8Ddd C_DATA66; 8Edd C_DATA66; 8Fdd C_MODRM; 90dd C_MODRM; 91dd C_MODRM; 92dd C_MODRM; 93dd C_MODRM; 94dd C_MODRM; 95dd C_MODRM; 96dd C_MODRM; 97dd C_MODRM; 98dd C_MODRM; 99dd C_MODRM; 9Add C_MODRM 9bdd C_ModRM;

9cdd C_MODRM; 9DDD C_MODRM; 9EDD C_MODRM; 9FDD 0; A0DD 0; A1DD 0; A2DD C_MODRM; A3DD C_MODRM C_DATA1; A4DD C_MODRM; A5DD -1; A6DD -1; A7DD 0; A8DD 0; A9DD 0; AADD C_MODRM; ABDD C_MODRM C_DATA1; ACdd C_MODRM; ADdd -1; AEdd C_MODRM; AFdd C_MODRM; B0dd C_MODRM; B1dd C_MODRM; B2dd C_MODRM; B3dd C_MODRM; B4dd C_MODRM; B5dd C_MODRM; B6dd C_MODRM; B7dd -1; B8dd -1; B9dd C_MODRM C_DATA1 ; BADD C_MODRM; BBDD C_MODRM; BDD C_MODRM; BDDD C_MODRM; BEDD C_MODRM; BFDD C_MODRM; C0DD C_MODRM; C1DD -1; C2DD -1; C3DD -1; C4DD -1; C5DD -1; C6DD -1; C7DD 0; C8DD 0; C9DD 0; CADD 0; CBDD 0; CCDD 0; CDDD 0; CEDD 0; CFDD -1; D0DD -1; D3DD -1; D2DD -1; D3DD -1; D4DD -1; D5DD -1; D6DD - 1; D7DD -1; D9DD -1; D9DD -1; DBDD -1; DCDD -1; DDDD -1; DEDD -1; DFDD -1; E0DD -1; E1DD -1; E2DD -1; E3DD -1; E5DD -1; E6DD -1; E7DD -1; E8DD -1; E9DD -1; ECD -1; EBDD -1; ECDD -1; EDD -1; EEDD -1; EFDD - 1; F0DD -1; F1DD -1; F2DD -1; F3DD -1; F4DD -1; F5DD -1; F6DD -1; F9DD -1; FADD -1; FBDD -1; FCDD -1; FDDD -1; fedd - 1; FFEND

Now we can get the command length of any address. We repeatedly call this function until 5 bytes are read. After completing, copy these bytes to Old_hook. We know the length of these instructions, so we can fill in the jump address in the lower instruction of the original function.

.386p.model flat, stdcall

...

.DATA

Kernel_name DB "kernel32.dll", 0Sleep_name DB "Sleep", 0

...

MEM_RELEASE DD 000008000H

16 NOPS a jump instruction Old_sleep DB 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H, 030H, 090H, 090H, 0E9H, 000H, 000H, 000H, 000H

.CODESTART: PUSH 5000CALL SLEP

Do_hook: Push Offset Kernel_nameCall GetModuleHandleapush Offset Sleep_namepush Eaxcall GetProcaddresspush Eaxmov ESI, ESI

xor ecx, ecxmov ebx, esiget_five_bytes: push ecxpush ebxcall get_instr_len; call LDE32pop ecxadd ecx, eaxadd ebx, eaxcmp ecx, 5jb get_five_bytesmov edi, offset old_sleep; branch address calculation mov [edi 011h], ebxsub [edi 011h], edisub DWORD PTR [EDI 011H], 015HREP MOVSBPOP EDI; The following code is before, so you don't need to annotate.

Push Page_readwritepush Mem_Commitpush Memory_basic_information_sizepush 0call Virtualaloctest ESIV ESI, ESI, ESI

Push Memory_basic_information_sizepush Esipush Edicall VirtualQueryTest Eax, Eaxjz Free_MEM

Call getCurrentProcesspush 5Push Edipush EaxCall Flushinstructioncache

Lea Eax, [ESI 014H] Push Eaxpush Page_execute_readwritelea Eax, [ESI 00ch] Push [EAX] Push [ESI] Call VirtualProtectTest Eax, Eaxjz Free_Mem

MOV BYTE PTR [EDI], 0E9HMOV EAX, Offset New_Sleepsub Eax, Edisub Eax, 5inc Edistosdos

Push Offset Old_Protectlea Eax, [ESI 014H] Push [EAX] Lea Eax, [ESI 00ch] Push [EAX] Push [ESI] Call VirtualProtect

free_mem: push MEM_RELEASEpush 0push esicall VirtualFreedo_sleep: push 5000call Sleeppush 0call ExitProcessnew_sleep: mov eax, dword ptr [esp 004h] add eax, eax; repeat delay push eaxmov eax, offset old_sleep; call the original function call eaxret 004h

It looks like this after the hook:

004010cc: 6888130000 push 000001388H004010D1: E818090000 CALL SLEP

Sleep:; Jump to IAT in IAT 004019EE: FF2514204000 JMP DWORD PTR [000402014H]

Tabulka: 00402014: 79 67 E8 77 6C 7D E8 77

Kernel32.sleep: 77E86779: E95FA95788 JMP 0004010DDH

New_sleep: 004010dd: 8b442404 MOV EAX, DWORD PTR [ESP 4] 004010E1: 03C0 Add Eax, Eax004010E3: 50 Push Eax004010E4: B827304000 MOV Eax, 000403027H004010E9: FFD0 Call Eax

old_sleep: 00403027: 6A00 push 000403029: FF742408 push dword ptr [esp 8] 0040302D: 90 nop0040302E: 90 nop0040302F: 90 nop00403030: 90 nop00403031: 90 nop00403032: 90 nop00403033: 90 nop00403034: 90 nop00403035: 90 nop00403036: 90 nop00403037: E94337A877 JMP kernel32.77e8677f; 1 byte after kernel32.sleep (77e86779)

Kernel32.77e8677f: 77e8677f: e803000000 Call kernel32.sleepex ...; later is not important

In order to make these looks clearer, this is the original version of kernel32.sleep:

Kernel32.sleep: 77E86779: 6A00 PUSH 077E8677B: FF742408 PUSH DWORD PTR [ESP 8] 77E8677F: E803000000 Call kernel32.sleepex77e86784: C20400 RET 00004H

Just like you see, what should we be copying the 1st and 2nd instructions (here a total of 6 bytes here) and the jump instruction points to the next instruction. Here we have to assume that the jump instruction is not placed as the instruction started by the function, otherwise we will encounter problems. The next question is the API such as NTDLL.DBGBREAKPOINT, which is too short, so it cannot be used with this method. And it is called by kernel32.debugbreak, so it cannot be hooked by modifying IAT. Although there is no one who will go to hook this only INT 3 function, but nothing is unable to do it, just think about it, you can find a solution. My method is to hook the function after it (it may be destroyed because of the five bytes of the previous function). The DBGBREAKPOINT function is 2 bytes, so we can set some flags and try to write condition jump instructions in the start of the second function ... But this is not our current problem. The problem of saving the original function has been described, and the hook is released. The linker is to restore the modified bytes to the original state. To modify the IAT method, if you want to release the hook, you will need to restore the original address in the table. In the method of modifying the entry point, you have to do it back to the start instruction of the original function. The two practices are very simple, so don't talk about it again.

===== [3.2.4 hook other processes] ======================================== ===

Now let's practice the running hook. Imagine who will want to hook only your own process? This is obviously very un practical. I will demonstrate three ways to hook other processes. Both of them use CreateremoteThread this API, which is only valid in Windows versions of NT technology. For me, there is not so interesting in the older Windows. I have forgot to say that I will introduce these three methods I have not practiced, so I may have a problem. First introduce CreateremoteThread. As if the help say, this function can create a new thread in any process and run it.

HANDLE CreateRemoteThread (HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); hProcess handle can be obtained by OpenProcess. Here we have to get sufficient permissions. LPSTARTADDRESS is a pointer to the first command address of the new thread in the target process address, because the new thread is created in the target process, so it exists in the address space of the target process. LPParameter is a pointer to the parameter submitted to the new thread.

===== [3.2.4.1 DLL injection] ====================================== ============

We can run our new thread anywhere in the target process address space. This doesn't seem to use it unless there is our complete code. The first method is to do so. It calls GetProcAddress to get the loadLibrary address. The loadLibrary is then assigned to the parameter lpstartaddress. The LoadLibrary function has only one parameter, just like the function of the new thread in the target process.

Hinstance loadLibrary (LPCTSTR LPLIBFILENAME);

We can use this similarity to assign the LPParameter parameter as the name of our DLL library. The location of LPParameter after the new thread is running is the location of LPLIBFileName. The most important thing here has been told before. The initialization portion is started after loading a new module to the target process. If we are placing a special function that can hook other functions is OK. After the initialization section is executed, this thread does not do and be closed, but our module is still in the address space. This method is very nice and easy to implement, its name is DLL injection. But if you don't like much DLL library as you, please see the following method. But if you don't mind multiple DLL libraries, this is really the fastest way (from programmers' perspective).

===== [3.2.4.2 independent code] ========================================= ==========

It is more difficult to achieve independent code, but it is easy to impress people. Independent code is a code that does not require any static address. It inside all things to point to some specific places inside the code. If we don't know if this code starts to execute, it can be done. Of course, it is also possible to get the address first and then re-link our code, which can work well in the new address, but this is more difficult than writing independent code. An example of this type of code is like a virus code. The virus infects the executable files in this way, which adds its own code to some places in the executable. The position of the virus code in different executable files is different, depending on the length of the file structure. First insert our code into the target process, then the CreateremoteThread function will be responsible for running our code. So the first step we have to do is to get the information and handle of the target process through the OpenProcess function, then call VirtualaLAllocex to assign some memory to our code in the target process address space, and finally call WriteProcessMemory to write our code to allocated memory. And run it. Call CreateRemoteThread's parameter lpstartaddress set to allocated memory addresses, LPParameter can set it casually. Because I don't like any unnecessary files, I use this method. ===== [3.2.4.3 Original modification] ======================================= ================

The old version of the non-NT kernel is no CreateRemThetRead function, so we cannot use the above methods. There may be many ways to be more than what I now introduced, in fact, this method has not practiced, but theoretically is feasible. We don't really need to put our code in the target process to hook its functions. There are two functions WriteProcessMemory and OpenProcess, which is valid in all versions of Windows. The function we need is VirtualProtectex to modify the memory page that enters the target process. I can't find any reason that does not directly hook the target process from our process ...

===== [4. Conclusion] ======================================== =========

I welcome anyone to make more hook methods not mentioned here, I am sure there will be a lot. I also welcome me that I will introduce it is not a very detailed method. You can also complete my lazy code part and send the source code to me. The purpose of this document is to demonstrate the details of hook technology, I hope I have done it. Special thanks to Z0MBIE code, saving me a lot of valuable time.

转载请注明原文地址:https://www.9cbs.com/read-53211.html

New Post(0)