C# Scar plugin function lister
Just for fun I wrote a simple little C# plugin function lister. It loads a scar plugin and dumps the functions provided.
It has minimal error checking, but does try to protect itself from library load failure and non-plugin libraries.
Code:
/*
* 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 3 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 may review the terms of the GNU GPL at <http://www.gnu.org/licenses/>.
*
* gryphook@live.com
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace Grippy.ScarPluginInfo
{
/// <summary>
/// Very simple container for Kernel32 library imports
/// </summary>
public static class Kernel32
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32.dll")]
public static extern int FreeLibrary(IntPtr hLibModule);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hwnd, string procedureName);
}
/// <summary>
/// ScarPlugin is a simple utility class to load a plugin that implements
/// the Scar-style plugin interface. It dynamically loads the library and
/// imports the two plugin functions, GetFunctionCount and GetFunctionInfo.
/// It's only function provides a list of all of the plugin functions that
/// the library publishes.
/// ToDo: scrape the PE format to extract the mangled names and match the
/// addresses to the plugin addresses, then generate the code to statically
/// link the library.
/// </summary>
public class ScarPlugin
{
// handle to the library, which will be required in the destructor to unload the library.
private IntPtr lib;
// Name of the library
private string libName;
// simple container for plugin function information.
public class PluginFunction
{
public string name;
public IntPtr address;
}
// delegate types for the plugin functions
public delegate int getFunctionCount();
public delegate int getFunctionInfo(int x, out IntPtr ProcAddr, ref StringBuilder ProcDef);
// variables through which the functions will be called
public getFunctionCount GetFunctionCount;
public getFunctionInfo GetFunctionInfo;
/// <summary>
/// Constructor, attempts to load the specified file and import the plugin functions.
/// Does not error check, so if the library is not a scar plugin, it probably will
/// fail badly.
/// </summary>
/// <param name="plugin"></param>
public ScarPlugin(string plugin)
{
libName = plugin;
// Load the library
lib = Kernel32.LoadLibrary(plugin);
// The Kernel32 function could be wrapped up neatly to do this error
// checking inside the Kernel32 function, but we'll just keep it simple.
if (lib == IntPtr.Zero)
throw new Exception(string.Format("Unable to load the specified library: \"{0}\", is it a dll?", libName));
// import the functions
var gfc = Kernel32.GetProcAddress(lib, "GetFunctionCount");
var gfi = Kernel32.GetProcAddress(lib, "GetFunctionInfo");
// More error checking
if ((gfc == IntPtr.Zero) || (gfi == IntPtr.Zero))
throw new Exception(string.Format("The specified file, \"{0}\", does not appear to be a Scar plugin", libName));
// Set up the delegates for the functions
GetFunctionCount = (getFunctionCount)Marshal.GetDelegateForFunctionPointer(gfc, typeof(getFunctionCount));
GetFunctionInfo = (getFunctionInfo)Marshal.GetDelegateForFunctionPointer(gfi, typeof(getFunctionInfo));
}
~ScarPlugin()
{
Kernel32.FreeLibrary(lib);
}
/// <summary>
/// Return a list all of the functions provided by this plugin.
/// </summary>
/// <returns></returns>
public List<PluginFunction> GetFunctions()
{
try
{
var functions = new List<PluginFunction>();
IntPtr addr;
for (int i = 0; i < GetFunctionCount(); i++)
{
// StringBuilder is mutable, so it can be used like a pchar.
// Reusing the same one seems to cause problems, so create a
// new one for each iteration.
StringBuilder name = new StringBuilder(300);
GetFunctionInfo(i, out addr, ref name);
functions.Add(new PluginFunction() { name = name.ToString(), address = addr });
}
return functions;
}
catch (Exception e)
{
throw new Exception("There was an error while listing the plugin functions.", e);
}
}
}
class ScarPluginInfo
{
static void Main(string[] args)
{
Console.WriteLine("* This is free software provided under the terms of the GNU GPL.");
Console.WriteLine("* You may review the terms of the GNU GPL at <http://www.gnu.org/licenses/>.");
Thread.Sleep(500);
if (args.Count() != 1)
{
Console.Error.WriteLine("Error, provide a single parameter, the name of the plugin to examine");
return;
}
if (!File.Exists(args[0]))
{
Console.Error.WriteLine("Error, specified file not found: {0}", args[0]);
return;
}
try
{
// Create a ScarPlugin instance to load the library
var plugin = new ScarPlugin(args[0]);
// Get a list of all the functions in this library
var functions = plugin.GetFunctions();
// Iterate through the list of functions and print the name
foreach (ScarPlugin.PluginFunction func in functions)
Console.WriteLine(func.name);
}
catch( Exception e )
{
Console.Error.WriteLine("Error: {0}", e.Message);
}
Console.ReadLine();
}
}
}