首页
社区
课程
招聘
[原创] 如何在驱动层完美隐藏内存
发表于: 2021-1-12 13:17 33094

[原创] 如何在驱动层完美隐藏内存

2021-1-12 13:17
33094

废话不多说直接开干.众所周知现在那些"见不得人的产品"都是层层防护用来对抗游戏公司和同行!
其中不乏很多产品都是带着驱动上线的,但玩具是玩具产品就要有个产品的样子.
首先稳定性和兼容性一定要达标.下面我分享的这些技术方案是从运行了几年的成熟产品当中提取出来的并非凭空臆想请放心.
一个真正"见不得人"的产品其中包括几项必做 :

我们今天分享的主要内容就是无内存.

真正的无内存 = 看不到地址 + 即使有地址访问时崩溃
检验无内存是否合格的标准很简单,使用PCHunter看内存属性以及是否能拷贝出来.如果看不到并且直接输入地址也拷贝不到,编写驱动也无法访问就OK了

当初我的方案也是在搜索引擎输入了 “驱动隐藏内存” 找到了一篇文章对我的启发很大.最终经过几天的修改测试才得以落地!
理论知识下面这个链接说的很明白了我就不废话了:
https://www.cnblogs.com/onetrainee/p/11750274.html

但是他的这个方案有几个问题 :

另外我的代码基础是建立在Blackbone上面升级的.(github搜索Blackbone)
它是一个成熟的驱动程序,包括ring3通信.重点看 src/BlackBoneDrv/
BlackBoneTest 写了很多demo 简单看看就能学会如何加载并通信!


上图粉色框里面的3块内存是游戏的.很多人一看就知道是什么游戏了
下面蓝色框里面的2块内存是我们的.进程内仅当前内存块中可读写

下面开始阅读驱动代码 :

OK了。同学们快去调通源代码吧.
至于驱动怎么签名别来问我,但是我可以告诉你也就是几百块钱的事。一条烟钱吧
另外WIN10平台需要过PG,否则半小时蓝屏.
动态过pg推荐去看看(github.com/9176324/Shark)
不想编译的同学们可以直接点击右面的 releases 下载一个exe和Shark.sys
双击的时候自动加载Shark.sys帮你过PG,当然这个Shark.sys也需要找人去签名.

 
/**
 *    隐藏内存
 *    IOCTL_BLACKBONE_HIDE_VAD 只能隐藏 Image
 */
#define IOCTL_BLACKBONE_HIDE_MEMORY  (ULONG)CTL_CODE(FILE_DEVICE_BLACKBONE, 0x80F, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_BLACKBONE_HIDE_MEMORYEX  (ULONG)CTL_CODE(FILE_DEVICE_BLACKBONE, 0x810, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
/**
 *    隐藏内存
 *    IOCTL_BLACKBONE_HIDE_VAD 只能隐藏 Image
 */
#define IOCTL_BLACKBONE_HIDE_MEMORY  (ULONG)CTL_CODE(FILE_DEVICE_BLACKBONE, 0x80F, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_BLACKBONE_HIDE_MEMORYEX  (ULONG)CTL_CODE(FILE_DEVICE_BLACKBONE, 0x810, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
/// <summary>
/// hide memory ex
/// </summary>
typedef struct _HIDE_MEMORYEX
{
    ULONGLONG beginAddress;
    ULONGLONG endinAddress;
    ULONG pid;                  // Target process ID
} HIDE_MEMORYEX, *PHIDE_MEMORYEX;
/// <summary>
/// hide memory ex
/// </summary>
typedef struct _HIDE_MEMORYEX
{
    ULONGLONG beginAddress;
    ULONGLONG endinAddress;
    ULONG pid;                  // Target process ID
} HIDE_MEMORYEX, *PHIDE_MEMORYEX;
case IOCTL_BLACKBONE_HIDE_MEMORYEX:
                    {
                        if (inputBufferLength >= sizeof(HIDE_MEMORYEX) && ioBuffer)
                        {
                            Irp->IoStatus.Status = BBHideMemoryEx((PHIDE_MEMORYEX)ioBuffer);
                        }
                        else
                        {
                            Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
                        }
                    }
                    break;
case IOCTL_BLACKBONE_HIDE_MEMORYEX:
                    {
                        if (inputBufferLength >= sizeof(HIDE_MEMORYEX) && ioBuffer)
                        {
                            Irp->IoStatus.Status = BBHideMemoryEx((PHIDE_MEMORYEX)ioBuffer);
                        }
                        else
                        {
                            Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
                        }
                    }
                    break;
/// <summary>
/// Hide Memory containing target address
/// </summary>
/// <param name="pData">Address info</param>
/// <returns>Status code</returns>
NTSTATUS BBHideMemoryEx(IN PHIDE_MEMORYEX pData);
/// <summary>
/// Hide Memory containing target address
/// </summary>
/// <param name="pData">Address info</param>
/// <returns>Status code</returns>
NTSTATUS BBHideMemoryEx(IN PHIDE_MEMORYEX pData);
NTSTATUS BBHideMemoryEx(IN PHIDE_MEMORYEX pData)
{
    NTSTATUS status = STATUS_SUCCESS;
    PEPROCESS pProcess = NULL;
    NTSTATUS status1 = STATUS_SUCCESS;
    NTSTATUS status2 = STATUS_SUCCESS;
 
    status = PsLookupProcessByProcessId((HANDLE)pData->pid, &pProcess);
    if (NT_SUCCESS(status))
    {
        // 检查是不是已经保存了PID, pid在游戏的父进程当中保存过来
        // 下面这个BBIsMemoryProcess,是我项目当中的检查函数.大家无需关心.因为我不想驱动谁给它一个指令都能工作
        if (BBIsMemoryProcess(pData->pid))
        {
            PMMVAD_SHORT pVadShort1 = NULL;
            PMMVAD_SHORT pVadShort2 = NULL;
            status1 = BBFindVAD(pProcess, pData->beginAddress, &pVadShort1);
            status2 = BBFindVAD(pProcess, pData->endinAddress, &pVadShort2);
            if (NT_SUCCESS(status1) && NT_SUCCESS(status2))
            {
                if (BBHideMemoryProcess(pData->pid, pVadShort1, pVadShort1->StartingVpn))
                {
                    // 隐藏内存
                    pVadShort1->StartingVpn = pVadShort2->EndingVpn;
                }
                else
                {
                    status = STATUS_INVALID_PARAMETER;
                }
            }
            else
            {
                status = STATUS_INVALID_PARAMETER;
            }
        }
    }
    else
    {
        DPRINT("BlackBone: %s: PsLookupProcessByProcessId failed with status 0x%X\n", __FUNCTION__, status);
    }
 
    if (pProcess) {
        ObDereferenceObject(pProcess);
    }
 
    return status;
}
NTSTATUS BBHideMemoryEx(IN PHIDE_MEMORYEX pData)
{
    NTSTATUS status = STATUS_SUCCESS;
    PEPROCESS pProcess = NULL;
    NTSTATUS status1 = STATUS_SUCCESS;
    NTSTATUS status2 = STATUS_SUCCESS;
 
    status = PsLookupProcessByProcessId((HANDLE)pData->pid, &pProcess);
    if (NT_SUCCESS(status))
    {
        // 检查是不是已经保存了PID, pid在游戏的父进程当中保存过来
        // 下面这个BBIsMemoryProcess,是我项目当中的检查函数.大家无需关心.因为我不想驱动谁给它一个指令都能工作
        if (BBIsMemoryProcess(pData->pid))
        {
            PMMVAD_SHORT pVadShort1 = NULL;
            PMMVAD_SHORT pVadShort2 = NULL;
            status1 = BBFindVAD(pProcess, pData->beginAddress, &pVadShort1);
            status2 = BBFindVAD(pProcess, pData->endinAddress, &pVadShort2);
            if (NT_SUCCESS(status1) && NT_SUCCESS(status2))
            {
                if (BBHideMemoryProcess(pData->pid, pVadShort1, pVadShort1->StartingVpn))
                {
                    // 隐藏内存
                    pVadShort1->StartingVpn = pVadShort2->EndingVpn;
                }
                else
                {
                    status = STATUS_INVALID_PARAMETER;
                }
            }
            else
            {
                status = STATUS_INVALID_PARAMETER;
            }
        }
    }
    else
    {
        DPRINT("BlackBone: %s: PsLookupProcessByProcessId failed with status 0x%X\n", __FUNCTION__, status);
    }
 
    if (pProcess) {
        ObDereferenceObject(pProcess);
    }
 
    return status;
}
/// <summary>
/// Save Hide Memory Process id
/// </summary>
/// <param name="pid">pid</param>
/// <returns>Status code</returns>
BOOLEAN BBHideMemoryProcess(IN size_t PID, IN VOID* pVadShort, IN size_t StartingVpn)
{
    //KdPrint(("Hide Memory Process : %d\n", PID));
 
    // 确认是否已经保存过
    for (int i = 0; i < CLIENT_NUMBER; i++)
    {
        ClientProcess * client = &clientList[i];
        if (client->pid == PID)
        {
            for (int j = 0; j < HIDE_MEMORY; j++)
            {
                ClientMemory *memory = &client->hideList[j];
                if (memory->pVadShort != NULL && memory->startingVpn != 0)
                {
                    if (memory->pVadShort == pVadShort && memory->startingVpn == StartingVpn)
                        return TRUE;
                }
            }
            for (int j = 0; j < HIDE_MEMORY; j++)
            {
                ClientMemory *memory = &client->hideList[j];
                if (memory->pVadShort == NULL && memory->startingVpn == 0)
                {
                    memory->pVadShort = pVadShort;
                    memory->startingVpn = StartingVpn;
                    return TRUE;
                }
            }
            return FALSE;
        }
    }
    return FALSE;
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 16
支持
分享
最新回复 (20)
雪    币: 2466
活跃值: (4561)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享,有空研究一下
2021-1-12 17:43
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
驱动里面隐藏内存,差不多就这两种,vad隐藏,pagefault隐藏
不管哪一种,都要先过pg
2021-1-13 01:57
0
雪    币: 355
活跃值: (435)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2021-1-13 08:09
0
雪    币: 14525
活跃值: (17543)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2021-1-13 10:10
0
雪    币: 919
活跃值: (1340)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
6
hh, 估计他都不知道文章被引用了,自我感觉内存隐藏就是利用内存管理的碎片,虚拟内存都是以区为单位管理,但是实际分为区块、区段,从某种角度来讲,只要抓住了后1种就可以实现间接的隐藏(虚拟内存管理的操作)。如果是从物理内存管理的角度,那么就要自己去操作PFN和映射相关。。。
当时知道这个也没往下研究!!感谢大佬分享。
2021-1-13 10:57
0
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
感谢分享
2021-1-22 09:52
1
雪    币: 0
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
在哪下载呢?
2021-1-26 14:27
0
雪    币: 31
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
感谢分享,大佬能否私个联系方式 有偿需要帮助
2021-3-13 12:42
0
雪    币: 1519
活跃值: (2127)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
10
自己make pte 也没有vad,但是在1809会出现 内存管理 1a 蓝屏
2021-3-13 14:21
0
雪    币: 2428
活跃值: (2571)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
VirtualQuery其实不只是能查vad信息,还能查workingset信息。对比一下vad信息和workingset信息,如果vad查不到workingset却查得到还是可执行属性的,就检测出vad隐藏了
2021-3-14 13:23
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
能私个联系方式吗?有偿求教
2021-3-23 12:53
0
雪    币: 864
活跃值: (5124)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
不明觉厉
2021-3-23 16:05
0
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
两年前玩过的 没卵用~
2021-7-2 10:09
0
雪    币: 0
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Aa
15
感谢分享
2021-12-17 22:15
0
雪    币: 1285
活跃值: (1764)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
大哥 貌似没贴BBFindVAD 这个函数啊
2022-12-5 21:30
0
雪    币: 1810
活跃值: (4025)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
mark
2023-1-3 13:39
0
雪    币: 1825
活跃值: (5354)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
dearfuture VirtualQuery其实不只是能查vad信息,还能查workingset信息。对比一下vad信息和workingset信息,如果vad查不到workingset却查得到还是可执行属性的,就检测出v ...
拒绝检测.你的电脑为何让它们查来查去?骂它耍流氓
2023-1-4 07:04
0
雪    币: 1025
活跃值: (628)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19

1.映射驱动的话PsSetCreateProcessNotifyRoutine有点难处理

2.Shark会被检测?兼容性能接受?

我目前的方案是使用PTE将.text内存属性修改为只读

2023-1-4 09:31
0
雪    币: 3738
活跃值: (3872)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
感谢分享!
2023-1-4 09:54
0
游客
登录 | 注册 方可回帖
返回
//