0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

安全開發(fā)之應用層Hook技術

蛇矛實驗室 ? 來源:蛇矛實驗室 ? 作者:蛇矛實驗室 ? 2022-10-31 14:32 ? 次閱讀

本環(huán)境是蛇矛實驗室基于"火天網(wǎng)演攻防演訓靶場"進行搭建,通過火天網(wǎng)演中的環(huán)境構建模塊,可以靈活的對目標網(wǎng)絡進行設計和配置,并且可以快速進行場景搭建和復現(xiàn)驗證工作。

前言

Hook中文譯為“鉤子”或“掛鉤”,這很容易聯(lián)想到釣東西,好比釣魚,但將其比作“網(wǎng)”更合適,在安全開發(fā)的過程中,Hook技術主要用于對程序的運行流程進行控制和攔截,對特定的消息或動作進行過濾。

Hook原理

在真正執(zhí)行原始API之前,對程序流程進行攔截,使其先執(zhí)行自定義的代碼后,再執(zhí)行原始API調用流程。

e58f24f4-56c7-11ed-a3b6-dac502259ad0.png

Hook分類

Hook根據(jù)其作用的權限,可分為應用層(R3)鉤子和內核(R0)鉤子,本文主要講解應用層鉤子。

從代碼實現(xiàn)角度,可將R3 Hook分為以下幾類:

基于地址修改,比如IAT Hook。

基于代碼修改,比如Inline Hook。

基于異?;蛘{試,比如VEH Hook。

由于篇幅有限,本文只包含部分Hook技術。

IAT Hook

IAT(import address table,導入地址表)是指PE文件格式中的一個表結構,說到IAT就離不開導入表,在實際的開發(fā)過程中,難免會使用到Windows API,這些API的代碼保存在Windows提供的不同的DLL(動態(tài)鏈接庫)文件中,DLL將這些API導出,在可執(zhí)行程序中使用到其他DLL的代碼或數(shù)據(jù)時,編譯器會將這些導入的信息填充到可執(zhí)行程序的導入表中。當可執(zhí)行程序運行時,系統(tǒng)會將可執(zhí)行程序和其依賴的DLL加載到內存中,其中windows加載器會定位所有導入函數(shù)的地址并將定位到的地址填充到IAT中供其使用,Windows加載器定位這些函數(shù)的地址需要依賴PE文件中的導入表,其中導入表存放了所使用到的DLL文件和導入的函數(shù)名稱和序號信息。

實現(xiàn)原理

通過替換IAT表中函數(shù)的原始地址從而實現(xiàn)Hook。

實現(xiàn)步驟

以Hook User32!MessageBoxA為例:

1. 定義基于User32!MessageBoxA的函數(shù)原型的函數(shù)指針;

2. 獲取User32!MessageBoxA的函數(shù)地址并保存;

3. 創(chuàng)建HookedMessageBoxA函數(shù)(函數(shù)原型同User32!MessageBoxA一樣),以攔截程序對User32!MessageBoxA的調用:

先執(zhí)行自定義的代碼;
再執(zhí)行原始User32!MessageBoxA函數(shù)。

4. 解析導入表,并在IAT中定位User32!MessageBoxA的位置;

5. 使用HookedMessageBoxA函數(shù)的地址替換IAT中User32!MessageBoxA的地址。

實現(xiàn)代碼

#include
#include
#include
#include
#pragmacomment (lib, "dbghelp.lib")

// 1. 定義基于User32!MessageBoxA的函數(shù)原型的函數(shù)指針
usingMessageBoxT = int(WINAPI*)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

// 2. 獲取User32!MessageBoxA的函數(shù)地址并保存;
MessageBoxT OriginalMessageBox = MessageBoxA;

// 3. 創(chuàng)建HookedMessageBoxA函數(shù)(函數(shù)原型同User32!MessageBoxA一樣),以攔截程序對User32!MessageBoxA的調用:
intWINAPI HookedMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
// 3.1 先執(zhí)行自定義的代碼
MessageBoxW(0, L"HookedMessageBox() called", L"IAT Hook", 0);

// 3.2 再執(zhí)行原始User32!MessageBoxA函數(shù)
returnOriginalMessageBox(hWnd, lpText, lpCaption, uType);
}

