日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Hook原理

發布時間:2025/4/5 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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的第一種方式,后面不再說明):

    ?

    1)新建一個dll文件:

    ?

    ?

    (2)在dll文件里面寫如下代碼:

    ?

    如果你的win10是64位的就編譯64位的,32位就編譯32位的

    ?

    // dllmain.cpp : 定義 DLL 應用程序的入口點。
    DWORD oldProtect;
    BYTE ?JmpBtye[5];
    BYTE ?OldByte[5];
    void?* OpenProcessaddr;
    bool?H1_OpenProcess();
    void?UnHook();
    BOOL APIENTRY?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;
    ? ?}
    ? ?return?TRUE;
    }

    HANDLE?MyOpenProcess(
    ? ?DWORD dwDesiredAccess,
    ? ?BOOL ?bInheritHandle,
    ? ?DWORD dwProcessId)
    {
    ? ?dwDesiredAccess &= ~PROCESS_TERMINATE;//去掉關閉程序的權限
    ? ?UnHook();//恢復Hook 任何調整到原來的地方執行.
    ? ?HANDLE h = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
    ? ?H1_OpenProcess();
    ? ?return?h;
    }



    void?*?F1_OpenProcess()
    {
    ? ?//尋找到OpenProcess的地址
    ? ?void?* addr =?0;
    ? ?//加載kernel32.dll
    ? ?HMODULE hModule = LoadLibraryA("kernelbase.dll");
    ? ?//獲取OpenProcess的地址
    ? ?addr=(void?*)GetProcAddress(hModule,?"OpenProcess");
    ? ?return?addr;
    }


    void?*?F2_OpenProcess()
    {
    ? ?return?(void?*)OpenProcess;
    }


    bool?H1_OpenProcess()
    {
    ? ?//1.開始尋找地址
    ? ?void?* addr = F1_OpenProcess();
    ? ?OpenProcessaddr = addr;
    ? ?//判斷是否尋找成功
    ? ?if?(addr ==?0)
    ? ?{
    ? ? ? ?MessageBoxA(NULL,"尋找地址失敗",NULL,0);
    ? ? ? ?return?false;
    ? ?}
    ? ?//2.進行Hook

    ? ?/*
    ? ?一般代碼段是不可寫的,我們需要把其改為可讀可寫.
    ? ?*/
    ? ?VirtualProtect((void?*)addr,?5, PAGE_EXECUTE_READWRITE,&oldProtect);

    ? ?//修改前面的5個字節為jmp 跳轉到我們的代碼.
    ? ?//內聯Hook 跳轉偏移計算方式:跳轉偏移=目標地址-指令地址-5
    ? ?//jmp 的OpCode 為:0xE9

    ? ?JmpBtye[0] =?0xE9;
    ? ?*(DWORD *)&JmpBtye[1] = (DWORD)((long?long)MyOpenProcess - (long?long)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);
    ? ?return?hProcee;
    }
    //提升權限
    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);
    }

    //進程注入

    BOOL?DoInjection(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);
    ? ? ? ?return?true;
    ? ?}
    ? ?else
    ? ?{
    ? ? ? ?MessageBox(NULL, TEXT("注入失敗"), TEXT("提示"), MB_OK);
    ? ? ? ?return?false;
    ? ?}
    }


    int?main()
    {
    ? ?//這里填寫窗口標題
    ? ?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:
    840473f1?64a124010000 ? ?mov ? ? eax,dword ptr?fs:[00000124h] ?;ETHREAD
    840473f7?c3?? ? ? ? ? ? ?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
    ? ?{
    ? ? ? ?return?STATUS_ABANDONED;
    ? ?}

    ? ?return?g_NtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
    }




    void?OffProtect()
    {
    ? ?__asm {?//關閉內存保護
    ? ? ? ?push eax;
    ? ? ? ?mov eax, cr0;
    ? ? ? ?and?eax, ~0x10000;//關閉CR0.WP位,關閉頁保護
    ? ? ? ?mov cr0, eax;
    ? ? ? ?pop eax;
    ? ?}
    }
    void?OnProtect()
    {
    ? ?__asm {?//恢復內存保護
    ? ? ? ?push eax;
    ? ? ? ?mov eax, cr0;
    ? ? ? ?or?eax,?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];

    }

    bool?H1_NtOpenProcess()
    {
    ? ?OldNtProcess = F2_NtOpenProcess();//Find
    ? ?g_NtOpenProcess = (NTOPENPROCESS)(*(ULONG *)OldNtProcess);//保存就地址
    ? ?OffProtect();//由于SSDT表是只讀的所以需要關閉頁寫入保護
    ? ?(*(ULONG *)OldNtProcess) = (ULONG)MyOpenProcess;//寫入自己的函數地址
    ? ?OnProtect();//恢復
    ? ?return?true;
    }

    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();
    ? ?return?STATUS_SUCCESS;
    }

    ?

    加載驅動程序(自己寫的一個小工具,也可以網上下載)

    ?

    ?

    4.查看效果


    ?

    ?

    ?

    ?

    ?

    SYSENTRY Hook

    ?

    ?

    這里我再說一些Hook,也是3歩曲WFH。但是我不再提供具體實現。

    ?

    我們知道以前windows系統是通過int?2e中斷進入系統內核的,但是現在是通過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
    ? +0x000?TypeList ? ? ? ? ? ? ? ? ? ? : _LIST_ENTRY
    ? +0x010?Name ? ? ? ? ? ? ? ? ? ? ? ? : _UNICODE_STRING
    ? +0x020?DefaultObject ? ? ? ? ? ? ? ?: Ptr64 Void
    ? +0x028?Index ? ? ? ? ? ? ? ? ? ? ? ?: UChar
    ? +0x02c?TotalNumberOfObjects ? ? ? ? : Uint4B
    ? +0x030?TotalNumberOfHandles ? ? ? ? : Uint4B
    ? +0x034?HighWaterNumberOfObjects ? ? : Uint4B
    ? +0x038?HighWaterNumberOfHandles ? ? : Uint4B
    ? +0x040?TypeInfo ? ? ? ? ? ? ? ? ? ? : _OBJECT_TYPE_INITIALIZER ?//1.這個
    ? +0x0b0?TypeLock ? ? ? ? ? ? ? ? ? ? : _EX_PUSH_LOCK
    ? +0x0b8?Key ? ? ? ? ? ? ? ? ? ? ? ? ?: Uint4B
    ? +0x0c0?CallbackList ? ? ? ? ? ? ? ? : _LIST_ENTRY


    ntdll!_OBJECT_TYPE_INITIALIZER
    ? +0x000?Length ? ? ? ? ? ? ? ? ? ? ? : Uint2B
    ? +0x002?ObjectTypeFlags ? ? ? ? ? ? ?: UChar
    ? +0x002?CaseInsensitive ? ? ? ? ? ? ?: Pos?0,?1?Bit
    ? +0x002?UnnamedObjectsOnly ? ? ? ? : Pos?1,?1?Bit
    ? +0x002?UseDefaultObject ? ? ? ? ? ? : Pos?2,?1?Bit
    ? +0x002?SecurityRequired ? ? ? ? ? ? : Pos?3,?1?Bit
    ? +0x002?MaintainHandleCount ? ? ? ? : Pos?4,?1?Bit
    ? +0x002?MaintainTypeList ? ? ? ? ? ? : Pos?5,?1?Bit
    ? +0x002?SupportsObjectCallbacks ? ? : Pos?6,?1?Bit
    ? +0x004?ObjectTypeCode ? ? ? ? ? ? ? : Uint4B
    ? +0x008?InvalidAttributes ? ? ? ? ? ?: Uint4B
    ? +0x00c?GenericMapping ? ? ? ? ? ? ? : _GENERIC_MAPPING
    ? +0x01c?ValidAccessMask ? ? ? ? ? ? ?: Uint4B
    ? +0x020?RetainAccess ? ? ? ? ? ? ? ? : Uint4B
    ? +0x024?PoolType ? ? ? ? ? ? ? ? ? ? : _POOL_TYPE
    ? +0x028?DefaultPagedPoolCharge ? ? : Uint4B
    ? +0x02c?DefaultNonPagedPoolCharge : Uint4B
    ? +0x030?DumpProcedure ? ? ? ? ? ? ? ?: Ptr64 ? ??void
    ? +0x038?OpenProcedure ? ? ? ? ? ? ? ?: Ptr64 ? ??long//打開 回調函數
    ? +0x040?CloseProcedure ? ? ? ? ? ? ? : Ptr64 ? ??void//關閉 回到函數
    ? +0x048?DeleteProcedure ? ? ? ? ? ? ?: Ptr64 ? ??void
    ? +0x050?ParseProcedure ? ? ? ? ? ? ? : Ptr64 ? ??long
    ? +0x058?SecurityProcedure ? ? ? ? : Ptr64 ? ??long
    ? +0x060?QueryNameProcedure ? ? ? ? : Ptr64 ? ??long?//查詢名稱 回調函數
    ? +0x068?OkayToCloseProcedure ? ? ? ? : 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。

    轉載于:https://www.cnblogs.com/davidwang456/articles/9212053.html

    《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的Hook原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。