diff --git a/oSpy1/HeapAgent/HeapAgent.cs b/oSpy1/HeapAgent/HeapAgent.cs new file mode 100644 index 0000000..4d37da9 --- /dev/null +++ b/oSpy1/HeapAgent/HeapAgent.cs @@ -0,0 +1,369 @@ +// +// Copyright (C) 2009 Ole André Vadla Ravnås +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using EasyHook; +using oSpy.Capture; +using System.Diagnostics; + +namespace oHeapAgent +{ + public class Controller : IEntryPoint + { + private IManager manager; + private EventCoordinator eventCoordinator; + + private LocalHook swapBuffersHook; + private LocalHook mallocHook; + private LocalHook callocHook; + private LocalHook reallocHook; + private LocalHook freeHook; + + #region Hook types + + private const string gdiDll = "gdi32.dll"; + private const string vcrDll = "msvcr90.dll"; + + [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)] + private delegate bool SwapBuffersHandler(IntPtr hdc); + [DllImport(gdiDll, SetLastError = true, CallingConvention = CallingConvention.StdCall)] + private static extern bool SwapBuffers(IntPtr hdc); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = false)] + private delegate IntPtr MallocHandler(int size); + [DllImport(vcrDll, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr malloc(int size); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = false)] + private delegate IntPtr CallocHandler(int num, int size); + [DllImport(vcrDll, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr calloc(int num, int size); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = false)] + private delegate IntPtr ReallocHandler(IntPtr memblock, int size); + [DllImport(vcrDll, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr realloc(IntPtr memblock, int size); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = false)] + private delegate void FreeHandler(IntPtr memblock); + [DllImport(vcrDll, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + private static extern void free(IntPtr memblock); + + #endregion + + public Controller(RemoteHooking.IContext context, + string channelName, + SoftwallRule[] softwallRules) + { + string url = "ipc://" + channelName + "/" + channelName; + manager = Activator.GetObject(typeof(IManager), url) as IManager; + eventCoordinator = new EventCoordinator(); + } + + public void Run(RemoteHooking.IContext context, + string channelName, + SoftwallRule[] softwallRules) + { + try + { + swapBuffersHook = LocalHook.Create( + LocalHook.GetProcAddress(gdiDll, "SwapBuffers"), + new SwapBuffersHandler(OnSwapBuffers), + this); + mallocHook = LocalHook.Create( + LocalHook.GetProcAddress(vcrDll, "malloc"), + new MallocHandler(OnMalloc), + this); + callocHook = LocalHook.Create( + LocalHook.GetProcAddress(vcrDll, "calloc"), + new CallocHandler(OnCalloc), + this); + reallocHook = LocalHook.Create( + LocalHook.GetProcAddress(vcrDll, "realloc"), + new ReallocHandler(OnRealloc), + this); + freeHook = LocalHook.Create( + LocalHook.GetProcAddress(vcrDll, "free"), + new FreeHandler(OnFree), + this); + + Int32[] excludedThreads = new Int32[] { RemoteHooking.GetCurrentThreadId() }; + foreach (LocalHook hook in new LocalHook[] { swapBuffersHook, mallocHook, callocHook, reallocHook, freeHook }) + { + hook.ThreadACL.SetExclusiveACL(excludedThreads); + } + } + catch (Exception ex) + { + MessageBox.Show("Exception: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + RemoteHooking.WakeUpProcess(); + + int myPid = RemoteHooking.GetCurrentProcessId(); + + try + { + manager.Ping(myPid); + + while (true) + { + Thread.Sleep(500); + ProcessAllocations(); + manager.Ping(myPid); + } + } + catch (Exception ex) + { + MessageBox.Show("Exception: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + MessageBox.Show("Terminating", "oHeapAgent", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private bool OnSwapBuffers(IntPtr hdc) + { + RegisterRenderFrame(); + return SwapBuffers(hdc); + } + + private IntPtr OnMalloc(int size) + { + IntPtr result = malloc(size); + RegisterAllocation(size, result); + return result; + } + + private IntPtr OnCalloc(int num, int size) + { + IntPtr result = calloc(num, size); + RegisterAllocation(num * size, result); + return result; + } + + private IntPtr OnRealloc(IntPtr memblock, int size) + { + IntPtr result = realloc(memblock, size); + RegisterReallocation(size, memblock, result); + return result; + } + + private void OnFree(IntPtr memblock) + { + RegisterDeallocation(memblock); + free(memblock); + } + + private List tmpEvents = null; + private List lastFrameEvents = null; + + private void RegisterRenderFrame() + { + lock (this) + { + if (tmpEvents != null) + lastFrameEvents = tmpEvents; + tmpEvents = new List(); + } + } + + private void RegisterAllocation(int size, IntPtr address) + { + if (size == 1382528) + { + Console.WriteLine(); + } + + lock (this) + { + if (tmpEvents == null) + return; + var ev = new AllocateEvent(size, address); + tmpEvents.Add(ev); + } + } + + private void RegisterReallocation(int size, IntPtr oldAddress, IntPtr newAddress) + { + if (size == 1382528) + { + Console.WriteLine(); + } + + lock (this) + { + if (tmpEvents == null) + return; + var ev = new ReallocateEvent(size, oldAddress, newAddress); + tmpEvents.Add(ev); + } + } + + private void RegisterDeallocation(IntPtr address) + { + lock (this) + { + if (tmpEvents == null) + return; + var ev = new DeallocateEvent(address); + tmpEvents.Add(ev); + } + } + + private class HeapEvent + { + protected HeapEvent() + { + } + } + + private class AllocateEvent : HeapEvent + { + public int Size; + public IntPtr Address; + + public AllocateEvent(int size, IntPtr address) + { + Size = size; + Address = address; + } + } + + private class ReallocateEvent : HeapEvent + { + public int Size; + public IntPtr OldAddress; + public IntPtr NewAddress; + + public ReallocateEvent(int size, IntPtr oldAddress, IntPtr newAddress) + { + Size = size; + OldAddress = oldAddress; + NewAddress = newAddress; + } + } + + private class DeallocateEvent : HeapEvent + { + public IntPtr Address; + + public DeallocateEvent(IntPtr address) + { + Address = address; + } + } + + private void ProcessAllocations() + { + List events = null; + + lock (this) + { + if (lastFrameEvents == null) + return; + events = lastFrameEvents; + lastFrameEvents = null; + } + + Dictionary countForSize = new Dictionary(); + foreach (HeapEvent ev in events) + { + // for now + if (ev is DeallocateEvent) + continue; + + int size = 0; + if (ev is AllocateEvent) + { + AllocateEvent allocEv = ev as AllocateEvent; + size = allocEv.Size; + } + else if (ev is ReallocateEvent) + { + ReallocateEvent reallocEv = ev as ReallocateEvent; + size = reallocEv.Size; + } + else + throw new NotImplementedException(); + + int oldCount = 0; + countForSize.TryGetValue(size, out oldCount); + countForSize[size] = oldCount + 1; + } + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(" Size | Count"); + sb.AppendLine("------------------"); + int[] sizes = countForSize.Keys.ToArray(); + Array.Sort(sizes); + Array.Reverse(sizes); + foreach (int size in sizes) + { + sb.AppendFormat("{0,7} | {1}\r\n", size, countForSize[size]); + } + + sb.AppendLine(); + + sb.AppendLine("Events:"); + sb.AppendLine("-------"); + int count = 0; + foreach (HeapEvent ev in events) + { + if (ev is AllocateEvent) + { + AllocateEvent allocEv = ev as AllocateEvent; + sb.AppendFormat("malloc({0}) => 0x{1:x8}\r\n", allocEv.Size, allocEv.Address); + } + else if (ev is ReallocateEvent) + { + ReallocateEvent reallocEv = ev as ReallocateEvent; + sb.AppendFormat("realloc(0x{0:x8}, {1}) => 0x{2:x8}\r\n", reallocEv.OldAddress, reallocEv.Size, reallocEv.NewAddress); + } + else if (ev is DeallocateEvent) + { + DeallocateEvent deallocEv = ev as DeallocateEvent; + sb.AppendFormat("free(0x{0:x8})\r\n", deallocEv.Address); + } + else + { + throw new NotImplementedException(); + } + + count++; + + if (count >= 100) + break; + } + + int remaining = events.Count - count; + if (remaining > 0) + sb.AppendFormat("...and {0} more...\r\n", remaining); + + Event.InvocationOrigin origin = new Event.InvocationOrigin("HeapAgent", null, 42); + PacketEvent pktEv = new PacketEvent(eventCoordinator, origin); + pktEv.Data = Encoding.UTF8.GetBytes(sb.ToString()); + manager.Submit(new Event[] { pktEv }); + } + } +} diff --git a/oSpy1/HeapAgent/Properties/AssemblyInfo.cs b/oSpy1/HeapAgent/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ca17f54 --- /dev/null +++ b/oSpy1/HeapAgent/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle ("oHeapAgent")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("Microsoft")] +[assembly: AssemblyProduct ("oHeapAgent")] +[assembly: AssemblyCopyright ("Copyright © Microsoft 2009")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible (false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid ("5be92aab-ad1c-48b4-8fb1-a4a9fa313a93")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion ("1.0.0.0")] +[assembly: AssemblyFileVersion ("1.0.0.0")] diff --git a/oSpy1/HeapAgent/oHeapAgent.csproj b/oSpy1/HeapAgent/oHeapAgent.csproj new file mode 100644 index 0000000..ebfacda --- /dev/null +++ b/oSpy1/HeapAgent/oHeapAgent.csproj @@ -0,0 +1,75 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {1D965D55-3FD5-450D-8B01-BD3707BF4020} + Library + Properties + oHeapAgent + oHeapAgent + v3.5 + 512 + true + oSpy.snk + + + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\EasyHook\Release\x86\EasyHook.dll + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + {32EB5641-50CC-4A6B-A913-E814EEE3B34D} + oSpy + + + + + + + + \ No newline at end of file diff --git a/oSpy1/oSpy1.sln b/oSpy1/oSpy1.sln index 0d32462..32ecf40 100644 --- a/oSpy1/oSpy1.sln +++ b/oSpy1/oSpy1.sln @@ -14,6 +14,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "oScoutAgent", "ScoutAgent\o EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libudis86", "udis86-1.7\libudis86.vcproj", "{0E5DBEF1-84B5-49B8-91A0-33830F153236}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "oHeapAgent", "HeapAgent\oHeapAgent.csproj", "{1D965D55-3FD5-450D-8B01-BD3707BF4020}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -74,6 +76,16 @@ Global {0E5DBEF1-84B5-49B8-91A0-33830F153236}.Release|Mixed Platforms.Build.0 = Release|Win32 {0E5DBEF1-84B5-49B8-91A0-33830F153236}.Release|Win32.ActiveCfg = Release|Win32 {0E5DBEF1-84B5-49B8-91A0-33830F153236}.Release|Win32.Build.0 = Release|Win32 + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Debug|Win32.ActiveCfg = Debug|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Release|Any CPU.Build.0 = Release|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1D965D55-3FD5-450D-8B01-BD3707BF4020}.Release|Win32.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE