首页
论坛
课程
招聘
[系统底层] [原创]win10 1909逆向(内存区对象(SectionObject)----1.页面支撑的内存区)
2021-4-1 00:40 3404

[系统底层] [原创]win10 1909逆向(内存区对象(SectionObject)----1.页面支撑的内存区)

2021-4-1 00:40
3404

0x0 前言


          逆它纯粹就是想了解win10共享内存的原理,剖析具体的逻辑实现,了解原理后可以扫描检测游戏高地址的注入,下面只会简单介绍一下基础,详细可以参考《windows内核原理与实现》书籍,这里会以展示核心代码为主,将复杂的旁支简化,尽量做到一目了然,里面创建的结构体是根据逆多少写多少的原则赋值,正确性不一定完全正确,能明白意思即可,尽量写的简单,0E异常会另写一个帖子!

          对了这两天无聊顺便写了个WIN10 64位下HOOK内核函数不会触发PG的代码,算是取巧了windows机制,总共100行代码,不VT和不用网上的过PG代码,等这几天找完工作,也会分享出来,下面的Windbg是特意让他触发显示,否则也会和PCHunter显示一样的内容,物理机测试60小时不PG。

0x1 基础(简单介绍,详情参考《windows内核原理与实现》书籍


           内存区对象(SectionObject):被称为文件映射对象。表示可以被两个或更多进程所共享的内存块,当两个进程对同一个内存区对象建立视图时,便发生了对内存区对象的共享。

          内存区对象是一种使用物理内存资源的方式,主要用于进程映像的加载,进程间共享内存,文件映射,缓存管理 等等。

          


         

0x2 创建过程(NtCreateSection)



     这是三环代码,最终会进入内核NtCreateSection。原型如下:

   typedef  NTSTATUS (*ZWCREATESECTION)(

OUT PHANDLE SectionHandle,

IN ACCESS_MASK DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,

IN PLARGE_INTEGER MaximumSize OPTIONAL,

IN ULONG SectionPageProtection,

IN ULONG AllocationAttributes,

IN HANDLE FileHandle OPTIONAL

);

     

当FileHandle和FileObject为NULL的时候,会直接MiCreatePageFileMap创建页面文件支撑的内存区。

MiCreatePageFileMap-->MiChargeCommit【判断提交的页面是否足够】

MyControlArea= ExAllocatePoolWithTag(0x200, 0x58 * Index+sizeof(CONTROL_AREA), 'aCmM');

struct _MyControlArea

{

_CONTROL_AREA ControlArea;

_SUBSECTION Subsection;

LIST_ENTRY SubSectionList;

PRTL_BALANCED_NODE pSubSectionNode; //pSubSectionNode-0x38=_Subsection

PVOID SubSectionNodeMark;

};


NewSegment = ExAllocatePoolWithTag(PagedPool, 0x50, 'geSM') //sizeof(_SEGMENT)+8;

struct _NewSegment

{

_SEGMENT Segment;

PMYSEGMENTEXTEND pSegmentExtend; //这里是在后面通过打开对象的回调产生,MiOpenSection里产生。

};


struct _MYSEGMENTEXTEND

{

PVOID NextMySegmentExtend;

PVOID u2;

PVOID u3;

PEPROCESS Process;

PVOID u5;

LIST_ENTRY ProcessSharedCommitLinks;

PVOID ControlArea;

};


ControlArea->NumberOfSubsections = 1;

ControlArea.ListHead.Blink = &ControlArea.ListHead;

ControlArea.ListHead.Flink = &ControlArea.ListHead;

ControlArea->Segment = NewSegment;

ControlArea->ControlArea.LockedPages = 1;

ControlArea->NumberOfSectionReferences = 1;

ControlArea->NumberOfUserReferences = 1;

if (AllocationAttributes & SEC_BASED) {

ControlArea->u.Flags.Based = 1;

}

if (AllocationAttributes & SEC_RESERVE) {

ControlArea->u.Flags.Reserve = 1;

}

if (AllocationAttributes & SEC_COMMIT) {

ControlArea->u.Flags.Commit = 1;

}

if (AllocationAttributes & SEC_64K_PAGES) {

ControlArea->u.Flags.PageSize64K= 1;

}

ControlArea.FileObjectLock.Value = 0;

memset(NewSegment , 0, sizeof(_SEGMENT));

if (AllocationAttributes & SEC_NOCACHE) {

NewSegment->SegmentFlags.Unused = 1;

}

if (AllocationAttributes & SEC_WriteCombined) {

NewSegment->SegmentFlags.WriteCombined= 1;

}

NewSegment->SegmentFlags.DefaultProtectionMask=ProtectionMask;

NewSegment->u1.CreatingProcessId= KeGetCurrentThread()->ApcState.Process->UniqueProcessId; //被创建进程的pid

NewSegment->SizeOfSegment = (UINT64)NumberOfPtes * PAGE_SIZE;

NewSegment->ControlArea = ControlArea;

NewSegment->TotalNumberOfPtes = NumberOfPtes;

Subsection = (PSUBSECTION)(ControlArea + 1);

Subsection->ControlArea = ControlArea;

Subsection->PtesInSubsection = (ULONG)NumberOfPtes;

Subsection->StartingSector = 0;

Subsection->SubsectionFlags.Protection=ProtectionMask;

if (AllocationAttributes & SEC_COMMIT)

{

PointPte = ExAllocatePoolWithTag(0x80000001, 8 * NumberOfPtes, 'tSmM');

Subsection->SubsectionBase = PointerPte

}

//Subsection->NextSubsection=(ULONG64)Subsection+0x58;

Subsection->NextSubsection=0;

NewSegment->PrototypePte=PointPte;


MiUpdateControlAreaCommitCount(ControlArea, NumberOfPtes);

1.增加内存分区MmPartition.Vp.SharedCommit+=NumberOfPtes;

2.如果ControlArea->FilePointer.Value有值,则ControlArea->Segment->NumberOfCommittedPages += NumberOfPtes;

否则ControlArea->u3.CommittedPageCount+=NumberOfPtes;

3.MiGetSubsectionHoldingCrossPartitionReferences[得到subsection的地址]


MiInitializePrototypePtes(SubsectionBase, NumberOfPtes,SubSection, isNotUsingFileExtents)

初始化SubsectionBase的原型PTE为(_MMPTE):0000200000000080 ,200000000000为MMPTE_SWIZZLE_BIT,0x80为

MiUpdateSystemProtoPtesTree(&ControlArea->subsection.SubsectionNode, Insert);  将susection加入到红黑树节点里。


MiFinishCreateSection(SectionPacket);

1。计算PagedPoolCharge和NonPagedPoolCharge。

2。ObCreateObjectEx( SectionPacket->PreviosMode,  MmSectionObjectType, SectionPacket->ObjectAttributes, SectionPacket->PreviosMode,

ObjectHeader, ObjectBodySize, PagedPoolCharge, NonPagedPoolCharge, &SectionObject, 0);

3。初始化SectionObject(最重要的是将ControlArea放入)。

返回*SectionObject=Section;

MiSectionControlArea(SectionObject)

+0x028 u1               : <anonymous-tag>

+0x000 ControlArea      : Ptr64 _CONTROL_AREA

+0x000 FileObject       : Ptr64 _FILE_OBJECT

+0x000 RemoteImageFileObject : Pos 0, 1 Bit

+0x000 RemoteDataFileObject : Pos 1, 1 Bit

通过2bit这个判断是得到当前ControlArea,还是得到FileObject->SectionObjectPointer->ImageSectionObject/DataSectionObject(CONTROL_AREA)

如果是文件对象另行处理。

插入句柄表,得到句柄,这里会有对象回调MiOpenSection产生一个结构体赋值,上面已有说明

ObInsertObjectEx(SectionObject, 0, DesiredAccess, 0, 0, 0, &Handle);

完成


0x3 映射过程(NtMapViewOfSection)

这是三环代码,最终会进入内核NtMapViewOfSection。

MiMapViewOfSectionCommon 用来判断参数是否合法,并且初始化SectionParameter结构。

status = MiMapViewOfSectionCommon( ProcessHandle, SectionHandle,  0, BaseAddress, ViewSize, SectionOffset, Protect, ZeroBits, PreviousMode,&SectionParameter);

struct _SECTIONPARAMETER

{

PVOID BaseAddress;

PVOID ViewSize;

PVOID SectionOffset;

ULONG ProtectMaskForAccess;

PSECTION SectionObject;

PEPROCESS ProcessObject;

};

MiMapParametersInitialize用来初始化MapParameter的部分参数。

STATUS = MiMapParametersInitialize( &MapParameter, SectionParameter.SectionObject, SectionParameter.ProcessObject,

SectionParameter.BaseAddress, SectionParameter.ViewSize, AllocationType,  Protect, ZeroBits);

部分初始化定义了,部分未初始化的未定义

struct _MAPPARAMETER

{

PVOID u1;

PVOID HighestUserAddress;

PVOID Alignments;   //默认64K,如果ControlArea不是PhysicalMemory 或者 Image,判断AllocationType大页是2M,DOS是4KB

PVOID ViewSize;

PVOID u5;

ULONG AllocationType;

ULONG Win32Protect;

PVOID IsErrorForAllocationType;

PVOID u8;

PVOID u9;

char *mapFlags; //1代表BaseAddress有值

PVOID u11;

PVOID TargetProcess;

PVOID CurrentProcess;

char PreviousMode;

PVOID u15;

PVOID u16;

};

MiMapViewOfSection--->MiMapViewOfDataSection

1。MiCheckPurgeAndUpMapCount(&ControlArea); //检查清除操作是否正在进行,如果正进行,等待清除完成。增加此控制区域的映射视图数。

++ControlArea->NumberOfMappedViews;

++ControlArea->NumberOfUserReferences;

2。TotalNumberOfPtes = MiGetControlAreaPtes(ControlArea)  //从segment得到TotalNumberOfPtes

3。SubSection = MiLocatePagefileSubsection(&zControlArea->Subsection, pSectionIndex);// 定位到页文件的节区

4。MiReferenceActiveSubsection(vvSubSection, CallerId, OldIrql);

5。vad = ExAllocatePoolWithTag(0x200, 0x88ui64, ' daV');// 创建VAD

TheFirstPrototypePte = &Subsection->SubsectionBase[PteOffset];

vad->Subsection = SubSection;

vad->FirstPrototypePte = TheFirstPrototypePte;

vad->Core.VadNode.ParentValue = 0xFFFFFFFFFFFFFFFE;

vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);

