隐蔽通讯常见种类介绍

正常通信流程:

R3->符号链接->设备对象->驱动对象->驱动功能

驱动通信实质上是设备通信

设备是挂在驱动上的DeviceObject上面的

正常IO通信

R0:

//创建设备名称
UNICODE_STRING Devicename;
RtlInitUnicodeString(&Devicename,L"\\Device\\MyDevice");

//创建设备
IoCreateDevice(
    pDriver,    //当前设备所属的驱动对象
    0,
    &Devicename,    //设备对象的名称
    FILE_DEVICE_UNKNOWN,
    FILE_DEVICE_SECURE_OPEN,
    FALSE,
    &pDeviceObj    //设备对象指针
);

R3:

CreateFileW(SYMBOL_LINK_NAME,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
DeviceIoControl(g_Device,KillAPP,&pEprocess,sizeof(DWORD),&outBuffer,sizeof(DWORD),NULL,NULL))

 

 

隐蔽通讯常见种类介绍

隐蔽通信原理与常见种类

正常的设备通信会被各种ark或其他工具遍历到

winobj:https://learn.microsoft.com/zh-cn/sysinternals/downloads/winobj

隐蔽通讯常见种类介绍

devicetree.exe

隐蔽通讯常见种类介绍

其次通过无模块加载技术加载的驱动没有驱动对象,也无法创建正常的io通信。所以我们另寻通信方式。

任何能从3环主动发起0环能接收到的API或方法都能作为通信方法,如果对通信的实时性没要求也可去掉“主动”一词

常见隐蔽通信方式

1.劫持io通信

故名意思,去劫持系统白名单驱动的通信。

在导入表有查看是否有建立IO通信的函数

隐蔽通讯常见种类介绍

隐蔽通讯常见种类介绍

发现没有IRP_MJ_DEVICE_CONTROL我们可以给他增加一个来作为我们的通信

2.minifilter端口

FltCreateCommunicationPort
FltCloseCommunicationPort
通过回调函数的InputBuffer接受用户态的消息并通过OutputBuffer回复
用户态通过FilterConnectCommunicationPort和FilterSendMessage通信

3.共享内存

3环申请一块内存,0环使用mdl映射

3.注册表

3环和内核都有可以读写注册表的函数,故而可以作为通信

 if (!WriteRegistryDword(HKEY_LOCAL_MACHINE, L"", L"oPid", GetCurrentProcessId())) { return false; }
    if (!WriteRegistryQword(HKEY_LOCAL_MACHINE, L"", L"oAddr", reinterpret_cast<DWORD_PTR>(req))) {
InitializeObjectAttributes(&ObjectAttributes, &RegPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);

	Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
	if (!NT_SUCCESS(Status)) {
		return Status;
	}

	KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
	Status = ZwQueryValueKey(KeyHandle, &AddrValueName, KeyValuePartialInformation, &KeyValueInfo, sizeof(KeyValueInfo) + sizeof(DWORD), &ResultLength);

	if (NT_SUCCESS(Status)) {
		if (KeyValueInfo.Type == REG_QWORD && KeyValueInfo.DataLength == sizeof(uintptr_t)) {
			RtlCopyMemory(&Buffer, KeyValueInfo.Data, sizeof(uintptr_t));
			oAddr = Buffer;
		}
		else {
			Status = STATUS_INVALID_PARAMETER;
		}
	}

	ZwClose(KeyHandle);

4.文件

同上3和0都有操作文件的函数

5.套接字

使用socket进行通信

6.data ptr

.data 是目前恶意程序最主流的通信方式,因为太多了,很难全部监控,如果找到的.data足够隐蔽,那么很难短时间内检测到。

有很多内核函数中是函数指针的调用方式,而这些函数指针存在.data区,或者.rdata区,我们通过交互指针的方法,让函数执行到我们模块的函数中。

_guard_dispatch_icall_fptr是Windows系统中与控制流防护(CFG)相关的关键函数指针,主要用于验证间接函数调用的合法性,我们可以搜索“_guard_dispatch_icall_fptr”来枚举可利用的指针。

ntoskrnl.exe被PG和各大安全软件监控较为严重,我们可以去其他模块中寻找data ptr(写个脚本让ida去跑)

File->Script file...

隐蔽通讯常见种类介绍

隐蔽通讯常见种类介绍

隐蔽通讯常见种类介绍

像这种NtUser开头的函数一般都可从3环调用到,并且可以发现具有可利用指针

利用InterlockedExchangePointer函数交换指针

 const  PVOID win32k = system::get_kernel_modulebase(("win32k.sys"), &nSize);
		 if (win32k) {
			 nt_qword = system::search_kernel((uintptr_t)win32k, NT_QWORD_SIG, NT_QWORD_MASK);

			 const uintptr_t nt_qword_deref = (uintptr_t)nt_qword + 7 + *(int*)((unsigned char*)nt_qword + 3);
			 *(void**)&oNtOldFun = InterlockedExchangePointer((void**)nt_qword_deref, (void*)NtFun);

	
		 }

我们只需要把一个参数当作结构体指针使用,并约定通信码,3环调用API填入指定参数即可完成通信,其他应用不知晓通信码,即可正确调用原API。

		(LoadLibraryA)(("user32.dll"));
		(LoadLibraryA)(("win32u.dll"));

		const HMODULE win32u = (GetModuleHandleA)(("win32u.dll"));
		if (!win32u)
		{

			return 0;
		}


		*(void**)&NtUserFun= GetProcAddress(win32u, ("函数名"));

隐蔽通讯常见种类介绍

检测方法

内存与文件做对比,检测指针有效性,栈回溯地址是否在合法模块等等

python脚本

1765376842-find_data_ptr