MmCreateSection/MmMapViewOfSection个人注释及理解(二)

    MmCreateSection/MmMapViewOfSection个人注释及理解(一)中提到内存映射的第一步创建Section对象建立Segment与文件的关系,彼时尚未建立虚拟内存因此不能被访问。此文是内存映射的第二步,MmMapViewOfSection建立虚拟映射。<windows内核中Section\Segment\ControlArea\Subsection\MMVAD之间的关系>一文中列出了5种和内存映射有关的对象,而<MmCreateSection/MmMapViewOfSection个人注释及理解(一)>中已经描述了其中4种对象,剩下的MMVAD对象肯定是要在这篇文章中出现了。

    MmMapViewOfSection说的是映射一个视图到进程地址空间中,视图怎么理解?个人认为,就是映射整个Segment->ThePtes原型pte阵列的一部分(也可以全部)当进程地址空间中。MmMapViewOfSection的核心思想是在进程vad树中搜索一段大小能容下将要被映射的视图的大小,且没有被占用的虚拟地址空间。

     MmMapViewOfSection的入口参数SectionToMap/Process是最主要的参数,从Section对象可以找到ControlArea,有了它可以找到其他所有相关的对象。而Process提供vad树,所有已被占用的虚拟内存都被记录在这颗树中。对于映像文件,进入MiMapViewOfImageSection函数。

首先设置映像文件起始地址:

BasedAddress = ControlArea->Segment->BasedAddress;

在MiCreateImageFileMap函数中设置:NewSegment->BasedAddress = (PVOID) NextVa;而NextVa的值为PE文件中设置的加载内存,就熟悉的0x40000。

然后计算将要被映射的文件占用的虚拟空间的起止,同时计算这段空间是否被占用,如果被占用还要在进程vad树中查找空闲的虚拟空间(要不然还不让加载程序不成?):

/*
		BasedAddress是整个段对象的起始地址,下面的运算
		(PVOID)((ULONG_PTR)BasedAddress +
                                    (ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));获得要建立视图的
              起始地址应该相对于BasedAddress偏移多少                    
		*/
        StartingAddress = (PVOID)((ULONG_PTR)BasedAddress +
                                    (ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));

        EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress +
                                    *CapturedViewSize - 1) | (PAGE_SIZE - 1));
。。。
<pre name="code" class="cpp">        Vad = (PMMVAD) TRUE;
        NeededViewSize = *CapturedViewSize;

        if ((StartingAddress >= MM_LOWEST_USER_ADDRESS) &&
            (StartingAddress <= MM_HIGHEST_VAD_ADDRESS) &&
            (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) -
                                (ULONG_PTR)StartingAddress >= *CapturedViewSize) &&

            (EndingAddress <= MM_HIGHEST_VAD_ADDRESS)) {

            Vad = (PMMVAD) (ULONG_PTR) MiCheckForConflictingVadExistence (Process, StartingAddress, EndingAddress);
        }

        //
        // If the VAD address is not NULL, then a conflict was discovered.
        // Attempt to select another address range in which to map the image.
        //

		//vad!=NULL,进程已被占用,只能另外找空闲的虚拟地址空间
        if (Vad != NULL) {
。。。
            if (Process->VmTopDown == 1) {

                if (ZeroBits != 0) {
                    HighestUserAddress = (PVOID)((ULONG_PTR)MM_USER_ADDRESS_RANGE_LIMIT >> ZeroBits);
                    if (HighestUserAddress > MM_HIGHEST_VAD_ADDRESS) {
                        HighestUserAddress = MM_HIGHEST_VAD_ADDRESS;
                    }
                }
                else {
                    HighestUserAddress = MM_HIGHEST_VAD_ADDRESS;
                }

                Status = MiFindEmptyAddressRangeDown (&Process->VadRoot,
                                                      NeededViewSize,
                                                      HighestUserAddress,
                                                      X64K,
                                                      &StartingAddress);
            }
            else {
                Status = MiFindEmptyAddressRange (NeededViewSize,
                                                  X64K,
                                                  (ULONG)ZeroBits,
                                                  &StartingAddress);
            }

            if (!NT_SUCCESS (Status)) {
                MiDereferenceControlArea (ControlArea);
                return Status;
            }

            EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress +
                                        *CapturedViewSize - 1) | (PAGE_SIZE - 1));

 MiFindEmptyAddressRange完成在进程vad树中搜索符合大小要求的空闲地址的任务,并返回其起始地址。 


