1.线性地址的管理
進程空間的地址劃分
線性地址有4G 但未必都能訪問,所以需要記錄,哪些地方分配了
比如你申請了一塊內存在0x12345678的位置大小為4字節,這些都要被記錄總不能在這申請了下次還在這申請
在_EPROCESS.+ 0x11c VadRoot 這是一顆搜索二叉樹,它里面的每一個節點都記錄了一塊被占用的線性地址空間。
VadRoot的類型為_MMVAD
kd> dt _MMVAD nt!_MMVAD+0x000 StartingVpn //當前節點線性地址起始位置(以頁為單位)+0x004 EndingVpn //當前節點線性地址結束位置(以頁為單位)+0x008 Parent //父節點+0x00c LeftChild //左子樹+0x010 RightChild //右子樹+0x014 u //用于標識內存屬性之類的+0x018 ControlArea //控制區+0x01c FirstPrototypePte //指向原型PTE第一個 239+0x020 LastContiguousPte //指向所映射視圖PTE最后一個+0x024 u2 +0x018 ControlArea 控制區:+0x000 Segment //指向段+0x004 DereferenceList +0x00c NumberOfSectionReferences +0x010 NumberOfPfnReferences+0x014 NumberOfMappedViews //反映關聯的內存區對象被映射多少次+0x018 NumberOfSubsections //+0x01a FlushInProgressCount+0x01c NumberOfUserReferences+0x020 u+0x024 FilePointer //文件指針+0x028 WaitingForDeletion+0x02c ModifiedWriteCount+0x02e NumberOfSystemCacheViews +0x024 FilePointer 文件指針:如果這個值為null,當前MMVAD的這塊內存就是一塊物理頁(文件映射來的), 如果不為null就是我們自己分配的內存 nt!_FILE_OBJECT+0x000 Type +0x002 Size +0x004 DeviceObject +0x008 Vpb +0x00c FsContext +0x010 FsContext2 +0x014 SectionObjectPointer +0x018 PrivateCacheMap +0x01c FinalStatus +0x020 RelatedFileObject +0x024 LockOperation +0x025 DeletePending +0x026 ReadAccess +0x027 WriteAccess +0x028 DeleteAccess +0x029 SharedRead +0x02a SharedWrite +0x02b SharedDelete +0x02c Flags +0x030 FileName //這塊內存對應的模塊名稱+0x038 CurrentByteOffset +0x040 Waiters +0x044 Busy +0x048 LastLock +0x04c Lock +0x05c Event +0x06c CompletionContext 所有的內存都可以分成兩類,一類為我們用VirtualAllocEx()申請的內存, 還有一類為文件映射,這個文件可能是.dll/.exe也可能是一個數據的文件上面的都是自己一層一層的找,windbg也提供了一個指令可以幫你類出來所有的節點, !vad 0xxxxx +0x014 u 用于標識內存屬性之類的union{ULONG_PTR LongFlags;MMVAD_FLAGS VadFlags; 一般只用這個成員} u;nt!_MMVAD_FLAGS+0x000 CommitCharge //最大可提供物理頁的數目+0x000 PhysicalMapping +0x000 ImageMap //1:鏡像文件 0:其他+0x000 UserPhysicalPages +0x000 NoChange +0x000 WriteWatch +0x000 Protection //當前內存屬性(讀?寫?執行) //1:READONLY 2:EXECUTE 3:EXECUTE _READ 4:READWITER //5:WRITECOPY 6:EXECUTE_READWITER 7:EXECUTE_WRITECOPY+0x000 LargePages +0x000 MemCommit +0x000 PrivateMemory 1:內存是Private 0:這塊內存是Mapped
《Windows內核原理與實現》- 243頁
Private Memory(私有內存)
通過VirtualAlloc() / VirtualAllocEx()申請的: Private Memory void main() {printf("程序運行了...按下回車開始申請內存");getchar();//參數1:要申請的地址//參數2:大小 (以頁為單位)//參數3:MEM_COMMIT(創建節點也分配物理頁) MEM_RESERVE(只創建節點不分配物理頁)//參數4:訪問權限LPVOID address = VirtualAlloc(NULL, 0x2000, MEM_COMMIT, PAGE_READWRITE);printf("內存地址:%X\n", address);getchar(); }申請前
申請后
堆內存與棧內存
int x = 0x1111;void main() {printf("按下回車開始申請內存");getchar();int y = 0x2222;int* z = (int*)malloc(sizeof(int)*128);printf("全局變量:%X\n", &x);printf("棧:%X\n", &y);printf("堆:%X\n", z);getchar(); }堆就是系統提前替我們申請的一塊很大的內存,相當于批發 當我們用的時候調用malloc(),就是從這些已經分配好的內存中拿出一塊無論是堆還是棧都是系統提前就用VirtualAlloc()分配好的內存, 全局變量是程序編譯完了它就會有一塊屬于自己的地址空間,屬于映射得到的Mapped Memory(映射內存)
通過CreateFileMapping() 映射的: Mapped Memory映射內存分兩類
共享內存
A進程 void main() {//創建物理頁HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUFSIZ, L"共享內存");//將物理頁與線性地址進行映射 LPTSTR lpBuff = (LPTSTR)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFSIZ);*(PDWORD)lpBuff = 0x12345678;printf("A進程寫入地址 內容:%p - %x ", g_lpBuff,*(PDWORD)g_lpBuff);getchar(); }B進程 void main() {HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,L"共享內存");LPTSTR buffer = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS,0,0,BUFSIZ);printf("B進程讀取:%x", *(PDWORD)buffer);getchar(); }共享文件
void main() {HANDLE hFile = CreateFile(L"C:\\XueTr.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, BUFSIZ, NULL);LPTSTR lpBuff = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFSIZ);printf("%x", lpBuff);getchar(); }
共享文件如果A進程改了該映射的文件,所有使用該文件的進程都會被影響。
鏡像文件
HMODULE hModule = ::LoadLibrary("C:\\XueTr.exe");寫拷貝:
修改該模塊里面的數據:系統是不讓你修改這個物理頁的,會映射一份新的物理頁。
你修改的數據會放到一個新的物理頁上,你的線性地址會指向這個新的物理頁。
不修改該模塊里面的數據:多個進程都是指向同一個物理頁
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結