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工作集做一些设置。
完成
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界