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