有了虚拟地址,这就要建立和初始化MMVAD来进行管理,这是和内存映射相关的5个对象中最后一个出场的:

Vad = ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD), MMVADKEY);
。。。
    RtlZeroMemory (Vad, sizeof(MMVAD));
    Vad->StartingVpn = MI_VA_TO_VPN (LargeStartingAddress);
    Vad->EndingVpn = MI_VA_TO_VPN (LargeEndingAddress);

还记得,Segment对象中的ThePtes原型pte阵列么?他记录了被映射的各个文件页面在整个文件中的偏移情况。现在这段页面即将被映射到进程空间中,总的有相应的对象记录吧,于是MMVAD承担了这项工作:

Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
    Vad->LastContiguousPte = MM_ALLOCATION_FILLS_VAD;
看看与Subsection->SubsectionBase相关的设置,在MiCreateImageFileMap中可以找到如下的代码段:

	
	//NewSegment->PrototypePte = &NewSegment->ThePtes[0];
	    PointerPte = NewSegment->PrototypePte;
	Subsection->SubsectionBase = PointerPte;

	。。。
	*Segment = NewSegment;
	        RtlCopyMemory (NewSegment, OldSegment, sizeof(SEGMENT));

	        //
	        // Align the prototype PTEs on the proper boundary.
	        //

	        NewPointerPte = &NewSegment->ThePtes[0];
	        NewSegment->PrototypePte = &NewSegment->ThePtes[0];

	PointerPte = NewSegment->PrototypePte +
	                                       (PointerPte - OldSegment->PrototypePte);

	。。。
	for (i = 0; i < SubsectionsAllocated; i += 1) {

	            //
	            // Note: SubsectionsAllocated is always 1 (for wx86), so this loop
	            // is executed only once.
	            //

	            NewSubsection->ControlArea = (PCONTROL_AREA) NewControlArea;

	            NewSubsection->SubsectionBase = NewSegment->PrototypePte +
	                    (Subsection->SubsectionBase - OldSegment->PrototypePte);


	。。。
	Subsection->SubsectionBase = PointerPte;

至此,MiMapViewOfImageSection调创建MMVAD对象完成并加入到进程VAD树,返回模块实际被映射的虚拟内存,最后退出:

MiInsertVad (Vad, Process);
 Process->VirtualSize += LargeOutputViewSize;
。。。
*CapturedViewSize = OutputViewSize;
    *CapturedBase = OutputStartingAddress;


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一:SSDT表的hook检测和恢复 ~!~~~ :IDT表的hook检测和恢复 ~~~~~~(idt多处理器的恢复没处理,自己机器是单核的,没得搞,不过多核的列举可以) 三:系统加载驱动模块的检测 通过使用一个全局hash表(以DRIVEROBJECT为对象)来使用以下的方法来存储得到的结果,最终显示出来 1.常规的ZwQuerySystemInformation来列举 2通过打开驱动对象目录来列举 3搜索内核空间匹配驱动的特征来列举(这个功能里面我自己的主机一运行就死机,别的机器都没事,手动设置热键来蓝屏都不行,没dump没法分析,哎,郁闷) 4从本驱动的Modulelist开始遍历来列举驱动 四:进程的列举和进程所加载的dll检测 采用以下方法来列举进程: 1ZwQuerySystemInformation参数SystemProcessesAndThreadsInformation来枚举 2进程EPROCESS 结构的Activelist遍历来枚举 3通过解析句柄表来枚举进程 4通过Handletablelisthead枚举进程 5进程创建时都会向csrss来注册,从这个进程里面句柄表来枚举进程 6通过自身进程的HANDLETABLE来枚举进程 7通过EPROCESS的SessionProcessLinks来枚举进程 8通过EPROCESS ---VM---WorkingSetExpansionLinks获取进程 9暴力搜索内存MmSystemRangeStart以上查找PROCESS对象 进程操作: 进程的唤醒和暂停通过获取PsSuspendProcess和PsResumeProcess来操作的 进程结束通过进程空间清0和插入apc。 采用以下方法查找DLL: 1遍历VAD来查找dll 2挂靠到对应的进程查找InLoadOrderLinks来枚举dll 3暴力搜索对应进程空间查找pe特征来枚举dll DLL的操作: Dll的卸载是通过MmUnmapViewOfSectionMmmapViewOfSection(从sdt表中相应函数搜索到的)来实现的(本来想直接清0 dll空间,有时行有时不行)(只要将这个进程的ntdll卸载了,进程就结束了,一个好的杀进程的办法撒,绿色环保无污染),注入dll使用的是插入apc实现的。(注入的dll必须是realse版的。Debug版会出现***错误,全局dll注入貌似也是)插入apc效果不是很好,要有线程有告警状态才执行。 五:线程信息的检测 遍历ThreadList来枚举线程 线程的暂停和唤醒都是通过反汇编获取PsResumeThread和PsSuspendThread直接从r3传来ETHREAD来操作的,通过插入APC来结束线程 六:shadow sdt表的hook检测与恢复 没有采用pdb来解决函数名问题,直接写入xp和03的shandow表函数名(主要是自己的网不稳定,连windbg有时都连不上微软) 七:系统所有的过滤驱动的检测 查看各device下是否挂接有驱动之类的,可直接卸载 八:系统常用回调历程的检测和清除 只检查了PsSetLoadImageNotifyRoutine PsSetCreateThreadNotifyRoutine PsSetCreateProcessNotifyRoutine CmRegisterCallback这几个,至于那个什么shutdown回调不知道是啥玩意,就没搞了,有知道的顺便告诉我下撒,谢谢 九:文件系统fat和ntfs的分发函数检测 直接反汇编fat和ntfs的DriverEntry得到对应的填充分发的偏移,然后和当前已经运行的文件系统的分发相比是否被hook,并附带恢复 十:文件查看功能 自己解析ntfs和fat的结构,来实现列举文件和直接写磁盘删除。附带有普通的删除和发生IRP来删除。不过这里面有点问题,ntfs删除有时把目录给搞坏了,大家凑合着吧, Ntfs网上删除这些操作的代码不多,就是sudami大大的利用ntfs-3g来实现的,看了下,太多了,充满了结构。然后自己对照着系统删除文件时目录的变化来自己实现的。只处理了$BITMAP对应的位清除,父目录的对应文件的索引项的覆盖,删除文件对应的filerecord清0. 另外偷懒时间都没处理,呵呵,y的,一个破时间都都搞好几个字节移来移去的。 十一:常用内核模块钩子的检测和恢复 这里只检测了主要的内核模块nkrnlpa**.exe的.win32k.sys,hal.dll,比对它们的原始文件从而查找eat inline hook,iat hook ,和inline hook。Inline是从TEXT段开始一段位置开始比较的。(有点慢貌似,等待显示扫描完成就好了) 十:应用层进程所加载dll的钩子 应用层钩子检测和内核模块钩子检测原理一样,不过为了能读写别的进程的空间,并没有使用openprocess去打开进程,而是通过KiattachProcess挂靠到当前进程,然后通过在r0直接读写进程空间的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值