先日、以下のスレッドで、プロセスの列挙についてアドバイスをいただきましたが、
そこで列挙されたプロセスをファイルとして保存するプログラムを作成したいと思っています。
https://www.petitmonte.com/bbs/answers?question_id=8828
先ずは、先日のスレッドの中でもご紹介いただいた、Mr.XRAY様の以下のサンプルを参考に、
ListViewのOnDoubleClicに以下のコードを追加しました。
[06_Toolhelp32Snapshot を使用したプロセスの列挙でフルパスを取得]
http://mrxray.on.coocan.jp/Delphi/plSamples/330_AppProcessList.htm#06
procedure TForm1.ListView1DblClick(Sender: TObject);
var
str : string;
int : integer;
pnt : pointer;
pmc : PPROCESS_MEMORY_COUNTERS;
cb : Integer;
hProcHandle : THandle;
begin
int := listview1.ItemIndex;
selected := strtoint(ListView1.Items[int].SubItems[0]);
str := ListView1.Items[int].Caption;
str := ExtractFileName(str);
cb := SizeOf(_PROCESS_MEMORY_COUNTERS);
GetMem(pmc, cb);
pmc^.cb := cb;
hProcHandle := OpenProcess(PROCESS_ALL_ACCESS, FALSE, selected);
GetProcessMemoryInfo(hProcHandle, pmc, cb);
FreeMem(pmc);
pnt := GetModuleBaseAddress(hProcHandle ,str);
if pnt = nil then
showmessage('unko');
end;
そして、そこから呼び出されるベースアドレスの取得関数GetModuleBaseAddressを、
以下の通り追加しました。
function GetModuleBaseAddress(PHandle: Thandle; MName: String): Pointer;
var
Modules : Array of HMODULE;
cbNeeded, i : Cardinal;
ModuleInfo : TModuleInfo;
ModuleName : Array[0..MAX_PATH] of Char;
Pandle : THandle;
begin
Result := nil;
SetLength(Modules, 1024);
cbNeeded := 0;
if (PHandle <> 0) then
begin
try
EnumProcessModules(PHandle, @Modules[0], 1024 * SizeOf(HMODULE),cbNeeded);
except
Exit;
end;
SetLength(Modules, cbNeeded div SizeOf(HMODULE));
if (Length(Modules) > 0) then
begin
for i := 0 to Length(Modules) - 1 do
begin
try
GetModuleBaseName(PHandle, Modules[i], ModuleName, SizeOf(ModuleName));
if (Pos(MName, ModuleName) > 0) then
begin
GetModuleInformation(PHandle, Modules[i], @MoDuleInfo, SizeOf(ModuleInfo));
Result := ModuleInfo.lpBaseOfDll;
Exit;
end;
Except
end;
end;
end;
end;
end;
しかし、Length(Modules)が常に0になるためループに入らず、結果Resultは毎回nilを返します。
EnumProcessModulesのPHandleの値が変化しても結果が変わらないので、呼び方が間違っている様なのですが、
MSDNのヘルプを読んでもどこが違うのか良く分かりません。どこが間違っているのでしょうか?
OSは64bit Windows7 professional Delphiは7です。
Delphi 10.2でも試しましたが、結果は同じでした。
よろしくおねがいします。
メモ帳を起動して確認してみてください.
メモ帳は以下のコマンドで起動ですます.
%SystemRoot%\System32\notepad.exe // 64 ビット版のメモ帳
%SystemRoot%\SysWow64\notepad.exe // 32 ビット版のメモ帳
>メモ帳は以下のコマンドで起動ですます.
Delphi のプログラム (Shellexecute 等を使用して ) からは起動しないでください.
[ ファイル名を指定して実行 ] のダイアログから起動してください.
Mr.XRAY様、ありがとうございます。
コマンドラインからメモ帳を起動してみました。
その結果、32ビット版については、上記と同じ状況でしたが、
64ビット版ではLength(Modules)が0より大きく(35)なり、
nil以外の値($6C000)が戻りました。
32ビットと64ビットのプロセスで、動作が変わるのですね。
勉強になります。
EnumProcessModules 32bitで検索したところ、
Vista以降にはEnumProcessModulesExというのがあるのが分かりましたが、
単純に書き変えても「未定義の識別子」になってしまいました。
以下を読む限り、ヘッダーは同じ様ですが
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms682633(v=vs.85).aspx
EnumProcessModulesEx 関数は 64 ビットのアプリ ( EXE ) 用です.
32 ビットのアプリ ( EXE ) でも使用できますが,( 末尾に Ex がない ) EnumProcessModules 関数と同じ動作仕様になります.
>単純に書き変えても「未定義の識別子」になってしまいました。
以下を参考にしてください.
[VCL で未定義の Windows API 関数の使用 - DLL の使用方法]
http://mrxray.on.coocan.jp/Delphi/Others/Usage_Win32API.htm
>EnumProcessModulesEx 関数は 64 ビットのアプリ ( EXE ) 用です.
>32 ビットのアプリ ( EXE ) でも使用できますが,( 末尾に Ex がない ) EnumProcessModules 関数と同じ動作機能になります.
EnumProcessModulesEx 関数本来の機能を使用するには,64 ビットの Kernel32.dll 内の関数を使用する必要があります.
一般的に 32 ビットの EXE からは 64 ビットの DLL は使用てきません.その逆も同じです.
ただし,以下の様な方法もあります.私はやったことがありませんが.
[プロセスの相互運用性]
https://ht-deko.com/tech070.html#0108
32 ビットの EXE からシステムフォルダ内の 64 ビットの DLL を呼び出すには工夫が必要です.
リダイレクトの機能を無効にしないと 32 ビットの DLL をロードしてしまいます.
Mr.XRAY様、ありがとうございます。
>[VCL で未定義の Windows API 関数の使用 - DLL の使用方法]
非常に勉強になります。Mr.XRAY様のサンプルプロジェクトでも出てくるinterface部の関数の定義の意味が
理解できました。サンプルを参考にして、関数の定義を以下の通り追加し、EnumProcessModulesExは
動作する様になりました。
function EnumProcessModulesEx(hProcess: Cardinal; lphModule: PDWORD;cb: Cardinal;
var lpcbNeeded: Cardinal): pointer; stdcall; external 'PsAPI.dll';
>EnumProcessModulesEx 関数本来の機能を使用するには,64 ビットの Kernel32.dll 内の関数を使用する必要があります.
ということは、追加した定義で動作はするものの、本来の機能は使用できていないのですね。
>一般的に 32 ビットの EXE からは 64 ビットの DLL は使用てきません.その逆も同じです.
更に私が作成しているのが32ビットのEXEなので、それをするためには、DLLの相互運用が必要なのですね。
ただ、メモ帳で試したところ、正常に動いていないのは32ビット版の方なので、Mr.XRAY様のご指摘にもある様に、
これは問題の解決にならないのですね。
64ビット版のメモ帳に関しては、一応ベースアドレスらしいものを返しているので、ロジック的な間違いではなさそうですが、
何が間違っているのでしょうか…
>64ビット版のメモ帳に関しては、一応ベースアドレスらしいものを返しているので
これ,もう一度確認してみてください.違うと思しますよ.
起動しているメモ帳が 32 ビット版か 64 ビット版かの判定は,例えば以下を参考にしてください.
[起動中のアプリが 32 ビットか 64 ビットかをタスクマネージャで確認]
http://mrxray.on.coocan.jp/Delphi/Others/32bitOr64bit.htm#02
Mr.XRAY様
>これ,もう一度確認してみてください.違うと思しますよ.
ご指摘の通りでした。
32ビット版がベースアドレスらしきものを返しており、64ビット版は駄目でした。
EnumProcessModulesExは追加はできたものの、64ビットKernel32.dll内の関数を使用していないため、
64ビット版のアプリケーションのベースアドレスが取得できないのですね。納得できました。
ところで、もう一つ教えていただいてよろしいでしょうか?
GetProcessMemoryInfo(hProcHandle, pmc, cb);
pnt := GetModuleBaseAddress(hProcHandle ,str);
現在、ここでベースアドレスを取得した後、以下の様な感じでプロセスをダンプしようと考えています。
しかしReadProcessMemoryで読み取るサイズが正しく取得できません。現在はPPROCESS_MEMORY_COUNTERSの
WorkingSetSizeをセットしているのですが、保存されたdump.exeをバイナリエディタで見みると、
PEヘッダの途中位でちょん切れています。セットしているサイズが短いのだと思いますが、この値は
どの様にしたら正しく取得できますか?
if GetInfo(selected, dBaseAddr, pmc^.WorkingSetSize) then
begin
hOpen := OpenProcess(PROCESS_VM_READ, FALSE, selected);
if hOpen <> INVALID_HANDLE_VALUE then
begin
dsize := pmc^.WorkingSetSize;
SetLength(bBuff, dSize);//ここの値が間違っている様なのですが…
ReadProcessMemory(hOpen, pnt, @bBuff[0], dSize, dRead);
CloseHandle(hOpen);
hFile := CreateFile(PChar('dump.exe'), GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
if hFile <> INVALID_HANDLE_VALUE then
begin
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
WriteFile(hFile, bBuff[0], dSize, dWritten, nil);
CloseHandle(hFile);
end;
end;
よろしくお願いします。
済みません。見間違いでした。
ダンプしたファイルのバイナリを見ると、先頭と終わりの位置は正しそうですが、
途中に0が沢山挿入された様になっていました。もう少しバイナリの中身を調べてみます。
>よろしくお願いします。
これは多分どなたにも無理だと思います.
> SetLength(bBuff, dSize);//ここの値が間違っている様なのですが…
だとすると dSize が問題となると思いますが,dSize は
>dsize := pmc^.WorkingSetSize;
となっています.これは GetInfo から取得しているようてすが,
肝心の GetInfo がどういう処理をしているか不明です.
最初に提示されたコードもそうですが,他の方が実際に確認可能なコードでない限り無理があります.
基本的に 64 ビットの Windows でシステム的な情報操作には 64 ビットの EXE が必要です.
もちろん,情報の内容によっては可能なものもあります.
では頑張ってくたさい.
Mr.XRAY様
ご指導ありがとうございました。
問題点もさることながら、未定義のAPIの呼び方とか、とても勉強になりました。
今までDelphi7を使っていましたが、64ビット用のコンパイラも使ってみたくなりました。
色々ありがとうございました。
ツイート | ![]() |