Hatena::Grouprubyist

Rubyで遊ぶよ

 | 

2011-04-05

Windows の Ruby が開いてる dll の一覧を見る

12:21

Win32API を使って、Ruby が開いている .dll を確認してみる。

方法はいくつかある。


方法1

まず、CreateToolhelp32Snapshot でプロセスのスナップショットを撮って、Module32First と Module32Next を使う方法。

↑のコードの主要部分を抜き出すと、こんな感じ。

require 'Win32API'

# http://msdn.microsoft.com/en-us/library/ms686849(v=vs.85).aspx

# load functions

# DWORD WINAPI GetCurrentProcessId(void);
win32_GetCurrentProcessId = Win32API.new('kernel32.dll', 'GetCurrentProcessId', 'V', 'L')
# HANDLE WINAPI CreateToolhelp32Snapshot( __in DWORD dwFlags, __in DWORD th32ProcessID );
win32_CreateToolhelp32Snapshot = Win32API.new('kernel32.dll', 'CreateToolhelp32Snapshot', 'LL', 'L')
# BOOL WINAPI Module32First( __in HANDLE hSnapshot, __inout LPMODULEENTRY32 lpme );
win32_Module32First = Win32API.new('kernel32.dll', 'Module32First', 'LP', 'I')
# BOOL WINAPI Module32Next( __in HANDLE hSnapshot, __out LPMODULEENTRY32 lpme );
win32_Module32Next = Win32API.new('kernel32.dll', 'Module32Next', 'LP', 'I')
# BOOL WINAPI CloseHandle( __in HANDLE hObject );
win32_CloseHandle = Win32API.new('kernel32.dll', 'CloseHandle', 'L', 'I')

# constants

_TH32CS_SNAPMODULE = 8
_INVALID_HANDLE_VALUE = -1
sizeof_MODULEENTRY32 = 548 # is this for i686 only?

# start here

dwPID = win32_GetCurrentProcessId.call()
hModuleSnap = win32_CreateToolhelp32Snapshot.call(_TH32CS_SNAPMODULE, dwPID)
if hModuleSnap == _INVALID_HANDLE_VALUE
  raise "Invalid handle value returned by CreateToolhelp32Snapshot"
end

# set dwSize first
me32 = [sizeof_MODULEENTRY32].pack("N") +
      "\0" * (sizeof_MODULEENTRY32 - 4) # sizeof DWORD = 4

if !win32_Module32First.call(hModuleSnap, me32)
  win32_CloseHandle.call(hModuleSnap)
  raise "Error in Module32First"
end

while 1
  #module_name = me32.slice(32, 256).sub(/\0.*/, '')
  executable = me32.slice(288, 260).sub(/\0.*/, '')

  puts executable

  break if 0 == win32_Module32Next.call(hModuleSnap, me32)
end

win32_CloseHandle.call(hModuleSnap)

LPMODULEENTRY32 というのはこういう構造体へのポインターになっている。

typedef struct tagMODULEENTRY32 {         // size
  DWORD   dwSize;                         // 4
  DWORD   th32ModuleID;                   // 4
  DWORD   th32ProcessID;                  // 4
  DWORD   GlblcntUsage;                   // 4
  DWORD   ProccntUsage;                   // 4
  BYTE    *modBaseAddr;                   // 1 (occupy 4 bytes due to alignment)
  DWORD   modBaseSize;                    // 4
  HMODULE hModule;                        // 4
  TCHAR   szModule[MAX_MODULE_NAME32 + 1];// 256
  TCHAR   szExePath[MAX_PATH];            // 260
} MODULEENTRY32, *PMODULEENTRY32;         // 545 (total of above)

ここで気になるのが、この構造体のサイズは alignment によって変わるという点。うちではワード長が4なので MODULEENTRY32 のサイズは548に切り上げられるため、上のコードではそれが前提になっているが、もしかすると546に切り上げられたり、545のままの環境があるかもしれない。

