切换进程上下文。正如我所料,这个函数只是重新加载CR3寄存器,再加上一点相关的操作。例如,用IopmOffset域的值建立TSS中的I/O位图的偏移。还必需将选择子的值加载到ldt(只用于VDM session)。
/************************* SwapContext ******************************/
SwapContext(NextThread,CurThread,WaitIrql)
{
NextThread.State=ThreadStateRunning; //2
KPCR.DebugActive=NextThread.DebugActive;
cli();
//Save Stack
CurThread.KernelStack=esp;
//Set stack
KPCR.StackLimit=NextThread.StackLimit;
KPCR.StackBase=NextThread.InitialStack;
tmp=NextThread.InitialStack-0x70;
newcr0=cr0&0xfffffff1|NextThread.NpxState|*(tmp+0x6c);
if(newcr0!=cr0)reloadcr0();
if(!*(tmp-0x1c)&0x20000)tmp-=0x10;
TSS=KPCB.TSS;
TSS->ESP0=tmp;
//set pTeb
KPCB.Self=NextThread.pTeb;
esp=NextThread.KernelStack;
sti();
//correct GDT
GDT=KPCB.GDT;
WORD(GDT->0x3a)=NextThread.pTeb;
BYTE(GDT->0x3c)=NextThread.pTeb>>16;
BYTE(GDT->0x3f)=NextThread.pTeb>>24;
//if we must swap processes, do it (like KiSwapProcess)
if(CurThread.ApcState.Process!=NextThread.ApcState.Process)
{
//******** like KiSwapProcess
}
NextThread->ContextSwitches++;
KPCB->KeContextSwitches++;
if(!NextThread->ApcState.KernelApcPending)return 0;
//popf;
// jnz HalRequestSoftwareInterrupt// return 0
return 1;
}
切换堆栈,修正GDT,以使FS寄存器指向TEB。如果线程属于当前进程,则不进行上下文切换。否则,进行的操作和KiSwapProcess中的大致差不多。
为了一致,我给出KeDetachProcess的原型。
KeDetachProcess(EPROCESS *Process,Irql);
我们看到——这些函数的伪码实际上完全描述出了操作系统的上下文切换。总的说来,代码分析表明,理解OS的主要途径就是要知道它的内部结构。
0a.某些未公开的内存管理器函数
==========================================================
SP3的ntoskrnl.exe的内存管理器导出了以下符号:
467 1D0 00051080 MmAdjustWorkingSetSize
468 1D1 0001EDFA+MmAllocateContiguousMemory
469 1D2 00051A14+MmAllocateNonCachedMemory
470 1D3 0001EAE8+MmBuildMdlForNonPagedPool
471 1D4 000206BC MmCanFileBeTruncated
472 1D5 0001EF5A+MmCreateMdl
473 1D6 0002095C MmCreateSection
474 1D7 00021224 MmDbgTranslatePhysicalAddress
475 1D8 000224AC MmDisableModifiedWriteOfSection
476 1D9 000230C8 MmFlushImageSection
477 1DA 0001FA9C MmForceSectionClosed
478 1DB 0001EEA0+MmFreeContiguousMemory
479 1DC 00051AFE+MmFreeNonCachedMemory
480 1DD 0001EEAC+MmGetPhysicalAddress
481 1DE 00024028 MmGrowKernelStack
482 1DF 0004E144 MmHighestUserAddress
483 1E0 0002645A+MmIsAddressValid
484 1E1 00026CD8+MmIsNonPagedSystemAddressValid
485 1E2 0001F5D8 MmIsRecursiveIoFault
486 1E3 00026D56+MmIsThisAnNtAsSystem
487 1E4 000766C8+MmLockPagableDataSection
488 1E5 000766C8 MmLockPagableImageSection
489 1E6 0001F160+MmLockPagableSectionByHandle
490 1E7 0001ED18+MmMapIoSpace
491 1E8 0001EB74+MmMapLockedPages
492 1E9 0001F5F6 MmMapMemoryDumpMdl
493 1EA 00076A14 MmMapVideoDisplay
494 1EB 0005206C MmMapViewInSystemSpace
495 1EC 00079B0E MmMapViewOfSection
496 1ED 0007A7EE+MmPageEntireDriver
497 1EE 0001E758+MmProbeAndLockPages
498 1EF 00026D50+MmQuerySystemSize
499 1F0 00052A8A+MmResetDriverPaging
500 1F1 0004E0A4 MmSectionObjectType
501 1F2 00079D28 MmSecureVirtualMemory
502 1F3 0001EFCE MmSetAddressRangeModified
503 1F4 0007684E MmSetBankedSection
504 1F5 0001EF2C+MmSizeOfMdl
505 1F6 0004E0A0 MmSystemRangeStart
506 1F7 0001F516+MmUnlockPagableImageSection
507 1F8 0001EA16+MmUnlockPages
508 1F9 0007669A+MmUnmapIoSpace
509 1FA 0001ECA8+MmUnmapLockedPages
510 1FB 00076A2E MmUnmapVideoDisplay
511 1FC 00052284 MmUnmapViewInSystemSpace
512 1FD 0007AFE4 MmUnmapViewOfSection
513 1FE 0007A00A MmUnsecureVirtualMemory
514 1FF 0004DDCC MmUserProbeAddress
这里的符号'+'表示函数在DDK中有记载。我这里给出某些未公开函数的原型。
// 调整working set的大小.
NTOSKRNL NTSTATUS MmAdjustWorkingSetSize(
DWORD MinimumWorkingSet OPTIONAL, // if both == -1
DWORD MaximumWorkingSet OPTIONAL, // empty working set
PVM Vm OPTIONAL);
//can file be truncated???
NTOSKRNL BOOLEAN MmCanFileBeTruncated(
PSECTION_OBJECT_POINTERS SectionPointer, // see FILE_OBJECT
PLARGE_INTEGER NewFileSize
);
// create section. NtCreateSection call this function...
NTOSKRNL NTSTATUS MmCreateSection (
OUT PVOID *SectionObject,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize,
IN ULONG SectionPageProtection,//PAGE_XXXX
IN ULONG AllocationAttributes,//SEC_XXX
IN HANDLE FileHandle OPTIONAL,
IN PFILE_OBJECT File OPTIONAL
);
typedef enum _MMFLUSH_TYPE {
MmFlushForDelete,
MmFlushForWrite
} MMFLUSH_TYPE;
NTOSKRNL BOOLEAN MmFlushImageSection (
IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
IN MMFLUSH_TYPE FlushType
);
NTOSKRNL DWORD MmHighestUserAddress; // 一般为0x7ffeffff
NTOSKRNL BOOLEAN MmIsRecursiveIoFault();
//其代码
#define _MmIsRecursiveIoFault() ( \
(PsGetCurrentThread()->DisablePageFaultClustering) | \
(PsGetCurrentThread()->ForwardClusterOnly) \
)
NTOSKRNL POBJECT_TYPE MmSectionObjectType; //标准的Section对象
NTOSKRNL DWORD MmSystemRangeStart; //一般为0x80000000
NTOSKRNL DWORD MmUserProbeAddress; //一般为0x7fff0000
NTOSKRNL PVOID MmMapVideoDisplay( // для i386 враппер в MmMapIoSpace
IN PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN CacheEnable
);
NTOSKRNL VOID MmUnmapVideoDisplay ( // для i386 враппер в MmUnmapIoSpace
IN PVOID BaseAddress,
IN ULONG NumberOfBytes
);
// 将frame的范围标记为更改并进行相应的操作
NTOSKRNL VOID MmSetAddressRangeModified(
PVOID StartAddress,
DWORD Length
);
// 在NtMapViewOfSection中调用
typedef enum _SECTION_INHERIT {
ViewShare=1;
ViewUnmap=2;
}SECTION_INHERIT;
NTOSKRNL NTSTATUS MmMapViewOfSection(
PVOID pSection,
PEPROCESS pProcess,
OUT PVOID *BaseAddress,
DWORD ZeroBits,
DWORD CommitSize,
OUT PLARGE_INTEGER SectionOffset OPTIONAL,
OUT PDWORD ViewSize,
SECTION_INHERIT InheritDisposition,
DWORD AllocationType,
DWORD ProtectionType
);
NTOSKRNL NTSTATUS MmUnmapViewOfSection(
PEPROCESS Process,
PVOID Address
);
PVOID MmLockPagableImageSection(
PVOID AddressWithinImageSection // same entry as MmLockPagableDataSection
);
// 减少StackLimit(堆栈增长)
NTSTATUS MmGrowKernelStack(
PVOID CurESP //栈顶的地址
);
I talk to the wind
My words are all carried away
I talk to the wind
The wind does not hear
The wind cannot hear.
King Crimson'69 -I Talk to the Wind
0b.结语
=============
就到这里吧。如果综合的来看所有这些描述,对内存管理器多少可以得到一些概念。遗憾的是,这些东西还远不能称之为完整。内存管理器,大概是最复杂和最重要的内核组件,对其要进行完整的描述,我还得深挖不止十个八个的函数。但是主要的基本的东西我这里都写到了。对于进一步反汇编内核来说,这些应该是很有帮助的吧,谁知道呢... ;)
Best Regards, Peter Kosy aka Gloomy.
Melancholy Coding '2001.
mailto:gl00my@mail.ru
P.S. 我知道我的“大作”不可避免的会有错误。我将非常高兴的听取批评和建议。
附录
0c.某些未公开的系统调用
==================================================
这里我描述了一些有用的Zw/Nt函数,这些函数可以在USER模式下或是驱动程序中调用(Zw类的)。几乎所有这些函数都来自于
Коберниченко的“Недокументированные возмождности Windows NT”一书。再加上Working Set结构体的值,就可以描述用于NtQueryVirtualMemory的MEMORY_WORKING_SET_INFORMATION。
NTSYSAPI NTSTATUS NTAPI NtAllocateVirtualMemory(
HANDLE Process,
OUT PVOID *BaseAddr,
DWORD ZeroBits,
OUT PDWORD RegionSize,
DWORD AllocationType,// MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN
DWORD Protect); // PAGE_XXXX...
NTSYSAPI NTSTATUS NTAPI NtFreeVirtualMemory(
HANDLE Process,
OUT PVOID* BaseAddr,
OUT PULONG RegionSize,
DWORD FreeType //MEM_DECOMMIT|MEM_RELEASE
);
NTSYSAPI NTSTATUS NTAPI NtCreateSection(
OUT PHANDLE Section,
ACCESS_MASK DesirdAccess, //SECTION_MAP_XXX...
POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
PLARGE_IBTEGER MaximumSize OPTIONAL,
DWORD SectionPageProtection, //PAGE_...
DWORD AllocationAttributes, //SEC_XXX
HANDLE FileHandle OPTIONAL // NULL - pagefile
);
typedef enum _SECTION_INHERIT {
ViewShare=1;
ViewUnmap=2;
}SECTION_INHERIT;
NTSYSAPI NTSTATUS NTAPI NtMapViewOfSection(
HANDLE Section,
HANDLE Process,
OUT PVOID *BaseAddress,
DWORD ZeroBits,
DWORD CommitSize,
OUT PLARGE_INTEGER SectionOffset OPTIONAL,
OUT PDWORD ViewSize,
SECTION_INHERIT InheritDisposition,
DWORD AllocationType, //MEM_TOP_DOWN,MEM_LARGE_BAGE,MEM_AUTO_ALIGN=0x40000000
DWORD ProtectionType // PAGE_...
);
#define UNLOCK_TYPE_NON_PRIVILEGED 0x00000001L
#define UNLOCK_TYPE_PRIVILEGED 0x00000002L
NTSYSAPI NTSTATUS NTAPI NtLockVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID *RegionAddress,
IN OUT PULONG RegionSize,
IN ULONG UnlockTypeRequired
);
NTSYSAPI NTSTATUS NTAPI NtUnlockVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID *RegionAddress,
IN OUT PULONG RegionSize,
IN ULONG UnlockTypeRequiested
);
NTSYSAPI NTSTATUS NTAPI NtReadVirtualMemory(
IN HANDLE ProcessHandle,
IN PVOID StartAddress,
OUT PVOID Buffer,
IN ULONG BytesToRead,
OUT PULONG BytesReaded OPTIONAL
);
NTSYSAPI NTSTATUS NTAPI NtWriteVirtualMemory(
IN HANDLE ProcessHandle,
IN PVOID StartAddress,
IN PVOID Buffer,
IN ULONG BytesToWrite,
OUT PULONG BytesWritten OPTIONAL
);
NTSYSAPI NTSTATUS NTAPI NtProtectVirtualMemory(
IN HANDLE ProcessHandle,
IN OUT PVOID *RegionAddress,
IN OUT PULONG RegionSize,
IN ULONG DesiredProtection,
OUT PULONG OldProtection
);
NTSYSAPI NTSTATUS NTAPI NtFlushVirtualMemory(
IN HANDLE ProcessHandle,
IN PVOID* StartAddress,
IN PULONG BytesToFlush,
OUT PIO_STATUS_BLOCK StatusBlock
);
typedef enum _MEMORYINFOCLASS {
MemoryBasicInformation,
MemoryWorkingSetInformation,
// 还有class 2 - 这是VAD中的信息, 我目前还不完全了解VAD结构体,也就不能写出相应的INFO结构。
} MEMORYINFOCLASS;
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
ULONG AllocationProtect;
ULONG RegionSize;
ULONG State;
ULONG Protect;
ULONG Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
#define WSFRAMEINFO_SHARED_FRAME 0x100
#define WSFRAMEINFO_INTERNAL_USE 0x4
#define WSFRAMEINFO_UNKNOWN 0x3
typedef struct _MEMORY_WORKING_SET_INFORMATION {
ULONG SizeOfWorkingSet;
DWORD WsEntries[ANYSIZE_ARRAY]; // is Page VA | WSFRAMEINFO...
} MEMORY_ENTRY_INFORMATION, *PMEMORY_ENTRY_INFORMATION;
NTSYSAPI NTSTATUS NTAPI NtQueryVirtualMemory(
IN HANDLE ProcessHandle,
IN PVOID RegionAddress,
IN MEMORYINFOCLASS MemoryInformationClass,
IN PVOID VirtualMemoryInfo,
IN ULONG Length,
OUT PULONG ActualLength OPTIONAL
);
0d.附注及代码分析草稿
==========================================
**** К MmCreateProcessAddressSpace ... ****
=============================================
__fastcall MiTotalCommitLimit(PVOID pProcess, DWORD NumOfPages); // edx:ecx
有statistic
dd MmTotalCommitLimit
dd MmTotalCommitedPages
如果NumOfPages+MmTotalCommitedPages不超过Limit - 一切OK,并只是简单的修正statistic.
否则开始线程间的协作。
选择time out值(如果请求>=10页,则为20秒),否则为-1秒。接着填充某个结构体,大概是这个样子:
typedef struct _REQUEST_FOR_COMMITED_MEMORY{
LIST_ENTRY ListEntry;
DWORD PagesToCommit;
DWORD Result;
KSEMAPHORE Semaphore;
}_REQUEST_FOR_COMMITED_MEMORY;
这个结构体(或链表的元素)被插入到全局结构体中的全局链表ListOfRequest:
[Pre List Item]<->[Our List Item]<->[ListOfRequest]
typedef struct _COMMIT_MEMORY_REQUEST_LIST{
KSEMAPHORE CommitMemorySemaphore;
LIST_ENTRY ListOfRequest;
}COMMIT_MEMORY_REQUEST_LIST;
之后对CommitMemorySemaphore使用KeReleaseSemaphore并等待REQUEST_FOR_COMMITED_MEMORY中带有time out的信号量。
如果未超出time out并因此Result不为0,则再校验一次Limit并输出OK(如果limit有问题——则所有都重新开始)。如果结果为0,MiCouseOverCommitPopup。如果发生了time out,分析如下:
如果ListOfReques.Flink==&ListOfReques.Flink,也就是说所有的请求都在队列的尾部,则再一次等待信号量——并且已经没有time out了,因为不是我们的问题;)
如果ListOfReques.Flink==&RequestForCommitedMemory.ListEntry,就是说队列中的下一个是我们的请求(???)。则从队列中收回请求,因为
是从我们这里来的。
现在来看我们想看的几个页。如果>=10则MiCouseOverCommitPopup,否则MiChargeCommitmentCantExpand,之后输出。
所有的操作都需要cli sti,同时使用FastMutex(进程的10ch偏移),在进程创建时调用这个函数不会进行此操作。
现在,MiCouseOverCommitPopup(PagesNum,CommitTotalLimitDelta);又做些什么呢——如果我们想要页数大于128——则ExRaiseStatus(STATUS_COMMITMENT_LIMIT); 如果小于则IoRaiseInformationalHardError(STATUS_COMMITMENT_LIMIT,0,0);(这些函数都是公开的)。如果成功调用最后一个函数——则累加statistic:
MiOverCommitCallCount++;
MmTotalCommitLimit+=CommitTotalLimitDelta;
MmExtendedCommit+=CommitTotalLimitDelta;
MmTotalCommittedPages+=PagesNum;
且不修正 MmPeakCommintment;
如果不成功但MiOverCommitCallCount==0,所有都等于statistic,否则ExRaiseStatus(STATUS_COMMITMENT_LIMIT);
辅助函数:
DWORD NTOSKRNL RtlRandom(PDWORD Seed);
不奇怪,这个函数没有公开。该函数使用一个128个DWORD的表。在操作之后被此表和Seed被修正。可以看到,这给出了最大周期。
如果有两个event
MmAvailablePagesEventHigh 和
MmAvailablePagesEventHigh.
MiSectionInitialization:
MmDereferenceSegmentHeader: это структура описанная выша с добавленным
spinlock сверху.
创建线程MiDereferenceSegmentThread
PsChargePoolQuota(PVOID Process,DWORD Type(NP/P),DWORD Charge);
[TO DO] -->> MmInfoCounters!!!! 使用相应的NtQueryInfo...可以获得非常多有用的信息,ПОСМОТРЕТЬ!!!
---------------------------------------------------------------------------
(c)Gloomy aka Peter Kosyh, Melancholy Coding'2001
http://gloomy.cjb.net
mailto:gl00my@mail.ru
董岩 译
http://greatdong.blog.edu.cn








