Created
February 9, 2020 04:31
-
-
Save bb010g/63b53484bccf68c9ca2a1c9d9ef9b12e to your computer and use it in GitHub Desktop.
Extract Windows library resources
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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