驱动开发:内核LoadLibrary实现DLL注入

这篇具有很好参考价值的文章主要介绍了驱动开发:内核LoadLibrary实现DLL注入。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

远程线程注入是最常用的一种注入技术,在应用层注入是通过CreateRemoteThread这个函数实现的,该函数通过创建线程并调用 LoadLibrary 动态载入指定的DLL来实现注入,而在内核层同样存在一个类似的内核函数RtlCreateUserThread,但需要注意的是此函数未被公开,RtlCreateUserThread其实是对NtCreateThreadEx的包装,但最终会调用ZwCreateThread来实现注入,RtlCreateUserThreadCreateRemoteThread的底层实现。

基于LoadLibrary实现的注入原理可以具体分为如下几步;

  • 1.调用AllocMemory,在对端应用层开辟空间,函数封装来源于《内核远程堆分配与销毁》章节;
  • 2.调用MDLWriteMemory,将DLL路径字符串写出到对端内存,函数封装来源于《内核MDL读写进程内存》章节;
  • 3.调用GetUserModuleAddress,获取到kernel32.dll模块基址,函数封装来源于《内核远程线程实现DLL注入》章节;
  • 4.调用GetModuleExportAddress,获取到LoadLibraryW函数的内存地址,函数封装来源于《内核远程线程实现DLL注入》章节;
  • 5.最后调用本章封装函数MyCreateRemoteThread,将应用层DLL动态转载到进程内,实现DLL注入;

总结起来就是首先在目标进程申请一块空间,空间里面写入要注入的DLL的路径字符串或者是一段ShellCode,找到该内存中LoadLibrary的基址并传入到RtlCreateUserThread中,此时进程自动加载我们指定路径下的DLL文件。

注入依赖于RtlCreateUserThread这个未到处内核函数,该内核函数中最需要关心的参数是ProcessHandle用于接收进程句柄,StartAddress接收一个函数地址,StartParameter用于对函数传递参数,具体的函数原型如下所示;

typedef DWORD(WINAPI* pRtlCreateUserThread)(
    IN HANDLE                    ProcessHandle,          // 进程句柄
    IN PSECURITY_DESCRIPTOR      SecurityDescriptor,
    IN BOOL                      CreateSuspended,
    IN ULONG                     StackZeroBits,
    IN OUT PULONG                StackReserved,
    IN OUT PULONG                StackCommit,
    IN LPVOID                    StartAddress,          // 执行函数地址 LoadLibraryW
    IN LPVOID                    StartParameter,        // 参数传递
    OUT HANDLE                   ThreadHandle,          // 线程句柄
    OUT LPVOID                   ClientID
    );

由于我们加载DLL使用的是LoadLibraryW函数,此函数在运行时只需要一个参数,我们可以将DLL的路径传递进去,并调用LoadLibraryW以此来将特定模块拉起,该函数的定义规范如下所示;

HMODULE LoadLibraryW(
  [in] LPCWSTR lpLibFileName
);

根据上一篇文章中针对注入头文件lyshark.h的封装,本章将继续使用这个头文件中的函数,首先我们实现这样一个功能,将一段准备好的UCHAR字符串动态的写出到应用层进程内存,并以宽字节模式写出在对端内存中,这段代码可以写为如下样子;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include "lyshark.h"

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	DWORD process_id = 7112;
	DWORD create_size = 1024;
	DWORD64 ref_address = 0;

	// 分配内存堆 《内核远程堆分配与销毁》 核心代码
	NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);

	DbgPrint("对端进程: %d \n", process_id);
	DbgPrint("分配长度: %d \n", create_size);
	DbgPrint("[*] 分配内核堆基址: %p \n", ref_address);

	UCHAR DllPath[256] = "C:\\hook.dll";
	UCHAR Item[256] = { 0 };

	// 将字节转为双字
	for (int x = 0, y = 0; x < strlen(DllPath) * 2; x += 2, y++)
	{
		Item[x] = DllPath[y];
	}

	// 写出内存 《内核MDL读写进程内存》 核心代码
	ReadMemoryStruct ptr;

	ptr.pid = process_id;
	ptr.address = ref_address;
	ptr.size = strlen(DllPath) * 2;

	// 需要写入的数据
	ptr.data = ExAllocatePool(PagedPool, ptr.size);

	// 循环设置
	for (int i = 0; i < ptr.size; i++)
	{
		ptr.data[i] = Item[i];
	}

	// 写内存
	MDLWriteMemory(&ptr);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行如上方所示的代码,将会在目标进程7112中开辟一段内存空间,并写出C:\hook.dll字符串,运行效果图如下所示;

