プロセスを実行可能な形でディスクに保存するには?

解決


もんちきTwin Turbo  2018-03-27 03:07:19  No: 49101

何時も参考にさせていただいています。ありがとうございます。

現在、Mr.Xray様の以下のプロジェクトを参考に、選択したプロセスを実行ファイルとして保存したいと
考えています。
http://mrxray.on.coocan.jp/Delphi/plSamples/330_AppProcessList.htm#06

現在作成中のプロジェクトでは、上記のプロジェクトに、以下の通りコードを追加しており、
ListView上でプロセスをダブルクリックすると、32bitプロセス限定ですがファイルとして保存されます。

(32bitプロセスに限定される理由については、先日、以下のスレッドで、ご教授いただきました。
https://www.petitmonte.com/bbs/answers?question_id=8833

しかし残念ながら、保存したファイルを実行しようとしても、有効なWin32アプリケーションではない旨のエラーが表示され、
動作してくれません。バイナリエディタでダンプしてみると、一見それっぽいのですが、ディスク上のファイルと比較すると、
PEヘッダの後方やファイルの末尾に0の列が追加されており、全体にフォーマットが崩れてしまっている様に見えます。

GetModuleInformationでサイズを取得すると、ディスク上のサイズより大きな値が戻る様です。
その差分の領域に0が並んでいる様にも見えますが、そうであるなら、ファイルの末尾に0が並びそうに思います。

何が間違っているかお気付きになることがあれば、お聞かせ下さい。
よろしくお願いします。

type
  TByteArray = array of Byte;

.....

function QueryFullProcessImageNameA(Process: THandle; Flags: DWORD; Buffer: PChar;
  Size: PDWORD): Boolean; stdcall; external 'kernel32.dll';
function EnumProcessModulesEx(hProcess: Cardinal; lphModule: PDWORD;cb: Cardinal;
  var lpcbNeeded: Cardinal): pointer; stdcall; external 'PsAPI.dll';

.....

procedure TForm1.ListView1DblClick(Sender: TObject);
var
    str         : string;
    int         : Integer;
    i           : Integer;
    cb          : Integer;
    selected    : Cardinal;
    cbNeeded    : Cardinal;
    pmc         : PPROCESS_MEMORY_COUNTERS;
    Modules     : Array of HMODULE;
    ModuleName  : Array[0..MAX_PATH] of Char;
    ModuleInfo  : TModuleInfo;
    pnt         : pointer;
    hProcHandle : THandle;
    hOpen       : THandle;
    hMod        : THandle;
    hFile       : THandle;
    null        : DWORD;
    dSize       : DWORD;
    dBaseAddr   : DWORD;
    dRead       : DWORD;
    dWritten    : DWORD;
    bBuff       : TByteArray;
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);

  SetLength(Modules, 1024);
  cbNeeded := 0;

  if (hProcHandle <> 0) then
  begin
    try
      EnumProcessModulesEx(hProcHandle, @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(hProcHandle, Modules[i], ModuleName, SizeOf(ModuleName));
          if (Pos(str, ModuleName) > 0) then
          begin
            GetModuleInformation(hProcHandle, Modules[i], @MoDuleInfo, SizeOf(ModuleInfo));
            pnt := ModuleInfo.lpBaseOfDll;
          end;
        Except
        end;
      end;
    end;
  end;
  FreeMem(pmc);

  hOpen := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, FALSE, selected);

  if hOpen <> INVALID_HANDLE_VALUE then
  begin
    EnumProcessModules(hOpen, @hMod, SizeOf(hMod), null);
    if GetModuleInformation(hOpen, hMod, @moduleinfo, SizeOf(moduleinfo)) then
    begin
      dSize  := Cardinal(moduleinfo.SizeOfImage);
      dBaseAddr := Cardinal(moduleinfo.lpBaseOfDll);
    end;
    CloseHandle(hOpen);
  end;

  hOpen := OpenProcess(PROCESS_VM_READ, FALSE, selected);

  if hOpen <> INVALID_HANDLE_VALUE then
  begin
    SetLength(bBuff, dSize);
    ReadProcessMemory(hOpen, pnt, @bBuff[0], dSize, dRead);
    CloseHandle(hOpen);
    hFile := CreateFile(PChar('dump.exe'), GENERIC_WRITE or GENERIC_READ, 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;
end;


Mr.XRAY  2018-03-27 06:30:21  No: 49102

「プロセス」ですか ?
この場合の「プロセスの保存」が何を意味するのかはよく解りませんが...

例えばエクセルを起動してブックを表示します.
あるシートのあるセルにに何かに入力します.

この「プロセス」を保存し,同じ状況を再現したいということでしょうか ?
だとしたらおそらく無理だと思います.
もし,できるとしても相当の困難があるのではないかと.


Mr.XRAY  2018-03-27 07:11:43  No: 49103

>だとしたらおそらく無理だと思います. 

EXE を実行すると 1 つ以上のプロセスが起動します.
プロセスというのは,EXE を実行するとメモリ上に展開されるオブジェクトの総称です.
あるいはインスタンス (実体) のことです.

実行可能とかいう問題,内容のものではありません.


Mr.XRAY  2018-03-27 17:20:48  No: 49104

> 1 つ以上のプロセスが起動します.

正確には「プロセスが生成される」または「プロセスを生成する」ですね.
CreateProcessという 関数はそのものズバリです.
Create (生成する) Process (プロセスを).
日本語英語なら ProcessCreate ですね.

ExecuteProcess でも RunProcess でもありません.


take  2018-03-27 17:52:03  No: 49105

内容に興味があったので調べました。
そして答えが何とコミケの同人誌にありました。

「exe ファイルをダブ ルクリックすると何 が起こるか」という書籍名で販売された後、
作者のご厚意で無償公開されているようです。

これによるとEXEや使用しているDLLがイメージローダーによりメモリ上に展開されてた後
エントリーポイントから実行が開始されるという事ですので
プロセス上にあるものは展開後のEXEであってここからEXEを作る事は困難なものと思われます。

コマンドプロンプト上で動いているものだったらあるいは・・・


もんちきTwin Turbo  2018-03-27 19:55:40  No: 49106

Mr.XRAY様、take様、ありがとうございます。

>この場合の「プロセスの保存」が何を意味するのかはよく解りませんが... 

説明が不十分で申し訳ありません。

私がイメージしていたのは単純に、例えば実行中のメモ帳(notepad.exe)を、
先頭からプロセスの長さ分書き出したら、notepad.exeとして保存できないか…
というもので、例えばその際、メモ帳上に編集中のデータがあった場合はどうなるかについては、
正直、考えが及んでいませんでした。

>プロセスというのは,EXE を実行するとメモリ上に展開されるオブジェクトの総称です. 
>あるいはインスタンス (実体) のことです. 

単純にexeがメモリ上にコピーされるのではないのですね…

言われてみれば、例えば条件分岐でインスタンスを動的に生成した場合としない場合では、
メモリ上は別物になるはずですね。

>実行可能とかいう問題,内容のものではありません.

そうなのですね。

私は、自分のプログラムの間違いで、正しく保存されていないために実行できないのかと思っていましたが、
そもそも実行中のプロセスは、そのまま書き出しても実行できるものではないのですね。勉強になりました。

>「exe ファイルをダブ ルクリックすると何 が起こるか」という書籍名で販売された後、 
>作者のご厚意で無償公開されているようです。 

早速、ネットで探しました。
専門的なので、私が理解するには時間がかかりそうですが、有益な情報ですね。
専門書の様な同人誌ですね。恐るべし…

>これによるとEXEや使用しているDLLがイメージローダーによりメモリ上に展開されてた後 
>エントリーポイントから実行が開始されるという事ですので 
>プロセス上にあるものは展開後のEXEであってここからEXEを作る事は困難なものと思われます。 

私は、てっきりコンパイルされる時、必要な機能がインクルードされる様にイメージしていましたが、
考えてみれば、DLLファイルは別にある訳ですから、実行される時にロードされる訳ですよね。
それだけでも、exeと実行時の状態は、別物ですね。

Mr.XRAY様とtake様にご教授いただいて、私の理解が随分間違っていたことが分かり、勉強になりました。
当初プログラムの問題かと思っていましたが、これはDelphiの質問では無かったかも知れませんね。

>コマンドプロンプト上で動いているものだったらあるいは・・・ 

なるほど…やってみます。


Mr.XRAY  2018-03-27 20:10:55  No: 49107

>私の理解が随分間違っていたことが分かり、勉強になりました。

間違いというかは分かりませんが,誤解されている方は多いと思います.多分 (^^;
一般的に「プロセスを起動」という使い方もします.
このような用語の使い方は他にも多くありますね.
理解して使っている分には問題ないと思います.


もんちきTwin Turbo  2018-03-28 01:57:06  No: 49108

>コマンドプロンプト上で動いているものだったらあるいは・・・ 

結果からご報告しますと、残念ながら駄目でした(>ω<)

Mr.XRAY様の以下のサンプルをそのまま使わせていただき、入力待ちをするコンソールアプリケーションで
試してみましたが、矢張りディスクに保存したプロセスは、実行できませんでした。
http://mrxray.on.coocan.jp/Delphi/plSamples/510_Console1.htm

更にオリジナルと比較しながら、増加している0をバイナリエディタで削除してみようとしましたが、
教えていただいたとおり、比較してみると、全体的に似ているものの途中が色々変化しており、
単純ではありませんでした。(ちなみに、文字を入力しエンターする前に保存すると、バイナリでは
Hello World!!の後ろに入力した文字列もそのまま書き出されました。)
ただ、この書き出したファイルを上手に整理したら、実行可能なファイルになりそうな気もします…

>間違いというかは分かりませんが,誤解されている方は多いと思います.多分 (^^; 

はい。少なくとも私は、誤解していました。

Delphiからずれた質問になってしまいましたが、とても勉強になりました。

Mr.XRAY様、take様、ありがとうございました。


※返信する前に利用規約をご確認ください。








  このエントリーをはてなブックマークに追加