{"id":5823,"date":"2007-03-22T20:18:00","date_gmt":"2007-03-22T20:18:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2007\/03\/22\/spy-update\/"},"modified":"2019-02-18T18:54:33","modified_gmt":"2019-02-18T18:54:33","slug":"spy-update","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/spy-update\/","title":{"rendered":"Spy++ Update"},"content":{"rendered":"<p class=\"MsoNormal\"><span>Hi, Pat Brenner again.<span>&nbsp; <\/span>I recently posted an article about <a href=\"http:\/\/blogs.msdn.com\/vcblog\/archive\/2007\/01\/16\/spy-internals.aspx\">Spy++ internals<\/a>.<span>&nbsp; <\/span>I have some updates that I want to share with you.<span>&nbsp; <\/span>Some of this article will be clearer if you first read that one.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>First, Spy++ has been updated for Visual Studio codename Orcas to handle the new common controls\/messages\/flags added in Windows Vista.<span>&nbsp; <\/span>This means if you run Spy++ on Vista, you should get all the new messages, and their parameters, decoded correctly so you can more accurately determine what&rsquo;s going on in your application and the system.<span>&nbsp; <\/span>These changes go hand-in-hand with a bunch of updates to MFC to wrap the new controls and messages (see related article <a href=\"http:\/\/blogs.msdn.com\/vcblog\/archive\/2007\/03\/21\/mfc-updates-for-vista-common-controls.aspx\">here<\/a>), so when you&rsquo;re using these new capabilities, MFC will make it easier to code them and Spy++ will make it easier to analyze.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Second, in addition to being built as a 32-bit application, Spy++ is now also being built as a 64-bit native application for IA64 and AMD64, so you can log messages in your native 64-bit applications as well.<span>&nbsp; <\/span>This is because a 32-bit application can only install hooks in other 32-bit applications and a 64-bit application can only install hooks in other 64-bit applications.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Third, there is an effort underway to get some real automated testing running on Spy++.<span>&nbsp; <\/span>Since its inception 15 years ago, Spy++ has never been tested with any automated test harness.<span>&nbsp; <\/span>All testing has been manual, and ad hoc, and I&rsquo;m sure some of the message and parameter cracking methods have never been tested because I&rsquo;ve never been able to generate those messages during normal usage.<span>&nbsp; <\/span>Now, however, Visual C++ QA is attempting to reach 70% code coverage on Spy++, so there will be automated tests running on Spy++ to cause those more obscure cracking methods to be invoked.<span>&nbsp; <\/span>I&rsquo;m excited about the new level of stability this should bring to Spy++.<span>&nbsp; <\/span>Ben Anderson from VC++ QA is doing that work; I will encourage him to post about that work here sometime in the future.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Last, I have finally tracked down a hook hanging issue that was causing problems on Windows Vista.<span>&nbsp; <\/span>I&rsquo;d been running into a recurring deadlock in the hooks where a hook would block waiting for another shared resource.<span>&nbsp; <\/span>In the past, this would have caused a system hang, since all the Windows message queues would be stopped waiting for that hook to unblock.<span>&nbsp; <\/span>Recently, I made a change to Spy++ so that the hooks would time out if a shared resource was not available, but this still meant disabling all the hooks so message logging was essentially aborted.<span>&nbsp; <\/span>After running Spy++ a number of times, and inserting some diagnostics, I finally came to the conclusion that the synchronization of the circular queue&mdash;or more specifically, the synchronization of the write and read offsets in the circular queue&mdash;was the problem.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Essentially, the synchronization of the read and write offsets was trying to guarantee that a pending write would not overwrite an area of the circular queue that had not yet been read.<span>&nbsp; <\/span>So the writers would loop, resetting the read event and waiting for it to be reset, until it was sure that the write would not overwrite unread data (more detail <a href=\"http:\/\/blogs.msdn.com\/vcblog\/archive\/2007\/01\/16\/spy-internals.aspx\">here<\/a>).<span>&nbsp; <\/span>I stared at the code doing this synchronization for quite a while, and finally came to an unusual conclusion: I didn&rsquo;t need it at all.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>I realized that with multiple writers and one reader, the queue was probably filling up very rapidly when Spy++ started logging messages, and from then on the writers were always waiting on the reader.<span>&nbsp; <\/span>I figured if that was the case, why store data for more than one message ever?<span>&nbsp; <\/span>So I essentially reduced the size of the circular queue so that it was only large enough to hold data about one message.<span>&nbsp; <\/span>This eliminated the need for offsets, and meant that the mutexes and the events were all I needed to accomplish the synchronization.<span>&nbsp; <\/span>So the sets of operations performed by the writers and reader have changed since my last post.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>The writers (in the hook DLL), when a message comes through one of their hooks, now do the following:<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Wait for the read event to be set.<span>&nbsp; <\/span>The event is auto-reset, so once the wait is satisfied, the event is reset.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoN\normal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Take the writer mutex, and then take the access mutex.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Copy the message data to the shared data area.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Set the written event.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Release the access mutex, and release the writer mutex.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>And the reader, which resides in a loop in a thread in the Spy++ application, now does the following:<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Wait for the written event to be set.<span>&nbsp; <\/span>The event is auto-reset, so once the wait is satisfied, the event is reset.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Take the access mutex.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Copy the message data from the message packet in the shared queue.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Release the access mutex.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Set the read event.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Send the copied packet data in a message to a hidden window owned by the main thread.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>This did solve my hook hang issue, but I was still seeing some strange behavior.<span>&nbsp; <\/span>After a lengthy discussion with the tester who was validating my fixes, we came to the realization that there were some security issues coming into play.<span>&nbsp; <\/span>Basically, since the hook is injected into every process, it runs at the integrity level of the process that it is injected into.<span>&nbsp; <\/span>This means that if the synchronization objects (in this case, the mutexes and events) are created in a process with a higher integrity level, then the hook in the lower-integrity process will not be able to open or manipulate those objects.<span>&nbsp; <\/span>This fact, in combination with the fact that the error checking on synchronization object manipulation was not robust, meant that some lower-integrity hooks were writing to the shared memory area without synchronizing properly.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>This integrity problem was also the reason for the original hook hangs that I was seeing.<span>&nbsp; <\/span>Some hooks were erroneously thinking that they could obtain the mutexes, but then could not really set the events, so once they entered a loop waiting for an event to be set (which depended on them setting an event), they would deadlock the other hooks.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>The fix for this is to set the MIC privilege on the synchronization objects to low.<span>&nbsp; <\/span>This is done using a couple of APIs, <span>ConvertStringSecurityDescriptorTo\nSecurityDescriptor and GetSecurityDescriptorSacl, in combination with SetSecurityInfo.<span>&nbsp; <\/span>This allows the low-integrity process to open and manipulate the synchronization objects.<span>&nbsp; <\/span>You can see more information on this topic <a href=\"http:\/\/msdn.microsoft.com\/library\/default.asp?url=\/library\/en-us\/ietechcol\/dnwebgen\/protectedmode.asp\">here<\/a> (see the section on Lowering Resource Integrity).<\/span><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>That pretty much covers the updates I wanted to share with you.<span>&nbsp; <\/span>I also wanted to mention a couple of usage tips, based on a question from the last article:<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>Spy++ can log messages for all windows in a process.<span>&nbsp; <\/span>This can be used to log all the creation messages, if you use the following trick.<span>&nbsp; <\/span>Step into your application in the debugger, and once you have the process identifier, start Spy++.<span>&nbsp; <\/span>Open a processes tree view (Spy.Processes).<span>&nbsp; <\/span>Select your process ID in the tree, and then do Spy.Messages.<span>&nbsp; <\/span>In the Message Options dialog, on the Windows tab, your process will already be the selected object.<span>&nbsp; <\/span>Select the messages you want to see on the Messages tab. <span>&nbsp;<\/span>OK the dialog, and then let your process run.<span>&nbsp; <\/span>Now you should see all the window creation messages for windows in your process.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&middot;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><span>You can do the same for a single thread within your process if you wish.<span>&nbsp; <\/span>Start with a threads tree view instead and follow the same steps.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Thanks, and again, I welcome any questions you might have about Spy++, or feature requests for future versions.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Pat Brenner<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Visual C++ Libraries Team<\/span><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi, Pat Brenner again.&nbsp; I recently posted an article about Spy++ internals.&nbsp; I have some updates that I want to share with you.&nbsp; Some of this article will be clearer if you first read that one. &nbsp; First, Spy++ has been updated for Visual Studio codename Orcas to handle the new common controls\/messages\/flags added in [&hellip;]<\/p>\n","protected":false},"author":289,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5823","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>Hi, Pat Brenner again.&nbsp; I recently posted an article about Spy++ internals.&nbsp; I have some updates that I want to share with you.&nbsp; Some of this article will be clearer if you first read that one. &nbsp; First, Spy++ has been updated for Visual Studio codename Orcas to handle the new common controls\/messages\/flags added in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/5823","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/289"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=5823"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/5823\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=5823"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=5823"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=5823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}