驱动开发:内核LoadLibrary实现DLL注入

此处你可以通过x64dbg附加到应用层进程内,并观察内存0000000002200000会看到如下字符串已被写出,双字类型则是每一个字符空一格,效果图如下所示;

驱动开发:内核LoadLibrary实现DLL注入

继续实现所需要的子功能,实现动态获取Kernel32.dll模块里面LiadLibraryW这个导出函数的内存地址,这段代码相信你可以很容易的写出来,根据上节课的知识点我们可以二次封装一个GetProcessAddress来实现对特定模块基址的获取功能,如下是完整代码案例;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include "lyshark.h"

// 实现取模块基址
PVOID GetProcessAddress(HANDLE ProcessID, PWCHAR DllName, PCCHAR FunctionName)
{
	PEPROCESS EProcess = NULL;
	NTSTATUS Status = STATUS_SUCCESS;
	KAPC_STATE ApcState;
	PVOID RefAddress = 0;

	// 根据PID得到进程EProcess结构
	Status = PsLookupProcessByProcessId(ProcessID, &EProcess);
	if (Status != STATUS_SUCCESS)
	{
		return Status;
	}

	// 判断目标进程是32位还是64位
	BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;

	// 验证地址是否可读
	if (!MmIsAddressValid(EProcess))
	{
		return NULL;
	}

	// 将当前线程连接到目标进程的地址空间(附加进程)
	KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);

	__try
	{
		UNICODE_STRING DllUnicodeString = { 0 };
		PVOID BaseAddress = NULL;

		// 得到进程内模块基地址
		RtlInitUnicodeString(&DllUnicodeString, DllName);

		BaseAddress = GetUserModuleAddress(EProcess, &DllUnicodeString, IsWow64);

		if (!BaseAddress)
		{
			return NULL;
		}

		DbgPrint("[*] 模块基址: %p \n", BaseAddress);

		// 得到该函数地址
		RefAddress = GetModuleExportAddress(BaseAddress, FunctionName, EProcess);
		DbgPrint("[*] 函数地址: %p \n", RefAddress);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return NULL;
	}

	// 取消附加
	KeUnstackDetachProcess(&ApcState);
	return RefAddress;
}

