Inline Hook


@author: dlive

IAT Hook時如果要鈎取的API不在IAT中(LoadLibrary后調用),則無法使用該技術。而Inline Hook不存在這個限制。

0x01 Inline Hook原理

原理比較簡單,將API代碼的前5個字節修改為JMP xxxxxx 指令來鈎取API。調用執行被鈎取的API時,JMP XXXXXX指令被執行,轉而跳轉至Hook函數。

0x02 相關API

用戶模式下檢測進程的相關API通常分為如下兩類:

  1. CreateToolhelp32Snapshot() 和 EnumProcess()

    這兩個API均在其內部調用了ntdll.ZwQuerySystemInformation

  2. ZwQuerySystemInformation()

    該API可獲得運行中的所有進程信息(結構體),形成一個鏈表,操作該鏈表從鏈表中刪除相關進程即可達到隱藏的目的。

0x03 隱藏進程時可能遇到的問題

  1. 要鈎取進程的個數。要想把某個進程隱藏起來,需要鈎取系統中運行的所有進程
  2. 需要對新啟動的進程也做同樣的鈎取操作

以上就是全局鈎取的概念

注意:鑒於系統安全性考慮,系統進程禁止進行注入操作

0x04 代碼分析

1. HideProc.cpp

InjectAllProcess

向所有進程注入/卸載DLL

BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
	DWORD                   dwPID = 0;
	HANDLE                  hSnapShot = INVALID_HANDLE_VALUE;
	PROCESSENTRY32          pe;

	// Get the snapshot of the system
	pe.dwSize = sizeof( PROCESSENTRY32 );
	hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

	// find process
	Process32First(hSnapShot, &pe);
	do
	{
		dwPID = pe.th32ProcessID;

        //鑒於系統安全性考慮,對於PID小於100的系統進程,不執行DLL諸如操作
		if( dwPID < 100 )
			continue;

        if( nMode == INJECTION_MODE )
		    InjectDll(dwPID, szDllPath);
        else
            EjectDll(dwPID, szDllPath);
	}
	while( Process32Next(hSnapShot, &pe) );

	CloseHandle(hSnapShot);

	return TRUE;
}

2. stealth.cpp

實際API的鈎取由stealth.dll負責

2.1 SetProcName

// global variable (in sharing memory)
#pragma comment(linker, "/SECTION:.SHARE,RWS")
#pragma data_seg(".SHARE")
    TCHAR g_szProcName[MAX_PATH] = {0,};
#pragma data_seg()

#pragma data_seg(".SHARE")一般用於DLL中,在DLL中定義一個共享的,有名字的節區。最關鍵的是:這個節區中的全局變量可以被多個進程共享。 這里將建立的節區命名為.SHARE。您可以將這段命名為任何一個您喜歡的名字。在這里的#pragma敘述之后的所有初始化了的變量都放在.SHARE節區中。

#pragma data_seg()敘述標示段的結束。對變量進行專門的初始化是很重要的,否則編譯器將把它們放在普通的未初始化數據段(.bss)中而不是放在shared中。

連結器必須知道有一個「shared」共享節區: #pragma comment(linker,"/SECTION:shared,RWS") 字母RWS表示節區具有讀、寫和共享屬性。

// ------------------------------   我是一個題外話  --------------------------------------
//說個題外話,通過共享節區變量可以做到防多開(騰訊游戲安全競賽的時候曾經用過這個方法)
//DLL加載時count++, 卸載時count--
//demo代碼如下,只是demo
#pragma data_seg("flag_data") 
int count=0; 
#pragma data_seg() 
#pragma comment(linker,"/SECTION:flag_data,RWS") 

if(count>1) 
{ 
MessageBox("已經啟動了一個應用程序","Warning",MB_OK); 
return FLASE; 
} 
count++;
// ------------------------------   我是一個題外話  --------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void SetProcName(LPCTSTR szProcName)
{
   //將steach.dll字符串存放在內存共享節區
    _tcscpy_s(g_szProcName, szProcName);
}
#ifdef __cplusplus
}
#endif

