zoukankan      html  css  js  c++  java
  • 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格式一致。

  • 相关阅读:
    MySQL入门(引擎、数据类型、约束)
    MySQL入门(一)
    MySQL数据库的卸载与安装
    并发编程(线程与进程)
    网络编程笔记
    JS(vue iview)分页解决方案
    关于JS中判断是数字和小数的正则表达式用法
    2017《面向对象程序设计》课程作业八
    2017《面向对象程序设计》课程作业七
    2017《面向对象程序设计》课程作业六
  • 原文地址:https://www.cnblogs.com/dliv3/p/6398967.html
Copyright © 2011-2022 走看看