[ Hooking
Windows API ]
Technics of hooking API
functions on Windows
--------------------------------------------
Author: Holy_Father
Version: 1.1 english
Date: 6.10.2002
=====[ 1. Contents
]============================================================
1. Contents
2. Introduction
3. Hooking methods
3.1 Hooking before running
3.2 Hooking during running
3.2.1 Own process hooking using IAT
3.2.2 Own process hooking using entry point rewriting
3.2.3 Original function saving
3.2.4 Other process hooking
3.2.4.1 DLL Injection
3.2.4.2 Independent code
3.2.4.3 Raw change
4. Ending
=====[ 2. Introduction
]========================================================
Đây
là bài viết về hooking API functions trên OS Windows. Tất cả các ví dụ
ở đây làm việc hòan tòan trên Windows
systems based on NT technology version NT 4.0
and higher (Windows NT 4.0, Windows
2000, Windows XP). Có lẽ cũng sẽ làm việc trên
các Windows systems khác.
Bạn
đã rất thân thuộc với các processes trên Windows, assembler, PE files
structure và vài API functions để
hiểu tòan bộ bài viết này.
Khi
sử dụng điều khỏan "Hooking API" ở đây, tôi chỉ có ý là thay đổi đầy
đủ API.
Vì vậy, khi calling hooked API, code
của chúng ta được chạy ngay lập tức. Tôi ko đề cập đến các
cases of chỉ giám sát API. Tôi sẽ
viết về hooking hòa tòan.
=====[ 3. Hooking methods
]=====================================================
Đích
của chúng ta nói chung là thay thế code of vài function với code của
chúng ta. Vấn đề này có thể đôi khi
được giải quyết trước khi running process. Điều này
có thể được thực hiện hầu hết với
các process lớp user mà chúng được run bởi chúng ta và đích cuối nghĩa là
thay đổi hành vi của chương trình.
Ví dụ điều này là có thể ứng dụng trong crack.
Nghĩa là chương trình mà nó muốn đĩa
CD-ROM gốc trong suốt quá trình startup (điều này trong game
Atlantis) và chúng ta muốn run nó mà
ko cần CDs. Nếu chúng ta thay đổi function cho
việc lấy một drive type chúng ta sẽ
có thể run chương trình này từ hard drive.
Điều
này ko thể làm được hay chúng ta ko muốn làm điều này khi muốn hook
system process (e.g. services) hay
trong cas chúng ta ko biết process nào sẽ
là target. Thì chúng ta sẽ sử dụng
kỹ thuật hooking suốt quá trình running . Ví dụ sử dụng
điều này có thể là rootkit hay virus
với các kỹ thuật anti-antivirus.
=====[ 3.1 Hooking before running
]=============================================
Đây
là về thay đổi physical module (hầu hét là .exe hay .dll) khi
function, mà chúng ta muốn thay đổi.
Chúng ta phải có ít nhất 3 khả năng ở đây
đề làm được điều này.
Trước
tiên là tìm entry point of function đó và về cơ bản là
rewrite code của nó. Điều này bị
giới hạn bởi function size nhưng chúng ta có thể load vài
modules khác một cách động dynamically
(API LoadLibrary), vì vậy nó có thể đủ.
Kernel functions (kernel32.dll) có thể
được sử dụng trong tất cả các cas vì
mỗi process trong windows có chính
bản copy module này của nó. Thuận lợi khác
là nếu chúng ta biết OS nào sẽ bị
thay đổi module run. Chúng ta có thể sử dụng các con trỏ
trực tiếp trong cas này có nghĩa là API
LoadLibraryA. Điều này như thế vì address
of kernel module trong memory là
tỉnh trong phạm vi một OS Windows version.
Chúng ta cũng có thể sử dụng hành vi
của module được load động. Trong cas này phần khởi trị đầu
của nó được run ngay lập tức sau khi
loading vào memory. Chúng ta ko bị giới hạn
trong phần khởi trị đầu của module
mới.
Khả
năng thứ hai của việc thay thế hàm trong module là phần mở rộng của nó.
Thì chúng ta phải chọn giữa 5 bytes
đầu tiên bởi relative jump
hay rewriting IAT. Trong trường hợp relative
jump, điều này sẽ redirect (chuyển tiếp) code
thực
thi đến code của chúng ta. Khi
calling function mà IAT record của chúng bị thay đổi,
code của chúng ta sẽ bị thực thi một
cách trực tiếp sau call này. Nhưng phần mở rộng của module
vì vậy ko dễ dàng vì chúng ta phải
bảo quản DLL header.
Khả
năng kế tiếp là thay thế tòan module. Đìều đó có nghĩa là chúng ta cài đặt
version of của chính module mà chúng
có thể load cái module gốc và call các original
functions
mà chúng ta ko quan tâm đến. Nhưng các hàm quan trọng sẽ hòan tòan mới
Phương pháp này ko tốt lắm vì các modules
lớn mà chúng có thể chứa hàm trăm
các exports.
=====[ 3.2 Hooking during running
]=============================================
Hooking
before running hầu như rất đặc biệt và định hướng một cách thân thiện
cho các khối ứng dụng (hay module). Nếu
chúng ta thay thế hàm trong kernel32.dll
hay trong ntdll.dll (chỉ trên NT OS)
thì chúng ta sẽ có được sự thay thế hòan hảo hàm này
trong tất cả các processes mà chúng
được run sau đó, nhưng nó cũng khó để làm được nó
vì chúng ta phải lấy cẩn thận về độ
chính xác và code chính of các
functions mới hay cả modules mới, nhưng
vấn đề chính chỉ process
mà nó được run sau sẽ bị hooked (vậy
với tất cả các process thì chúng ta phải reboot
system). Vấn đề kế tiếp là có thể
truy xuất các files này bởi vì NT OS cố bảo vệ chúng.
Quá nhiều cách giải quyết tuyệt vời
cho việc hook process suốt quá trịnh running. PP này
yêu cầu nhiều kiến thức nhưng kết
quả thì rất hòan hảo. Hooking during running
có thể chỉ được thực hiện trên
process mà với chúng chúng ta viết truy xuất đến memory của chúng.
Đối với việc viết vào chính nó chúng
ta sẽ sử dụng API function WriteProcessMemory. Chúng ta sẽ
bắt đầu từ việc hooking process của
chính chúng ta trong suốt quá trình running.
=====[ 3.2.1 Own process hooking
using IAT ]====================================
Có
nhiều khả năng ở đây. Trước tiên tôi sẽ chỉ cho bạn như thế nào để hook
function bằng cách rewriting IAT
(viết lại IAT). Following picture shows structure of PE file:
+-------------------------------+
- offset 0
| MS DOS Header ("MZ") and stub |
+-------------------------------+
|
PE signature ("PE")
|
+-------------------------------+
| .text | - module code
| Program
Code |
| |
+-------------------------------+
| .data | - initialized (global static) data
| Initialized Data |
| |
+-------------------------------+
| .idata | - information for imported functions
| Import Table | and data
| |
+-------------------------------+
| .edata | - information for exported functions
| Export Table | and data
| |
+-------------------------------+
| Debug symbols |
+-------------------------------+
Phần
quan trọng đối với chúng ta ở đây là Import Address Table (IAT) trong .idata
part. Phần này chứa các mô tả của
các imports và phần lớn các imported functions
addresses. Bây giờ quan trọng để
biết các PE files created như thế nào. Khi calling
bất kỳ API một cách gián tiếp trong programming
language (điều đó có ý nghĩa là chúng ta call nó sử dụng
tên của nó, ko phải sử dụng địa chỉ
riêng OS của nó) thì compiler ko link trực tiếp các
calls đến module nhưng nó links call
đến IAT trên chỉ thị jmp mà chúng sẽ được
filled bởi process loader khi OS được
loading process vào memory. Điều này là tại sao
chúng ta có thể sử dụng binary giống
như vậy trên 2 version khác nhau của Windows mà ở đó các modules
có thể được loaded vào các addresses
khác nhau. Process loader sẽ lấp ra fill out chỉ thị direct jmp
trong IAT mà chúng được sử dụng bởi các
calls của chúng ta từ program code. Vì vậy,
nếu chúng ta có thể tìm ra function
đặc trưng trong IAT mà chúng a muốn hook chúng,
chúng ta có thể dễ dàng thay đổi chỉ
thị jmp ở đó và redirect (chuyển tiếp) code đến address của chúng ta.
Mọi call sau đó thực thi hàm này sẽ
thực thi code của chúng ta. Tiện ích của pp này
là sự hòan chỉnh của nó. Bát tiện là
thông thường phần lớn các functions mà nó được
hooked (e.g. nếu chúng ta muốn thay
đổ hành vi chương trình trong file searching APIs
thì chúng ta sẽ phải thay đổi các
functions FindFirstFile và FindNextFile, nhưng chúng ta phải
biết rằng các functions này có ANSI và
WIDE version của nó, vì vậy chúng ta phải thay đổi
IAT address cho FindFirstFileA,
FindFirstFileW, FindNextFileA và cũng như
FileNextFileW. Nhưng vẫn có vài hàm
khác giống như FindFirstFileExA và WIDE
version của nó FindFirstFileExW mà
chúng được gọi bởi các hàm đã nói đến trước đó.
Chúng ta biết rằng FindFirstFileW
calls FindFirstFileExW nhưng điều này được thực hiện một cách trực tíep
- ko sử dụng IAT. Và vẫn vài cách
khác để thực hiện. Đó là e.g. ShellAPI
functions giống như
SHGetDesktopFolder mà chúng cũng calls FindFirstFileW một cách trực tiếp hay
FindFirstFileExW). Nhưng nếu chúng
ta có tất cả chúng, result sẽ rất hòan hảo.
Chúng
ta có thể sử dụng ImageDirectoryEntryToData từ imagehlp.dll để tìm ra IAT
một cách dễ dàng.
PVOID ImageDirectoryEntryToData(
IN LPVOID Base,
IN BOOLEAN MappedAsImage,
IN USHORT DirectoryEntry,
OUT PULONG Size
);
Chúng ta sẽ sử dụng Instance of ứng
dụng của chúng ta như là Base (Instance có thể có được bằng cách calling
GetModuleHandle:
hInstance = GetModuleHandleA(NULL);
), và như DirectoryEntry chúng at sẽ
sử dụng hằng số IMAGE_DIRECTORY_ENTRY_IMPORT.
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
Result của hàm này là pointer trỏ
đến IAT record đầu tiên. IAT records là cấu trúc
structures mà nó được định nghĩa bởi
I IMAGE_IMPORT_DESCRIPTOR. Vậy, result
là một pointer trên
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;
Giá
trị Name trong IMAGE_IMPORT_DESCRIPTOR là một tham chiếu tương ứng với
name of module. Nếu chúng ta muốn hook
một function e.g. từ kernel32.dll
chúng ta phải tìm ra trong imports mà
nó phụ thuộc vào descriptor với name là
kernel32.dll. Chúng ta sẽ call
ImageDirectoryEntryToData tại lúc bắt đầu và rồi chúng ta sẽ thử
tìm descriptor với name
"kernel32.dll" (ở đó có thể nhiều hơn 1
descriptor với name này). Cuối cùng
chúng ta sẽ phải tìm ra function của chúng ta trong
danh sách tất cả các functions trong
record (address of function của chúng ta có thể có được
bởi hàm GetProcAddress function). Nếu
chúng ta tìm nó chúng ta phải sử dụng VirtualProtect để thay đổi
memory page protection và sau đó thì
chúng ta có thể write phần này của memory.
Sau khi rewriting address chúng ta
phải thay đổi protection trở lại. Trước khi
calling VirtualProtect chúng ta phải
biết vài thông tin về memory page này.
Điều này được làm nởi VirtualQuery. Chúng
ta có thể thêm vài tests trong cas vài calls sẽ bị
fail (e.g. chúng ta ko tiếp tục nếu VirtualProtect
call đầu tiên bị failed, etc)>
PCSTR pszHookModName = "kernel32.dll",pszSleepName
= "Sleep";
HMODULE hKernel = GetModuleHandle(pszHookModName);
PROC pfnNew = (PROC)0x12345678, //new address will be here
pfnHookAPIAddr = GetProcAddress(hKernel,pszSleepName);
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
hInstance,
TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ulSize
);
while (pImportDesc->Name)
{
PSTR pszModName = (PSTR)((PBYTE) hInstance +
pImportDesc->Name);
if (stricmp(pszModName, pszHookModName) == 0)
break;
pImportDesc++;
}
PIMAGE_THUNK_DATA pThunk =
(PIMAGE_THUNK_DATA)((PBYTE) hInstance +
pImportDesc->FirstThunk);
while (pThunk->u1.Function)
{
PROC* ppfn = (PROC*) &pThunk->u1.Function;
BOOL bFound = (*ppfn == pfnHookAPIAddr);
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++;
}
Result of calling Sleep(1000) có thể
cho ví dụ này:
00407BD8: 68E8030000 push
0000003E8h
00407BDD: E812FAFFFF call
Sleep
Sleep: ;this is
jump on address in IAT
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
Vì vậy jump cuối cùng là 0x12345678.
=====[ 3.2.2 Own process hooking
using entry point rewriting ]==================
PP
rewriting vài chỉ thị đầu tiên trên function entry
point rất đơn giản. Như trong cas rewritng
address trong IAT mà chúng ta phải
thay
đổi page protection lúc đầu tiên. Ở đây nó sẽ là 5 bytes đầu tiên of hàm đã cho
mà
chúng ta muốn hook chúng. Để tiền dùng sau này chúng ta sẽ sử dụng cấp phát
động
cấu trúc MEMORY_BASIC_INFORMATION
structure. Điểm bắt đầu hàm có được bởi gọi hàm
GetProcAddress một lần nữa. Trên
addr này chúng ta sẽ insert relative jump đến
code của chúng ta. Chương trình sau
đây calls Sleep(5000) (vì vậy nó sẽ chờ 5 seconds), rồi
Sleep functions bị hooked và
redirected chuyển tiếp đến new_sleep, cuối cùng nó call
Sleep(5000) một lần nữa. Bởi vì new
function new_sleep ko làm gì và trả về returns
ngay lập tức nguyên chương trình sẽ
mất chỉ 5 trong 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 got Sleep address
push PAGE_READWRITE
push MEM_COMMIT
push MEMORY_BASIC_INFORMATION_SIZE
push 0
call VirtualAlloc
test eax,eax
jz do_sleep
mov esi,eax ;alocation for MBI
push MEMORY_BASIC_INFORMATION_SIZE
push esi
push edi
call VirtualQuery ;inforamtion about the memory page
test eax,eax
jz free_mem
call GetCurrentProcess
push 5
push edi
push eax
call FlushInstructionCache ;just to be sure :)
lea eax,[esi+014h]
push eax
push PAGE_EXECUTE_READWRITE
lea eax,[esi+00Ch]
push [eax]
push [esi]
call VirtualProtect ;we will change protection for a
moment
;so we will be able to write
there
test eax,eax
jz free_mem
mov byte ptr [edi],0E9h ;to write relative jump
mov eax,offset
new_sleep
sub eax,edi
sub eax,5
inc edi
stosd ;this
is relative address for jump
push offset
old_protect
lea eax,[esi+014h]
push [eax]
lea eax,[esi+00Ch]
push [eax]
push [esi]
call VirtualProtect ;return back the protection of page
free_mem:
push MEM_RELEASE
push 0
push esi
call VirtualFree ;free memory
do_sleep:
push 5000
call Sleep
push 0
call ExitProcess
new_sleep:
ret 004h
end start
Result of call Sleep thứ hai là như
vầy:
004010A4: 6888130000 push
000001388h
004010A9: E80A000000 call
Sleep
Sleep: ;toto je
jump na adresu v IAT
004010B8: FF2514204000 jmp
dword ptr [000402014h]
tabulka:
00402014: 79 67 E8 77 6C 7D E8 77
Kernel32.Sleep:
77E86779: E937A95788 jmp
0004010B5h
new_sleep:
004010B5: C20400 ret
004h
=====[ 3.2.3 Original function
saving ]=========================================
Hầu
như chúng ta cần hơn hook một hàm. Cho ví dụ, trong cas khi chúng ta
ko muốn replace hàm đã cho nhưng chỉ
check result của nó, hay trong cas khi
chúng ta chỉ muốn đôi khi replace
function e.g. khi nó được called với các đối số
đặc biệt. Ví dụ good về điều này là các
files thật sự đã nói trước đó hiding
được làm bởi replacing FindXXXFile functions.
Vì vậy nếu chúng ta muốn hide các file đặc trưng
và ko muốn thông báo thì chúng ta
phải bỏ original function cho tất cả các file
khác mà ko cần thay đổi hành vi của
các hàm. Điều này đon giản khi sử dụng pp
rewriting IAT. Đói với việc calling
original function chúng ta có thể có original
address của nó với việc gọi hàm GetProcAddress
và rồi gọi nó một cách trực tiếp. Nhưng vấn đề xảy ra
khi sử dụng rewriting entry point
method. Bởi rewriting 5 bytes này tại
functions entry point chúng ta mất original
function mà ko thể thu hồi được. Vì vậy chúng ta cần
save các
instructions đầu tiên. Chúng ta có thể sử dụng kỹ thuật sau.
Chúng ta biết rằng chúng ta sẽ chỉ rewrite
5 bytes đầu tiên nhưng ko biết bao nhiêu
chỉ thị
ở đó hay chúng dài bao nhiêu bytes. Chúng ta phải để dành đủ memory
cho các
chỉ thị đầu tien. 16 bytes có thể là đủ bởi vì thường ko có
các chỉ
thị dài tại lúc bắt đầu hà. Có lẽ chúng ta sử dụng ít hơn 16.
Tòan bộ
vùng memory để dành được lấp với giá trị 0x90 (0x90 = nop) trong case có các
chỉ thị
ngắn hơn.Kế đến 5bytes sẽ là relative jump mà nó sẽ được láp sau.
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
Bây
giờ chúng ta sẳn sàng copy các chỉ thị đầu tiên. Nó là một stuff dài để có
chiều dài của instruction, điều này
là tại sao chúng ta sẽ làm việc với một engine hòan chỉnh. Engine này
được làm bởi Z0MBiE. Nhập Input đói
số vào là instruction address mà chúng ta muốn lấy chiều dài
của chúng. Output thông thường trong
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:
; returns opcode length
in EAX or -1 if error
; input: pointer to
opcode
; __fastcall EAX
; __cdecl [ESP+4]
;this is my first change
here, it's the label only for calling this function
get_instr_len:
mov ecx, [esp+4] ; ECX = opcode ptr
xor edx, edx ; flags
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 @@modrm
@@exitmodrm:
test dl, C_MEM67
jnz @@mem67
@@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
;my second change heer,
there was retn only in original version
@@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
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 @@exitmodrm
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 @@mem1
cmp ah, 80h
je @@mem4
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 @@mem1
cmp ah, 80h
jne @@exitmodrm
@@mem2: or dl, C_MEM2
jmp @@exitmodrm
endp
.data
;0F -- analyzed in code, no flags (i.e.flags
must be 0)
;F6,F7 -- --//-- (ttt=000 -- 3 bytes, otherwise 2
bytes)
;CD -- --//-- (6 bytes if CD 20, 2 bytes
otherwise)
table_1 label dword
; normal instructions
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 ; 09
dd 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 ; 43
dd 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 ; 7C
dd 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 ; B5
dd 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 ; EE
dd 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-prefixed instructions
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 ; 26
dd -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 ; 60
dd -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 ; 9A
dd 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
Bây
giờ chúng ta có thể lấy được chiều dài của chỉ thị trên address bất kỳ. chúng
ta sẽ
repeat call này cho đến 5 bytes được
read. Sau đó chúng ta sẽ copy các bytes này đến
old_hook. Chúng ta biết bao nhiêu
bytes các chỉ thị đều tiên. vì vậy chúng ta có thể fill out the
relative (tương đối) jump address trên
next instruction trong original function.
.386p
.model flat, stdcall
...
.data
kernel_name db
"kernel32.dll",0
sleep_name db
"Sleep",0
...
MEM_RELEASE dd
000008000h
;16 nops + one relative
jump
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 ;calling LDE32
pop ecx
add ecx,eax
add ebx,eax
cmp ecx,5
jb get_five_bytes
mov edi,offset old_sleep ;counting relative jump address
mov [edi+011h],ebx
sub [edi+011h],edi
sub dword ptr
[edi+011h],015h
rep movsb
pop edi
;following code was
above, so without comments
push PAGE_READWRITE
push MEM_COMMIT
push MEMORY_BASIC_INFORMATION_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 VirtualProtect
free_mem:
push MEM_RELEASE
push 0
push esi
call VirtualFree
do_sleep:
push 5000
call Sleep
push 0
call ExitProcess
new_sleep:
mov eax,dword ptr
[esp+004h]
add eax,eax ;doubling timeout
push eax
mov eax,offset
old_sleep ;calling old
function
call eax
ret 004h
After the hook it will look like
this:
004010CC:
6888130000 push 000001388h
004010D1:
E818090000 call Sleep
Sleep: ;this is jump on 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_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 placed 1 byte
after first instruction at Kernel32.Sleep
(77E86779)
Kernel32.77E8677F:
77E8677F:
E803000000 call Kernel32.SleepEx
... ;following is unimportant
To make this clearer, this is how
the original version of Kernel32.Sleep looks:
Kernel32.Sleep:
77E86779:
6A00 push 0
77E8677B:
FF742408 push dword ptr [esp+8]
77E8677F:
E803000000 call Kernel32.SleepEx
77E86784:
C20400 ret 00004h
Như
bạn có thể thấy chúng ta đã copied chỉ thị đầu tiên và thứ hai (nó là 6 bytes
ở đây) và relative jump được trỏ đến
trên next instruction và đó là nó sẽ như thế nào
Chúng ta phải tưởng tượng ở đây các
relative jumps ko được đặt như
các bytes đầu tiên của các hàm functions.
Nếu là thế chúng ta có một vấn đề. Vấn đề
kết tíep là với các hàm APIs giống
như ntdll.DbgBreakPoint. Chúng quá ngắn cho pp này
để hooking. Và thấy rằng khi nó được
gọi bởi Kernel32.DebugBreak, nó ko thể hook bởi
thay đổi IAT. Nhưng ai muốn hook
function mà nó chỉ gọi
int 3? Nhưng ko có gì là ko thể. Bạn
có thể nghĩ về nó và bạn có thể
tìm cách như thế nào để giải quyết
vấn đề này . Khi bạn nghĩ về điều này bạn có thể hook hàm
sau đây sau hàm này (nó sẽ bị damaged
bởi rewritng first 5 bytes of hàm trước đó).
Hàm DbgBreakPoint là 2 bytes chiều
dài, vì vậy chúng ta có thể set vài
flags ở đây và thữ write conditional
jump trên begining of the second
function ... Nhưng bây giờ điều ày
ko phải là vấn đề của chúng ta.
Với vấn đề saving original function rồi
thuật lại unhooking.
Unhooking thay đổi thay thế các
bytes trở lại trạng thái gốc original state. Khi rewriting
IAT bạn sẽ phải return original
address đến table nếu bạn muốn làm
unhooking. Khi sử dụng 5 byte patch bạn
sẽ phải copy các chỉ thị gốc đàu tiên trở lại.
Cảc hai cách thật sự đơn giản và ko
cần viết nhềiu về điều này.
=====[ 3.2.4 Other process hooking
]============================================
Bây
giờ chúng ta sẽ làm vài thứ thực tế với việc hooking trong suốt quá trình
running. Ai múôn
đề cập đến hooking cho chính process
của nó? Đó chỉ tốt cho việc học căn bản
nhưng nó ko thực tế.
Tôi
sẽ chỉ cho bạn 3 pp để hooking process khác. Hai trong chúng sử dụng
API CreateRemoteThread mà chúng chỉ
có trong Windows với NT technology. Vấn đề
of hooking ko đáng quan tâm cho các older
windows version đối với tôi. Sau tất cả
tôi sẽ thử giải thích pp thứ ba mà
tôi ko thực hành. vì vậy nó có thể ko
họat động.
Dầu
tiên nói vài diều về CreateRemoteThread. Như phần help nói rằng hàm này
creates new thread trong bất kỳ process
nào và runs code của nó.
HANDLE
CreateRemoteThread(
HANDLE
hProcess,
LPSECURITY_ATTRIBUTES
lpThreadAttributes,
DWORD
dwStackSize,
LPTHREAD_START_ROUTINE
lpStartAddress,
LPVOID
lpParameter,
DWORD
dwCreationFlags,
LPDWORD
lpThreadId
);
Handle
hProcess có thể có bởi OpenProcess. Ở đây ch1ng ta phải có quyền
cần thiết. Pointer lpStartAddress trỏ
đến memory place trong TARGET
process mà ở đó chỉ thị đầu tiên cho
new thread . Bởi vì new thread được
created trong target process nó ở
trong memory of target process. Con trỏ
lpParameter trỏ đến trên đối số mà
nó sẽ được tham chiếu đén new thread.
=====[ 3.2.4.1 DLL Injection
]==================================================
Chúng
ta có thể run new thread từ bất kỳ nơi nào trong target process memory.
Ở đây trừ phi chúng ta có chính code
trong nó. PP đầu tiên cheats cho điều này.
Nó sử dụng GetProcAddress để có addr
thực cho hàm LoadLibrary. Rồi định tuyến
lpStartAddress đến address of
LoadLibrary. Function LoadLibary chỉ có một
parameter giống như hàm cho new
thread trong target process.
HINSTANCE
LoadLibrary(
LPCTSTR
lpLibFileName
);
Chúng
ta sẽ sử dụng sự đồng dạng này và chúng ta sẽ tham chiếu đến name of our DLL
library như là lpParameter. Sau đó running
new thread lpParameter sẽ trên vị trí của
of lpLibFileName. Điều quan trọng
nhất ở đây là hành vi được mô tả trước đó.
Sau khi loading new module vào trong
target process memory phần khởi trị đầu đã được
thực thi. Nếu chúng ta đặt các hàm functions
đặc biệt mà chúng sẽ hook các functions mà chúng ta muốn
chúng ta sẽ chiến thắng trong stuff
này. Sau khi thực thi phần khởi trị đầu, thread sẽ ko có gì
dể làm và close nhưng module của
chúng ta sẽ vẫn trong memory. PP này thật sự
rất đẹp và dễ thực hiện. Đây
được gọi là DLL Injection. Nhưng nếu bạn thích như tôi
bạn bạn ko thích phải có nhiều DLL
library. Nhưng nếu ai đó ko quan tâm
về việc có nhiều library này thì nó
là pp dễ nhất và nhanh nhất (từ gốc độ
lập trình viên).
=====[ 3.2.4.2 Independent code
]===============================================
Tiếp
tục với cách independent code là rất khó nhưng cũng rất ấn
tượng.
Independent code là code mà ko có các addresses tỉnh nào.
Mọi thứ
đều là tương đối trong nó theo hướng một
nơi mốc nào đó trong nó. Code này
hầu hết
được làm nếu chúng ta ko biết address mà ở đó code này sẽ được thực thi.
Chắc
chắn, nó có thể có addr này và rồi relink code của chúng ta như là nó xử lý
trên new address mà ko có lỗi nào
nhưng điều này ngay cả khó hơn là
coding
independent code. Ví dụ về lọai code này có thể là virus code.
Virus mà
chúng lây nhiễm vào các file thực thi trong cách mà nó add chính nó vào một nơi
nào đó
trong file thực thi này Các file
thực thi khác sẽ là virus code trên các nơi khác
mà nó phụ thuộc. e.g. on file
structure on length.
Đầu
tiên chúng ta phải insert code của chúng
ta trong target process. Rồi
function
CreateRemoteThread sẽ săn sóc running
code của chúng ta. Vì vậy, đầu tiên chúng ta phải
lấy vài thông tin về target process và
lấy handle với OpenProcess.
Rồi dùng VirtualAllocEx sẽ cách phát
vài space trong remote process memory cho
code của chúng ta.Cuối cùng chúng ta
sẽ sử dụng WriteProcessMemory để write code của chúng ta trên
memory được cấp phát và run nó.
Trong CreateRemoteThread lpStartAddress sẽ tham chiếu đến
memory được cấp phát và lpParameter có
thể là bất cứ gì chúng ta muốn. Bởi vì Tôi thật sự ko thích các
files ko cần thíết nào nên tôi sử
dụng pp này.
=====[ 3.2.4.3 Raw change
]=====================================================
Ko
có hàm CreateRemoteThread trong older windows version (ngọai trừ NT).
Vì vậy cúng ta ko thể sử dụng hàm
này để hooking. Có lẽ có pp khác và tốt hơn các pp
hook tôi sẽ nói về một pp khác ngay
bây giờ. Trong thực tế tôi ko biết nếu pp này
làm việc trong thực tiển (chưa có ai
biết khi sử dụng Windows) nhưng về mặt lý thuyết thì mọi thứ
đều OK .
Chúng
ta ko cần phải có code của chúng ta trong target process để hook các hàm functions
của nó hòan tòan. Chúng ta có hàm WriteProcessMemory
(hàm này có trong all các Windows
version) và chúng ta củng có hàm OpenProcess.
Sau cùng chúng ta cần là VirtualProtectEx
mà chúng có thể thay đổi truy xấut
memory pages trong target process. Tôi ko thể thấy bất cứ
lý do gì mà ko hook được target
process functions một cách trực tiếp từ process của chúng ta...
=====[ 4. Ending
]==============================================================
This
small document ends. I will greet (đón chào) any extension which will
describe unmentionde (ko được nhắc
đến ) methods of hook, I am sure there are a lot of them. I will
also greet (chào đón) any extensions
in the parts which were not described so intimately (sâu sắc).
You can also send me some source
codes if they are fecund (đẻ nhiều) for the problem of
hooking e.g. for parts where I was
lazy to write the code. The goal of this
document is to show details of every
technics of hooking. I hope I've done
the part of this.
Special
thanks to Z0MBiE for his work, so I haven't to code it myself
and spend ages by studying tables
for getting instruction length.
===================================[
End ]======================================