VOID Unload(PDRIVER_OBJECT pDriverObj)
{
	DbgPrint("[-] 驱动卸载 \n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath)
{
	DbgPrint("Hello LyShark.com \n");

	// 取模块基址
	PVOID pLoadLibraryW = GetProcessAddress(5200, L"kernel32.dll", "LoadLibraryW");

	DbgPrint("[*] 所在内存地址 = %p \n", pLoadLibraryW);

	DriverObject->DriverUnload = Unload;
	return STATUS_SUCCESS;
}

编译并运行如上驱动代码,将自动获取PID=5200进程中Kernel32.dll模块内的LoadLibraryW的内存地址,输出效果图如下所示;

驱动开发:内核LoadLibrary实现DLL注入

实现注入的最后一步就是调用自定义函数MyCreateRemoteThread该函数实现原理是调用RtlCreateUserThread开线程执行,这段代码的最终实现如下所示;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include "lyshark.h"

// 定义函数指针
typedef PVOID(NTAPI* PfnRtlCreateUserThread)
(
	IN HANDLE ProcessHandle,
	IN PSECURITY_DESCRIPTOR SecurityDescriptor,
	IN BOOLEAN CreateSuspended,
	IN ULONG StackZeroBits,
	IN OUT size_t StackReserved,
	IN OUT size_t StackCommit,
	IN PVOID StartAddress,
	IN PVOID StartParameter,
	OUT PHANDLE ThreadHandle,
	OUT PCLIENT_ID ClientID
);

// 实现取模块基址
PVOID GetProcessAddress(HANDLE ProcessID, PWCHAR DllName, PCCHAR FunctionName)
{
	PEPROCESS EProcess = NULL;
	NTSTATUS Status = STATUS_SUCCESS;
	KAPC_STATE ApcState;
	PVOID RefAddress = 0;

	// 根据PID得到进程EProcess结构
	Status = PsLookupProcessByProcessId(ProcessID, &EProcess);
	if (Status != STATUS_SUCCESS)
	{
		return Status;
	}

	// 判断目标进程是32位还是64位
	BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;

	// 验证地址是否可读
	if (!MmIsAddressValid(EProcess))
	{
		return NULL;
	}

	// 将当前线程连接到目标进程的地址空间(附加进程)
	KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);

	__try
	{
		UNICODE_STRING DllUnicodeString = { 0 };
		PVOID BaseAddress = NULL;

		// 得到进程内模块基地址
		RtlInitUnicodeString(&DllUnicodeString, DllName);

		BaseAddress = GetUserModuleAddress(EProcess, &DllUnicodeString, IsWow64);

		if (!BaseAddress)
		{
			return NULL;
		}

		DbgPrint("[*] 模块基址: %p \n", BaseAddress);

		// 得到该函数地址
		RefAddress = GetModuleExportAddress(BaseAddress, FunctionName, EProcess);
		DbgPrint("[*] 函数地址: %p \n", RefAddress);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return NULL;
	}

	// 取消附加
	KeUnstackDetachProcess(&ApcState);
	return RefAddress;
}

// 远程线程注入函数
BOOLEAN MyCreateRemoteThread(ULONG pid, PVOID pRing3Address, PVOID PParam)
{
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	PEPROCESS pEProcess = NULL;
	KAPC_STATE ApcState = { 0 };

	PfnRtlCreateUserThread RtlCreateUserThread = NULL;
	HANDLE hThread = 0;

	__try
	{
		// 获取RtlCreateUserThread函数的内存地址
		UNICODE_STRING ustrRtlCreateUserThread;
		RtlInitUnicodeString(&ustrRtlCreateUserThread, L"RtlCreateUserThread");
		RtlCreateUserThread = (PfnRtlCreateUserThread)MmGetSystemRoutineAddress(&ustrRtlCreateUserThread);
		if (RtlCreateUserThread == NULL)
		{
			return FALSE;
		}

		// 根据进程PID获取进程EProcess结构
		status = PsLookupProcessByProcessId((HANDLE)pid, &pEProcess);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}

		// 附加到目标进程内
		KeStackAttachProcess(pEProcess, &ApcState);

		// 验证进程是否可读写
		if (!MmIsAddressValid(pRing3Address))
		{
			return FALSE;
		}

		// 启动注入线程
		status = RtlCreateUserThread(ZwCurrentProcess(),
			NULL,
			FALSE,
			0,
			0,
			0,
			pRing3Address,
			PParam,
			&hThread,
			NULL);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}

		return TRUE;
	}

	__finally
	{
		// 释放对象
		if (pEProcess != NULL)
		{
			ObDereferenceObject(pEProcess);
			pEProcess = NULL;
		}

		// 取消附加进程
		KeUnstackDetachProcess(&ApcState);
	}

	return FALSE;
}

