Hook Windows API

xiaoxiao2021-03-06  66

Hook Windows API

Author: holy_father

Version: 1.1 ENGLISH

Date: 6.10.2002

Content 1. Content

2. Introduction

3. Hook method

3.1 Hook before running

3.2 Running hook

3.2.1 Hook the process using IAT

3.2.2 Remove the entry point hook this process

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 Conclusion

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 Hook 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 Running hooks The hook is usually very special before running, and is the specific application (or module) internally. 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 There are many possibilities here using IAT hooks. 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

| Intrelated data |

| | |

-----------------------------

| .Idata | - Import function information and data

| Import Table |

| | |

-----------------------------

| .Edata | - Export function information and data

| Export Table |

| | |

-----------------------------

| Commissioning symbol |

-----------------------------

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 ImageDirectoryEntryTodata

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 {

Union {

Pbyte forwarderstring;

PDWORD FUNCTION;

DWORD Ordinal

PIMAGE_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 PfnNNEW = (Proc) 0x12345678, // Here you save your 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 == pfnhook);

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 the address that jumps to IAT

004075F4: FF25BCA14000 JMP DWORD PTR [00040A1BCH]

Original table:

0040a1bc: 79 67 E8 77 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 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 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 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 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 Removing entry Point Hook Some bytes from the Process Rewriting Function Inlet 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.LIB

Sleep Proto: DWORD

GetModuleHandlea Proto: DWORD

GetProcadDress Proto: DWORD,: DWORD

VirtualQuery Proto: DWORD,: DWORD,: DWORD

VirtualProtect Proto: DWORD,: DWORD,: DWORD,: DWORD

VirtualaLloc Proto: DWORD,: DWORD,: DWORD,: DWORD

VirtualFree Proto: DWORD,: DWORD,: DWORD

Flushinstructioncache Proto: DWORD,: DWORD,: DWORD

GetCurrentProcess Proto

EXITPROCESS Proto: DWORD

.DATA

KERNEL_NAME DB "kernel32.dll", 0

Sleep_name DB "Sleep", 0

OLD_PROTECT DD?

Memory_basic_information_size EQU 28

Page_readwrite dd 000000004H

Page_execute_readwrite dd 000000040H

MEM_COMMIT DD 000001000H

MEM_RELEASE DD 000008000H

.code

Start:

Push 5000

Call Sleep

DO_HOOK:

Push offset kernel_name

Call getModuleHandlea

Push Offset Sleep_name

Push EAX

Call getProcAddress

Mov EDI, EAX; finally gets the SLEEP address

Push Page_Readwrite

Push Mem_Commit

Push memory_basic_information_size

PUSH 0

Call Virtualalloc

Test Eax, EAX

JZ DO_SLEP

Mov ESI, EAX; Distributing Memory Push Memory_basic_information_size for MBI Structure

PUSH ESI

Push EDI

Call VirtualQuery; memory page information

Test Eax, EAX

JZ Free_MEM

Call getCurrentProcess

Push 5

Push EDI

Push EAX

Call flushinstructioncache; just to determine :)

Lea Eax, [ESI 014H]

Push EAX

Push Page_execute_readwrite

Lea Eax, [ESI 00CH]

Push [EAX]

Push [ESI]

Call VirtualProtect; We want to modify the protection properties so that you can write to the code

Test Eax, EAX

JZ Free_MEM

MOV BYTE PTR [EDI], 0E9H; Writing Jump Directive

Mov Eax, Offset New_Sleep

Sub Eax, EDI

Sub eax, 5

Inc EDI

STOSD; here is a jump address

Push Offset Old_Protect

Lea Eax, [ESI 014H]

Push [EAX]

Lea Eax, [ESI 00CH]

Push [EAX]

Push [ESI]

Call VirtualProtect; Recovery Page Protection Attribute

Free_MEM:

Push Mem_Release

PUSH 0

PUSH ESI

Call VirtualFree; Release Memory

DO_SLEP:

Push 5000

Call Sleep

PUSH 0

Call EXITPROCESS

NEW_SLEP:

RET 004H

End Start

The result of the second call SLEEP is this:

004010A4: 6888130000 Push 000001388H

004010A9: E80A000000 Call Sleep

Sleep:; here is the address that jumps 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_SLEP:

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, 090H

DB 090H, 090H, 090H, 090H, 090H, 090H, 090H, 090H

DB 0E9H, 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 Simultaneously

C_MEM4 EQU 0004H; | |

C_Data1 EQU 0100H; | |

C_Data2 EQU 0200H; | May Be Used Simultaneously

C_Data4 EQU 0400H; | |

C_67 EQU 0010H; Used with c_prefix

C_MEM67 EQU 0020H; C_67? C_MEM2: C_MEM4

C_66 EQU 1000h; Used with c_prefix

C_DATA66 EQU 2000H; C_66? C_Data2: c_data4

C_PREFIX EQU 0008H; prefix. Take opcode again

C_MODRM EQU 4000H; ModxxxR / M

C_DataW0 EQU 8000H; OPC & 1? C_data66: c_data1

P386

Model flat

Locals @@

.code

Public disasm_main

PUBLIC _DISASM_MAIN

Public @disasm_main

Public disasm_main

DISASM_MAIN:

_DISASM_MAIN:

@disasm_main:

DISASM_MAIN:

__fastcall eax

; __cdecl [ESP 4]

This is my first modification, it is just the statement of this function.

GET_INSTR_LEN:

MOV ECX, [ESP 4]; ECX = Opcode Ptrxor 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_PREFIX

JNZ @@ prefix

CMP Al, 0F6H

JE @@ TEST

CMP Al, 0F7h

JE @@ TEST

CMP Al, 0CDH

JE @@ int

CMP Al, 0FH

JE @@ 0f

@@ Cont:

Test DH, C_DataW0 SHR 8

JNZ @@ DataW0

@@ DataW0Done:

TEST DH, C_MODRM SHR 8

JNZ @@ modm

@@ exitmodrm:

TEST DL, C_MEM67

JNZ @@mie

@@ Mem67Done:

TEST DH, C_DATA66 SHR 8

JNZ @@ data66

@@ Data66Done:

MOV EAX, ECX

Sub eax, [ESP 4]

AND EDX, C_MEM1 C_MEM2 C_MEM4 C_DATA1 C_DATA2 C_DATA4

Add Al, DL

Add Al, DH

Here is my second place to modify, only in the original version is Retn

@@ EXIX: RET 00004H

@@ Test: OR DH, C_MODRM SHR 8

TEST BYTE PTR [ECX], 00111000B; F6 / F7 - TEST

JNZ @@ cont

OR DH, C_DATAW0 SHR 8

JMP @@und

@@ int: OR DH, C_DATA1 SHR 8

CMP Byte PTR [ECX], 20H

JNE @@ cont

OR DH, C_DATA4 SHR 8

JMP @@und

@@ 0f: MOV Al, [ECX]

Inc ECX

OR EDX, TABLE_0F [EAX * 4]

CMP EDX, -1

JNE @@ cont

@@ error: MOV EAX, EDX

JMP @@ EXIT

@@ DataW0: xor DH, C_Data66 SHR 8

Test Al, 00000001B

JNZ @@ dataw0done

XOR DH, (C_Data66 C_Data1) SHR 8

JMP @@ dataw0done

@@ MEM67: XOR DL, C_MEM2TEST DL, C_67

JNZ @@mem67done

XOR DL, C_MEM4 C_MEM2

JMP @@MEM67DONE

@@ data66: xor DH, C_Data2 SHR 8

TEST DH, C_66 SHR 8

JNZ @@ data66done

XOR DH, (C_Data4 C_Data2) SHR 8

JMP @@ data66done

@@ modrm: MOV Al, [ECX]

Inc ECX

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

And Ax, 0C007H

CMP AH, 0C0H

JE @@ ixitmodrm

TEST DL, C_67

JNZ @@ modm16

@@ modrm32: CMP Al, 04H

JNE @@ a

MOV Al, [ECX]; SIB

Inc ECX

And Al, 07h

@@ A: CMP AH, 40H

JE @@mie

CMP AH, 80H

JE @@mie

CMP AX, 0005H

JNE @@ exitmodrm

@@ MEM4: OR DL, C_MEM4

JMP @@ exitmodrm

@@ Mem1: OR DL, C_MEM1

JMP @@ exitmodrm

@@ modRM16: CMP AX, 0006H

JE @@MEM2

CMP AH, 40H

JE @@mie

CMP AH, 80H

JNE @@ exitmodrm

@@ MEM2: OR DL, C_MEM2

JMP @@ exitmodrm

ENDP

.DATA

; 0F - Analysis in the code, no sign (that is, 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; 00

DD C_MODRM; 01

DD C_MODRM; 02

DD C_MODRM; 03

DD C_DATAW0; 04

DD C_DATAW0; 05

DD 0; 06

DD 0; 07

DD C_MODRM; 08

DD C_MODRM; 09DD C_MODRM; 0A

DD C_MODRM; 0B

DD C_DATAW0; 0C

DD C_DATAW0; 0D

DD 0; 0E

DD 0; 0f

DD C_MODRM; 10

DD C_MODRM; 11

DD C_MODRM; 12

DD C_MODRM; 13

DD C_DATAW0; 14

DD C_DATAW0; 15

DD 0; 16

DD 0; 17

DD C_MODRM; 18

DD C_MODRM; 19

DD C_MODRM; 1A

DD C_MODRM; 1B

DD C_DATAW0; 1C

DD C_DATAW0; 1D

DD 0; 1E

DD 0; 1F

DD C_MODRM; 20

DD C_MODRM; 21

DD C_MODRM; 22

DD C_MODRM; 23

DD C_DATAW0; 24

DD C_DATAW0; 25

DD C_PREFIX; 26

DD 0; 27

DD C_MODRM; 28

DD C_MODRM; 29

DD C_MODRM; 2A

DD C_MODRM; 2B

DD C_DATAW0; 2C

DD C_DATAW0; 2D

DD C_PREFIX; 2E

DD 0; 2F

DD C_MODRM; 30

DD C_MODRM; 31

DD C_MODRM; 32

DD C_MODRM; 33

DD C_DATAW0; 34

DD C_DATAW0; 35

DD C_PREFIX; 36

DD 0; 37

DD C_MODRM; 38

DD C_MODRM; 39

DD C_MODRM; 3A

DD C_MODRM; 3B

DD C_DATAW0; 3C

DD C_DATAW0; 3D

DD C_PREFIX; 3E

DD 0; 3F

DD 0; 40

DD 0; 41

DD 0; 42

DD 0; 43DD 0; 44

DD 0; 45

DD 0; 46

DD 0; 47

DD 0; 48

DD 0; 49

DD 0; 4A

DD 0; 4B

DD 0; 4C

DD 0; 4D

DD 0; 4E

DD 0; 4F

DD 0; 50

DD 0; 51

DD 0; 52

DD 0; 53

DD 0; 54

DD 0; 55

DD 0; 56

DD 0; 57

DD 0; 58

DD 0; 59

DD 0; 5A

DD 0; 5B

DD 0; 5C

DD 0; 5D

DD 0; 5E

DD 0; 5F

DD 0; 60

DD 0; 61

DD C_MODRM; 62

DD C_MODRM; 63

DD C_PREFIX; 64

DD C_PREFIX; 65

DD C_PREFIX C_66; 66

DD C_PREFIX C_67; 67

DD C_DATA66; 68

DD C_MODRM C_DATA66; 69

DD C_DATA1; 6A

DD C_MODRM C_DATA1; 6B

DD 0; 6C

DD 0; 6D

DD 0; 6E

DD 0; 6F

DD C_DATA1; 70

DD C_DATA1; 71

DD C_DATA1; 72

DD C_DATA1; 73

DD C_DATA1; 74

DD C_DATA1; 75

DD C_DATA1; 76

DD C_DATA1; 77

DD C_DATA1; 78

DD C_DATA1; 79

DD C_DATA1; 7A

DD C_DATA1; 7B

DD C_DATA1; 7CDD C_DATA1; 7D

DD C_DATA1; 7E

DD C_DATA1; 7F

DD C_MODRM C_DATA1; 80

DD C_MODRM C_DATA66; 81

DD C_MODRM C_DATA1; 82

DD C_MODRM C_DATA1; 83

DD C_MODRM; 84

DD C_MODRM; 85

DD C_MODRM; 86

DD C_MODRM; 87

DD C_MODRM; 88

DD C_MODRM; 89

DD C_MODRM; 8A

DD C_MODRM; 8B

DD C_MODRM; 8C

DD C_MODRM; 8D

DD C_MODRM; 8E

DD C_MODRM; 8F

DD 0; 90

DD 0; 91

DD 0; 92

DD 0; 93

DD 0; 94

DD 0; 95

DD 0; 96

DD 0; 97

DD 0; 98

DD 0; 99

DD C_DATA66 C_MEM2; 9A

DD 0; 9b

DD 0; 9C

DD 0; 9D

DD 0; 9E

DD 0; 9F

DD C_MEM67; A0

DD C_MEM67; A1

DD C_MEM67; A2

DD C_MEM67; A3

DD 0; A4

DD 0; A5

DD 0; A6

DD 0; A7

DD C_DATA1; A8

DD C_DATA66; A9

DD 0; AA

DD 0; ab

DD 0; AC

DD 0; ad

DD 0; AE

DD 0; AF

DD C_DATA1; B0

DD C_DATA1; B1

DD C_DATA1; B2

DD C_DATA1; B3

DD C_DATA1; B4

DD C_DATA1; B5DD C_DATA1; B6

DD C_DATA1; B7

DD C_DATA66; B8

DD C_DATA66; B9

DD C_DATA66; Ba

DD C_DATA66; BB

DD C_DATA66; BC

DD C_DATA66; BD

DD C_DATA66; BE

DD C_DATA66; BF

DD C_MODRM C_DATA1; C0

DD C_MODRM C_DATA1; C1

DD C_DATA2; C2

DD 0; C3

DD C_MODRM; C4

DD C_MODRM; C5

DD C_MODRM C_DATA1; C6

DD C_MODRM C_DATA66; C7

DD C_DATA2 C_DATA1; C8

DD 0; C9

DD C_DATA2; CA

DD 0; CB

DD 0; CC

DD 0; CD

DD 0; CE

DD 0; CF

DD C_MODRM; D0

DD C_MODRM; D1

DD C_MODRM; D2

DD C_MODRM; D3

DD C_DATA1; D4

DD C_DATA1; D5

DD 0; D6

DD 0; D7

DD C_MODRM; D8

DD C_MODRM; D9

DD C_MODRM; DA

DD C_MODRM; DB

DD C_MODRM; DC

DD C_MODRM; DD

DD C_MODRM; DE

DD C_MODRM; DF

DD C_DATA1; E0

DD C_DATA1; E1

DD C_DATA1; E2

DD C_DATA1; E3

DD C_DATA1; E4

DD C_DATA1; E5

DD C_DATA1; E6

DD C_DATA1; E7

DD C_DATA66; E8

DD C_DATA66; E9

DD C_DATA66 C_MEM2; EA

DD C_DATA1; EB

DD 0; EC

DD 0; ED

DD 0; EEDD 0; EF

DD C_PREFIX; F0

DD 0; F1

DD C_PREFIX; F2

DD C_PREFIX; F3

DD 0; F4

DD 0; F5

DD 0; F6

DD 0; F7

DD 0; F8

DD 0; F9

DD 0; fa

DD 0; FB

DD 0; FC

DD 0; fd

DD C_MODRM; Fe

DD C_MODRM; FF

Table_0f label dword; 0f is the prefix instruction

DD C_MODRM; 00

DD C_MODRM; 01

DD C_MODRM; 02

DD C_MODRM; 03

DD -1; 04

DD -1; 05

DD 0; 06

DD -1; 07

DD 0; 08

DD 0; 09

DD 0; 0A

DD 0; 0B

DD -1; 0C

DD -1; 0D

DD -1; 0E

DD -1; 0F

DD -1; 10

DD -1; 11

DD -1; 12

DD -1; 13

DD -1; 14

DD -1; 15

DD -1; 16

DD -1; 17

DD -1; 18

DD -1; 19

DD -1; 1A

DD -1; 1B

DD -1; 1C

DD -1; 1D

DD -1; 1E

DD -1; 1F

DD -1; 20

DD -1; 21

DD -1; 22

DD -1; 23

DD -1; 24

DD -1; 25

DD -1; 26DD -1; 27

DD -1; 28

DD -1; 29

DD -1; 2A

DD -1; 2B

DD -1; 2C

DD -1; 2D

DD -1; 2E

DD -1; 2F

DD -1; 30

DD -1; 31

DD -1; 32

DD -1; 33

DD -1; 34

DD -1; 35

DD -1; 36

DD -1; 37

DD -1; 38

DD -1; 39

DD -1; 3A

DD -1; 3B

DD -1; 3C

DD -1; 3D

DD -1; 3E

DD -1; 3F

DD -1; 40

DD -1; 41

DD -1; 42

DD -1; 43

DD -1; 44

DD -1; 45

DD -1; 46

DD -1; 47

DD -1; 48

DD -1; 49

DD -1; 4A

DD -1; 4B

DD -1; 4C

DD -1; 4D

DD -1; 4E

DD -1; 4F

DD -1; 50

DD -1; 51

DD -1; 52

DD -1; 53

DD -1; 54

DD -1; 55

DD -1; 56

DD -1; 57

DD -1; 58

DD -1; 59

DD -1; 5A

DD -1; 5B

DD -1; 5C

DD -1; 5D

DD -1; 5E

DD -1; 5F

DD -1; 60DD -1; 61

DD -1; 62

DD -1; 63

DD -1; 64

DD -1; 65

DD -1; 66

DD -1; 67

DD -1; 68

DD -1; 69

DD -1; 6A

DD -1; 6B

DD -1; 6C

DD -1; 6D

DD -1; 6E

DD -1; 6F

DD -1; 70

DD -1; 71

DD -1; 72

DD -1; 73

DD -1; 74

DD -1; 75

DD -1; 76

DD -1; 77

DD -1; 78

DD -1; 79

DD -1; 7A

DD -1; 7B

DD -1; 7C

DD -1; 7D

DD -1; 7E

DD -1; 7F

DD C_DATA66; 80

DD C_DATA66; 81

DD C_DATA66; 82

DD C_DATA66; 83

DD C_DATA66; 84

DD C_DATA66; 85

DD C_DATA66; 86

DD C_DATA66; 87

DD C_DATA66; 88

DD C_DATA66; 89

DD C_DATA66; 8A

DD C_DATA66; 8B

DD C_DATA66; 8C

DD C_DATA66; 8D

DD C_DATA66; 8E

DD C_DATA66; 8F

DD C_MODRM; 90

DD C_MODRM; 91

DD C_MODRM; 92

DD C_MODRM; 93

DD C_MODRM; 94

DD C_MODRM; 95

DD C_MODRM; 96

DD C_MODRM; 97

DD C_MODRM; 98

DD C_MODRM; 99

DD C_MODRM; 9ADD C_MODRM; 9B

DD C_MODRM; 9C

DD C_MODRM; 9D

DD C_MODRM; 9E

DD C_MODRM; 9F

DD 0; A0

DD 0; A1

DD 0; A2

DD C_MODRM; A3

DD C_MODRM C_DATA1; A4

DD C_MODRM; A5

DD -1; A6

DD -1; A7

DD 0; A8

DD 0; A9

DD 0; AA

DD C_MODRM; AB

DD C_MODRM C_DATA1; AC

DD C_MODRM; AD

DD -1; AE

DD C_MODRM; AF

DD C_MODRM; B0

DD C_MODRM; B1

DD C_MODRM; B2

DD C_MODRM; B3

DD C_MODRM; B4

DD C_MODRM; B5

DD C_MODRM; B6

DD C_MODRM; B7

DD -1; b8

DD -1; b9

DD C_MODRM C_DATA1; Ba

DD C_MODRM; BB

DD C_MODRM; BC

DD C_MODRM; BD

DD C_MODRM; BE

DD C_MODRM; BF

DD C_MODRM; C0

DD C_MODRM; C1

DD -1; C2

DD -1; C3

DD -1; C4

DD -1; C5

DD -1; C6

DD -1; C7

DD 0; C8

DD 0; C9

DD 0; CA

DD 0; CB

DD 0; CC

DD 0; CD

DD 0; CE

DD 0; CF

DD -1; D0

DD -1; D1

DD -1; D2

DD -1; D3

DD -1; D4

DD -1; D5

DD -1; D6

DD -1; D7

DD -1; D8

DD -1; D9

DD -1; DA

DD -1; DB

DD -1; DC

DD -1; DD

DD -1; DE

DD -1; DF

DD -1; E0

DD -1; E1

DD -1; E2

DD -1; E3

DD -1; E4

DD -1; E5

DD -1; E6

DD -1; E7

DD -1; e8

DD -1; E9

DD -1; EA

DD -1; EB

DD -1; EC

DD -1; ED

DD -1; EE

DD -1; EF

DD -1; F0

DD -1; F1

DD -1; F2

DD -1; F3

DD -1; F4

DD -1; F5

DD -1; F6

DD -1; F7

DD -1; F8

DD -1; F9

DD -1; fa

DD -1; FB

DD -1; FC

DD -1; fd

DD -1; Fe

DD -1; FF

end

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", 0

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

0E9H, 000H, 000H, 000H, 000H

.code

Start:

Push 5000

Call Sleep

DO_HOOK:

Push offset kernel_name

Call getModuleHandlea

Push Offset Sleep_name

Push EAX

Call getProcAddress

Push EAX

Mov ESI, EAX

XOR ECX, ECX

MOV EBX, ESI

GET_FIVE_BYTES:

Push ECX

Push EBX

Call get_instr_len; call LDE32

POP ECX

Add ECX, EAX

Add Ebx, EAX

CMP ECX, 5

JB GET_FIVE_BYTES

MOV EDI, OFFSET OLD_SLEP; calculate jump address

MOV [EDI 011H], EBX

SUB [EDI 011h], EDI

Sub DWORD PTR [EDI 011H], 015H

REP MOVSB

POP EDI

The following code is there, so you don't need to annotate.

Push Page_Readwrite

Push Mem_Commit

Push memory_basic_information_size

PUSH 0

Call Virtualalloc

Test Eax, EAX

JZ DO_SLEP

Mov ESI, EAX

Push memory_basic_information_size

PUSH ESI

Push EDI

Call VirtualQuery

Test Eax, EAX

JZ Free_MEM

Call getCurrentProcess

Push 5

Push EDI

Push EAX

Call Flushinstructioncache

Lea Eax, [ESI 014H]

Push EAX

Push Page_execute_readwrite

Lea Eax, [ESI 00CH]

Push [EAX]

Push [ESI]

Call VirtualProtect

Test Eax, EAX

JZ Free_MEM

MOV Byte Ptr [EDI], 0E9H

Mov Eax, Offset New_Sleep

Sub Eax, EDI

Sub eax, 5

Inc EDI

Stosd

Push Offset Old_Protect

Lea Eax, [ESI 014H]

Push [EAX]

Lea Eax, [ESI 00CH]

Push [EAX]

Push [ESI]

Call VirtualProtect

Free_MEM:

Push Mem_Release

PUSH 0

PUSH ESI

Call VirtualFree

DO_SLEP:

Push 5000

Call Sleep

PUSH 0

Call EXITPROCESS

NEW_SLEP:

MOV EAX, DWORD PTR [ESP 004H]

Add Eax, Eax; Repeated delay

Push EAX

MOV EAX, OFFSET OLD_SLEP; call the original function

Call EAX

RET 004H

It looks like this after the hook:

004010cc: 6888130000 push 000001388H004010D1: E818090000 CALL SLEP

Sleep:; Jump to the address 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_SLEP:

004010DD: 8B442404 MOV EAX, DWORD PTR [ESP 4]

004010E1: 03c0 Add Eax, EAX

004010E3: 50 push eax

004010E4: B827304000 MOV EAX, 000403027H

004010E9: FFD0 Call EAX

Old_sleep:

00403027: 6A00 PUSH 0

00403029: FF742408 PUSH DWORD PTR [ESP 8]

0040302D: 90 NOP

0040302e: 90 NOP

0040302F: 90 NOP

00403030: 90 NOP

00403031: 90 NOP

00403032: 90 NOP

00403033: 90 NOP

00403034: 90 NOP

00403035: 90 NOP

00403036: 90 NOP

00403037: E94337A877 JMP kernel32.77e8677f

This instruction is 1 byte after Kernel32.Sleep (77E86779)

KERNEL32.77E8677F:

77E8677F: E803000000 CALL KERNEL32.SLEPEX

...; it is not important behind

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

Kernel32.sleep:

77E86779: 6A00 PUSH 0

77E8677B: FF742408 PUSH DWORD PTR [ESP 8]

77E8677F: E803000000 CALL KERNEL32.SLEPEX

77E86784: 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 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 LPTHREADATIADATTRIBUTES,

DWORD DWSTACKSIZE,

LPTHREAD_START_ROUTINE LPSTARTDRESS,

LPVOID LPPARETER,

DWORD DWCREATIONFLAGS,

LPDWORD LPTHREADID

);

The handle HProcess 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 loadingLibrary (

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.

connection

http://www.xfocus.net/articles/200403/681.html

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

New Post(0)