Skip to content

Instantly share code, notes, and snippets.

@bb010g
Created February 9, 2020 04:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bb010g/63b53484bccf68c9ca2a1c9d9ef9b12e to your computer and use it in GitHub Desktop.
Save bb010g/63b53484bccf68c9ca2a1c9d9ef9b12e to your computer and use it in GitHub Desktop.
Extract Windows library resources
using System;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
namespace ExtractLibraryResource
{
[System.Security.SecurityCritical]
public class SafeLibraryHandle :
Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
[System.Security.SecurityCritical]
internal SafeLibraryHandle() : base(true) {}
[System.Security.SecurityCritical]
public SafeLibraryHandle(IntPtr preexistingHandle, bool ownsHandle) :
base(ownsHandle)
{
SetHandle(preexistingHandle);
}
[System.Security.SecurityCritical]
override protected bool ReleaseHandle()
{
return FreeLibrary(handle);
}
public bool IsDatafile
{
get { return ((int)handle & 1) == 1; }
}
public bool IsImagemapping
{
get { return ((int)handle & 2) == 2; }
}
public bool IsResource
{
get { return IsImagemapping || IsDatafile; }
}
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
[ResourceExposure(ResourceScope.Process)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool FreeLibrary(IntPtr hLibModule);
}
public class Library: IDisposable
{
internal const string KERNEL32 = "kernel32.dll";
internal const string USER32 = "user32.dll";
private SafeLibraryHandle _handle;
private Library(SafeLibraryHandle handle)
{
this._handle = handle;
}
[Flags]
public enum LoadFlags {
None = 0,
IgnoreCodeAuthzLevel = 0x01,
AsImageResource = 0x02,
}
public enum LoadAsDatafile {
Disable = 0,
Enable = 1,
Exclusive = 2,
}
[Flags]
public enum LoadSearchFlags {
AlteredPath = 0,
None = 0x01,
DllLoadDir = 0x02,
ApplicationDir = 0x04,
UserDirs = 0x08,
System32 = 0x10,
DefaultDirs = 0x20,
}
public static Library Load(string name,
LoadFlags flags = LoadFlags.None,
LoadAsDatafile datafile = LoadAsDatafile.Disable,
LoadSearchFlags searchFlags = LoadSearchFlags.None)
{
SafeLibraryHandle handle;
if (flags == LoadFlags.None &&
datafile == LoadAsDatafile.Disable &&
searchFlags == LoadSearchFlags.None)
{
handle = LoadLibrary(name);
}
else
{
LoadLibraryExFlags callFlags = LoadLibraryExFlags.None;
if ((flags & LoadFlags.IgnoreCodeAuthzLevel) != 0)
{
callFlags |= LoadLibraryExFlags.IgnoreCodeAuthzLevel;
}
if ((flags & LoadFlags.AsImageResource) != 0)
{
callFlags |= LoadLibraryExFlags.AsImageResource;
}
if (datafile != LoadAsDatafile.Disable)
{
if (!Enum.IsDefined(typeof(LoadAsDatafile), datafile))
{
throw new ArgumentOutOfRangeException("datafile",
datafile, "Invalid LoadAsDatafile");
}
if (datafile == LoadAsDatafile.Exclusive)
{
callFlags |= LoadLibraryExFlags.AsDatafileExclusive;
}
else
{
callFlags |= LoadLibraryExFlags.AsDatafile;
}
}
if (searchFlags == LoadSearchFlags.AlteredPath)
{
callFlags |= LoadLibraryExFlags.WithAlteredSearchPath;
}
else
{
if ((searchFlags & LoadSearchFlags.DllLoadDir) != 0)
{
callFlags |= LoadLibraryExFlags.SearchDllLoadDir;
}
if ((searchFlags & LoadSearchFlags.ApplicationDir) != 0)
{
callFlags |= LoadLibraryExFlags.SearchApplicationDir;
}
if ((searchFlags & LoadSearchFlags.UserDirs) != 0)
{
callFlags |= LoadLibraryExFlags.SearchUserDirs;
}
if ((searchFlags & LoadSearchFlags.System32) != 0)
{
callFlags |= LoadLibraryExFlags.SearchSystem32;
}
if ((searchFlags & LoadSearchFlags.DefaultDirs) != 0)
{
callFlags |= LoadLibraryExFlags.SearchDefaultDirs;
}
}
handle = LoadLibraryEx(name, IntPtr.Zero, callFlags);
}
return handle.IsInvalid ? null : new Library(handle);
}
public static Library LoadDatafile(string name,
LoadFlags flags = LoadFlags.None,
bool exclusive = false,
LoadSearchFlags searchFlags = LoadSearchFlags.None)
{
return Load(name, flags | LoadFlags.AsImageResource,
exclusive ? LoadAsDatafile.Exclusive :
LoadAsDatafile.Enable,
searchFlags);
}
public enum GetHandleMode {
LeaveRefcount,
IncrementRefcount,
PinUntilTermination,
}
internal static Library GetHandle(string moduleName, IntPtr? address,
GetHandleMode mode)
{
IntPtr ptr;
bool shouldOwn = false;
if (address == null && mode == GetHandleMode.LeaveRefcount)
{
ptr = GetModuleHandle(moduleName);
}
else if (!Enum.IsDefined(typeof(GetHandleMode), mode))
{
throw new ArgumentOutOfRangeException("mode", mode,
"Invalid GetHandleMode");
}
else
{
GetModuleHandleExFlags flags = GetModuleHandleExFlags.None;
if (mode == GetHandleMode.IncrementRefcount)
{
shouldOwn = true;
}
else if (mode == GetHandleMode.PinUntilTermination)
{
flags |= GetModuleHandleExFlags.Pin;
}
else
{
flags |= GetModuleHandleExFlags.UnchangedRefcount;
}
bool ret = (moduleName == null && address != null) ?
GetModuleHandleExFromAddress(flags, (IntPtr)address,
out ptr) :
GetModuleHandleEx(flags, moduleName, out ptr);
if (!ret)
{
return null;
}
}
SafeLibraryHandle handle = new SafeLibraryHandle(ptr, shouldOwn);
return handle.IsInvalid ? null : new Library(handle);
}
public static Library GetHandle(string moduleName,
GetHandleMode mode = GetHandleMode.LeaveRefcount)
{
return GetHandle(moduleName, null, mode);
}
public static Library GetHandleFromAddress(IntPtr address,
GetHandleMode mode = GetHandleMode.LeaveRefcount)
{
return GetHandle(null, address, mode);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
protected virtual void Dispose(bool disposing)
{
if (_handle != null && !_handle.IsInvalid)
{
_handle.Dispose();
}
}
public bool IsDatafile
{
get { return _handle.IsDatafile; }
}
public bool IsImagemapping
{
get { return _handle.IsImagemapping; }
}
public bool IsResource
{
get { return _handle.IsResource; }
}
public string LoadString(uint id)
{
IntPtr str;
int ret = LoadStringPtr(_handle, id, out str, 0);
return ret == 0 ? null : Marshal.PtrToStringAuto(str, ret);
}
public int LoadString(uint id, StringBuilder buffer)
{
int capacity = buffer.Capacity;
int ret = LoadString(_handle, id, buffer, buffer.Capacity);
if (ret == 0)
{
return 0;
}
buffer.Length = ret;
return ret;
}
public delegate bool EnumResType(Library lib, string type);
public delegate bool EnumResTypeWithParam(Library lib, string type,
ref int param);
public bool EnumResourceTypes(EnumResType enumFunc)
{
int param = 0;
EnumResTypeProc lpEnumFunc = delegate(SafeLibraryHandle hInstance,
IntPtr lpszType, ref int lParam)
{
Library lib = new Library(hInstance);
string type = ((long)lpszType >> 16) == 0L ?
$"#{(int)lpszType}" : Marshal.PtrToStringAuto(lpszType);
return enumFunc(lib, type);
};
return EnumResourceTypes(_handle, lpEnumFunc, ref param);
}
public bool EnumResourceTypes(EnumResTypeWithParam enumFunc,
ref int param)
{
EnumResTypeProc lpEnumFunc = delegate(SafeLibraryHandle hInstance,
IntPtr lpszType, ref int lParam)
{
Library lib = new Library(hInstance);
string type = ((long)lpszType >> 16) == 0L ?
$"#{(int)lpszType}" : Marshal.PtrToStringAuto(lpszType);
return enumFunc(lib, type, ref lParam);
};
return EnumResourceTypes(_handle, lpEnumFunc, ref param);
}
public List<string> ListResourceTypes()
{
List<string> types = new List<string>();
EnumResourceTypes(
(lib, type) => { types.Add(type); return true; });
return types;
}
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr GetModuleHandle(string lpLibFileName);
[Flags]
internal enum GetModuleHandleExFlags : uint
{
None = 0,
Pin = 0x00000001,
UnchangedRefcount = 0x00000002,
FromAddress = 0x00000004,
}
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool GetModuleHandleEx(
GetModuleHandleExFlags dwFlags, string lpLibFileName,
out IntPtr phModule);
[DllImport(KERNEL32,
EntryPoint = "GetModuleHandleEx",
SetLastError = true)]
internal static extern bool GetModuleHandleExFromAddress(
GetModuleHandleExFlags dwFlags, IntPtr lpLibFileName,
out IntPtr phModule);
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern SafeLibraryHandle LoadLibrary(
string lpLibFileName);
[Flags]
internal enum LoadLibraryExFlags : uint
{
None = 0,
DontResolveDllReferences = 0x00000001,
AsDatafile = 0x00000002,
WithAlteredSearchPath = 0x00000008,
IgnoreCodeAuthzLevel = 0x00000010,
AsImageResource = 0x00000020,
AsDatafileExclusive = 0x00000040,
SearchDllLoadDir = 0x00000100,
SearchApplicationDir = 0x00000200,
SearchUserDirs = 0x00000400,
SearchSystem32 = 0x00000800,
SearchDefaultDirs = 0x00001000,
}
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern SafeLibraryHandle LoadLibraryEx(
string lpLibFileName, IntPtr hFile,
LoadLibraryExFlags dwFlags);
[DllImport(USER32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int LoadString(SafeLibraryHandle hInstance,
uint uID, StringBuilder lpBuffer, int cchBufferMax);
[DllImport(USER32, EntryPoint = "LoadString",
CharSet = CharSet.Auto,
SetLastError = true)]
internal static extern int LoadStringPtr(SafeLibraryHandle hInstance,
uint uID, out IntPtr lpBuffer, int cchBufferMax);
internal delegate bool EnumResTypeProc(SafeLibraryHandle hInstance,
IntPtr lpszType, ref int lParam);
[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool EnumResourceTypes(
SafeLibraryHandle hModule, EnumResTypeProc lpEnumFunc,
ref int lParam);
}
public static class Extract {
public static List<string> LibraryResourceTypes(string libFileName)
{
List<string> result;
using (Library lib = Library.LoadDatafile(libFileName))
{
result = lib.ListResourceTypes();
}
return result;
}
public static string ExtractLibraryString(string libFileName, uint id)
{
string result;
using (Library lib = Library.LoadDatafile(libFileName))
{
result = lib.LoadString(id);
}
return result;
}
public static List<string> ExtractLibraryStringRange(
string libFileName, uint startId, int count)
{
List<string> result = new List<string>();
using (Library lib = Library.LoadDatafile(libFileName))
{
for (uint id = startId; id < count; id++)
{
result.Add(lib.LoadString(id));
}
}
return result;
}
}
}
// vim:ft=cs:et:sw=4:tw=78
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment