抽出する必要があるデータを含むグリッドを含むレガシー アプリケーションがあります。
そのアプリケーションのコードがなく、通常の方法 (プログラムですべてのセルを選択してクリップボードにコピーするなど) でデータを取得することは不可能です。
そこで、セクション「II. The CreateRemoteThread & LoadLibrary Technique」で説明されているように、DLL インジェクションを使用することにしました。
http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
私の計画は
- レガシー アプリケーションのアドレス空間に DLL をロードします。
- DLL にグリッドからデータを読み取らせ、(たとえば名前付きパイプを介して) 書き出させます。
最初のステップは、レガシー アプリケーションのアドレス空間に DLL を挿入することです (上記のステップ a))。
そのために次のコードを書きました。
int InjectDll (HANDLE hProcess);
int _tmain(int argc, _TCHAR* argv[])
{
printf("DllInjector\n");
/**
* Find out PID of the legacy application (START)
*/
HWND windowHandle = FindWindowW(NULL, L"FORMSSSSS");
DWORD* processID = new DWORD;
GetWindowThreadProcessId(windowHandle, processID);
DWORD delphiAppProcessId = *processID;
/**
* Find out PID of the legacy application (END)
*/
printf("Process ID of legacy app: %lu\n", delphiAppProcessId);
// Now we need the handle of the legacy app
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, delphiAppProcessId);
if (hProcess != NULL)
{
printf("Found handle, ready for injection\n");
int result = InjectDll(hProcess);
CloseHandle( hProcess );
printf("Injection complete, result=%d\n", result);
}
else
{
printf("Handle not found\n");
}
system("pause");
return 0;
}
int InjectDll( HANDLE hProcess )
{
HANDLE hThread;
const char* const szLibPath = "D:\\mycompany\\SampleDll\\Debug\\SampleDll.dll";
void* pLibRemote = 0; // the address (in the remote process) where
// szLibPath will be copied to;
DWORD hLibModule = 0; // base adress of loaded module (==HMODULE);
HMODULE hKernel32 = ::GetModuleHandle(L"Kernel32");
// 1. Allocate memory in the remote process for szLibPath
// 2. Write szLibPath to the allocated memory
pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath), MEM_COMMIT, PAGE_READWRITE );
if( pLibRemote == NULL )
return false;
::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,sizeof(szLibPath),NULL);
// Load "LibSpy.dll" into the remote process
// (via CreateRemoteThread & LoadLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"LoadLibraryA"),
pLibRemote, 0, NULL );
if( hThread == NULL )
goto JUMP;
::WaitForSingleObject( hThread, INFINITE );
// Get handle of loaded module
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
JUMP:
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
if( hLibModule == NULL ) // (1)
return false;
// Unload "LibSpy.dll" from the remote process
// (via CreateRemoteThread & FreeLibrary)
hThread = ::CreateRemoteThread( hProcess,
NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"FreeLibrary"),
(void*)hLibModule,
0, NULL );
if( hThread == NULL ) // failed to unload
return false;
::WaitForSingleObject( hThread, INFINITE );
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
// return value of remote FreeLibrary (=nonzero on success)
return hLibModule;
}
いくつかのコメント:
- レガシー プログラムのタイトルは「FORMSSSSS」です。
- サンプル DLL には、次の DllMain メソッドがあります。
-
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
{
OutputDebugStringA("DllMain called: ");
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
OutputDebugStringA("DLL_PROCESS_ATTACH\n");
case DLL_THREAD_ATTACH:
OutputDebugStringA("DLL_THREAD_ATTACH\n");
case DLL_THREAD_DETACH:
OutputDebugStringA("DLL_THREAD_DETACH\n");
case DLL_PROCESS_DETACH:
OutputDebugStringA("DLL_PROCESS_DETACH\n");
break;
}
return TRUE;
}
呼び出されると、アプリケーションの標準出力にテキストが書き込まれます。
上記のプログラム (_tmain メソッドを使用するプログラム) を実行すると、次のテキストが表示されるはずです。
DllMain called: DLL_PROCESS_ATTACH
コンソール出力に表示されます (これは、DLL インジェクションが成功したことを意味します)。
しかし、それは起こりません。
考えられる原因の 1 つは、レガシー アプリケーションの PID が正しく決定されていないことです。
HWND windowHandle = FindWindowW(NULL, L"FORMSSSSS");
DWORD* processID = new DWORD;
GetWindowThreadProcessId(windowHandle, processID);
DWORD delphiAppProcessId = *processID;
しかし、値 delphiAppProcessId はタスク マネージャーに表示される PID と同じであるため、この潜在的なバグを除外できます。
デバッガーを使用すると、コメント (1) のある行で実行が停止することがわかりました。
JUMP:
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
if( hLibModule == NULL ) // (1)
return false;
サンプル DLL を "FORMSSSSS" というタイトルのアプリケーションのアドレス空間に挿入するには、何を変更する必要がありますか?
更新、2012 年 9 月 16 日:
私はすべての出現を置き換えました
sizeof(szLibPath)
pathLength による、ここで
const int pathLength = strlen(szLibPath)+1;
今
::WaitForSingleObject( hThread, INFINITE );
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
// return value of remote FreeLibrary (=nonzero on success)
return hLibModule;
}
hLibModule はゼロ以外です。これは、インジェクションが成功したことを意味します。
しかし、プログラムの出力にサンプル DLL のログ出力がまだ表示されません。
更新、2012 年 9 月 16 日 (2):
私が
a) サンプル DLL の DllMain に AllocConsole() への呼び出しを追加し、b) 再構築し、c) 注入プログラムを実行します。
Delphi アプリケーションと同じアイコンのコンソール ウィンドウが表示されます。
DllMain 関数から AllocConsole を削除し、注入アプリケーションを実行すると、コンソール ウィンドウが表示されません。
したがって、注射は実際に機能する可能性があります。