2.2 DllMain

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    char            szCurProc[MAX_PATH] = {0,};
    char            *p = NULL;

    // #1. 判斷進程名稱,不向HideProc.exe注入dll
    GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
    p = strrchr(szCurProc, '\\');
    if( (p != NULL) && !_stricmp(p+1, "HideProc.exe") )
        return TRUE;

    switch( fdwReason )
    {
        // #2. API Hooking
        case DLL_PROCESS_ATTACH : 
        hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, 
                     (PROC)NewZwQuerySystemInformation, g_pOrgBytes);
        break;

        // #3. API Unhooking 
        case DLL_PROCESS_DETACH :
        unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, 
                       g_pOrgBytes);
        break;
    }

    return TRUE;
}

2.3 hook_by_code

BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
    FARPROC pfnOrg;
    DWORD dwOldProtect, dwAddress;
    BYTE pBuf[5] = {0xE9, 0, };
    PBYTE pByte;

    // 獲取要鈎取的API地址
    pfnOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pfnOrg;

    // 若已經被鈎取,則返回False
    if( pByte[0] == 0xE9 )
        return FALSE;

    // 修改內存屬性
    VirtualProtect((LPVOID)pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // 備份原有的5字節
    memcpy(pOrgBytes, pfnOrg, 5);

    // JMP  (E9 XXXX)
    // => XXXX = pfnNew - pfnOrg - 5
    dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5;
    memcpy(&pBuf[1], &dwAddress, 4);

    // Hook - 5 byte (JMP XXXX)
    memcpy(pfnOrg, pBuf, 5);

    // 恢復內存屬性
    VirtualProtect((LPVOID)pfnOrg, 5, dwOldProtect, &dwOldProtect);
    
    return TRUE;
}

JMP目的地址的計算,xxxxxxxx為相對地址

長跳轉
E9 xxxxxxxx
xxxxxxxx = 要跳轉的絕對地址 - 當前指令絕對地址 - 當前指令長度(5)

還有另外的一些跳轉指令

短跳轉 short jmp
EB xx (指令長度為兩字節, xx為相對地址)

跳轉到絕對地址
push xxxxxxxx; ret
68 xxxxxxxx c3

跳轉到絕對地址
move eax, xxxxxxxx
jmp eax
B8 xxxxxxxx 
FFE0

2.4 unhook_by_code

BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
    FARPROC pFunc;
    DWORD dwOldProtect;
    PBYTE pByte;

    // 獲取API地址
    pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pFunc;

    // 判斷API 代碼是否被修改過
    if( pByte[0] != 0xE9 )
        return FALSE;

    // 將API開始5Byte內存屬性修改為RWX
    VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // Unhook,將原始的5Byte數據修改回來
    memcpy(pFunc, pOrgBytes, 5);

    // 恢復內存屬性
    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}

2.5 NewZwQuerySystemInformation

Zw開頭的函數和Nt開頭的函數的區別:

在RING3下,應用程序調用NT或者ZW效果是一樣的, 在dll中執行的是同一段代碼。
在RING0下,調用NT函數跟ZW函數就不一樣了,ZW開頭的函數是通過eax中系統服務號去SSDT中查找相應的系統服務,然后調用之。
若在驅動中直接調用NT開頭的函數是不會經過SSDT的 也不會被SSDT HOOK攔截的。
即:在R0下通過調用NT系列函數可以繞過SSDT HOOK 。微軟推薦使用Zw開頭的函數

ZwQuerySystemInformation微軟官方手冊(對各個字段解釋不完整)

https://msdn.microsoft.com/en-us/library/windows/desktop/ms725506(v=vs.85).aspx

#define STATUS_SUCCESS						(0x00000000L) 

typedef LONG NTSTATUS;

//將SYSTEM_INFORMATION_CLASS設置為SystemProcessInformation(5)后調用ZwQuerySystemInformation API
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemProcessInformation = 5,
    SystemProcessorPerformanceInformation = 8,
    SystemInterruptInformation = 23,
    SystemExceptionInformation = 33,
    SystemRegistryQuotaInformation = 37,
    SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

