Transfer from Jiakang
===== [1. content] ======================================== ===== 1. Content 2. Introduction 3. Hook Method 3.1 Running Hook 3.2 Running Hook 3.2.1 Using IAT Hooks Procedure 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 about 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, our purpose is to replace the code in some functions with 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] ======================================== ==== Here, modify the physical module we want to modify the function 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 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 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 logo ("MZ") and DOS blocks | ---------------------------- | PE logo ("PE ") | ---------------------------- | .TEXT | - Module Code | Program Code | | | ------------------------------ | .Data | - Initialized (global static) data | has been initialized | | | ------------------------------- | .idata | - Import Function Information and Data | Import Table | | | ------------------------------- | .edata | - Export Functions Information and Data | Export Table | | | ------------------------------- | Commissioning symbol | --------- ---------------------- Here is 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); where Base parameters can Instance our program (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 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 FigStthunk;} image_import_descriptor, * pimage_import_descriptor; image_import_descriptor's NAME member variable in the module name is a pointer. 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 == pfnhookApiaddd); 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) is shown in: 00407BD8: 68E8030000 push 0000003E8H 00407BDD: E812FAFFFFF CALL SLEP SLEEP:; This is the address of the address 004075F4: FF25BCA14000 JMP DWORD PTR [00040A1BCH] Original table: 0040A1BC: 79 67 E8 77 00 00 00 00 New table: 0040A1BC: 78 56 34 12 00 00 00 00 So finally jump to 0x12345678. ===== [3.2.2 Remove the entry point hook this process] ================== These methods starting with some bytes starting in the 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, stdcallincludelib 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,: DWORDFlushInstructionCache PROTO: DWORD,: DWORD,: DWORDGetCurrentProcess PROTOExitProcess PROTO: DWORD.datakernel_name db "kernel32.dll", 0sleep_name db "Sleep", 0old_protect dd MEMORY_BASIC_INFORMATION_SIZE equ 28PAGE_READWRITE? dd 000000004hPAGE_EXECUTE_READWRITE dd 000000040hMEM_COMMIT dd 000001000hMEM_RELEASE dd 000008000h.codestart: push 5000 call Sleepdo_hook: push offset kernel_name call GetModuleHandleA push offset sleep_name push eax call GetProcAddress mov edi, eax; Sleep finally obtained address push PAGE_READWRITE push MEM_COMM IT push MEMORY_BASIC_INFORMATION_SIZE push 0 call VirtualAlloc test eax, eax jz do_sleep mov esi, eax; is MBI structure allocate memory push MEMORY_BASIC_INFORMATION_SIZE push esi push edi call VirtualQuery; information memory page 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 Inc. Test Eax, EAX JZ FREE_MEM MOV BYTE PTR [EDI], 0E9H;
Write Jump Command MOV Eax, Offset New_Sleep Sub Eax, EDI Sub Eax, 5 Inc EDI Stosd; This is the jump address Push Offset Old_Protect Lea Eax, [ESI 014H] Push [EAX] Lea Eax, [ESI 00CH] push [eax] push [esi] call VirtualProtect; restore page protection attributes free_mem: push MEM_RELEASE push 0 push esi call VirtualFree; release the memory do_sleep: push 5000 call Sleep push 0 call ExitProcessnew_sleep: ret result 004hend start a second call Sleep Thus: 004010A4: 6888130000 push 000001388h 004010A9: E80A000000 call Sleep Sleep:; here is to jump to the IAT address 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] =========================== =========== What we need is 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 are ready to copy Starting instructions. 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.05C_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; ModxxR / MC_DATAW0 EQU 8000H; OPC & 1? c_data66: c_data1 p386 Model Flat Lo cals @@ .codepublic disasm_mainpublic _disasm_mainpublic @disasm_mainpublic DISASM_MAINdisasm_main: _disasm_main: @disasm_main: DISASM_MAIN :; __fastcall EAX; __cdecl [ESP 4]; this is my first at the changes, it's just a statement get_instr_len this function: 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, 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 @@ modrm @@ exitmodrm: Test DL, C_MEM67 JNZ @@mie: 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 @@ EXIT: 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 @@ cont @@
INT: OR DH, C_DATA1 SHR 8 CMP BYTE PTR [ECX], 20H JNE @@ Cont or DH, C_DATA4 SHR 8 JMP @@ Cont @@ 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_MEM2 TEST DL, C_67 JNZ @@mem67done xor DL, C_MEM4 C_MEM2 JMP @@mem67done @@ data66: xor DH , C_data2 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 @@ EXIXTMODRM TEST DL, C_67 JNZ @@ modrm16 @@
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 @@mem4 cmp AX, 0005H JNE @ @ EXITMODRM @@mem4: or DL, c_MEM4 JMP @@ EXITMODRM @@ Mem1: OR DL, C_MEM1 JMP @@ EXIXTMODRM @@ ModRM16: CMP AX, 0006H JE @@mem2 cmp Ah, 40h JE @@ Mem1 CMP AH, 80H JNE @@ exitmodrm @@ MEM2: OR DL, C_MEM2 JMP @@ EXITMODRM ENDP .DATA; 0F - Analysis of the code, no mark (also the flag (Flag) must be 0); F6, F7 - // - (TTT = 000 - 3 bytes, otherwise 2 bytes); CD - - // (if the CD 20 is 6 bytes, otherwise 2 bytes) Table_1 Label DWORD; General instruction 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; 44dD 0; 45dd 0; 46dd 0; 47dd 0; 48dd 0; 49dd 0; 4Add 0; 4BDD 0; 4CDD 0;
4ddd 0; 4edD 0; 50DD 0; 51DD 0; 52DD 0; 53DD 0; 54DD 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; 7Cdd C_DATA1; 7Ddd C_DATA1; 7Edd 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; 87DD C_MODRM; 88DD C_MODRM; 89DD C_MODRM;
8ADD C_MODRM; 8BDD C_MODRM; 8DD C_MODRM; 8DD C_MODRM; 8EDD C_MODRM; 8FDD 0; 90DD 0; 93DD 0; 94DD 0; 95DD 0; 96DD 0; 97DD 0; 98DD 0; 99DD C_DATA66 C_MEM2; 9ADD 0; 9 scDD 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 ; ABDD 0; ACDD 0; AddD 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_DATA2; CADD 0; CBDD 0; CCDD 0; CDDD 0; CEDD 0; CFDD C_MODRM; D0DD C_MODRM; D2DD 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; EADD C_DATA1; EBDD 0; ECDD 0; EDD 0; EEDD 0; EFDD C_PREFIX; F0DD 0; F1DD C_PREFIX; F2DD C_PREFIX; F3DD 0; F4DD 0; F5DD 0; F6DD 0; F9DD 0; FADD 0; FBDD 0; FCDD 0; FDDD C_MODRM; FEDD C_MODRM; FFTABLE_0F Label DWORD; 0F is the prefix directive 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; 13DD -1; 14DD -1; 15DD -1; 16DD -1; 19DD -1; 1ADD -1; 1BDD -1; 1CDD -1; 1DDD -1; 1EDD -1; 1FDD -1; 20DD -1; 21DD -1; 22DD -1; 23DD -1; 24DD -1; 25DD -1; 26DD -1; 27DD-1 Study on; 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; 3BDD -1; 3CDD -1; 3DDD -1; 3EDD -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; 7dd -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; C6DD -1; C7DD 0; C8DD 0; C9DD 0; CADD 0; CBDD 0; CCDD 0 CDDD 0; CEDD 0; CFDD -1; D0DD -1; D1DD -1; D2DD -1; D3DD -1; D6DD -1; D7DD -1; D8DD -1; D9DD -1 DADD -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; EADD -1; EBDD -1; ECDD -1; EDDD -1; EEDD -1; EFDD -1; F0DD -1; F1DD -1; F3DD -1; F4DD -1; F5DD -1; F6DD -1; F7DD -1; F8DD -1; F9DD -1; FADD -1 FBDD -1;
FCDD -1; fddd -1; fedd -1; ff end now we can get the length of the command 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 .... datakernel_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, 0E9h, 000h, 000h, 000h, 000h.codestart: push 5000 call Sleepdo_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, esiget_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_sleep; calculated Jump address MOV [EDI 011H], EBX SUB DWORD PTR [EDI 011H], 015H Rep Movsb Pop Edi; The following code is in front, so don't have to annotate Push Page_Readwrite Push Mem_Commit Push Memory_basic_informa TION_SIZE push 0 call VirtualAlloc test eax, eax jz do_sleep 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 VirtualProtectfree_mem: push MEM_RELEASE push 0 push esi call VirtualFreedo_sleep: push 5000 call Sleep push 0 call ExitProcessnew_sleep: mov eax, dword ptr [ ESP 004H] Add Eax, Eax; Repeat Delay Push Eax Mov Eax, Offset Old_sleep; call the original function Call Eax RET 004H lounged to look like this: 004010CC: 6888130000 push 000001388H 004010D1: E818090000 Call Sleep Sleep:; Jump to IAT address 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, 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 in Kernel32.sleep (77E86779) 1 byte kernel32.77e8677f: 77e8677f: E803000000 Call kernel32.sleepex ... The later is not important to make these look 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.sleepex 77e86784: C20400 RET 00004H is like you see, in we have copied first and second An instruction (6 bytes here) and what should be taken after 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 run 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 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, 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 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.