Search code examples
c#resourcesportable-executable

C# Extract Resource from Native PE


I seem to be failing to find the correct answer to what I am trying to do here.

In my C# code, I would like to extract resources from another executable that would be passed by user prompt. The other executable would be a native PE file.

As an example, I need to pull the KDATA\106 resource from this PE file (sample.exe). This is what it looks like in ResourceHacker.

ResourceHacker display of PE resources

I seem to be only able to find info on how to extract from my program, or parsing from another project.


Solution

  • I ended up marshalling the code and handling this as if it was C++.

    One important thing to note about FindResource() is that it expects the lpName to be prepended with # if you pass it a string instead of an integer. MSDN Page

    If the first character of the string is a pound sign (#), the remaining characters represent a decimal number that specifies the integer identifier of the resource's name or type. For example, the string "#258" represents the integer identifier 258.

    First I marshal out all of the important functions from kernel32.dll in a separate class, then call the resource by name and type. (I grabbed most of this class from a blog that I cannot find again, will link if I locate it again.) Note that I have marshaled the parameters of FindResource as strings and not integers, for simplicity in C# (no need for hacks with MAKEINTRESOURCE).

    ResourceManager.cs

    class ResourceManager
    {    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
    
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr LoadLibrary(string lpFileName);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);
        //  public static extern IntPtr FindResource(IntPtr hModule, int lpName, uint lpType);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LockResource(IntPtr hResData);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool EnumResourceNames(IntPtr hModule, string lpType, IntPtr lpEnumFunc, IntPtr lParam);
    
        public static byte[] GetResourceFromExecutable(string lpFileName, string lpName, string lpType)
        {
            IntPtr hModule = LoadLibrary(lpFileName);
            if (hModule != IntPtr.Zero)
            {
                IntPtr hResource = FindResource(hModule, lpName, lpType);
                if (hResource != IntPtr.Zero)
                {
                    uint resSize = SizeofResource(hModule, hResource);
                    IntPtr resData = LoadResource(hModule, hResource);
                    if (resData != IntPtr.Zero)
                    {
                        byte[] uiBytes = new byte[resSize];
                        IntPtr ipMemorySource = LockResource(resData);
                        Marshal.Copy(ipMemorySource, uiBytes, 0, (int)resSize);
                        return uiBytes;
                    }
                }
            }
            return null;
        }
    }
    

    Main.cs

    public Main(){
    
        string path = @"C:\sample.exe";
        // Get the raw bytes of the resource
        byte[] resource = ResourceManager.GetResourceFromExecutable(path, "#106", "KDATA");
    
    }