当前位置:   article > 正文

驱动无模块注入dll_驱动注入

驱动注入

实现效果

在这里插入图片描述

可以看到dll已经成功执行,但是在内存区域里面并没有我们的模块,并且在模块列表里面,也没有我们的dll模块

三环无模块注入的方案

实际上无模块注入的方案在三环也可以完成,这种技术叫反射型dll注入。实现的原理就是在dll内部实现了一个loader函数代替LoadLibrary,然后把这个函数导出直接调用。

BlackBone里面已经封装好了相关的接口,直接调用就可以了

//隐藏注入dll 进程名 dll路径 适配32位和64位
void HideInject(const std::wstring& processName, const std::wstring& dllPath)
{
	vector<DWORD> vecPid = Process::EnumByName(processName);

	if (vecPid.empty())
	{
		MessageBoxA(0, "不存在目标进程", "提示", 0);
		return;
	}

	//首先要拿到目标进程的信息
	Process proc;
	proc.Attach(vecPid.front());

	//确保LdrInitializeProcess被调用
	proc.EnsureInit();

	//将PE文件映射到目标进程 1.PE文件路径 flags(手动映射导入函数) 回调函数
	auto image = proc.mmap().MapImage(dllPath, ManualImports, &MapCallback2);

	printf("ImageBase:%llx\n", image);

	//获取导出函数地址s
	auto g_loadDataPtr = proc.modules().GetExport(image.result(), "g_LoadData");

	//调用导出函数
	auto g_loadData = proc.memory().Read<DllLoadData>(g_loadDataPtr->procAddress);
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

我们把这个函数调用来看一下效果

HideInject(L"WeChat.exe", L"C:\\Users\\Administrator\\Desktop\\Test.dll");
  • 1

实现效果如下:

在这里插入图片描述

在VAD树里可以看到这块内存在分配时的属性是可读可写可执行,而当前的内存属性是可读,说明BlackBone还是做了一些防护措施的,在dllmain执行完成后,把内存属性修改为了只读。

在这里插入图片描述

在枚举模块的位置已经找不到我们注入的dll了。

反射型dll注入方式的改进

事实上这种注入方式已经很隐蔽了,查探不到模块,但是依然还有两个问题,第一是内存区域指向的位置有PE头,第二是在申请时的内存属性为可执行。

那么我们可以针对这种方式进行改进:

  1. 在dll入口函数执行完成之后,把PE头抹掉
  2. 在申请内存时候,只申请可读可写的内存
  3. 在调用dllmain前,将属性修改为可执行
  4. 在执行完dllmain之后,将属性修改为只读

完成这些操作之后,在VAD树的分配时内存属性就会变成读写,当前的内存属性就会变成只读,再把PE头抹掉,就实现了三环的无模块注入了。

各位有需要可以参考这个思路进行自行魔改。

零环无模块注入方案

接下来再来说零环的无模块注入方案,零环的无模块注入方案首先要用到一个项目,将我们的dll文件转成shellcode

petoshellcode

pe转shellcode的原理就是在完整的dll外层套上一个用shellcode的加载器,然后由这个加载器把我们的dll跑起来。

https://github.com/monoxgas/sRDI
  • 1

这里需要用到一个开源项目叫sRDI,做红队相关的同学应该比较眼熟了。

在这里插入图片描述

这个项目里面实现了一个加载dll的函数,使用shellcode的方式编写的,与位置无关,编译完成之后,可以直接把二进制扣出来用。

在这里插入图片描述

里面也有一个调用示例。我们需要修改下这个函数,把参数的部分去掉,就可以使用这个loader来加载任意一个dll了。

无模块注入流程

接着就可以开始实现驱动层的无模块注入了,流程如下:

  1. 将要注入的dll提取成硬编码存存放到char数组里
  2. 附加目标进程,申请一块不可执行的内存,用来存放硬编码的dll
  3. 用隐藏可执行内存的方式,申请一块可执行的内存,用来存放dllLoader
  4. 修改dllLoader内的dll入口函数地址
  5. 附加到目标进程,并创建线程执行dllLoder
  6. 等待线程执行完成,释放内存

实现代码

实现代码如下:


NTSTATUS Inject(HANDLE pid, char * shellcode, SIZE_T shellcodeSize)
{
	PEPROCESS Process = NULL;
	NTSTATUS status = PsLookupProcessByProcessId(pid, &Process);
	KAPC_STATE kApcState = {0};

	if (!NT_SUCCESS(status))
	{
		return status;
	}

	if (PsGetProcessExitStatus(Process) != STATUS_PENDING)
	{
		ObDereferenceObject(Process);
		return NULL;
	}

	PUCHAR kfileDll = ExAllocatePool(PagedPool, shellcodeSize);
	memcpy(kfileDll, shellcode, shellcodeSize);

	BOOLEAN isuFileAllocatedll = FALSE;
	BOOLEAN isuShellcode = FALSE;
	BOOLEAN isuimageDll = FALSE;

	PUCHAR ufileDll = NULL;
	PUCHAR uShellcode = NULL;
	SIZE_T uShellcodeSize = 0;
	PUCHAR uImage = NULL;
	SIZE_T uImageSize = 0;

	KeStackAttachProcess(Process, &kApcState);
	do 
	{
		//这里填充的是dll
		ufileDll = AllocateMemoryNotExecute(pid, shellcodeSize);

		if (!ufileDll)
		{
			break;
		}
		
		memcpy(ufileDll, kfileDll, shellcodeSize);

		isuFileAllocatedll = TRUE;


		//这里填充的是dllLoader
		uShellcode = AllocateMemory(pid, sizeof(MemLoadShellcode_x64));

		if (!uShellcode)
		{
			break;
		}

		isuShellcode = TRUE;

		memcpy(uShellcode, MemLoadShellcode_x64, sizeof(MemLoadShellcode_x64));

		PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ufileDll;
		PIMAGE_NT_HEADERS pNts = (PIMAGE_NT_HEADERS)(ufileDll + pDos->e_lfanew);
		uImageSize = pNts->OptionalHeader.SizeOfImage;

		//申请内存 存放展开后的dll
		uImage = AllocateMemory(pid, uImageSize);

		DbgPrint("Base:%llx\n", uImage);

		if (!uImage)
		{
			break;
		}

		//替换shellcode里面的dll地址 mov rax,uImage
		uShellcode[0x50f] = 0x90;
		uShellcode[0x510] = 0x48;
		uShellcode[0x511] = 0xb8;
		*(PULONG64)&uShellcode[0x512] = (ULONG64)uImage;

		
		//附加到目标进程 然后创建线程去跑dllLoader
		PETHREAD thread = NULL;
		if (CreateRemoteThreadByProcess(pid, uShellcode, ufileDll, &thread))
		{
			KeWaitForSingleObject(thread, Executive, KernelMode, FALSE, NULL);
			memset(uImage, 0, PAGE_SIZE);
		}
		else 
		{
			isuimageDll = TRUE;
		}
	} while (0);


	//释放内存
	if (isuFileAllocatedll)
	{
		FreeMemory(pid, ufileDll, shellcodeSize);
	}

	if (isuShellcode)
	{
		FreeMemory(pid, uShellcode, uShellcodeSize);
	}

	if (isuimageDll)
	{
		FreeMemory(pid, uImage, uImageSize);
	}

	KeUnstackDetachProcess(&kApcState);

	ExFreePool(kfileDll);

	return status;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

到这里所有的步骤就已经完成了,之前没研究过的时候总听人说驱动无模块注入,以为有多厉害,其实就是把三环的dlltoshellcode搬到了零环,再改掉分页属性,既隐藏掉了模块,也隐藏了可执行内存属性。

Xenos注入方案研究

既然都研究到这了,就索性把各路大神的注入姿势都研究一下。

https://github.com/DarthTon/Xenos
  • 1

这个注入工具是一位大佬推荐给我的,说里面有几个比较牛逼的内核注入姿势,不过现在应该被各大游戏公司给杀了个遍。

具体什么原理当时也没看,现在回过头来瞅瞅。

在这里插入图片描述

我拿到了一份汉化版,居然还带BlackBone的驱动,驱动代码估计也是直接引用的。

在这里插入图片描述

里面有三种内核注入方式

在这里插入图片描述

找到这个枚举,然后分别查看引用,就能知道三种注入方案的原理了

在这里插入图片描述

都是调用的同一个函数

在这里插入图片描述

里面都是调用的BlackBone的接口,看来研究内核这个项目是绕不开了。

在这里插入图片描述

最终都是来到了这个函数,里面确实有三种注入的姿势。不得不说他这个注入写的确实好,考虑到的比较全。关保护,抹PE头,判断进程位数等等。自己写的话估计要踩很多坑,才能到他的代码健壮度。

不过这个Xenos的注入工具确实是太水了,调个接口都能拿1.5Kstar,还被传的牛逼哄哄的。。。。这我就不理解了

总结一下这个函数的三种注入方式:

IT_MMap注入

把目标PE在内存中展开后,创建线程调用导出函数LdrLoadDll进行内存加载;跟反射型注入的MapImage很像,大概翻了一下代码还是有区别的,MapImage反射注入dll是在三环把PE展开,而IT_MMap 的注入方式则是在驱动层把dll手动展开,并且抹掉了PE头,不知道为啥反射注入不抹掉。。。搞不懂作者咋想的。

IT_Thread注入

原理就是附加目标进程后,起了一个线程去执行代码

IT_Apc注入

这个就更没什么说的了,就是APC注入。

三个方式对比还是第一个最优

在这里插入图片描述

也就是这个玩意,有机会把里面的Map注入方式的代码给抠出来自己拿来用。

火绒的注入思路

再来看看火绒早期的注入

https://gitee.com/DragonQuestHero/nahequdongzhurudll
  • 1

在这里插入图片描述

他这里是注册了一个模块监控的回调,相当于现在可以监控任意一个进程的主模块,等这个主模块跑起来的时候,再注入dll

在这里插入图片描述

先搜一下原理,再来看代码

在这里插入图片描述

大概就是等到目标进程跑起来的时候,替换一下IAT,这样就能实现在进程启动的时候注入dll了。没写注释看的头疼。。。就这样吧。加上这一个一共就有五种驱动注入的姿势了,后面想怎么注就怎么注了。

驱动无模块注入完整源码:

https://download.csdn.net/download/qq_38474570/87263833?spm=1001.2014.3001.5501

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/204211
推荐阅读
相关标签
  

闽ICP备14008679号