/*
4. 解析導入表,并在IAT中定位User32!MessageBoxA的位置;
5. 使用HookedMessageBoxA函數(shù)的地址替換IAT中User32!MessageBoxA的地址。
*/
boolSetHook(std::stringdllName, std::stringorigFunc, PROC hookingFunc)
{
ULONG size;
DWORD i;

LPCSTR importDllName = NULL;
HMODULE importDllImageBase = NULL;

// 獲取主模塊句柄
HMODULE imageBase = GetModuleHandle(NULL);

// 定位主模塊的導入表
PIMAGE_IMPORT_DESCRIPTOR importDescTab = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx(imageBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size, NULL);

// 尋找目標DLL
boolfound = false;
for(i = 0; i < size; i++)
??{
????importDllName = (LPCSTR)importDescTab[i].Name + (DWORD_PTR)imageBase; // 獲取導入DLL名稱
????if?(_stricmp(dllName.c_str(), importDllName) == 0)
????{
??????found = true;
??????break;
????}
??}

??// 沒找到目標DLL,返回
??if?(!found)
????return?false;

??// 找到目標dll
??importDllImageBase = GetModuleHandleA(importDllName);
??if?(!importDllImageBase) return?false;

??PIMAGE_THUNK_DATA originalFirstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)imageBase + importDescTab[i].OriginalFirstThunk); // 定位導入名稱表INT
??PIMAGE_THUNK_DATA firstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)imageBase + importDescTab[i].FirstThunk); // 定位導入地址表IAT

??// 尋找Hook的API
??while?(originalFirstThunk->u1.AddressOfData)
{
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)imageBase + originalFirstThunk->u1.AddressOfData);
if(_stricmp(origFunc.c_str(), functionName->Name) == 0)
{
// 確保內存可寫
DWORD oldProtect = 0;
VirtualProtect((LPVOID)(&firstThunk->u1.Function), 4096, PAGE_READWRITE, &oldProtect);

// 替換IAT中的函數(shù)地址
firstThunk->u1.Function = (ULONG_PTR)hookingFunc;

// 恢復內存屬性
VirtualProtect((LPVOID)(&firstThunk->u1.Function), 4096, oldProtect, &oldProtect);
}
++originalFirstThunk;
++firstThunk;
}

returntrue;
}

intmain()
{
// Hook前
MessageBoxA(0, "Before Hooking", "IAT HOOKS", 0);

// 進行IAT Hook
SetHook("user32.dll", "MessageBoxA", (PROC)HookedMessageBox);

// Hook后
MessageBoxA(0, "After Hooking", "IAT Hook", 0);

return0;
}

上述代碼第一次調用MessageBoxA時,正常彈出,為了之后在調用MessageBoxA時,先執(zhí)行自定義的函數(shù)代碼(HookedMessageBox),首先在進程的IAT中定位到MessageBoxA的地址,這個過程是先通過進程的導入表找到MessageBoxA所在的DLL模塊(user32.dll),找到之后,通過INT(導入名稱表)得到MessageBoxA函數(shù)地址在IAT中的下標,此時使用自定義函數(shù)的地址替換掉IAT中MessageBoxA函數(shù)的地址,即可達到IAT Hook的效果,Hook之后在調用MessageBoxA時,程序會先執(zhí)行HookedMessageBox函數(shù),在執(zhí)行原始的MessageBoxA函數(shù)(需要提前獲取MessageBoxA的地址)。

效果

e5a47dc2-56c7-11ed-a3b6-dac502259ad0.gif

Inline Hook

Inline Hook實際上是一種通過修改機器碼的方式來實現(xiàn)Hook的技術。

實現(xiàn)原理

通過直接修改API函數(shù)在內存中對應的二進制代碼,通過跳轉指令將其代碼的執(zhí)行執(zhí)行流程改變從而執(zhí)行用戶編寫的代碼進而進行Inline Hook。

實現(xiàn)步驟

以Hook User32!MessageBoxA為例:

1. 在指定進程中內存中找到MessageBoxA函數(shù)地址,并保存函數(shù)頭部若干字節(jié)(用于后續(xù)unpatch);

2. 創(chuàng)建HookedMessageBoxA函數(shù)(函數(shù)原型同User32!MessageBoxA一樣),以攔截程序對User32!MessageBoxA的調用:

先執(zhí)行自定義的代碼;
恢復先前保存的MessageBoxA原始字節(jié);
執(zhí)行原函數(shù)
再次對MessageBoxA進行Hook;

3. 構造跳轉指令,用于后續(xù)替換MessageBoxA函數(shù)代碼頭部字節(jié);

4. 修改MessageBoxA函數(shù)首地址代碼為跳轉指令。

實現(xiàn)代碼

#include
#include

#ifdefined(_WIN64)
#defineORIG_BYTES_SIZE 14
#else
#defineORIG_BYTES_SIZE 7
#endif