VOID Unload(PDRIVER_OBJECT pDriverObj)
{
	DbgPrint("[-] 驱动卸载 \n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath)
{
	DbgPrint("Hello LyShark.com \n");

	ULONG process_id = 5200;
	DWORD create_size = 1024;
	DWORD64 ref_address = 0;

	// -------------------------------------------------------
	// 取模块基址
	// -------------------------------------------------------

	PVOID pLoadLibraryW = GetProcessAddress(process_id, L"kernel32.dll", "LoadLibraryW");
	DbgPrint("[*] 所在内存地址 = %p \n", pLoadLibraryW);

	// -------------------------------------------------------
	// 应用层开堆
	// -------------------------------------------------------

	NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);

	DbgPrint("对端进程: %d \n", process_id);
	DbgPrint("分配长度: %d \n", create_size);
	DbgPrint("分配的内核堆基址: %p \n", ref_address);

	// 设置注入路径,转换为多字节
	UCHAR DllPath[256] = "C:\\lyshark_hook.dll";
	UCHAR Item[256] = { 0 };

	for (int x = 0, y = 0; x < strlen(DllPath) * 2; x += 2, y++)
	{
		Item[x] = DllPath[y];
	}

	// -------------------------------------------------------
	// 写出数据到内存
	// -------------------------------------------------------

	ReadMemoryStruct ptr;

	ptr.pid = process_id;
	ptr.address = ref_address;
	ptr.size = strlen(DllPath) * 2;

	// 需要写入的数据
	ptr.data = ExAllocatePool(PagedPool, ptr.size);

	// 循环设置
	for (int i = 0; i < ptr.size; i++)
	{
		ptr.data[i] = Item[i];
	}

	// 写内存
	MDLWriteMemory(&ptr);

	// -------------------------------------------------------
	// 执行开线程函数
	// -------------------------------------------------------

	// 执行线程注入
	// 参数1:PID
	// 参数2:LoadLibraryW内存地址
	// 参数3:当前DLL路径
	BOOLEAN flag = MyCreateRemoteThread(process_id, pLoadLibraryW, ref_address);
	if (flag == TRUE)
	{
		DbgPrint("[*] 已完成进程 %d 注入文件 %s \n", process_id, DllPath);
	}

	DriverObject->DriverUnload = Unload;
	return STATUS_SUCCESS;
}

编译这段驱动程序,并将其放入虚拟机中,在C盘下面放置好一个名为lyshark_hook.dll文件,运行驱动程序将自动插入DLL到Win32Project进程内,输出效果图如下所示;

驱动开发:内核LoadLibrary实现DLL注入

回到应用层进程,则可看到如下图所示的注入成功提示信息;

驱动开发:内核LoadLibrary实现DLL注入文章来源地址https://www.toymoban.com/news/detail-480758.html

到了这里,关于驱动开发:内核LoadLibrary实现DLL注入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 内核中劫持线程注入DLL并隐藏

    在本文中,我们将讨论如何在Windows驱动层劫持进程线程、注入DLL并隐藏注入后的内存。但请注意,本文描述的方法仅用于教育和研究目的,不得用于非法或恶意目的。对于任何可能导致对他人计算机、数据或设备造成损害的行为,本文概不负责。 以下是在Windows驱动层劫持进

    2024年02月20日
    浏览(34)
  • 驱动开发:内核实现SSDT挂钩与摘钩

    在前面的文章 《驱动开发:内核解析PE结构导出表》 中我们封装了两个函数 KernelMapFile() 函数可用来读取内核文件, GetAddressFromFunction() 函数可用来在导出表中寻找指定函数的导出地址,本章将以此为基础实现对特定 SSDT 函数的 Hook 挂钩操作,与 《驱动开发:内核层InlineHoo

    2024年02月07日
    浏览(30)
  • 驱动开发:内核实现进程汇编与反汇编

    在笔者上一篇文章 《驱动开发:内核MDL读写进程内存》 简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功能的实现分为两部分,内核部分只负责读写字节集,应用层部

    2024年02月06日
    浏览(39)
  • 查看开源操作系统ReactOS源码,解决dll库动态库加载失败问题(调用LoadLibrary加载失败)

    目录 1、动态加载dll库去调用库中的函数 1.1、调用系统dll库中未公开的接口

    2023年04月11日
    浏览(39)
  • Linux驱动开发——内核模块

    目录 内核模块的由来 第一个内核模块程序  内核模块工具  将多个源文件编译生成一个内核模块  内核模块参数 内核模块依赖 关于内核模块的进一步讨论  习题 最近一直在玩那些其它的技术,眼看快暑假了,我决定夯实一下我的驱动方面的技能,迎接我的实习,找了一本

    2024年02月04日
    浏览(76)
  • Linux内核驱动开发(一)

    linux操作系统历史 开发模式 git 分布式管理 git clone 获取 git push 提交 git pull 更新 邮件组 mailing list patch 内核代码组成 Makfile arch 体系系统架构相关 block 块设备 crypto 加密算法 drivers 驱动(85%) atm 通信 bluetooth 蓝牙 firmware:外设 fs 文件系统 include 头文件 init 启动代码 ipc 进程通

    2023年04月11日
    浏览(42)
  • linux内核网络驱动框架(linux驱动开发篇)

    网络驱动的核心: 1、就是初始化 net_device 结构体中的各个成员变量, 2、然后将初始化完成以后的 net_device 注册到 Linux 内核中 1、网络设备(用net_device结构体) 2、网络设备的操作集( net_device_ops结构体 ) 3、sk_buff结构体 网络是分层的,对于应用层而言不用关系具体的底层是

    2023年04月08日
    浏览(75)
  • 驱动开发:内核物理内存寻址读写

    在某些时候我们需要读写的进程可能存在虚拟内存保护机制,在该机制下用户的 CR3 以及 MDL 读写将直接失效,从而导致无法读取到正确的数据,本章我们将继续研究如何实现物理级别的寻址读写。 首先,驱动中的物理页读写是指在驱动中直接读写物理内存页(而不是虚拟内

    2024年02月11日
    浏览(39)
  • 驱动开发:内核遍历文件或目录

    在笔者前一篇文章 《驱动开发:内核文件读写系列函数》 简单的介绍了内核中如何对文件进行基本的读写操作,本章我们将实现内核下遍历文件或目录这一功能,该功能的实现需要依赖于 ZwQueryDirectoryFile 这个内核API函数来实现,该函数可返回给定文件句柄指定的目录中文件

    2024年02月08日
    浏览(71)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包