內容
(1)API函數分析:選取特定的API函數,通過逆向工程技術分析其二進制代碼結構和執行流程,了解其參數傳遞方式和返回值處理方法。
(2)HOOK技術實現:介紹HOOK技術的基本原理和實現方法。
(3)OD的動態分析、IDA的靜態分析工具的使用。
(4)基于攻防世界逆向題easy hook的靜態分析。
簡單介紹一下hook
HOOK是一種截取信息、更改程序執行流向、添加新功能的強大技術。通過在執行真正的目標函數之前執行實現插入的代碼,獲取程序執行過程的決定權。
二次開發或補丁信息截獲安全防護 HOOK使用最多的戰場,各類安全軟件都會在應用層和內核中安裝hook、系統回調、過濾驅動等。從而對系統中所有的進/線程、窗口、文件、網絡操作等進行檢查、過濾和攔截,在x86系統中,流行在KiFastCallEntry中進行HOOK,以達到間接hook所有系統調用的目的,同時在應用層中通常也會有一些安全模塊,通過安裝一些應用層HOOK來配合操作。
API HOOK
API HOOK是一種HOOK技術,它的原理是通過修改目標程序中的API調用來實現對程序的攔截和修改。在Windows操作系統中,每個API都有一個唯一的標識符,稱為函數地址或入口地址。通過修改目標程序中API調用的入口地址,可以將程序的流程控制轉移到HOOK函數中,從而實現對程序的攔截和修改。
具體來說,API HOOK的實現分為以下幾個步驟:
(1)獲取目標程序中API的入口地址;
(2)保存原始的API入口地址,以便在HOOK函數中調用;
(3)修改目標程序中API的入口地址,將其指向HOOK函數;
(4)在HOOK函數中實現自定義的功能或修改;
如果需要,可以在HOOK函數中調用原始的API函數。
通過API HOOK技術,可以實現對目標程序中的API進行攔截和修改,比如監控系統調用、實現自定義的功能、加強安全性等。但是,使用API HOOK技術也有一定的風險和副作用,如果使用不當可能會導致系統不穩定或者安全漏洞。因此,在使用API HOOK技術時需要注意安全性和穩定性問題。
HOOK 流程