BYTE OriginalBytes[ORIG_BYTES_SIZE]{}; // 用于保存MessageBoxA的部分原始代碼字節(jié)
BYTE PatchBytes[ORIG_BYTES_SIZE]{}; // 構造的跳轉指令

usingMessageBoxAT = int(WINAPI*)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
MessageBoxAT OriginalMessageBox = nullptr;

intWINAPI HookedMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
// 執(zhí)行自定義的代碼
SIZE_T bytesOut = 0;

MessageBoxW(0, L"HookedMessageBox() called", L"Inline Hook", 0);

// unpatch MessageBoxA
WriteProcessMemory(GetCurrentProcess(), (LPVOID)OriginalMessageBox, OriginalBytes, sizeof(OriginalBytes), &bytesOut);

// 調用原來的MessageBoxA
intresult = MessageBoxA(NULL, lpText, lpCaption, uType);

// 再次patch MessageBoxA
WriteProcessMemory(GetCurrentProcess(), OriginalMessageBox, PatchBytes, sizeof(PatchBytes), &bytesOut);

returnresult;
}

boolSetHook(std::stringdllName, std::stringorigFunc, FARPROC hookingFunc)
{
SIZE_T bytesIn = 0;
SIZE_T bytesOut = 0;

// 保存MessageBoxA原始地址
OriginalMessageBox = (MessageBoxAT)GetProcAddress(GetModuleHandleA(dllName.c_str()), origFunc.c_str());

// 保存MessageBoxA的部分原始代碼字節(jié)
ReadProcessMemory(GetCurrentProcess(), OriginalMessageBox, OriginalBytes, ORIG_BYTES_SIZE, &bytesIn);

memset(PatchBytes, 0, sizeof(PatchBytes));
#ifdefined(_WIN64)
/*
JMP [RIP+0];
xFFx25x00x00x00x00
x00x11x22x33x44x55x66x77
*/
memcpy(PatchBytes, "xFFx25", 2);
memcpy(PatchBytes + 6, &hookingFunc, 8);
#else
/*
mov eax, &hookingFunc
jmp eax
*/
memcpy(PatchBytes, "xB8", 1);
memcpy(PatchBytes + 1, &hookingFunc, sizeof(ULONG_PTR));
memcpy(PatchBytes + 5, "xFFxE0", 2);
#endif

// patch the MessageBoxA
WriteProcessMemory(GetCurrentProcess(), OriginalMessageBox, PatchBytes, sizeof(PatchBytes), &bytesOut);

returntrue;
}

intmain()
{
// Hook前
MessageBoxA(0, "Before Hooking", "Inline Hook", 0);

// 進行Inline Hook
SetHook("user32.dll", "MessageBoxA", (FARPROC)HookedMessageBox);

// Hook后
MessageBoxA(0, "After Hooking", "Inline Hook", 0);

return0;
}

程序中調用了2次MessageBoxA,第一次調用時未被掛鉤,之后Inline Hook方式使用對MessageBoxA進行掛鉤,當之后再次調用MessageBoxA時,程序會首先進入自寫函數(shù)(HookedMessageBox)中,在該函數(shù)中,自定義的代碼部分使用MessageBoxW彈出內容,隨后修復MessageBoxA被修改的字節(jié)代碼后,開始執(zhí)行原始MessageBoxA代碼。

效果

e5d414ba-56c7-11ed-a3b6-dac502259ad0.gif

VEH Hook

VEH Hook是一種基于異常處理的Hook手段,通過主動觸發(fā)異常從在獲取程序控制權來達到Hook的手段。其中VEH(Vectored Exception Handler,向量化異常處理)是Windows中處理異常的一種方式。

實現(xiàn)原理

由于VEH的異常處理發(fā)生在之前,所以通過`主動拋出異常,使程序觸發(fā)異常,進而使控制權交給異常處理例程的這一系列操作來實現(xiàn)Hook。

實現(xiàn)步驟

以Hook User32!MessageBoxA為例:

1. 獲取MessageBoxA地址,并保存;

2. 安裝VEH異常處理程序,編寫VEHHandler(VEH的異常處理函數(shù));

3. 設置鉤子:人為在Hook點構造異常(比如修改目標函數(shù)第一個字節(jié)位0xCC等),并保存觸發(fā)異常的地址等信息;

4. 在VEHHandler函數(shù)內部修改目標函數(shù)原始流程,并在執(zhí)行完畢后主動修復異常。

實現(xiàn)代碼

#include
#include

// 獲取目標函數(shù)的地址
ULONG_PTR OriginalMessageBox = NULL;

structEXCEPTION_HOOK
{
ULONG_PTR address; // 用來記錄異常產生的地址,后面將用來確保是我們人為構造的異常
BYTE originalBytes; // 用來記錄原始目標函數(shù)的第一個字節(jié)
};
EXCEPTION_HOOK HookInfo;

// 異常處理函數(shù)
// 用來修改目標函數(shù)原始流程,并在執(zhí)行完我們功能后修復異常
LONG NTAPI VEHHandler(EXCEPTION_POINTERS* ExceptionInfo)
{
if(ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT && // 異常類型為斷點異常
(ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress == HookInfo.address) // 發(fā)生異常的地址為我們主動構造的異常地址
{
// 在這里編寫自定義的代碼,或者修改Hook API的相關參數(shù)
MessageBoxW(0, L"VEHHandler() called", L"VEH Hook", 0);

// 解除鉤子
DWORD oldProtect = 0;
VirtualProtect((LPVOID)HookInfo.address, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*(BYTE*)HookInfo.address = HookInfo.originalBytes;
VirtualProtect((LPVOID)HookInfo.address, 1, oldProtect, &oldProtect);

returnEXCEPTION_CONTINUE_EXECUTION; // 回到異常發(fā)生的地方,由于已經(jīng)修復了異常問題,所以之后能夠正確執(zhí)行
}
returnEXCEPTION_CONTINUE_SEARCH; // 向上繼續(xù)尋找異常處理程序
}

voidSetHook(ULONG_PTR address)
{
AddVectoredExceptionHandler(1, VEHHandler); // 添加VEH的異常處理函數(shù)

HookInfo.address = address; // 保存目標函數(shù)發(fā)生異常的地址
HookInfo.originalBytes = *(BYTE*)address; // 保存目標函數(shù)原始的第一個字節(jié)

DWORD oldProtect = 0;
VirtualProtect((LPVOID)address, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*(UCHAR*)address = 0xCC; // 人為構造異常,將目標函數(shù)代碼處的第一個字節(jié)改為0xCC
VirtualProtect((LPVOID)address, 1, oldProtect, &oldProtect);
}

intmain()
{
// 保存MessageBoxA原始地址
OriginalMessageBox = (ULONG_PTR)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");

MessageBoxA(0, "Before Hooking", "VEH Hook", 0);

SetHook(OriginalMessageBox);// 安裝鉤子用以觸發(fā)異常
MessageBoxA(0, "After Hooking", "VEH Hook", 0);

return0;
}

在上述代碼中,先保存了MessageBoxA函數(shù)在當前進程中的地址,之后第一次調用MessageBoxA,此時該函數(shù)還沒有被Hook,隨后為了人為構造異常,將MessageBoxA函數(shù)代碼的第一個字節(jié)修改為0xCC,當之后再次調用MessageBoxA后,程序將會觸發(fā)0xCC異常,由于程序中添加了VEH異常處理,那么程序將跳轉到VEHHandler(自寫的異常處理函數(shù)中),在該函數(shù)代碼中,首先過濾得到主動觸發(fā)的異常,滿足的條件下,開始執(zhí)行自定義的代碼,這里為了說明,使用MessageBoxW彈出對話框,由于異常被程序接管,所以在異常處理函數(shù)中,執(zhí)行完自定義的代碼后,需要修復異常,進而返回原始觸發(fā)異常的位置繼續(xù)執(zhí)行。

效果

e5f6a836-56c7-11ed-a3b6-dac502259ad0.gif

PS:通常來說,我們將Hook的功能代碼編寫進一個DLL文件中,在將該DLL文件通過進程注入的方式注入到需要改變程序流程的進程中來達到目的。

丈八網(wǎng)安蛇矛實驗室成立于2020年,致力于安全研究、攻防解決方案、靶場對標場景仿真復現(xiàn)及技戰(zhàn)法設計與輸出等相關方向。團隊核心成員均由從事安全行業(yè)10余年經(jīng)驗的安全專家組成,團隊目前成員涉及紅藍對抗、滲透測試、逆向破解、病毒分析、工控安全以及免殺等相關領域。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • API
    API
    +關注

    關注

    2

    文章

    1473

    瀏覽量

    61752
  • 應用層
    +關注

    關注

    0

    文章

    46

    瀏覽量

    11487
  • HOOK
    +關注

    關注

    0

    文章

    15

    瀏覽量

    8364

原文標題:安全開發(fā)之應用層Hook技術

文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Android安全開發(fā)WebView中的地雷

    `Android安全開發(fā)WebView中的地雷0X01 About WebView在Android開發(fā)中,經(jīng)常會使用WebView來實現(xiàn)WEB頁面的展示,在Activiry中啟動自己的瀏覽器,或者
    發(fā)表于 09-09 19:35

    應用層通信安全

    `歡迎工程師的我們!電子發(fā)燒友E2E技術沙龍-北京站,本期討論主題是關于“應用層通信安全”E2E活動北京站活動鏈接~~http://url.elecfans.com/u/e524cb1b33
    發(fā)表于 03-18 17:55

    學習嵌入式Linux應用層開發(fā)

    1 應用層與驅動要想學習嵌入式Linux應用層開發(fā),首先要區(qū)分好應用層和驅動之間的關系。我
    發(fā)表于 11-02 10:20

    嵌入式應用層開發(fā)通常有哪些問題

    嵌入式應用層開發(fā)通常有哪些問題?
    發(fā)表于 12-24 06:54

    【學習打卡】OpenHarmony的應用層說明

    以增加額外的網(wǎng)絡安全;3.確保存在必要的通信接口,例如發(fā)送方計算機中是否有以太網(wǎng)或Wi-Fi接口;4.確保雙方就錯誤恢復程序、數(shù)據(jù)完整性和隱私達成一致;5.在應用層確定協(xié)議和數(shù)據(jù)語法規(guī)則;6.將接收
    發(fā)表于 07-14 08:44

    講講Hook技術的攻防對抗思路

    1、論Hook技術的攻防對抗  首先,簡單認識下Hook 技術。  Hook技術是一門廣泛用于計
    發(fā)表于 09-28 11:12

    嵌入式開發(fā)系列課程五:Windows CE安全開發(fā)

    嵌入式開發(fā)系列課程五:Windows CE安全開發(fā)與配置
    發(fā)表于 03-25 08:58 ?22次下載

    應用層和后臺分析便攜式電子產品的節(jié)能技術

    應用層和后臺分析便攜式電子產品的節(jié)能技術  便攜式電子產品的節(jié)能技術基本上可以按照其執(zhí)行方式分為應用層技術及后臺
    發(fā)表于 11-30 10:03 ?608次閱讀
    從<b class='flag-5'>應用層</b>和后臺分析便攜式電子產品的節(jié)能<b class='flag-5'>技術</b>

    SIP應用層網(wǎng)關技術

    本文提出了“SIP應用層網(wǎng)關”技術,并將其應用于網(wǎng)絡通信中來建立相對合理、完善的SIP網(wǎng)絡,以解決SIP私網(wǎng)遠程控制中穿越NAT/FireWall的難題
    發(fā)表于 04-20 11:37 ?5729次閱讀

    Zigbee應用層規(guī)范

    本內容介紹了Zigbee應用層規(guī)范
    發(fā)表于 05-24 11:37 ?85次下載
    Zigbee<b class='flag-5'>應用層</b>規(guī)范

    電子功能安全開發(fā)及汽車EPS電機控制設計

    實現(xiàn)認證并開始你的功能安全開發(fā)
    的頭像 發(fā)表于 08-14 00:15 ?4879次閱讀

    認知無線電MAC應用層仿真軟件

    認知無線電MAC應用層仿真軟件(澳萊特電源技術有限公司)-該文檔為認知無線電MAC應用層仿真軟件總結文檔,是一份很不錯的參考資料,具
    發(fā)表于 09-15 11:40 ?11次下載
    認知無線電MAC<b class='flag-5'>層</b>與<b class='flag-5'>應用層</b>仿真軟件

    嵌入式Linux應用層開發(fā)教程(一)基本概念

    1 應用層與驅動要想學習嵌入式Linux應用層開發(fā),首先要區(qū)分好應用層和驅動之間的關系。我
    發(fā)表于 11-01 17:59 ?14次下載
    嵌入式Linux<b class='flag-5'>應用層</b><b class='flag-5'>開發(fā)</b>教程(一)基本概念

    什么是SEooC?SEooC和正常功能安全開發(fā)有什么不同?

    在功能安全開發(fā)過程中,很多時候我們會遇到獨立于環(huán)境的安全要素開發(fā)(Safety Element out of Context, SEooC)
    的頭像 發(fā)表于 04-27 16:52 ?8796次閱讀
    什么是SEooC?SEooC和正常功能<b class='flag-5'>安全開發(fā)</b>有什么不同?

    物聯(lián)網(wǎng)的技術架構及應用層是什么?

    物聯(lián)網(wǎng)的技術架構包括感知、網(wǎng)絡、平臺應用層。應用層是物聯(lián)網(wǎng)的頂層,它的主要功能是將感知
    的頭像 發(fā)表于 07-15 08:56 ?3542次閱讀