Hook原理
Hook原理
對於會Hook的人來說,Hook其實也就那麼回事.對於沒有Hook過的人來說,會感覺Hook很高大上(其實也沒毛病).
那麼今天我們就來探討一些Hook的原理是什麼.
我認為任何Hook都可以分為以下三步(簡稱WFH):
需要Hook的是什麼,在哪裡(後面簡稱Where).
尋找到Hook的地方.(後面簡稱Find)
進行Hook.(後面簡稱Hook)
當然了同一個型別的Hook所在的地方一般是一樣的.但尋找到Hook的地方,和進行Hook卻會有許多不同的方法.我們要抓住的是不變的地方.
根據這3點我們舉例來驗證一下.
Hook API
第一個我儘量說得詳細一些.
舉例子:Hook API:OpenProcess 讓win10 64位的工作管理員關閉不了任何程式.
1. Where.
Hook API:OpenProcess. 在kernelbase.dll裡面.
2.Find.
方式1:
通過LoadLibrary載入kernelbase.dll模組基地址.
通過GetProcAddress獲取OpenProcess的地址.
方式2:程式設計直接引用OpenProcess的地址,因為在Windows下3大模組user32.dll,kernelbase.dll,ntdll.dll的載入基地址在每個應用程式中都是一樣的.
方式3:通過尋找目標的IAT找到OpenProcess
3.Hook.
方式1:通過注入dll到目標程式進行,可以替換kernelbase.dll裡面的OpenProcess的前面5個位元組為jmp跳轉到我們自己的地址,也可以修改目標程式的IAT.
方式2:通過WriteProcessMemory寫入程式碼,修改目標程式的OpenProcess跳轉到我們的程式碼.
程式碼例項:F1+H1(Find的第二種方式,Hook的第一種方式,後面不再說明):
新建一個dll檔案:
在dll檔案裡面寫如下程式碼
如果你的win10是64位的就編譯64位的,32位就編譯32位的
//dllmain.cpp : 定義 DLL 應用程式的入口點。
DWORD oldProtect;
BYTE JmpBtye[5];
BYTE OldByte[5];
void*OpenProcessaddr;
boolH1_OpenProcess();
void UnHook();
BOOLAPIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
H1_OpenProcess();
break;
case DLL_PROCESS_DETACH:
UnHook();
break;
}
returnTRUE;
}
HANDLE MyOpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId)
{
dwDesiredAccess &=~PROCESS_TERMINATE;//去掉關閉程式的許可權
UnHook();//恢復Hook 任何調整到原來的地方執行.
HANDLE h=OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
H1_OpenProcess();
returnh;
}
void*F1_OpenProcess()
{
//尋找到OpenProcess的地址
void*addr=0;
//載入kernel32.dll
HMODULE hModule=LoadLibraryA("kernelbase.dll");
//獲取OpenProcess的地址
addr=(void*)GetProcAddress(hModule,"OpenProcess");
returnaddr;
}
void*F2_OpenProcess()
{
return(void*)OpenProcess;
}
boolH1_OpenProcess()
{
//1.開始尋找地址
void*addr=F1_OpenProcess();
OpenProcessaddr=addr;
//判斷是否尋找成功
if(addr==0)
{
MessageBoxA(NULL,"尋找地址失敗",NULL,0);
returnfalse;
}
//2.進行Hook
/*
一般程式碼段是不可寫的,我們需要把其改為可讀可寫.
*/
VirtualProtect((void*)addr,5, PAGE_EXECUTE_READWRITE,&oldProtect);
//修改前面的5個位元組為jmp 跳轉到我們的程式碼.
//內聯Hook 跳轉偏移計算方式:跳轉偏移=目標地址-指令地址-5
//jmp 的OpCode 為:0xE9
JmpBtye[0]=0xE9;
*(DWORD*)&JmpBtye[1]=(DWORD)((longlong)MyOpenProcess-(longlong)addr-5);
//儲存原先位元組
memcpy(OldByte, (void*)addr,5);
//替換原先位元組
memcpy((void*)addr, JmpBtye,5);
}
void UnHook()
{
//恢復原先位元組
memcpy((void*)OpenProcessaddr, OldByte,5);
//恢復屬性
DWORD p;
VirtualProtect((void*)OpenProcessaddr,5, oldProtect, &p);
}
把dll注入工作管理員,因為注入不是我們主題,所以這裡我只是簡單的貼出程式碼,直接拿來用就可以
```
#include <windows.h>
//獲取程式控制程式碼
HANDLE GetThePidOfTargetProcess(HWND hwnd)
{
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
HANDLE hProcee=::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_CREATE_THREAD,0, pid);
returnhProcee;
}
//提升許可權
void Up()
{
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tp;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
tp.PrivilegeCount=1;
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid=luid;
AdjustTokenPrivileges(hToken,0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
}
//程式注入
BOOLDoInjection(char*DllPath, HANDLE hProcess)
{
DWORD BufSize=strlen(DllPath)+1;
LPVOID AllocAddr=VirtualAllocEx(hProcess, NULL, BufSize, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, AllocAddr, DllPath, BufSize, NULL);
PTHREAD_START_ROUTINE pfnStartAddr=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")),"LoadLibraryA");
HANDLE hRemoteThread;
hRemoteThread=CreateRemoteThread(hProcess, NULL,0, pfnStartAddr, AllocAddr,0, NULL);
if(hRemoteThread)
{
MessageBox(NULL, TEXT("注入成功"), TEXT("提示"), MB_OK);
returntrue;
}
else
{
MessageBox(NULL, TEXT("注入失敗"), TEXT("提示"), MB_OK);
returnfalse;
}
}
intmain()
{
//這裡填寫視窗標題
HWND hwnd=FindWindowExA(NULL, NULL, NULL,"工作管理員");
Up();
HANDLE hP=GetThePidOfTargetProcess(hwnd);
//開始注入
//這裡填寫Dll路徑
DoInjection("E:\\studio\\VS2017\\F2H1.MessageBox\\x64\\Release\\F2H1.MessageBox.dll", hP);
}
```
注入之後看效果
)
其實還有很多方式,剩下的方式你就可以自己慢慢嘗試了.
SSDT Hook.
剛才說了使用者層的Hook,接下來我們再說一下核心層的Hook,其實還是3歩曲.WFH
實現相似的功能,讓所有程式關閉不了自己的程式.
1.Where
Windows 作業系統共有4個系統服務描述符.其中只用了兩個,第一個是SSDT,第二個是ShadowSSDT
系統描述符結構如下:
typedef struct _KSYSTEM_SERVICE_TABLE
{
ULONG*ServiceTableBase; //服務表基址 第一個表示SSDT 緊接著下一個ShadowSSDT
ULONG*ServiceCounterTableBase;//計數表基址
ULONG NumberOfServices; //表中項的個數
UCHAR*ParamTableBase; //參數列基址
}KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE;
SSDT Hook:NtOpenProcess,在ntkrnlpa.exe核心模組中的系統服務描述符表中的SSDT表中的第190號.
使用PCHunter32檢視
2.Find
方式1:在Win7 32下,系統服務描述符表直接匯出符號KeServiceDescriptorTable,可以直接獲取其地址,然後通過其第一個ServiceTableBase就是SSDT的地址,接著找到第190號函式.
方式2:可以通過PsGetCurrentThread 獲取ETHREAD結構,該結構的第一個欄位KTHREAD有一個欄位ServiceTable儲存著系統描述符表的地址KeServiceDescriptorTable.通過其第一個ServiceTableBase就是SSDT的地址,接著找到第190號函式.
0: kd> u PsGetCurrentThread
nt!PsGetCurrentThread:
840473f164a124010000 mov eax,dword ptr fs:[00000124h] ;ETHREAD
840473f7c3 ret
3.Hook
方式1:替換找到的地方,換成我們自己的函式
方式2:獲取找到的地方的函式指標,改變其程式碼跳轉到自己的程式碼(其實就是inline Hook).
例子:F2H1
新建一個驅動程式:
2.程式碼如下:
#include <ntifs.h>
#pragma pack(1)
typedef struct _KSYSTEM_SERVICE_TABLE
{
ULONG*ServiceTableBase; //服務表基址 第一個表示SSDT 緊接著下一個是ShadowSSDT
ULONG*ServiceCounterTableBase;//計數表基址
ULONG NumberOfServices; //表中項的個數
UCHAR*ParamTableBase; //參數列基址
}KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE;
#pragma pack()
void*OldNtProcess=0;
//匯入系統描述符表
extern"C"NTSYSAPI KSYSTEM_SERVICE_TABLE KeServiceDescriptorTable;
typedef NTSTATUS(NTAPI*NTOPENPROCESS)(PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId);
NTOPENPROCESS g_NtOpenProcess=NULL;
NTSTATUS NTAPI MyOpenProcess(
PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PCLIENT_ID ClientId
)
{
if(ClientId->UniqueProcess==(HANDLE)916)//指定保護的程式ID
{
returnSTATUS_ABANDONED;
}
returng_NtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
void OffProtect()
{
__asm {//關閉記憶體保護
push eax;
mov eax, cr0;
andeax, ~0x10000;//關閉CR0.WP位,關閉頁保護
mov cr0, eax;
pop eax;
}
}
void OnProtect()
{
__asm {//恢復記憶體保護
push eax;
mov eax, cr0;
oreax,0x10000;//開啟CR0.WP位,開啟頁保護
mov cr0, eax;
pop eax;
}
}
void*F1_NtOpenProcess()
{
return(void*)&KeServiceDescriptorTable.ServiceTableBase[190];
}
void*F2_NtOpenProcess()
{
PETHREAD eThread=PsGetCurrentThread();
PKSYSTEM_SERVICE_TABLE kServiceTable=(PKSYSTEM_SERVICE_TABLE)(*(ULONG*)((ULONG)eThread+0xbc));
return(void*)&kServiceTable->ServiceTableBase[190];
}
boolH1_NtOpenProcess()
{
OldNtProcess=F2_NtOpenProcess();//Find
g_NtOpenProcess=(NTOPENPROCESS)(*(ULONG*)OldNtProcess);//儲存就地址
OffProtect();//由於SSDT表是隻讀的所以需要關閉頁寫入保護
(*(ULONG*)OldNtProcess)=(ULONG)MyOpenProcess;//寫入自己的函式地址
OnProtect();//恢復
returntrue;
}
void UnHook()
{
OffProtect();//由於SSDT表是隻讀的所以需要關閉頁寫入保護
(*(ULONG*)OldNtProcess)=(ULONG)g_NtOpenProcess;//恢復函式
OnProtect();//恢復
}
void Unload(PDRIVER_OBJECT pDri)
{
UNREFERENCED_PARAMETER(pDri);
UnHook();
}
extern"C"NTSTATUS DriverEntry(PDRIVER_OBJECT pDri, PUNICODE_STRING pRegStr)
{
UNREFERENCED_PARAMETER(pRegStr);
pDri->DriverUnload=Unload;
H1_NtOpenProcess();
returnSTATUS_SUCCESS;
}
載入驅動程式(自己寫的一個小工具,也可以網上下載)
)
4.檢視效果
SYSENTRY Hook
這裡我再說一些Hook,也是3歩曲WFH.但是我不再提供具體實現.
我們知道以前windows系統是通過int2e中斷進入系統核心的,但是現在是通過cpu提供的一個功能sysentry進入系統的(32位是sysentry,64位是syscall).這是一個CPU指令,如果對該指令不知道的話,可以檢視我另外一篇文章:
1.Where
SYSENTRY Hook:190號功能號,功能號儲存在eax中.
SYSENTRY指令進入系統核心的地址儲存在MSR暫存器裡面的**IA32_SYSENTER_EIP**(0x176)號暫存器.
2.Find
通過指令rdmsr讀取**IA32_SYSENTER_EIP**MSR暫存器.其中ecx儲存的是讀取msr的序號,也就是0x176號,返回的結果儲存在edx:eax(64位,edx儲存高32位,eax儲存低32位).因為是32位系統,所以只需要eax的值即可.
3.Hook
通過wrmsr寫入我們自己的地址,地址放在edx:eax(64位,edx儲存高32位,eax儲存低32位).即可完成Hook.
Object Hook
每一個不同的核心物件,都對應著一個不同的型別索引:TypeIndex.通過該索引可以找到該核心物件的型別:OBJECT_TYPE
1.Where
在核心物件的TypeInfo中.
2.Find
通過ObGetObjectType核心函式獲取核心物件型別(OBJECT_TYPE)的OBJECT_TYPE中有一個欄位TypeInfo(型別_OBJECT_TYPE_INITIALIZER),其中儲存著,在建立核心物件,銷燬核心物件的一系列建構函式.
對應結構:
//OBJECT_TYPE-->TypeInfo:_OBJECT_TYPE_INITIALIZER
ntdll!_OBJECT_TYPE
+0x000TypeList : _LIST_ENTRY
+0x010Name : _UNICODE_STRING
+0x020DefaultObject : Ptr64 Void
+0x028Index : UChar
+0x02cTotalNumberOfObjects : Uint4B
+0x030TotalNumberOfHandles : Uint4B
+0x034HighWaterNumberOfObjects : Uint4B
+0x038HighWaterNumberOfHandles : Uint4B
+0x040TypeInfo : _OBJECT_TYPE_INITIALIZER //1.這個
+0x0b0TypeLock : _EX_PUSH_LOCK
+0x0b8Key : Uint4B
+0x0c0CallbackList : _LIST_ENTRY
ntdll!_OBJECT_TYPE_INITIALIZER
+0x000Length : Uint2B
+0x002ObjectTypeFlags : UChar
+0x002CaseInsensitive : Pos0,1Bit
+0x002UnnamedObjectsOnly : Pos1,1Bit
+0x002UseDefaultObject : Pos2,1Bit
+0x002SecurityRequired : Pos3,1Bit
+0x002MaintainHandleCount : Pos4,1Bit
+0x002MaintainTypeList : Pos5,1Bit
+0x002SupportsObjectCallbacks : Pos6,1Bit
+0x004ObjectTypeCode : Uint4B
+0x008InvalidAttributes : Uint4B
+0x00cGenericMapping : _GENERIC_MAPPING
+0x01cValidAccessMask : Uint4B
+0x020RetainAccess : Uint4B
+0x024PoolType : _POOL_TYPE
+0x028DefaultPagedPoolCharge : Uint4B
+0x02cDefaultNonPagedPoolCharge : Uint4B
+0x030DumpProcedure : Ptr64 void
+0x038OpenProcedure : Ptr64 long//開啟 回撥函式
+0x040CloseProcedure : Ptr64 void//關閉 回到函式
+0x048DeleteProcedure : Ptr64 void
+0x050ParseProcedure : Ptr64 long
+0x058SecurityProcedure : Ptr64 long
+0x060QueryNameProcedure : Ptr64 long//查詢名稱 回撥函式
+0x068OkayToCloseProcedure : Ptr64 unsigned char
3.Hook
根據找到的位置替換裡面回撥函式指標為我們自己寫的函式即可.比如替換OpenProcedure.
IDT Hook
1.Where
在中斷描述符表(IDT)中.
2.Find
idtr暫存器指向中斷描述符表.通過idtr找到.
說明:idtr是一個48位暫存器,其中低16位儲存中斷描述符表長度.高32位是中斷描述符表.的基地址.
3.Hook
通過構造一箇中斷門或者陷阱門,其中中斷門或陷阱門的偏移地址寫自己的地址.然後把中斷門或者陷阱門寫入都相應的IDT表項中.
總結:
從上面我們可以看到,其實Hook都是一樣的,只是對應的地方不同,尋找的方法不同,替換(修改)的方法不同而已.
有的人可能就要反問了,SetWindowsHookEx,就不要知道Hook的地方在哪了,也不需要尋找.確實,這兩歩不需要我們自己做,但並不代表不需要,這只是作業系統為我們做了而已,我們只需要提供一個回撥函式即可.
所以下面我留下一個小測試:就是自己自己實現SetWindowsHookEx.
相關文章
- Android外掛化原理解析——Hook機制之Binder Hook2021-09-09AndroidHook
- [譯] 深入 React Hook 系統的原理2019-03-26ReactHook
- Android Hook框架Xposed原理與原始碼分析2019-01-16AndroidHook框架原始碼
- iOS 逆向 - Hook / fishHook 原理與符號表2019-11-12iOSHook符號
- hook的幾種方式及原理學習2024-06-01Hook
- 深入淺出 PLT/GOT Hook與原理實踐2023-12-01GoHook
- Activity外掛化原理第二種方案:Hook IActivityManager2019-02-02Hook
- Activity外掛化原理第一種方案:Hook Instrumentation2019-01-30Hook
- hook初識之inline hook2024-04-19Hookinline
- Hook技術之Hook Activity2019-02-17Hook
- hook!2018-11-12Hook
- iOS底層原理 runtime - super、hook、以及簡單應用--(8)2019-07-22iOSHook
- Android 外掛化原理解析——Hook機制之AMS&PMS2021-09-09AndroidHook
- react hook2018-11-19ReactHook
- IAT Hook2019-06-04Hook
- Javascript Hook2024-06-24JavaScriptHook
- exit hook2022-01-05Hook
- Hook踩坑記:React Hook react-unity-webgl2020-06-09HookReactUnityWeb
- 以pytorch的forward hook為例探究hook機制2024-10-09PyTorchForwardHook
- 使用Hook寫Redux2019-07-29HookRedux
- An example about git hook2022-09-06GitHook
- PC微信機器人介面api之徹底搞懂hook原理及手動實現2021-06-25機器人APIHook
- 為ExecutorService增加shutdown hook2018-11-19Hook
- React學習之Hook2024-03-07ReactHook
- 新特性 Hook 簡述2019-08-06Hook
- 域滲透——Hook PasswordChangeNotify2020-08-19Hook
- React Hook測試指南2020-07-22ReactHook
- React Hook快速入門2019-06-10ReactHook
- JS HOOK 程式碼段2024-11-04JSHook
- 羽夏逆向指引—— Hook2022-03-28Hook
- php->所謂"hook"2021-09-09PHPHook
- React Hook 入門使用2020-12-27ReactHook
- PostgreSQL外掛hook機制2018-10-31SQLHook
- Windows核心程式設計_Hook2018-06-22Windows程式設計Hook
- iOS逆向 程式碼注入+Hook2019-12-04iOSHook
- 翻譯|How to Use the useReducer Hook2019-04-28useReducerHook
- [譯] X 為啥不是 hook?2019-02-20Hook
- react-hook-form驗證2024-08-03ReactHookORM