//API獲得的信息為SYSTEM_PROCESS_INFORMATION結構體組成的單向鏈表的起始地址,該結構體重存儲着運行中所有進程的信息
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;		//鏈表中下一個結構體相對於當前結構體的偏移量
    ULONG NumberOfThreads;		//線程數目;
    BYTE Reserved1[48];
    PVOID Reserved2[3];   		//Reserved2[1]為進程名稱,進程名稱為Unicode字符串
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;  

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)
                 (SYSTEM_INFORMATION_CLASS SystemInformationClass, 
                  PVOID SystemInformation, //參數單向鏈表的起始地址
                  ULONG SystemInformationLength, 
                  PULONG ReturnLength);

#define DEF_NTDLL                       ("ntdll.dll")
#define DEF_ZWQUERYSYSTEMINFORMATION    ("ZwQuerySystemInformation")
//NTSTATUS 是被定義為32位的無符號長整型。在驅動程序開發中,人們習慣用 NTSTATUS 返回狀態。
NTSTATUS WINAPI NewZwQuerySystemInformation(
                SYSTEM_INFORMATION_CLASS SystemInformationClass, 
                PVOID SystemInformation, 
                ULONG SystemInformationLength, 
                PULONG ReturnLength)
{
    NTSTATUS status;
    FARPROC pFunc;
    PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
    char szProcName[MAX_PATH] = {0,};
    
    // 首先脫鈎
    unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes);

    // 調用原始API
    pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL), 
                           DEF_ZWQUERYSYSTEMINFORMATION);
    status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
              (SystemInformationClass, SystemInformation, 
              SystemInformationLength, ReturnLength);

    if( status != STATUS_SUCCESS )
        goto __NTQUERYSYSTEMINFORMATION_END;

    // 判斷調用API時是否為要鈎取的調用方式(SystemProcessInformation(5))
    if( SystemInformationClass == SystemProcessInformation )
    {
        // pCur 存儲單鏈表的首地址
        pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

        while(TRUE)
        {
            // 比較進程名稱
            // g_szProcName為要隱藏的進程
            if(pCur->Reserved2[1] != NULL)
            {
                if(!_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName))
                {
                    // 刪除鏈表節點
                    if(pCur->NextEntryOffset == 0)
                        pPrev->NextEntryOffset = 0;
                    else
                        pPrev->NextEntryOffset += pCur->NextEntryOffset;
                }
                else		
                    pPrev = pCur;
            }
			
          	//遍歷結束
            if(pCur->NextEntryOffset == 0)
                break;
          
			// 相當於p=p->next
            pCur = (PSYSTEM_PROCESS_INFORMATION)
                    ((ULONG)pCur + pCur->NextEntryOffset);
        }
    }

__NTQUERYSYSTEMINFORMATION_END:

    // 重新Hook API
    hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, 
                 (PROC)NewZwQuerySystemInformation, g_pOrgBytes);

    return status;
}

0x05 全局API鈎取

全局API鈎取技術針對的進程:

  1. 當前運行的所有進程
  2. 將來要運行的所有進程

前面的示例程序並不是全局API鈎取的例子,因為它並不滿足全局API鈎取定義中的第二個條件

1.相關API

//Kernel32.CreateProcess
BOOL WINAPI CreateProcess(
  _In_opt_    LPCTSTR               lpApplicationName,
  _Inout_opt_ LPTSTR                lpCommandLine,
  _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_        BOOL                  bInheritHandles,
  _In_        DWORD                 dwCreationFlags,
  _In_opt_    LPVOID                lpEnvironment,
  _In_opt_    LPCTSTR               lpCurrentDirectory,
  _In_        LPSTARTUPINFO         lpStartupInfo,
  _Out_       LPPROCESS_INFORMATION lpProcessInformation
);

該API用於創建新進程,其他啟動運行進程的API(WinExec, ShellExecute, system)在其內部調用的都是該函數

WinExec:

https://msdn.microsoft.com/en-us/library/ms687393(v=vs.85).aspx

可以直接鈎取父進程(通常是explorer.exe)的CreateProcess API來監控子進程創建,從而達到鈎取子進程的目的。

