两个关于内核对象的问题

时间:2021-11-04 17:33:49
1。当我们用CREATEPROCESS创建一个子进程时。
   子进程的主线程的句柄是在主进程中。还是在子进程的内核
   对象句柄表中?此时子进程和子进程的主线程这两个内核对象
   的引用计数分别是多少?
2。我把EXPLORE当作以前DOS的COMMAND。COM为所有进程的主进程。
   那么当我们打开两个NOTEPAD时。此时关于这个NOTEPAD的进程内核对象
   只有一份。但有两个索引。即相当与有2个PROCESSID。但进程的进程句柄
   应该只有一份。是不是该这样理解?
  但那个伪句柄又怎么理解呢?从那派生出来的?为什么用DUPLICATE又能产生
  一份真的?
 以上两个问题因为很有关系。所以放在一起问了。谢谢各位大虾指教。

27 个解决方案

#1


(1).子进程的主线程的句柄是在子进程的内核对象句柄表中。内核对象右内核来管理,如果父进程没有讲子进程的handle关闭,则它也引用了子进程这个内核对象,所以子进程的内核对象记数应为2,否则为一。由于父进程并没有获得引用子进程的主线程内核对象,所以子进程的Main thread的引用记数为1。
(2)进程的进程是只有一份。由于内核对象有内核来管理,那么其生成与销毁都由内核来处理,用于创建内核对象的所有函数均返回与进程相关的聚丙,这些局并可以被在相同进程中运行的任何或所有线程成功地加以使用。该handle实际上释放如进程的句柄表中的索引,它用于表示内核对象的信息存放的位置。当创建一个子进程时,father process内核对象handle表中中可以被继承的kernel object会被原样复制到son process 的knernal object handle table中。所以内核对象只有一份,索引有两份。没有伪handle之说

#2


我前几天发现一点很特殊的情况。子进程的主线程句柄不在子进程的内核对象句柄表中,而是在CSRSS进程的内核对象句柄表中!!
我写过一个NT4OpenThread函数,用来在NT4下打开一个线程的句柄。在非主线程的情况下工作的很好,但是试图打开主线程句柄时总是失败。后来发现,这个主线程句柄根本没有在子进程的内核对象句柄表中;但是如果在全系统中搜索,你会在CSRSS进程的内核句柄表中找到这个句柄。

#3


这些问题,怎么不看看<windows 高级编程指南>Jeffrey Richter的书呢,第二章和第三章讲的很详细了

#4


to Chrysler:
1。好象不是。你看《ADVANCED。。》第57页的“运行分离的子进程”
你看他主进程关了两个句柄。包括子主线程的句柄。所以好象
两个计数器都是2?在主的。子的都登记?
子进程的引用记数为2。那么它自己也登记了自己的进程句柄?
2。好象用GETCURRENTPROCESS得到的是伪句柄哦。参照第70页。 

TO singlerace(独行者) :
那你首先认为子进程及子进程的主线程这两个内核对象的记数是多少?

#5


我个人的理解:

1。子进程的主线程的句柄在主进程的内核对象句柄表中,同时也在子进程的内核对象句柄表中。
当主进程把子进程CloseHandle之后,子进程的主线程的句柄就不在主进程的内核对象句柄表中,但仍然在子进程的内核对象句柄表中。
子进程及子进程的主线程这两个内核对象的记数是2和1

2。打开两个NOTEPAD时。此时关于这个NOTEPAD的进程内核对象应该有两份,但它们的代码段被映射到相同的物理页上。

#6


to yoci(阿呸) :
你的第一点理解和我不谋而和。如果没有CLOSEHANDLE。那应该都是2。
但照这么理解。当我们CREATETHREAD时。也是2?
因为我们在CREATETHREAD后。也要CLOSEHANDLE。
但如果是2的话。还有一份登记在那?(注意现在只有一个进程。)

我对你的第2点理解保持怀疑。
如果把EXPLORE当作夫进程。当我们CREATEPROCESS两次时。
这时并不会产生2份啊。只是增加记数。
不知我的理解对不对。


#7


请各位大虾多多指教。呵呵

#8