HOOK 本函數MessageBoxA的源代碼(能運行)
#include
#include
typedef int (WINAPI* lpMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
BYTE olddata32[5] = { 0 };
void hook();
void Unhook();
//回調函數 當調用hook時,hook執行完成后調用該函數后再進入main函數
int WINAPI MyMessageboxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
Unhook();
lpMessageBoxA messagebox = (lpMessageBoxA)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
int ret = messagebox(0, "inlinehook", "tip3", 0);
hook();//函數釋放前再次HOOK,為了攔截下次調用
return ret;
}
void hook()
{
//獲取messagebox的基地址
DWORD messagebox = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
BYTE data[5] = { 0xe9, };
DWORD offset = (DWORD)MyMessageboxA - messagebox - 5; //計算jmp跳轉的偏移量
//保存前五個字節的數據
memcpy(olddata32, (const void *)messagebox, 5);
//把偏移量與JMP指令拼接
memcpy(&data[1], &offset, 4);
DWORD oldProtect = 0;
//更改頁面屬性 將內存改為可讀可寫可執行
VirtualProtect((LPVOID)messagebox, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((void*)messagebox, data, 5);
//還原屬性
VirtualProtect((LPVOID)messagebox, 5, oldProtect, &oldProtect);
}
void Unhook()
{
DWORD messagebox = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
DWORD oldProtect = 0;
VirtualProtect((LPVOID)messagebox, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((void*)messagebox, olddata32, sizeof(olddata32));
VirtualProtect((LPVOID)messagebox, 5, oldProtect, &oldProtect);
}
void main()
{
MessageBoxA(0, "hello", "tip1", 0);
hook();
MessageBoxA(0, "hello", "tip2", 0);
}
結果分析
Hook前

Hook后,我們的彈窗本該是hello的但是hook后,程序流程被我們修改了。

過程分析
(1)首先我們先給MessageBoxA API下軟件斷點
這里我提供三種方法:
方法一: Ctrl + F 快捷鍵輸入MessageBoxA,跳到一個地址,下軟件斷點。

方法二:在Command后面紅色標注的命令框里輸入:BP MessageBoxA。

方法三:利用吾愛破解里面的OD插件給MessageBoxA下斷點。

(2)下好斷點后程序運行后跳到了760D34B0地址

我們可以看到760D34B0就是我們BP MessageBoxA下斷點后的地址,我們可以記住這個地址,因為我們HOOK主要修改的就是該地址,下面我們從匯編代碼分析該函數流程。
760D34B0 > 8BFF mov edi,edi
它只是一個占位符,方便在調試和優化代碼時進行修改。
760D34B2 55 push ebp
760D34B3 8BEC mov ebp,esp
通過這兩條指令,函數就可以在堆棧中為局部變量分配存儲空間,并在函數執行過程中保存和恢復現場。這樣做的好處是可以避免局部變量和其他函數之間的沖突,同時也可以提高函數的可讀性和可維護性。
760D34B5 6A FF push -0x1
760D34B7 6A 00 push 0x0
通常被用于函數的參數傳遞,將參數壓入堆棧中,以便被調用函數在堆棧中獲取。
760D34B9 FF75 14 push dword ptr ss:[ebp+0x14]
760D34BC FF75 10 push dword ptr ss:[ebp+0x10] ;
760D34BF FF75 0C push dword ptr ss:[ebp+0xC] ;
760D34C2 FF75 08 push dword ptr ss:[ebp+0x8]
這四個push是將MessageBoxA的四個參數壓入堆棧中去。
760D34C5 E8 D6010000 call user32.MessageBoxTimeoutA
該函數是顯示MessageBoxA的彈窗函數。
(3)執行函數后我們繼續運行,執行該函數后OD顯示如圖所示:

00C4295C 6A 00 push 0x0
00C4295E 68 187CC400 push hookmess.00C47C18 ; ASCII "tip2"
00C42963 68 107CC400 push hookmess.00C47C10 ; ASCII "hello"
00C42968 6A 00 push 0x0
這四個push是hook前的函數流程,將該四個push壓入堆棧后,在運行到光標的call中跳入到前面的MessageBoxA函數流程。
(4)hook后我們F7進入該函數

我們可以看到760D34B0-760D34B7地址的匯編指令被我們修改了
Hook前的執行指令:
760D34B0 > 8BFF mov edi,edi
760D34B2 55 push ebp
760D34B3 8BEC mov ebp,esp
Hook后執行過程的指令被我們修改成了jmp 00C4110E
00C4110E地址就是我們修改MessageBoxA函數的地址
(5)HOOK函數的逆向分析如下:
這個跳轉是VC編譯器的特性可以不用管。

00C41730是HOOK函數的起始地址。

這下面的四個push就是將我們修改MessageBoxA也就是MyMessageBoxA的參數壓入堆棧,然后通過下面的call調用。

最后執行以前的函數流程,將修改后的MessageBoxA函數的彈窗顯示出來。

攻防世界逆向題Easy Hook分析過程
查殼
如下圖所示,我們可以知道該程序是32位的PE文件。

IDA靜態分析
步驟一,拉進IDA中按下f5得到main函數的反匯編代碼。

步驟二:分析偽代碼,如下圖是我整理的偽代碼注釋,我們分析完畢后可以知道flag的長度為19,sub_401220()函數就是hook函數,hook的是WriteFile函數。加密過程也在該函數里面。

步驟三:我們運行程序輸入1234567890123456789,可以在程序運行目錄下找到一個文件,我們用文本的形式打開該文件可以得到下面的結果,并不是我們的輸入。

步驟四:進入sub_401220()函數分析,這個函數邏輯清晰地找到了庫函數 WriteFile 的地址,向 byte_40C9BC 中載入了 JUMP 指令的字節碼 E9,計算了 sub_401080 距離 WriteFile 的第五個字節的距離。我們分析先sub_4010D0()函數。

步驟五:進入sub_4010D0()函數分析,我們分析該函數就可以確定是hook了,我們回到 sub_401220()函數,可以知道sub_401080就是整個程序的關鍵了。

步驟六:進入sub_401080()函數分析,該函數就是我們HOOK的返回值,通過sub_401080()函數重新定義了一個WriteFile函數,修改了參數里面的值。我們先分析sub_401140()函數有什么用。

步驟七:進入sub_401140()函數分析,在 sub_401000() 函數加密完 lpBuffer 后,sub_401140又把 WriteFile 函數還原,繼續寫操作。

步驟八:進入sub_401000()函數分析,該函數就是我們加密的函數,flag由此解密,簡單的異或,第一個判斷i=18時,flag[i] = a1[i] ^ 0x13; i 不等于18時,第二個判斷,if (i % 2),滿足條件flag[i] = (a1[i] ^ i) + i,不滿足條件flag[i+2] = (res[i] ^ i)。至此IDA的分析就完成了,接下來就是寫解密腳本了。

最后寫解密腳本,得到flag。

#include
unsigned char res[] = {
0x61, 0x6A, 0x79, 0x67, 0x6B, 0x46, 0x6D, 0x2E,
0x7F, 0x5F, 0x7E, 0x2D, 0x53, 0x56, 0x7B, 0x38,
0x6D, 0x4C, 0x6E, 0x00};
char flag[19];
int main()
{for (int i = 18; i >= 0; i--){
if (i == 18)
flag[i] = res[i] ^ 0x13;
else{
if (i % 2){
flag[i] = (res[i] ^ i) + i;
}else{
flag[i+2] = (res[i] ^ i);
} } }
for (int i = 1; i < 19; i++)
printf("%c", flag[i]);}
安全牛
看雪學苑
雷石安全實驗室
關鍵基礎設施安全應急響應中心
中國信息安全
聚銘網絡
安全內參
安全圈
安全圈
一顆小胡椒
聚銘網絡
安全內參