但是這種方法存在一些問題,且不說需要同時鈎取CreateProcessA, CreateProcessW,CreateProcessInternalA, CreateProcessInternalW, 還存在在NewCreateProcess(攻擊者自定義的Hook函數)調用CreateProcess創建子進程時,極短時間內,子進程可能在未鈎取的狀態下運行。

//Ntdll.ZwResumeThread
ZwResumeThread(
	IN HANDLE ThreadHandle,
  	OUT PULONG SuspendCount OPTIONAL
)

該API也是一個未公開API,它是比CreateProcess更低級的API,它在進程創建后,主線程運行前被調用執行。所以只要鈎取這個函數即可在不運行子進程代碼的情況下鈎取API。

但由於其為未公開API,隨着OS的升級,API可能失效。

2.代碼分析

stealth2.cpp中增加了對CreateProcessA和CreateProcessW的Hook操作,並對新創建的進程進行dll注入d

有NewCreateProcessA為例:

BOOL WINAPI NewCreateProcessA(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
    BOOL bRet;
    FARPROC pFunc;

    // unhook
    unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);

    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
    bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

    // 如果新進程創建成果,將stealth.dll注入到新進程中
    if( bRet )
        InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

    // hook
    hook_by_code("kernel32.dll", "CreateProcessA", 
                 (PROC)NewCreateProcessA, g_pOrgCPA);

    return bRet;
}

0x06 利用熱補丁技術鈎取API

前面demo程序中通過代碼修改技術進行API Hook存在的缺點

  1. 頻繁脫鈎,掛鈎會造成整體性能下降
  2. 多線程環境,當一個線程嘗試運行某段代碼,而另一進程剛好在對該代碼進行寫操作,這時就會發生沖突,會引起非法訪問異常(Access Violation)。《windows核心編程》指出,利用代碼修改技術鈎取API會對系統安全造成威脅

1.熱補丁(修改7Byte代碼)

Windows系統庫中的函數,如kernel32.CreateProcessA/W, user32.MessageBoxA,gdi32.TextOutW有一個相似點

  1. API以MOV EDI, EDI指令開始(IA-32 0X8bff)
  2. API上方有5個NOP指令(IA-32 0X90)

微軟做此設計的目的就是方便打熱補丁。

使用熱補丁Hook API的過程如下

  1. 修改API開始的兩個字節MOV EDI,EDI為SHORT JMP指令EB F9, 跳轉的目標地址是address_of_api - 5,即5個NOP指令的第一條
  2. 修改5個NOP指令為長跳轉E9 XXXXXXXX,跳轉到用戶自定義API
  3. 用戶自定義代碼調用原始API時,直接以API+2的地址為API地址調用原API,這樣就不會引起內存非法訪問

2.代碼分析

2.1 DllMain

和之前代碼一樣,在Dll附加進程的時候調用hook方法,在dll卸載時調用unhook方法

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    char            szCurProc[MAX_PATH] = {0,};
    char            *p = NULL;

    // 判斷進程名稱,不向HideProc2.exe注入dll
    GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
    p = strrchr(szCurProc, '\\');
    if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") )
        return TRUE;

    // change privilege
    SetPrivilege(SE_DEBUG_NAME, TRUE);

    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH : 
            // hook
            hook_by_hotpatch("kernel32.dll", "CreateProcessA", 
                             (PROC)NewCreateProcessA);
            hook_by_hotpatch("kernel32.dll", "CreateProcessW", 
                             (PROC)NewCreateProcessW);
        	//可以看到這里沒有對ZwQuerySystemInformation使用熱補丁Hook,具體原因見"3.熱補丁Hook的缺點"
            hook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                         (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
            break;

        case DLL_PROCESS_DETACH :
            // unhook
            unhook_by_hotpatch("kernel32.dll", "CreateProcessA");
            unhook_by_hotpatch("kernel32.dll", "CreateProcessW");
            unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                           g_pOrgZwQSI);
            break;
    }

    return TRUE;
}

2.2 hook_by_hotpatch

修改API中無用的7字節數據,跳轉到用戶自定義Hook函數

BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew)
{
	FARPROC pFunc;
	DWORD dwOldProtect, dwAddress;
	BYTE pBuf[5] = { 0xE9, 0, };
    BYTE pBuf2[2] = { 0xEB, 0xF9 };
	PBYTE pByte;

	pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
	pByte = (PBYTE)pFunc;
	if( pByte[0] == 0xEB )
		return FALSE;

	VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // 1. NOP (0x90)
	dwAddress = (DWORD)pfnNew - (DWORD)pFunc;
	memcpy(&pBuf[1], &dwAddress, 4);
	memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);
    
    // 2. MOV EDI, EDI (0x8BFF)
    memcpy(pFunc, pBuf2, 2);

	VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, dwOldProtect, &dwOldProtect);

	return TRUE;
}

2.3 unhook_by_hotpatch

將7字節數據修改為原數據

BOOL unhook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName)
{
    FARPROC pFunc;
    DWORD dwOldProtect;
    PBYTE pByte;
    BYTE pBuf[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 };
    BYTE pBuf2[2] = { 0x8B, 0xFF };


    pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pFunc;
    if( pByte[0] != 0xEB )
        return FALSE;

    VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // 1. NOP (0x90)
    memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);
    
    // 2. MOV EDI, EDI (0x8BFF)
    memcpy(pFunc, pBuf2, 2);

    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}

2.4 NewCreateProcess

這里需要注意一點,在使用之前的hook方法進行API鈎取時,在用戶自定義NewCreateProcess函數開頭需要調用unhook方法(防止進入鈎取的死循環),函數結尾需要調用hook方法進行重新鈎取。

但是使用熱補丁Hook時不需要這樣反復脫鈎,掛鈎,只需在調用原始API時,使用address of API + 2的地址調用API即可

BOOL WINAPI NewCreateProcessA(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
    BOOL bRet;
    FARPROC pFunc;

    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
  	//以API地址+2為函數地址調用CreateProcessA函數
    pFunc = (FARPROC)((DWORD)pFunc + 2);
    bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

    // 注入steach3.dll
    if( bRet )
        InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

    return bRet;
}

3.使用熱補丁進行Hook的缺點

熱補丁鈎取技術有個明顯的缺點,即不符合鈎取條件(7字節無用代碼)的API無法使用熱補丁鈎取

這樣的API有,ntdll.dll提供的API 和kernel32.GetStartInfoA() 等

並非所有API都能使用熱補丁鈎取,所以使用前需要先確認要鈎取的API是否支持,若不支持則需使用前面的5字節代碼修改技術

0x07 Ntdll.dll API鈎取技巧

Ntdll.dll中提供的API代碼都較短,鈎取這些API時有一種非常好的方法,使用這種方法時先將原API備份到用戶內存區域,然后使用5字節代碼修改技術修改原API的起始部分。在用戶鈎取函數內部調用API時,只需調用備份的API即可,這樣實現的API鈎取既簡單又穩定。由於Ntdll.all API代碼較短,且代碼內部地址無依賴性,所以它們非常適合用該技術鈎取

0x08 一些問題

  1. Dll在內存中只會加載一次,那么在Hook時對Dll進行修改后豈不是會影響所有使用該dll的進程?

    主要是Copy-On-Write機制,a.dll在內存中只有一份拷貝,增加的是dll的引用計數,當dll的引用計數為0時,系統會回收對應內存和刪除對應的映射。
    如果有一個程序對dll的內存進行了相應的修改,比如說修改內存加載鈎子,那么系統會將對應頁面復制一份出來給程序,此時,兩個程序指向的dll的對應內存就不同了。

    http://bbs.csdn.net/topics/330026043

  2. 使用HideProc注入時注入失敗

    Windows 7/Vista中使用了會話隔離技術,可能導致Dll注入失敗。出現這個問題時,不要使用kernel32.CreateRemoteThread而使用ntdll.NtCreateThreadEx就可以了。

    此外,嘗試向PE32+格式的進程注入PE32格式的DLL時也會失敗(反之亦然)。注入時必須保證注入的DLL文件和目標進程PE格式一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM