Sunday, August 22, 2010

Unhooking a leaking C# object from Microsoft.Win32.SystemEvents.UserPreferenceChanged

Before I get started I'd like to clarify what the tem "leak" means for the purposes of this discussion. Any object that your application is no longer using but cannot be garbage collected is "leaking" for the purposes of this discussion. I know that technically the memory is still accessible and it is possible to release it but the fact of the matter is that your application is not using this memory and it will not ever be released. Given enough time this will eventually lead to an out of memory exception.

For anyone that has used a memory profiler it's possible that you've come across leaks related to the System.Windows.Form.ToolStripTextBox control. If you haven't, here's a link to a blog post that can shed some light on this issue. It's a very good explanation of the problem but in summary, the ToolStripTextBox control is hooking the Microsoft.Win32.SystemEvents.UserPreferenceChanged repeatedly and unhooking only once. This is causing leaks because the UserPreferenceChanged event is static and does not go out of scope for the life of your application. This creates a root reference for any listeners of that event. Unfortunately, the workaround posted on the linked page did not seem to work very well for me. After modifying the workaround until it sufficiently suited my needs I found that the ToolStripTextBox control is not the only class that fails to unhook from this event causing leaks. For the project I'm working on this was causing a chain reaction of leaking objects that was fairly significant.

There are three ways to solve this problem that I can see. The first option is that you can try to predict the nature of the bug in the class that is leaking. Using this information you can code a very specific solution to this problem. The problem with this is that it requires that you are correct in your analysis of the problem which is buried in a class that you don't have the source code for and your targeted solution also works correctly. The second option I see here is the brute force method. Just unhook the method a bunch of times and hopefully the problem goes away. If I need to explain what's wrong with that don't waste your time reading further. The third option I see for solving this problem is to create a generic method that will look at the listeners of this event, find all references to the leaking object, and unhook them. This is the approach I have used to solve this problem.

