隐蔽通讯常见种类介绍
- 逆向
- 2025-12-10
- 7热度
- 0评论
正常通信流程:
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脚本