vad->u.VadFlags.Protection = ProtectionMask; //PAGE_READWRITE

vad->Core.PushLock.Value = 0;

vad->FileObject = FileObject; //FileObject通过Section的U1得到。0或者具体地址

根据Eprocess的VmTopDown来调用:MiFindEmptyAddressRangeDown或者MiFindEmptyAddressRange


6。MiFindEmptyAddressRange实现

RtlFindClearBitsEx(VmStruct, NumberToFind, HintIndex)

HintBitMap=0xffff89854032f000+0x542/64*8=0xffff89854032f0a8;


not 00007fff`800fffff = FFFF80007FF00000

bsf     rcx, r8   从右向左扫描字或双字操作数oprd2中第一个含"1"的位, 并把扫描到的第一个含'1'的位的位号送操作数rcx[0x14]

StartPosition=0x542&~0xF+rcx=0x554

VirtualAddr = (StartPosition + 8 * (VmStruct->BitMap.Buffer - gVmBitMapBase))  * 64K


7。VAD继续赋值

BaseAddress = VirtualAddr;

EndArress = mapParameter->ViewSize + VirtualAddr - 1;

StartVpn = BaseAddress >> 12;

EndVpn  = EndArress >> 12;

vad->Core.StartingVpnHigh = BaseAddress >> 0x2C;

vad->Core.EndingVpnHigh  =  EndArress >> 0x2C;

vad->Core.StartingVpn =StartVpn;

vad->Core.EndingVpn = EndVpn;

PteOffset += (ULONG)(Vad->EndingVpn - Vad->StartingVpn)

vad->LastContiguousPte = MiComputeContiguousSubsectionPte(SubSection, PteOffset);

if (ControlArea->FilePointer == NULL)

{

MiInsertSharedCommitNode(ControlArea, TargetProcess, a3)

}

8。MiInsertVadCharges(vad, TargetProcess);主要实现如下:

PsChargeProcessNonPagedPoolQuota

PspChargeQuota

RtlSetBitsEx(VmStruct, StartingIndex, NumberToSet);  //修改工作集里虚拟地址的bitmap

9。MiGetWsAndInsertVad(vad);

1.将VAD加入二叉树。

2.添加VAD参数。

vad->VadsProcess = (Process | 1);

vad->ViewLinks.Flink=&ControlArea->ListHead;

vad->ViewLinks.Blink=&ControlArea->ListHead;

ControlArea->ListHead.Flink=&vad->ViewLinks;

ControlArea->ListHead.Blink=&vad->ViewLinks;

10。VM工作集里的bitMap,需要将0x542+1

segment->FirstMappedVa=BaseAddress;

MiAdvanceVadHint(StartVpn, EndVpn, vmStruct); //VM工作集做一些设置。

完成


[公告] 2021 KCTF 春季赛 防守方征题火热进行中!

收藏
点赞3
打赏
分享
最新回复 (18)
雪    币: 29
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
0x太上 活跃值 2021-4-1 00:48
2
0
6666
雪    币: 1866
活跃值: 活跃值 (497)
能力值: ( LV3,RANK:10 )
在线值:
发帖
回帖
粉丝
のばら 活跃值 2021-4-1 00:49
3
0
厉害
雪    币: 43
活跃值: 活跃值 (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
云清-sky 活跃值 2021-4-1 00:51
4
0
厉害
雪    币: 249
活跃值: 活跃值 (69)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Besttwuya 活跃值 2021-4-1 02:32
5
0
牛皮豆豆
雪    币: 458
活跃值: 活跃值 (288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
青丝梦 活跃值 2021-4-1 08:51
6
0
豆总牛逼
雪    币: 856
活跃值: 活跃值 (1180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
丿一叶知秋 活跃值 2021-4-1 09:33
7
0
bitmap利用的地方,我感觉是挺多的,比如 UMAB,可以先导VirtualAllocEx/NUAM,申请空间的范畴
雪    币: 2791
活跃值: 活跃值 (2780)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-4-1 10:32
8
0
丿一叶知秋 bitmap利用的地方,我感觉是挺多的,比如 UMAB,可以先导VirtualAllocEx/NUAM,申请空间的范畴
如果不嫌麻烦,利用点很多。
雪    币: 705
活跃值: 活跃值 (476)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Oday小斯 活跃值 2021-4-1 10:34
9
0
感谢分享
雪    币: 1377
活跃值: 活跃值 (405)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dearfuture 活跃值 2021-4-1 10:37
10
0
感觉高地址注入就是申请一块非分页内存,然后手动映射dll,然后把内存对应的各级页表的U/S位改成1。因为是手动映射,所以不存在什么section,controlarea,原型pte,vad之类的玩意。那么应该怎么用这个来检测高地址注入?
雪    币: 1377
活跃值: 活跃值 (405)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dearfuture 活跃值 2021-4-1 11:27
11
0

hook内核函数不PG,原理是不是给目标进程的内核函数地址挂一个新的物理页来hook?因为不同进程的pml4前XX项都是相同的,所以要从pml4这一层开始构造各级页表来避免内核地址共享?只要System进程的内核函数地址挂的还是原来的物理页,就不会PG?

最后于 2021-4-1 11:27 被dearfuture编辑 ,原因:
雪    币: 2791
活跃值: 活跃值 (2780)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-4-1 11:48
12
0
dearfuture 感觉高地址注入就是申请一块非分页内存,然后手动映射dll,然后把内存对应的各级页表的U/S位改成1。因为是手动映射,所以不存在什么section,controlarea,原型pte,vad之类的玩意。 ...
手法
雪    币: 2791
活跃值: 活跃值 (2780)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-4-1 11:55
13
0
dearfuture hook内核函数不PG,原理是不是给目标进程的内核函数地址挂一个新的物理页来hook?因为不同进程的pml4前XX项都是相同的,所以要从pml4这一层开始构造各级页表来避免内核地址共享?只要Syste ...
原理如此,但样本测试过少,不敢保证说肯定过PG
雪    币: 9734
活跃值: 活跃值 (2474)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
hzqst 活跃值 3 2021-4-1 12:16
14
0
学技术打豆豆 原理如此,但样本测试过少,不敢保证说肯定过PG
只要不碰pg自己保存的关键函数(kebugcheckex那些什么的)一切都好说,非关键函数只会在workitem里面扫描,保存在context中的关键函数是在dpc下刚进context时就验证的,改了就炸
雪    币: 1377
活跃值: 活跃值 (405)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dearfuture 活跃值 2021-4-1 13:11
15
0
hzqst 只要不碰pg自己保存的关键函数(kebugcheckex那些什么的)一切都好说,非关键函数只会在workitem里面扫描,保存在context中的关键函数是在dpc下刚进context时就验证的,改了 ...
dpc执行时的cr3有可能是目标进程的cr3?
雪    币: 9734
活跃值: 活跃值 (2474)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
hzqst 活跃值 3 2021-4-1 16:42
16
0
dearfuture dpc执行时的cr3有可能是目标进程的cr3?
dpc执行的时候cr3可以是任何进程,dpc是进程/线程无关的
雪    币: 2791
活跃值: 活跃值 (2780)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-4-1 16:54
17
0
hzqst 只要不碰pg自己保存的关键函数(kebugcheckex那些什么的)一切都好说,非关键函数只会在workitem里面扫描,保存在context中的关键函数是在dpc下刚进context时就验证的,改了 ...
666
雪    币: 66
活跃值: 活跃值 (267)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tmflxw 活跃值 2021-4-2 03:50
18
0
hzqst dpc执行的时候cr3可以是任何进程,dpc是进程/线程无关的
意思是不要hook dpc里保护的地方那这种方法就可以,否则在hook的时候刚好遇到dpc在目标进程cr3就gg
雪    币: 635
活跃值: 活跃值 (752)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
Chords 活跃值 2021-4-3 15:50
19
0
dearfuture 感觉高地址注入就是申请一块非分页内存,然后手动映射dll,然后把内存对应的各级页表的U/S位改成1。因为是手动映射,所以不存在什么section,controlarea,原型pte,vad之类的玩意。 ...
把pte的用户位修改为1 既对r3 可见,但是这个在1809-1909不适用,因为windows在1809开始增加了校验至于2004 好像已经去掉了这个校验了
游客
登录 | 注册 方可回帖
返回