[{"data":1,"prerenderedAt":581},["ShallowReactive",2],{"page-\u002Fsystem\u002Fwindows\u002F会话空间":3},{"id":4,"title":5,"body":6,"description":568,"extension":569,"meta":570,"navigation":576,"path":577,"seo":578,"stem":579,"__hash__":580},"content\u002Fsystem\u002Fwindows\u002F会话空间.md","会话空间",{"type":7,"value":8,"toc":561},"minimark",[9,18,22,34,43,66,69,86,89,126,129,134,141,212,234,237,240,248,251,254,257,260,266,273,279,282,307,340,354,360,373,393,417,431,450,453,459,462,480,486,489,500,506,509,512,529,534,554,557],[10,11,12,13,17],"p",{},"“会话空间”（Session Space）是 Windows 内核里一块",[14,15,16],"strong",{},"按用户会话隔离的私有内存区域","。",[19,20,21],"h2",{"id":21},"历史",[10,23,24,25,29,30,33],{},"在 Windows XP 之前，Windows 是单用户的。但后来微软要搞“多用户同时登录”（比如远程桌面 RDP，或者快速用户切换）。如果两个用户同时登录，他们各自都会打开桌面、运行 ",[26,27,28],"code",{},"explorer.exe","、运行各种 GUI 软件。GUI 相关的内核代码（比如 ",[26,31,32],{},"Win32k.sys","，负责画窗口、处理鼠标键盘）如果在内存里只有一份，两个用户的数据就会互相覆盖，直接导致系统崩溃。",[10,35,36,37,40,41],{},"为了解决这个问题，微软把",[14,38,39],{},"内核地址空间","切了一块出来，叫做",[14,42,5],{},[44,45,46,50,59],"ul",{},[47,48,49],"li",{},"每个登录的用户（Session）都有自己专属的会话空间。",[47,51,52,53,55,56,58],{},"用户 A 的 ",[26,54,32],{}," 和用户 B 的 ",[26,57,32],{}," 虽然代码段共享，但数据段（比如窗口句柄表、桌面堆）是物理隔离的。",[47,60,61,62,65],{},"你平时用 ",[26,63,64],{},"Ctrl+Shift+Esc"," 看到的 Session 0、Session 1，就是不同的会话。",[19,67,68],{"id":68},"里面装了什么",[10,70,71,72,77,78,81,82,85],{},"会话空间主要由一个极其核心的结构体来管理：",[14,73,74],{},[26,75,76],{},"_MM_SESSION_SPACE","（内存管理器会话空间）。",[79,80],"br",{},"\n这个结构体就像一个“集装箱”，里面装着该会话所有的家当。你可以用 WinDbg 在内核调试下输入 ",[26,83,84],{},"dt nt!_MM_SESSION_SPACE"," 查看它。",[10,87,88],{},"里面最核心的几个部分包括：",[90,91,92,98,108,120],"ol",{},[47,93,94,97],{},[14,95,96],{},"Session (ID)","：当前是第几号会话。",[47,99,100,103,104,107],{},[14,101,102],{},"ProcessList","：",[14,105,106],{},"（划重点！）"," 这是一个双向链表，该会话里所有的进程，都会被挂在这个链表上。",[47,109,110,113,114,119],{},[14,111,112],{},"ImageFile","：指向的通常是 ",[14,115,116],{},[26,117,118],{},"win32k.sys","（负责 Windows 图形和窗口管理的内核驱动）",[47,121,122,125],{},[14,123,124],{},"PagedPool","：该会话专属的换页内存池（GUI 对象通常在这里分配）。",[10,127,128],{},"我会在这里顺便把windbg的相关操作也一起讲了。",[130,131,133],"h3",{"id":132},"通过windbg查看processlist","通过windbg查看ProcessList",[10,135,136,137,140],{},"在windows内核调试中，nt是内核主文件ntoskrnl.exe的简称，整个系统的核心结构体比如_EPROCESS_ 等在这个符号库、.pdb文件内，我们输入 ",[26,138,139],{},"dt nt!_EPROPSS"," 查结构体的时候windbg实际上就是在nt这个模块的符号库里查询。",[142,143,148],"pre",{"className":144,"code":145,"language":146,"meta":147,"style":147},"language-dbg shiki shiki-themes github-light github-dark","kd> .reload \u002Ff nt\nSYMSRV:  BYINDEX: 0x3\n         C:\\ProgramData\\Dbg\\sym\n         ntkrnlmp.pdb\n         C29EBFB06B78B3C020DCA66D99713F9E1\nSYMSRV:  UNC: C:\\ProgramData\\Dbg\\sym\\ntkrnlmp.pdb\\C29EBFB06B78B3C020DCA66D99713F9E1\\msfz0\\ntkrnlmp.pdb - path not found\nSYMSRV:  PATH: C:\\ProgramData\\Dbg\\sym\\ntkrnlmp.pdb\\C29EBFB06B78B3C020DCA66D99713F9E1\\ntkrnlmp.pdb\nSYMSRV:  RESULT: 0x00000000\nDBGHELP: nt - public symbols  \n        C:\\ProgramData\\Dbg\\sym\\ntkrnlmp.pdb\\C29EBFB06B78B3C020DCA66D99713F9E1\\ntkrnlmp.pdb\n","dbg","",[26,149,150,158,164,170,176,182,188,194,200,206],{"__ignoreMap":147},[151,152,155],"span",{"class":153,"line":154},"line",1,[151,156,157],{},"kd> .reload \u002Ff nt\n",[151,159,161],{"class":153,"line":160},2,[151,162,163],{},"SYMSRV:  BYINDEX: 0x3\n",[151,165,167],{"class":153,"line":166},3,[151,168,169],{},"         C:\\ProgramData\\Dbg\\sym\n",[151,171,173],{"class":153,"line":172},4,[151,174,175],{},"         ntkrnlmp.pdb\n",[151,177,179],{"class":153,"line":178},5,[151,180,181],{},"         C29EBFB06B78B3C020DCA66D99713F9E1\n",[151,183,185],{"class":153,"line":184},6,[151,186,187],{},"SYMSRV:  UNC: C:\\ProgramData\\Dbg\\sym\\ntkrnlmp.pdb\\C29EBFB06B78B3C020DCA66D99713F9E1\\msfz0\\ntkrnlmp.pdb - path not found\n",[151,189,191],{"class":153,"line":190},7,[151,192,193],{},"SYMSRV:  PATH: C:\\ProgramData\\Dbg\\sym\\ntkrnlmp.pdb\\C29EBFB06B78B3C020DCA66D99713F9E1\\ntkrnlmp.pdb\n",[151,195,197],{"class":153,"line":196},8,[151,198,199],{},"SYMSRV:  RESULT: 0x00000000\n",[151,201,203],{"class":153,"line":202},9,[151,204,205],{},"DBGHELP: nt - public symbols  \n",[151,207,209],{"class":153,"line":208},10,[151,210,211],{},"        C:\\ProgramData\\Dbg\\sym\\ntkrnlmp.pdb\\C29EBFB06B78B3C020DCA66D99713F9E1\\ntkrnlmp.pdb\n",[10,213,214,215,218,219,222,223,225,226,229,230,233],{},"在这里我们找到了基础结构：如 ",[26,216,217],{},"_EPROCESS","、",[26,220,221],{},"_UNICODE_STRING"," 。删除了大量深层内部结构体包括你 ",[26,224,76],{},"，只保留偏移量。",[26,227,228],{},"msfz0"," 代表微软的压缩格式（MSFZ）。第一次下载时它会尝试找压缩包，没找到就直接读了解压后的 ",[26,231,232],{},"ntkrnlmp.pdb","。这属于正常现象，不用管。",[10,235,236],{},"process是windbg的查看进程的扩展命令，比dt好用很多，他会自动把底层十六进制数据翻译成人能看懂的摘要（比如进程名、PID、父进程等）。 第一个参数是进程ID，windows里 4 是System进程， 第二个参数是详细程度 0 表示只显示最基础的摘要信息，7就是进程的所有线程、堆栈、句柄都显示出来。",[10,238,239],{},"我们输出的!process 4 0 :",[142,241,246],{"className":242,"code":244,"language":245},[243],"language-text","kd> !process 0 0 explorer.exe\nPROCESS ffffa381459b1080\n    SessionId: none  Cid: 071c    Peb: 006a0000  ParentCid: 0994\n    DirBase: 139571002  ObjectTable: ffffdb0cb3e8c0c0  HandleCount: 3127.\n    Image: explorer.exe\n    \nkd> !process 0 0 explorer.exe\nPROCESS ffffa381459b1080\n    SessionId: none  Cid: 071c    Peb: 006a0000  ParentCid: 0994\n    DirBase: 139571002  ObjectTable: ffffdb0cb3e8c0c0  HandleCount: 3127.\n    Image: explorer.exe\n","text",[26,247,244],{"__ignoreMap":147},[10,249,250],{},"!process 0 0 explorer.exe就是在所有进程里把资源管理器拿出来，第一个参数 填 0 就是所有进程，这个很方便，要记住。",[10,252,253],{},"接下来的一步，我们需要去拿到会话指针，为了拿到真实的指针，就要去用户桌面环境下的进程，而文件管理器就是最标准的常驻GUI进程。",[10,255,256],{},"我们有了explorer的基地址之后，就可以通过dt命令把Session指针拿到手。",[10,258,259],{},"如下：",[142,261,264],{"className":262,"code":263,"language":245},[243],"kd> dt nt!_EPROCESS ffffa381459b1080 Session\n   +0x2e8 Session : 0xffffa381`432fa740 _PSP_SESSION_SPACE\n",[26,265,263],{"__ignoreMap":147},[10,267,268,269,272],{},"这个就是当前桌面会话空间的内存首地址叫做 ",[26,270,271],{},"_PSP_SESSIOIN_SPACE结构体"," ，我们可以强行读取内存，查看内部结构，这里使用dq命令，以8个字节为单位，把指定内存地址里的数据以16进制的格式打印出来，并携带L10参数， L10是十六进制，所以就是16行。",[142,274,277],{"className":275,"code":276,"language":245},[243],"dq 0xffffa381432fa740 L10\nkd> dq 0xffffa381432fa740 L10\nffffa381`432fa740  00000005`0000002e 00000030`00000001\nffffa381`432fa750  ffffa381`444d0370 ffffa381`487f52b0\nffffa381`432fa760  ffffa381`43da09f0 ffffffff`80000678\nffffa381`432fa770  00000000`00000409 00000000`00000000\nffffa381`432fa780  00000000`00000000 00000000`00000000\nffffa381`432fa790  fffff802`a6705d40 ffffa381`4420f2b0\nffffa381`432fa7a0  00000000`00000000 00000000`00000000\nffffa381`432fa7b0  ffffa381`4420f2c0 fffff802`377b7bf0\n",[26,278,276],{"__ignoreMap":147},[10,280,281],{},"第二行ffffa开头的就是ProcessList的链表头，这是逆向基本功，windbg里面没有标出ProcessList的名字，但是通过分析用户态空间和内核态空间就可以知道：",[44,283,284,298],{},[47,285,286,289,290,293,294,297],{},[14,287,288],{},"用户态空间","：通常在 ",[26,291,292],{},"0x00000000'00000000"," 到 ",[26,295,296],{},"0x00007FFF'FFFFFFFF"," 之间。",[47,299,300,289,303,306],{},[14,301,302],{},"内核态空间",[26,304,305],{},"0xFFFF8000'00000000"," 以上。",[10,308,309,312,313,316,317,315,320,323,324,327,328,331,332,335,336,339],{},[26,310,311],{},"dq"," 输出里看到的 ",[26,314,315],{},"ffffa381","444d0370",[26,318,319],{},"和",[26,321,322],{},"487f52b0","，开头全是 ",[26,325,326],{},"0xffffa381...","。这毫无疑问是内核地址。而且，因为我们的 ",[26,329,330],{},"Session"," 基址是 ",[26,333,334],{},"0xffffa381432fa740","，这两个指针离它非常近（都在 ",[26,337,338],{},"0xffffa381"," 这一段），符合 Windows 内核池（Pool）连续分配内存的特征。",[10,341,342,343,346,347,349,350,353],{},"我们在",[26,344,345],{},"0xffffa381432fa750","（也就是 ",[26,348,330],{}," 基址 + ",[26,351,352],{},"0x10"," 的位置）看到了两个连续的指针：",[142,355,358],{"className":356,"code":357,"language":245},[243],"ffffa381`432fa750  ffffa381`444d0370 ffffa381`487f52b0\n",[26,359,357],{"__ignoreMap":147},[10,361,362,363,369,370,372],{},"这就是一个标准的 ",[14,364,365,368],{},[26,366,367],{},"_LIST_ENTRY","（双向链表头）"," 结构。",[26,371,367],{}," 在内存里占 16 个字节（两个 8 字节的指针）：",[44,374,375,384],{},[47,376,377,378,383],{},"第一个指针是 ",[14,379,380],{},[26,381,382],{},"Flink","（Forward Link，指向下一个节点）。",[47,385,386,387,392],{},"第二个指针是 ",[14,388,389],{},[26,390,391],{},"Blink","（Backward Link，指向上一个节点）。",[10,394,395,396,398,399,402,403,406,407,410,411,413,414,17],{},"找到了链表就需要读取并转换为能看懂的字符，windows内核里的进程名是asni编码，刚才我们拿到的 ",[26,397,382],{}," 是 ",[26,400,401],{},"ffffa381444d0370","。  由于我们知道它是指向 ",[26,404,405],{},"SessionProcessLinks"," 的，减去偏移 ",[26,408,409],{},"0x230","，得到 ",[26,412,217],{}," 基址：  ",[26,415,416],{},"ffffa381444d0370 - 0x230 = ffffa381444d0140",[10,418,419,420,423,424,427,428],{},"现在，我们加上 ",[26,421,422],{},"ImageFileName"," 的偏移 ",[26,425,426],{},"0x338","，得到真实的内存地址：  ",[26,429,430],{},"ffffa381444d0140 + 0x338 = ffffa381444d0478",[10,432,433,434,437,438,441,442,445,446,449],{},"ImageFilepName是windows内核_EPROCESS里的结构体的一个字段，是进程的名字，这是char数组，最大长度15字节，",[26,435,436],{},"[15]UChar","。因为是 15 字节，如果进程名特别长（比如 ",[26,439,440],{},"GoogleUpdateSetup.exe","），这里只能存下前 15 个字符（",[26,443,444],{},"GoogleUpdateSet","），后面的会被砍掉。\n在内核里，它的首字母有时会被强制转成大写（比如 ",[26,447,448],{},"System","），这取决于具体的内核版本。",[10,451,452],{},"这里我们使用da命令，一个字节一个字节读取数据，并翻译为ascii码，一直读到00结尾字符为止。",[142,454,457],{"className":455,"code":456,"language":245},[243],"kd> da ffffa381444d0478\nffffa381`444d0478  \"csrss.exe\"\n",[26,458,456],{"__ignoreMap":147},[10,460,461],{},"这是客户端\u002F服务器运行时子系统，使windows系统里的最核心最底层的进程，cmd底层都是它负责渲染和维护的，当任何用户程序要启动的时候，操作系统都不会直接运行，而是通过csrss.exe来负责创建和销毁进程、分配句柄、管理内存基础结构，这里不细讲，自己看详细相关笔记。",[10,463,464,465,468,469,471,472,468,474,476,477,479],{},"接下里我们可以继续向下获取，既然这是一个双向链表，",[26,466,467],{},"csrss.exe"," 的 ",[26,470,405],{}," 里一定存着指向下一个进程的指针。  我们需要读取 ",[26,473,467],{},[26,475,405],{}," 结构（地址是 ",[26,478,401],{},"）。",[142,481,484],{"className":482,"code":483,"language":245},[243],"dq ffffa381444d0370 L2\nffffa381`444d0370  ffffa381`4450e2b0 fffa381`432fa750\n",[26,485,483],{"__ignoreMap":147},[10,487,488],{},"下一个节点就是e2b0：",[10,490,491,492,494,495,103,497],{},"减去 ",[26,493,409],{},"，加上 ",[26,496,426],{},[26,498,499],{},"ffffa3814450e080 + 0x338",[142,501,504],{"className":502,"code":503,"language":245},[243],"kd> da ffffa3814450e080 + 0x338\nffffa381`4450e3b8  \"winlogon.exe\"\n",[26,505,503],{"__ignoreMap":147},[10,507,508],{},"我们的Process大致是这个原理，我们可以用!process \u002Fs 1 0 0来查看。 !process \u002Fs 1 表示只查看session 1里面的进程。",[19,510,112],{"id":511},"imagefile",[10,513,514,515,518,519,522,523,525,526,528],{},"指向的是",[14,516,517],{},"内核模块文件","（",[26,520,521],{},".sys","），比如 ",[26,524,118],{},"，它是用来支撑整个桌面的的基础。在 Windows 里，图形界面（GUI）相关的代码都在 ",[26,527,118],{}," 这个内核驱动里。当你的系统有多个用户同时登录时（比如你在本地登录，同时有人用 RDP 远程连进来），为了让这两个用户的桌面互不干扰，系统必须给每个会话建立一个“专属的图形环境”**。",[10,530,531,533],{},[26,532,112],{}," 就是用来记录和管理这个图形环境加载的驱动文件的：",[90,535,536,545],{},[47,537,538,541,542,544],{},[14,539,540],{},"管理映射","：每个会话都有自己独立的 ",[26,543,118],{}," 实例（虽然有部分代码段共享，但数据段是物理隔离的）。",[47,546,547,550,551,553],{},[14,548,549],{},"引用计数","：系统通过 ",[26,552,112],{}," 这个对象，知道这个会话的图形模块被谁在用，什么时候可以卸载。",[10,555,556],{},"继续更新...",[558,559,560],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":147,"searchDepth":160,"depth":160,"links":562},[563,564,567],{"id":21,"depth":160,"text":21},{"id":68,"depth":160,"text":68,"children":565},[566],{"id":132,"depth":166,"text":133},{"id":511,"depth":160,"text":112},"“会话空间”（Session Space）是 Windows 内核里一块按用户会话隔离的私有内存区域。","md",{"date":571,"tags":572},"2026-10-20",[573,574,575],"windows","win32","system",true,"\u002Fsystem\u002Fwindows\u002F会话空间",{"description":568},"system\u002Fwindows\u002F会话空间","IGUsb-4OiiEhQmkMh7v1sx5Pfz8S1XUCq_0Cs8m3B8g",1792775418115]