这本书相信很多人看过吧。大家随便发表点看法吧。畅所欲言吧。呵呵。
to Luck04():
你知道还有关于这个别的参考书吗?好象市面上只有这一本。如果有请指教。
我也想多参考参考。呵呵

#9


关注啊。关注

#10


关注啊。关注 

#11


高手跑那去了?
那些高手呢?
回答啊回答。。。。

#12


我不相信没人知道。。。。

#13


要坚持啊。。。。

#14


坚持就是胜利。。。呵呵

#15


内核对象是由内核维护的,分配给他一块内存,用来记录他的相关信息,包括计数,句柄的第一字段保存了这块内存的地址,所以父子进程都有子进程主线程的句柄。(要注意,内核对象,总是由内核维护,创建内核对象的进程和其它进程面对的都是一样的内存)。拥有内核对象句柄的东西调用CloseHandle(),引发内核到内核对象的相关内存把计数值减1。

#16


to mhonline(踏雪无痕) :
你先说计数是多少呢?

#17


子进程的进程句柄和主线程句柄既不属于父进程,也不属于子进程,他们都属于CSRSS进程,引用计数都为1。

#18


to singlerace(独行者):
你说的可能不对。。。。

#19


xrbeck(xiaozi) 
我觉得是这样的,
EXPLORE是父进程。当我们CREATEPROCESS两次时。
这时的确产生2份NOTEPAD的进程内核对象,而不只是增加记数。
否则就没办法控制第二个notepad了。
不信你可以用spy看看

#20


to yoci(阿呸);
你说的这点应该是对的。。但是这个计数问题呢?

#21


小弟看的是<<核心编程>>,其中提到:
...创建新进程可使系统建立一个进程内核对象和一个线程内核对象,在创建进程的时候,系统为每个对象赋予一个初始使用计数1,然后在CreateProcess(...,&pi)返回之前,该函数打开进程对象和线程对象,并将每个对象的与进程相关的句柄放入PROCESS_INFORMATION结构的hProcess和hThread成员中,当CreateProcess在内部打开这些对象时,每个对象的使用计数就变成2.

#22


CreateProcess成功后,会返回子进程的进程句柄和主线程句柄,这时,他们的引用计数都是2。如果你以后不会用到这些句柄,这时应当将其关闭。这时他们的计数是1,都由CSRSS拥有。
我说的绝对正确。

实际上,一个内核对象有两个计数器:一个是句柄计数,句柄是给用户态用的;另一个是指针计数,也叫引用计数,因为核心态也常常用到内核对象,为了方便,在核心态的代码用指针直接访问对象,所以Object Manager维护了这个指针引用计数。只有在句柄计数和引用计数都为0时,对象才被释放。一般而言,指针引用计数值比句柄计数值大。

再进一步,实际上,在用户态其实是可以查询一个内核对象的句柄计数和引用计数的。Ntdll.dll里导出的NtQueryObject函数可以查询内核对象的当前状态,只不过它没有被文档化。

函数声明如下:
DWORD WINAPI NtQueryObject( HANDLE handle, DWORD nQueryIndex, VOID* pOutBuffer, DWORD cbInBufferSize, VOID* cbOutBufferSize);

handle -- 待查询的句柄
nQueryIndex -- 0为查询对象的当前状态,包括句柄计数,引用计数等等。
pOutBuffer -- 存放查询结果
cbInBufferSize -- pOutBuffer的大小,注意,如果nQueryIndex为0,这里一定得是0x38
cbOutBufferSize -- 实际大小。

返回值:如果成功则返回0

pOutBuffer里返回的数据结构如下:
typedef struct _SYSTEM_HANDLE_STATE {
DWORD r1;
DWORD GrantedAccess;
DWORD HandleCount; // 减1为句柄计数
DWORD ReferenceCount; // 减1为指针引用计数
DWORD r5;
DWORD r6;
DWORD r7;
DWORD r8;
DWORD r9;
DWORD r10; 
DWORD r11; 
DWORD r12; 
DWORD r13; 
DWORD r14; 
}SYSTEM_HANDLE_STATE, *PSYSTEM_HANDLE_STATE;
我现在只弄清楚了这几个。

#23