To implement this solution I created a class UnhookSystemEventUserPreferenceChangedEvent. In this class there is a static public method UnhookObject(object pObjectToUnhook). This will take any object that is directly hooked (ToolStripTextBox and ToolStripItemOverflow are the two I've had problems with) to the Microsoft.Win32.SystemEvents.UserPreferenceChanged event and will unhook them. It's preferable to call this method in response to the disposing event of the leaking object. Rather than an abstract explanation of the code I've tried to comment the code itself as thoroughly as possible so that I could just post the code and let you walk through it. If there's another solution out there I didn't see it so for anyone who's been agonizing over this I hope this helps.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;

namespace HelperClasses
{
     public class UnhookSystemEventUserPreferenceChangedEvent
     {
          //we'll use a static List to cache a reference to the internal list of UserPreferenceChangedEvent listeners so that
          // we do not need to search for it every time.
          static System.Collections.IList _UserPreferenceChangedList = null;

          static public void UnhookObject(object pObjectToUnhook)
          {
               //First check for null and get a ref to the UserPreferenceChangedEvent's internal list of listeners if needed.
               if (_UserPreferenceChangedList == null) GetUserPreferenceChangedList();
               //then, scan that list for any delegates that point to pObjectToUnhook.
               SearchListAndRemoveEventHandlers(pObjectToUnhook);
          }

          static private void GetUserPreferenceChangedList()
          {
               Type oSystemEventsType = typeof(SystemEvents);

               //Using reflection, get the FieldInfo object for the internal collection of handlers
               // we will use this collection to find the handler we want to unhook and remove it.
               // as you can guess by the naming convention it is a private member.
               System.Reflection.FieldInfo oFieldInfo = oSystemEventsType.GetField("_handlers",
                                   System.Reflection.BindingFlags.Static |
                                   System.Reflection.BindingFlags.GetField |
                                   System.Reflection.BindingFlags.FlattenHierarchy |
                                   System.Reflection.BindingFlags.NonPublic);

               //now, get a reference to the value of this field so that you can manipulate it.
               //pass null to GetValue() because we are working with a static member.
               object oFieldInfoValue = oFieldInfo.GetValue(null);

               //the returned object is of type Dictionary<object, List<SystemEventInvokeInfo>>
               //each of the Lists<> in the Dictionary<> is used to maintain a different event implementation.
               //It may be more efficient to figure out how the UserPreferenceChanged event is keyed here but a quick-and-dirty
               // method is to just scan them all the first time and then cache the List<> object once it's found.

               System.Collections.IDictionary dictFieldInfoValue = oFieldInfoValue as System.Collections.IDictionary;
               foreach (object oEvent in dictFieldInfoValue)
               {
                    System.Collections.DictionaryEntry deEvent = (System.Collections.DictionaryEntry)oEvent;
                    System.Collections.IList listEventListeners = deEvent.Value as System.Collections.IList;

                    //unfortunately, SystemEventInvokeInfo is a private class so we can't declare a reference of that type.
                    //we will use object and then use reflection to get what we need...
                    List<Delegate> listDelegatesToRemove = new List<Delegate>();

                    //we need to take the first item in the list, get it's delegate and check the type...
                    if (listEventListeners.Count > 0 && listEventListeners[0] != null)
                    {
                         Delegate oDelegate = GetDelegateFromSystemEventInvokeInfo(listEventListeners[0]);
                         if (oDelegate is UserPreferenceChangedEventHandler)
                         { _UserPreferenceChangedList = listEventListeners; }
                    }
                    //if we've found the list, no need to continue searching
                    if (_UserPreferenceChangedList != null) break;
               }
          }

          static private void SearchListAndRemoveEventHandlers(object pObjectToUnhook)
          {
               if (_UserPreferenceChangedList == null) return; //Do not run if we somehow haven't found the list.

               //unfortunately, SystemEventInvokeInfo is a private class so we can't declare a reference of that type.
               //we will use object and then use reflection to get what we need...
               List<UserPreferenceChangedEventHandler> listDelegatesToRemove = new List<UserPreferenceChangedEventHandler>();

               //this is NOT threadsafe. Unfortunately, if the collection is modified an exception will be thrown during iteration.
               // This will happen any time another thread hooks or unhooks the UserPreferenceChanged event while we iterate.
               // Modify this to be threadsafe somehow if that is required.
               foreach (object oSystemEventInvokeInfo in _UserPreferenceChangedList)
               {
                    UserPreferenceChangedEventHandler oDelegate =
                         GetDelegateFromSystemEventInvokeInfo(oSystemEventInvokeInfo) as UserPreferenceChangedEventHandler;

                    if (oDelegate != null && oDelegate.Target == pObjectToUnhook)
                    {
                         //at this point we have found an event handler that must be unhooked.
                         listDelegatesToRemove.Add(oDelegate);
                    }
               }

               //We should unhook using the public method because the internal implementation of this event is unknown.
               // iterating the private internal list is already shady enough without manipulating it directly...
               foreach (UserPreferenceChangedEventHandler itemToRemove in listDelegatesToRemove)
               { SystemEvents.UserPreferenceChanged -= itemToRemove; }
          }

          static private Delegate GetDelegateFromSystemEventInvokeInfo(object pSystemEventInvokeInfo)
          {
               Type typeSystemEventInvokeInfo = pSystemEventInvokeInfo.GetType();
               System.Reflection.FieldInfo oTmpFieldInfo = typeSystemEventInvokeInfo.GetField("_delegate",
                                   System.Reflection.BindingFlags.Instance |
                                   System.Reflection.BindingFlags.GetField |
                                   System.Reflection.BindingFlags.FlattenHierarchy |
                                   System.Reflection.BindingFlags.NonPublic);

               //Here we are NOT working with a static field so we will supply the SystemEventInvokeInfo
               // object that we found in the List<> object to the GetValue() function.
               Delegate oReturn = oTmpFieldInfo.GetValue(pSystemEventInvokeInfo) as Delegate;

               return oReturn;
          }
     }
}

Thursday, October 22, 2009

CyberCede Cyber-Warfare Legitimate Militia Botnet

In a post today on one of our other blogs, cyber-jutsu, we announced that CyberCede is now accepting emails from persons interested in joining the CyberCede Cyber-Warfare Militia.

As a part of this new community organization, I'd like to write a program that can be installed on the systems belonging to militia members, which would allow them to be a part of a global, legitimate, bot-net. Participation would be 100% voluntary.

Who wants to help out in writing such a program?

Metajunkie

google AdSense Account Disabled

cross posted from our cyber-justu blog ...


Some of you may have noticed that the cyber-justsu dojo walls seem a little bare. The Google Advertisements are missing.

Google has disabled our AdSense account.

In an email, they have asserted that our "AdSense account has posed a significant risk to [their] AdWords advertisers".

This would appear to happen frequently enough, that they have a FAQ established to provide more information.

From the FAQ:

"Because we have a need to protect our proprietary detection system, we're unable to provide our publishers with any information about their account activity, including any web pages, users, or third-party services that may have been involved.

As you may know, Google treats invalid click activity very seriously, analyzing all clicks and impressions to determine whether they fit a pattern of use that may artificially drive up an advertiser's costs or a publisher's earnings. If we determine that an AdSense account may pose a risk to our AdWords advertisers, we may disable that account to protect our advertisers' interests.

Lastly, please note that as outlined in our Terms and Conditions, Google will use its sole discretion when determining instances of invalid click activity."

So, we really have no idea why our account was disabled. If any of our readers have been randomly or blindly clicking on advertisements, you have not helped us. In fact, you may have shut down what might have been a great source of passive income for our blogs.

We have petitioned google to reinstate our account. If that happens, I encourage you all to only click on advertisements which are of interest to you. Don't be afraid to click on advertisements, that is why they are there - but please refrain from just clicking because you know it is generating revenue for us.

I don't usually cross-post between these blogs - but I will put this message on all of the blogs.

Thank you for your understanding and cooperation.

Sensei Metajunkie


Saturday, October 17, 2009

What is your favorite programming language?

I'd like to open up a place for folks to leave some comments on what their favorite programming language is, and the sorts of applications they like to code.

So, here it is.

What's your digital poison? :)

Thursday, October 15, 2009

Hacking Code Vs. Code Hacking

I consider the terms "hacking code" and "programming" to be synonymous, or virtually synonymous. In some instances I may say that I had to "hack together some perl code" in order to merge elements from two different databases together. People who know me, and are programmers, would agree that what I ended up with was in fact a hack.

That is not to say that it defeated security in any way, but rather it was probably something less glorious than the same sort of program that might have been designed and then built by a team of professional programmers who were hell bent on selling the world the next great database merge tool with bells and whistles hidden beyond the ken of any human user. What I ended up with may or may not have been elegant - but it did work. It was a successful perl hack.

The term hack and hacker can have both positive and negative connotations, depending upon the context in which they are used. For example, consider this comment made by one IT professional when talking about a particular network engineer's "fix" to protect a large hospital's exposure to the conficker malware: "If he would have spent half the time coming up with that fire-wall hack as he did surfing the web this morning, we might have actually avoided having the entire hospital infected." Clearly, in this instance, the hack was not a success.

Had that network engineer been a member of our cyber-jutsu blog, he might have been better educated. He might have still been accused of implementing a hack, but it would have then been a good thing. Luckily for him, that hospital's CIO doesn't know the difference, and he still has a job.

I recommend a google search for "The New Hacker's Dictionary" to help get you up to speed on all the jargon you'll need to survive here at Hacking Code.

In case you want to check out a good online reference for the same: see Jargon File
There are many copies of the Jargon file being hosted by different sites. Google it and see for yourself.

So, the difference between hacking code and code hacking - to me - would be this:

If I am hacking code, I am programming.

If I am code hacking, I am trying to find a way around or through some sort of code or cypher.

If I'm hacking a web server, I'm probably trying to bypass the security of the web server. If I'm hacking together a web server, I'm writing a bare-bones computer program to do the work of a web server rather than using someone else's fully developed web server program (such as Apache 2).

Some might say that I'm not really a freelance writer or professional blogger, that I'm just a hack. Therein we see the negative connotation of hack as applied to a writer.

To this I say: :>p


That's it in a nutshell. I hope this was edifying.

Hacking Code Blog

Welcome to the Hacking Code Blog.

This blog is about writing computer programs. While I am willing to take it in various directions based upon my readers and comment contributors, the initial focus will be made with a wide-angle lens.

While I am a CISSP, and a GREM (not to be confused with a gremlin), I am also interested in programming things besides hacker tools and exploit code. We will no doubt be covering actual penetration testing tool code at some point, but this blog is dedicated to the actual programming languages we will discuss rather than "hacker techniques". For hacker techniques, and cyber-jutsu in general, I suggest one of my other blogs:


What is your favorite language, and how do you write the obligatory hello world program in it?