【安全技术】关于几种dll注入方式的学习

在这里插入图片描述

何为dll注入

DLL注入技术,一般来讲是向一个正在运行的进程插入/注入代码的过程。我们注入的代码以动态链接库(DLL)的形式存在。DLL文件在运行时将按需加载(类似于UNIX系统中的共享库(share object,扩展名为.so))。然而实际上,我们可以以其他的多种形式注入代码(正如恶意软件中所常见的,任意PE文件,shellcode代码/程序集等)。

全局钩子注入

在Windows大部分应用都是基于消息机制,他们都拥有一个消息过程函数,根据不同消息完成不同功能,windows通过钩子机制来截获和监视系统中的这些消息。一般钩子分局部钩子与全局钩子,局部钩子一般用于某个线程,而全局钩子一般通过dll文件实现相应的钩子函数。

核心函数

SetWindowsHookEx

HHOOK WINAPI SetWindowsHookEx(
__in int idHook, \\钩子类型
__in HOOKPROC lpfn, \\回调函数地址
__in HINSTANCE hMod, \\实例句柄
__in DWORD dwThreadId); \\线程ID

通过设定钩子类型与回调函数的地址,将定义的钩子函数安装到挂钩链中。如果函数成功返回钩子的句柄,如果函数失败,则返回NULL

实现原理

由上述介绍可以知道如果创建的是全局钩子,那么钩子函数必须在一个DLL中。这是因为进程的地址空间是独立的,发生对应事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数的实现代码在DLL中,则在对应事件发生时,系统会把这个DLL加较到发生事体的进程地址空间中,使它能够调用钩子函数进行处理。

在操作系统中安装全局钩子后,只要进程接收到可以发出钩子的消息,全局钩子的DLL文件就会由操作系统自动或强行地加载到该进程中。因此,设置全局钩子可以达到DLL注入的目的。创建一个全局钩子后,在对应事件发生的时候,系统就会把 DLL加载到发生事件的进程中,这样,便实现了DLL注入。

为了能够让DLL注入到所有的进程中,程序设置WH_GETMESSAGE消息的全局钩子。因为WH_GETMESSAGE类型的钩子会监视消息队列,并且 Windows系统是基于消息驱动的,所以所有进程都会有自己的一个消息队列,都会加载 WH_GETMESSAGE类型的全局钩子DLL。

那么设置WH_GETMESSAGE就可以通过以下代码实现,记得加上判断是否设置成功

// 设置全局钩子

BOOL SetHook()
{
   
    g_Hook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllMoudle, 0);

    if (g_Hook == NULL)
    {
   
        return FALSE;
    }

    return TRUE;
}

这里第二个参数是回调函数,那么我们还需要写一个回调函数的实现,这里就需要用到CallNextHookEx这个api,主要是第一个参数,这里传入钩子的句柄的话,就会把当前钩子传递给下一个钩子,若参数传入0则对钩子进行拦截

// 钩子回调函数
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
   
    return ::CallNextHookEx(g_Hook, code, wParam, lParam);
}

既然我们写入了钩子,如果不使用的情况下就需要将钩子卸载掉,那么这里使用到UnhookWindowsHookEx这个api来卸载钩子

// 卸载钩子
BOOL UnsetHook()
{
   
    if (g_Hook)
    {
   
        ::UnhookWindowsHookEx(g_Hook);
    }
}

既然我们使用到了SetWindowsHookEx这个api,就需要进行进程间的通信,进程通信的方法有很多,比如自定义消息、管道、dll共享节、共享内存等等,这里就用共享内存来实现进程通信

// 共享内存
#pragma data_seg("mydata")
    HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS"

实现过程

首先新建一个dll【网络安全学习资料·攻略
在这里插入图片描述
在pch.h头文件里面声明这几个我们定义的函数都是裸函数,由我们自己平衡堆栈

extern "C" _declspec(dllexport) int SetHook();
extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
extern "C" _declspec(dllexport) BOOL UnsetHook();

在这里插入图片描述
然后在pch.cpp里面写入三个函数并创建共享内存

// pch.cpp: 与预编译标头对应的源文件

#include "pch.h"
#include <windows.h>
#include <stdio.h>


extern HMODULE g_hDllModule;

// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")

//钩子回调函数
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
   
    return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

// 设置钩子
BOOL SetHook() {
   
    g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
    if (NULL == g_hHook) {
   
        return FALSE;
    }
    return TRUE;
}

// 卸载钩子
BOOL UnsetHook() {
   
    if (g_hHook) {
   
        UnhookWindowsHookEx(g_hHook);
    }
    return TRUE;
}

在这里插入图片描述
然后再在dllmain.cpp设置DLL_PROCESS_ATTACH,然后编译生成Golbal.dll

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
HMODULE g_hDllModule = NULL;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
   
    switch (ul_reason_for_call)
    {
   
    case DLL_PROCESS_ATTACH: 
    {
   
        g_hDllModule = hModule;
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

在这里插入图片描述
再创建一个控制台项目
在这里插入图片描述
使用LoadLibrabryW加载dll,生成GolbalInjectDll.cpp文件

// GolbalInjectDll.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>

int main()
{
   
    typedef BOOL(*typedef_SetGlobalHook)();
    typedef BOOL(*typedef_UnsetGlobalHook)();
    HMODULE hDll = NULL;
    typedef_SetGlobalHook SetGlobalHook = NULL;
    typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
    BOOL bRet = FALSE;

    do
    {
   
        hDll = ::LoadLibraryW(TEXT("F:\\C++\\GolbalDll\\Debug\\GolbalDll.dll"));
        if (NULL == hDll)
        {
   
            printf("LoadLibrary Error[%d]\n", ::GetLastError());
            break;
        }

        SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");
        if (NULL == SetGlobalHook)
        {
   
            printf("GetProcAddress Error[%d]\n", ::GetLastError());
            break;
        }

        bRet = SetGlobalHook();
        if (bRet)
        {
   
            printf("SetGlobalHook OK.\n");
        }
        else
        {
   
            printf("SetGlobalHook ERROR.\n");
        }

        system("pause");

        UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetHook");
        if (NULL == UnsetGlobalHook)
        {
   
            printf("GetProcAddress Error[%d]\n", ::GetLastError());
            break;
        }
        UnsetGlobalHook();
        printf("UnsetGlobalHook OK.\n");

    } while (FALSE);

    system("pause");
    return 0;
}

执行即可注入GolbalDll.dll
在这里插入图片描述
在这里插入图片描述

远程线程注入

远程线程函数顾名思义,指一个进程在另一个进程中创建线程。

核心函数

CreateRemoteThread

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID                 lpParameter,
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);

lpStartAddress:A pointer to the application-defined function of type
LPTHREAD_START_ROUTINE to be executed by the thread and represents the
starting address of the thread in the remote process. The function
must exist in the remote process. For more information, see
ThreadProc).

lpParameter:A pointer to a variable to be passed to the thread
function.

lpStartAddress即线程函数,使用LoadLibrary的地址作为线程函数地址;lpParameter为线程函数参数,使用dll路径作为参数

VirtualAllocEx

是在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0。

LPVOID VirtualAllocEx(
  HANDLE hProcess,
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flAllocationType,
  DWORD  flProtect
);

hProcess:申请内存所在的进程句柄

lpAddress:保留页面的内存地址;一般用NULL自动分配 。

dwSize:欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍。

flAllocationType

可取下列值:

MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储

MEM_PHYSICAL :分配物理内存(仅用于地址窗口扩展内存)

MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用

MEM_RESET :指明在内存中由参数lpA

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值