また、うちでは sizeof(HANDLE) が4なんだけど、HANDLE の実体はポインターなので、long に変換するのは良くない気がしないでもない。(Windows 7 は 64bit OS でもポインターのサイズが 32bit なんだっけ?これは Ruby が 32bit でビルドされてるからか


方法2

もうひとつは EnumProcessModules を使う方法。こっちもありがたくサンプルコードがあるので、写経するだけ。

#!/usr/bin/ruby

require 'Win32API'
# http://msdn.microsoft.com/en-us/library/ms682621(v=vs.85).aspx

# functions

# DWORD WINAPI GetCurrentProcessId(void);
win32_GetCurrentProcessId = Win32API.new('kernel32.dll', 'GetCurrentProcessId', 'V', 'L')
# HANDLE WINAPI OpenProcess( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in DWORD dwProcessId );
win32_OpenProcess = Win32API.new('kernel32.dll', 'OpenProcess', 'LIL', 'L')
# BOOL WINAPI EnumProcessModules( __in HANDLE hProcess, __out HMODULE *lphModule, __in DWORD cb, __out LPDWORD lpcbNeeded );
win32_EnumProcessModules =
  Win32API.new('kernel32.dll', 'EnumProcessModules', 'LPLP', 'I') rescue
  Win32API.new('psapi.dll', 'EnumProcessModules', 'LPLP', 'I') # psapi.dll on XP
# DWORD WINAPI GetModuleFileName( __in_opt HMODULE hModule, __out LPTSTR lpFilename, __in DWORD nSize );
win32_GetModuleFileName = Win32API.new('kernel32.dll', 'GetModuleFileName', 'LPL', 'L')
# BOOL WINAPI CloseHandle( __in HANDLE hObject );
win32_CloseHandle = Win32API.new('kernel32.dll', 'CloseHandle', 'L', 'I')

# constants

_PROCESS_QUERY_INFORMATION = 0x0400
_PROCESS_VM_READ = 0x0010
_MAX_PATH = 260
sizeof_HANDLE = 4

# start here

buflen = 1024
hMods = "\0" * sizeof_HANDLE * buflen

processID = win32_GetCurrentProcessId.call()
hProcess = win32_OpenProcess.call(_PROCESS_QUERY_INFORMATION | _PROCESS_VM_READ, 0, processID)
raise "Error in OpenProcess" if 0 == hProcess

cbNeeded = "\0" * 4
if 0 != win32_EnumProcessModules.call(hProcess, hMods, hMods.length, cbNeeded)
  len = cbNeeded.unpack("l!")[0] / sizeof_HANDLE
  hMods.unpack("l!#{len}").each {|hModule|
    szModName = "\0" * _MAX_PATH

    if 0 != win32_GetModuleFileName.call(hModule, szModName, _MAX_PATH)
      puts szModName.sub(/\0.*/, '')
    end
  }
end

win32_CloseHandle.call(hProcess)

とりあえず alignment の問題はなくなった。HANDLE と DWORD のサイズが4であるとしているところや、GetModuleFileName が LPTSTR を受け取るべきなのに勝手に LPSTR と想定しているのが問題と言えば問題。しかしうちでは動いているのが謎。Ruby のコンパイルのときに UNICODE が付いてないってことだろうか?


方法3

EnumerateLoadedModulesEx とか EnumerateLoadedModules64 というのがあるらしい。しかし、これらはコールバック関数ベースなので、たぶん Ruby で使うのは無理だと思う。


実行結果

方法1と方法2の実行結果は同じ。

CygwinRuby から使ってみると、こんな感じ。

% ruby dlls.rb
C:\cygwin\bin\ruby.exe
C:\Windows\SysWOW64\ntdll.dll
C:\Windows\syswow64\kernel32.dll
C:\Windows\syswow64\KERNELBASE.dll
C:\cygwin\bin\cygruby18.dll
C:\cygwin\bin\cygcrypt-0.dll
C:\cygwin\bin\cygwin1.dll
C:\Windows\syswow64\ADVAPI32.DLL
C:\Windows\syswow64\msvcrt.dll
C:\Windows\SysWOW64\sechost.dll
C:\Windows\syswow64\RPCRT4.dll
C:\Windows\syswow64\SspiCli.dll
C:\Windows\syswow64\CRYPTBASE.dll
C:\Windows\syswow64\USER32.dll
C:\Windows\syswow64\GDI32.dll
C:\Windows\syswow64\LPK.dll
C:\Windows\syswow64\USP10.dll
C:\Windows\system32\IMM32.DLL
C:\Windows\syswow64\MSCTF.dll
\\?\C:\cygwin\lib\ruby\1.8\i386-cygwin\Win32API.so
C:\Windows\syswow64\psapi.dll

もしスクリプトの一番上で require 'iconv' と書いたら、↓のような行が増える。

\\?\C:\cygwin\lib\ruby\1.8\i386-cygwin\iconv.so
C:\cygwin\bin\cygiconv-2.dll
C:\cygwin\bin\cyggcc_s-1.dll

この \\?\ というのは、Windows のパス指定のときの習慣で、MSDN に説明がある(と教えてもらった)。

とにかく、cygwin 由来の .dll や .so についてはこの接頭辞が付くことが多いので、cygwin の中間層で付け足して開いているのだと思う。

実際、mingw32 や mswin32 版の Ruby ではそういうのはつかない。

% /cygdrive/c/Ruby186/bin/ruby dlls.rb
C:\Ruby186\bin\ruby.exe
C:\Windows\SysWOW64\ntdll.dll
C:\Windows\syswow64\kernel32.dll
C:\Windows\syswow64\KERNELBASE.dll
C:\Ruby186\bin\msvcrt-ruby18.dll
C:\Windows\syswow64\ADVAPI32.DLL
C:\Windows\syswow64\msvcrt.dll
C:\Windows\SysWOW64\sechost.dll
C:\Windows\syswow64\RPCRT4.dll
C:\Windows\syswow64\SspiCli.dll
C:\Windows\syswow64\CRYPTBASE.dll
C:\Windows\syswow64\SHELL32.DLL
C:\Windows\syswow64\SHLWAPI.dll
C:\Windows\syswow64\GDI32.dll
C:\Windows\syswow64\USER32.dll
C:\Windows\syswow64\LPK.dll
C:\Windows\syswow64\USP10.dll
C:\Windows\syswow64\WS2_32.DLL
C:\Windows\syswow64\NSI.dll
C:\Windows\system32\apphelp.dll
C:\Windows\AppPatch\AcLayers.DLL
C:\Windows\syswow64\ole32.dll
C:\Windows\syswow64\OLEAUT32.dll
C:\Windows\system32\USERENV.dll
C:\Windows\system32\profapi.dll
C:\Windows\system32\WINSPOOL.DRV
C:\Windows\system32\MPR.dll
C:\Windows\system32\IMM32.DLL
C:\Windows\syswow64\MSCTF.dll
C:\Windows\system32\mswsock.dll
C:\Ruby186\lib\ruby\1.8\i386-mingw32\iconv.so
C:\Ruby186\bin\libiconv2.dll
C:\Ruby186\lib\ruby\1.8\i386-mingw32\Win32API.so
C:\Windows\syswow64\psapi.dll

結論

Win32API は遊び以上の目的で使うものではないかな。

PhilipkagPhilipkag 2017/04/12 05:26 http://stemmeries.xyz <a href="http://stemmeries.xyz">norsk kasino</a> http://stemmeries.xyz - norsk kasino

ゲスト



トラックバック - http://rubyist.g.hatena.ne.jp/edvakf/20110405
リンク元
 |