3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LoadStringのループでまだ消耗しているの!?dllなどからストリングテーブルを根こそぎ取得する方法

Last updated at Posted at 2018-05-22

はじめに

Qiita:メッセージテーブル (RT_MESSAGETABLE) に関する姉妹編です。
文字列リソースは一般的にLoadString関数で読み出しますが、リソースIDが不明でも抜き出す方法があります。

コードと解説

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //  サンプルDLL
            var targetFilePath = @"C:\Windows\System32\shell32.dll";

            var strings = EnumResourceStrings(targetFilePath);
            if (!strings.Any())
            {
                Console.WriteLine("読み込めませんでした");
                return;
            }
            foreach (var str in strings)
            {
                Console.WriteLine("{0} : {1}", str.Item1, str.Item2);
            }
            Console.WriteLine("おわりました");

            Console.Read();
        }

        static IEnumerable<System.Tuple<Int32, string>> EnumResourceStrings(string filePath)
        {
            var retVal = new System.Collections.Generic.List<System.Tuple<Int32, string>>();

            //  メッセージテーブルリソースをもつファイルを読み込みます。
            var hModule = NativeMethods.LoadLibraryEx(filePath, IntPtr.Zero, NativeMethods.LoadLibraryExFlags.DONT_RESOLVE_DLL_REFERENCES | NativeMethods.LoadLibraryExFlags.LOAD_LIBRARY_AS_DATAFILE);
            if (!IntPtr.Zero.Equals(hModule))
            {
                try
                {
                    // STRINGテーブルのエントリを列挙させます。
                    NativeMethods.EnumResourceNames(hModule, new IntPtr(NativeMethods.RT_STRING), (hMod, typ, nam, prm) => 
                    {
                        // 文字列リソースIDは、得られたリソース名(INTRESOURCE)-1の16倍
                        var resId = (nam.ToInt32() - 1) * 16;
                        var hRes = NativeMethods.FindResource(hMod, nam, typ);
                        if (!IntPtr.Zero.Equals(hRes))
                        {
                            var hGlobal = NativeMethods.LoadResource(hMod, hRes);
                            if (!IntPtr.Zero.Equals(hGlobal))
                            {
                                var bytesLen = NativeMethods.SizeofResource(hMod, hRes);
                                var ptr = NativeMethods.LockResource(hGlobal);
                                if (!IntPtr.Zero.Equals(ptr))
                                {
                                    Int32 offset = 0;
                                    while(offset < bytesLen)
                                    {
                                        var strLen = (UInt16)Marshal.ReadInt16(ptr + (int)offset);
                                        offset += 2;
                                        if (strLen > 0)
                                        {
                                            var str = Marshal.PtrToStringUni(ptr + (int)offset, strLen);
                                            offset += strLen * 2;
                                            retVal.Add(new System.Tuple<Int32, string>(resId, str));
                                        }
                                        resId += 1;
                                    }
                                }
                            }
                        }
                        return true;
                    }, IntPtr.Zero);
                }
                finally
                {
                    // 読み込んだファイルを閉じます。
                    NativeMethods.FreeLibrary(hModule);
                }
            }

            return retVal;
        }

        class NativeMethods
        {
            public const int RT_STRING = 0x00000006;

            [Flags]
            public enum LoadLibraryExFlags : UInt32
            {
                DONT_RESOLVE_DLL_REFERENCES = 0x0000001,
                LOAD_LIBRARY_AS_DATAFILE = 0x00000002
            }

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr LoadLibraryEx(
                string lpFileName,
                IntPtr hFile,
                LoadLibraryExFlags dwFlags
                );

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool FreeLibrary(
                IntPtr hModule
                );

            [return: MarshalAs(UnmanagedType.Bool)]
            public delegate bool EnumResNameProc(
                IntPtr hModule,
                IntPtr lpszType,
                IntPtr lpszName,
                IntPtr lParam
            );

            [DllImport("kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool EnumResourceNames(
                IntPtr hModule,
                IntPtr lpszType,
                EnumResNameProc lpEnumFunc,
                IntPtr lParam
            );

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr FindResource(
                IntPtr hModule,
                IntPtr lpszName,
                IntPtr lpszType
            );

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr LoadResource(
                IntPtr hModule,
                IntPtr hRsrc
            );

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern UInt32 SizeofResource(
                IntPtr hModule,
                IntPtr hRsrc
            );

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr LockResource(
                IntPtr hResData
            );
        }
    }
}

データ構造

STRINGリソース(RT_STRING)を指定してEnumResourceNames関数を呼び出すと、整数値リソース名が列挙されます。
FindResource関数、LoadResource関数、LockResource関数で得られるリソースデータは、ロックして得られた領域の最後(SizeOfResource関数で領域のバイトサイズが得られます)まで "文字数(2バイト整数) + UNICODE文字列" が連続して格納されています。
先頭の文字列リソースIDは、列挙された整数値リソース名の値から1を引いて16倍したもので、次につづく文字列リソースのリソースIDはその前の文字列リソースに1を加えたものです(つまり、リソースIDは連続しています)

おわりに

内容や誤字のご指摘、お待ちしております。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?