to singlerace(独行者):
你说的都是2。这点我想应该是了。我也是这么认为的。
但是你想:为什么是2。我觉得xiaoyuer(鱼头)兄说的对。
我也在书上看到了这段话。
那么是不是这样理解:创建子进程时。两个都为1。
当使用CREATEPROCESS时。因为他会调用CREATETHREAD
所以相当于这两个函数又打开了内核对象。所以又各加1 。所以都为2。
(xiaoyuer(鱼头)兄是不是这样?)
这一点可以证明(第57页的“运行分离的子进程”)其中关了两个句柄。

但是我们在看看进程的内核对象句柄表:首先子进程的内核句柄登记在
父进程的句柄表中(也只有这一份,因为还有一份是CREATEPROCESS打开的)
那么线程呢?按照这个思路。也应该只有1份。所以我认为此时的子线程也只有1份。
是登记在子进程的句柄表中。而主进程没有。。。。我想子进程创建了子线程。
它理所当然应该有子线程的句柄。而不是你说的什么CSRSS(这个是什么?呵呵)



 

#24


CSRSS (Client/Server Runtime SubSystem)就是Win32子系统,所有的win32进程都由他维护。
要想深入理解windows nt的底层机制,最好的参考书是"Windows NT技术内幕" by David A. Solomon,而不是<windows 高级编程指南>。
这本书中,作者把进程的创建分为6个阶段:
1,设置EPROCESS块;
2,创建初始进程地址空间;
3,创建内核进程块;
4,决定进程地址空间的设置;
5,设置PEB(Process Environment Block);
6,完成执行体进程对象的设置。
在第3阶段结束后,进程和线程的内核对象被创建,这时句柄计数都为1。然后kernel32.dll向Win32子系统(也就是CSRSS)发送消息,让他设置新创建的进程和线程。
Win32子系统收到消息后,首先做的就是Duplicate进程和线程句柄,并把句柄计数增加为2。...
CreateProcess调用成功后,返回子进程和线程句柄给父进程。如果父进程不使用这些句柄,这时就应该将其关闭。因此现在起,他们的句柄计数为1。
OK?

#25


不OK。你说的那本书我还没看呢。。呵呵。
稍微等一下吧。。让我想想。。再参考参考书籍。。。恩。

#26


不OK。你说的那本书我还没看呢。。呵呵。
稍微等一下吧。。让我想想。。再参考参考书籍。。。恩。
而且可能98跟NT也不一样啊。。。

#27


哎呀。。结帐了。。。。呵呵

#1


(1).子进程的主线程的句柄是在子进程的内核对象句柄表中。内核对象右内核来管理,如果父进程没有讲子进程的handle关闭,则它也引用了子进程这个内核对象,所以子进程的内核对象记数应为2,否则为一。由于父进程并没有获得引用子进程的主线程内核对象,所以子进程的Main thread的引用记数为1。
(2)进程的进程是只有一份。由于内核对象有内核来管理,那么其生成与销毁都由内核来处理,用于创建内核对象的所有函数均返回与进程相关的聚丙,这些局并可以被在相同进程中运行的任何或所有线程成功地加以使用。该handle实际上释放如进程的句柄表中的索引,它用于表示内核对象的信息存放的位置。当创建一个子进程时,father process内核对象handle表中中可以被继承的kernel object会被原样复制到son process 的knernal object handle table中。所以内核对象只有一份,索引有两份。没有伪handle之说

#2


我前几天发现一点很特殊的情况。子进程的主线程句柄不在子进程的内核对象句柄表中,而是在CSRSS进程的内核对象句柄表中!!
我写过一个NT4OpenThread函数,用来在NT4下打开一个线程的句柄。在非主线程的情况下工作的很好,但是试图打开主线程句柄时总是失败。后来发现,这个主线程句柄根本没有在子进程的内核对象句柄表中;但是如果在全系统中搜索,你会在CSRSS进程的内核句柄表中找到这个句柄。

#3


这些问题,怎么不看看<windows 高级编程指南>Jeffrey Richter的书呢,第二章和第三章讲的很详细了

#4


to Chrysler:
1。好象不是。你看《ADVANCED。。》第57页的“运行分离的子进程”
你看他主进程关了两个句柄。包括子主线程的句柄。所以好象
两个计数器都是2?在主的。子的都登记?
子进程的引用记数为2。那么它自己也登记了自己的进程句柄?
2。好象用GETCURRENTPROCESS得到的是伪句柄哦。参照第70页。 

TO singlerace(独行者) :
那你首先认为子进程及子进程的主线程这两个内核对象的记数是多少?

#5


我个人的理解:

1。子进程的主线程的句柄在主进程的内核对象句柄表中,同时也在子进程的内核对象句柄表中。
当主进程把子进程CloseHandle之后,子进程的主线程的句柄就不在主进程的内核对象句柄表中,但仍然在子进程的内核对象句柄表中。
子进程及子进程的主线程这两个内核对象的记数是2和1

2。打开两个NOTEPAD时。此时关于这个NOTEPAD的进程内核对象应该有两份,但它们的代码段被映射到相同的物理页上。

#6


to yoci(阿呸) :
你的第一点理解和我不谋而和。如果没有CLOSEHANDLE。那应该都是2。
但照这么理解。当我们CREATETHREAD时。也是2?
因为我们在CREATETHREAD后。也要CLOSEHANDLE。
但如果是2的话。还有一份登记在那?(注意现在只有一个进程。)

我对你的第2点理解保持怀疑。
如果把EXPLORE当作夫进程。当我们CREATEPROCESS两次时。
这时并不会产生2份啊。只是增加记数。
不知我的理解对不对。


#7


请各位大虾多多指教。呵呵

#8


这本书相信很多人看过吧。大家随便发表点看法吧。畅所欲言吧。呵呵。
to Luck04():
你知道还有关于这个别的参考书吗?好象市面上只有这一本。如果有请指教。
我也想多参考参考。呵呵

#9


关注啊。关注

#10


关注啊。关注 

#11


高手跑那去了?
那些高手呢?
回答啊回答。。。。

#12


我不相信没人知道。。。。

#13


要坚持啊。。。。

#14


坚持就是胜利。。。呵呵

#15


内核对象是由内核维护的,分配给他一块内存,用来记录他的相关信息,包括计数,句柄的第一字段保存了这块内存的地址,所以父子进程都有子进程主线程的句柄。(要注意,内核对象,总是由内核维护,创建内核对象的进程和其它进程面对的都是一样的内存)。拥有内核对象句柄的东西调用CloseHandle(),引发内核到内核对象的相关内存把计数值减1。

#16


to mhonline(踏雪无痕) :
你先说计数是多少呢?

#17


子进程的进程句柄和主线程句柄既不属于父进程,也不属于子进程,他们都属于CSRSS进程,引用计数都为1。

#18


to singlerace(独行者):
你说的可能不对。。。。

#19


xrbeck(xiaozi) 
我觉得是这样的,
EXPLORE是父进程。当我们CREATEPROCESS两次时。
这时的确产生2份NOTEPAD的进程内核对象,而不只是增加记数。
否则就没办法控制第二个notepad了。
不信你可以用spy看看

#20


to yoci(阿呸);
你说的这点应该是对的。。但是这个计数问题呢?

#21


小弟看的是<<核心编程>>,其中提到:
...创建新进程可使系统建立一个进程内核对象和一个线程内核对象,在创建进程的时候,系统为每个对象赋予一个初始使用计数1,然后在CreateProcess(...,&pi)返回之前,该函数打开进程对象和线程对象,并将每个对象的与进程相关的句柄放入PROCESS_INFORMATION结构的hProcess和hThread成员中,当CreateProcess在内部打开这些对象时,每个对象的使用计数就变成2.

#22


CreateProcess成功后,会返回子进程的进程句柄和主线程句柄,这时,他们的引用计数都是2。如果你以后不会用到这些句柄,这时应当将其关闭。这时他们的计数是1,都由CSRSS拥有。
我说的绝对正确。

实际上,一个内核对象有两个计数器:一个是句柄计数,句柄是给用户态用的;另一个是指针计数,也叫引用计数,因为核心态也常常用到内核对象,为了方便,在核心态的代码用指针直接访问对象,所以Object Manager维护了这个指针引用计数。只有在句柄计数和引用计数都为0时,对象才被释放。一般而言,指针引用计数值比句柄计数值大。

再进一步,实际上,在用户态其实是可以查询一个内核对象的句柄计数和引用计数的。Ntdll.dll里导出的NtQueryObject函数可以查询内核对象的当前状态,只不过它没有被文档化。

函数声明如下:
DWORD WINAPI NtQueryObject( HANDLE handle, DWORD nQueryIndex, VOID* pOutBuffer, DWORD cbInBufferSize, VOID* cbOutBufferSize);

handle -- 待查询的句柄
nQueryIndex -- 0为查询对象的当前状态,包括句柄计数,引用计数等等。
pOutBuffer -- 存放查询结果
cbInBufferSize -- pOutBuffer的大小,注意,如果nQueryIndex为0,这里一定得是0x38
cbOutBufferSize -- 实际大小。

返回值:如果成功则返回0

pOutBuffer里返回的数据结构如下:
typedef struct _SYSTEM_HANDLE_STATE {
DWORD r1;
DWORD GrantedAccess;
DWORD HandleCount; // 减1为句柄计数
DWORD ReferenceCount; // 减1为指针引用计数
DWORD r5;
DWORD r6;
DWORD r7;
DWORD r8;
DWORD r9;
DWORD r10; 
DWORD r11; 
DWORD r12; 
DWORD r13; 
DWORD r14; 
}SYSTEM_HANDLE_STATE, *PSYSTEM_HANDLE_STATE;
我现在只弄清楚了这几个。

#23


to singlerace(独行者):
你说的都是2。这点我想应该是了。我也是这么认为的。
但是你想:为什么是2。我觉得xiaoyuer(鱼头)兄说的对。
我也在书上看到了这段话。
那么是不是这样理解:创建子进程时。两个都为1。
当使用CREATEPROCESS时。因为他会调用CREATETHREAD
所以相当于这两个函数又打开了内核对象。所以又各加1 。所以都为2。
(xiaoyuer(鱼头)兄是不是这样?)
这一点可以证明(第57页的“运行分离的子进程”)其中关了两个句柄。

但是我们在看看进程的内核对象句柄表:首先子进程的内核句柄登记在
父进程的句柄表中(也只有这一份,因为还有一份是CREATEPROCESS打开的)
那么线程呢?按照这个思路。也应该只有1份。所以我认为此时的子线程也只有1份。
是登记在子进程的句柄表中。而主进程没有。。。。我想子进程创建了子线程。
它理所当然应该有子线程的句柄。而不是你说的什么CSRSS(这个是什么?呵呵)



 

#24


CSRSS (Client/Server Runtime SubSystem)就是Win32子系统,所有的win32进程都由他维护。
要想深入理解windows nt的底层机制,最好的参考书是"Windows NT技术内幕" by David A. Solomon,而不是<windows 高级编程指南>。
这本书中,作者把进程的创建分为6个阶段:
1,设置EPROCESS块;
2,创建初始进程地址空间;
3,创建内核进程块;
4,决定进程地址空间的设置;
5,设置PEB(Process Environment Block);
6,完成执行体进程对象的设置。
在第3阶段结束后,进程和线程的内核对象被创建,这时句柄计数都为1。然后kernel32.dll向Win32子系统(也就是CSRSS)发送消息,让他设置新创建的进程和线程。
Win32子系统收到消息后,首先做的就是Duplicate进程和线程句柄,并把句柄计数增加为2。...
CreateProcess调用成功后,返回子进程和线程句柄给父进程。如果父进程不使用这些句柄,这时就应该将其关闭。因此现在起,他们的句柄计数为1。
OK?

#25


不OK。你说的那本书我还没看呢。。呵呵。
稍微等一下吧。。让我想想。。再参考参考书籍。。。恩。

#26


不OK。你说的那本书我还没看呢。。呵呵。
稍微等一下吧。。让我想想。。再参考参考书籍。。。恩。
而且可能98跟NT也不一样啊。。。

#27


哎呀。。结帐了。。。。呵呵