У меня есть устаревшее приложение, которое содержит сетку с данными, которые мне нужно извлечь.
У меня нет кода для этого приложения, и невозможно извлечь из него данные обычным способом (например, программным путем выбрать все ячейки и скопировать их в буфер обмена).
Поэтому я решил использовать DLL-инъекцию, как описано в разделе «II. CreateRemoteThread & LoadLibrary Technique «в
http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
Мой план
Первым шагом является внедрение DLL в адресное пространство унаследованного приложения (шаг а) выше).
Я написал следующий код для этого:
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;
}
Некоторые комментарии:
—
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 прошло успешно).
Но этого не происходит.
Одна из возможных причин заключается в том, что 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»?
Обновление от 16.09.2012:
Я заменил все вхождения
SizeOf (szLibPath)
по длине, где
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 в выводе программы.
Обновление от 16.09.2012 (2):
Когда я
а) добавить вызов AllocConsole () в DllMain примера DLL,
б) восстановить его и
в) выполнить инъекционную программу,
затем появится консольное окно, которое имеет тот же значок, что и приложение Delphi.
Когда я удаляю AllocConsole из функции DllMain и запускаю приложение для инъекций, окно консоли не появляется.
Таким образом, инъекция может действительно работать.
Самая большая проблема, которую я вижу, состоит в том, что sizeof(szLibPath)
оценивает размер указателя. использование strlen(szLibPath)+1
вместо.
Наверняка это означает, что ваша инъекция не удастся, потому что путь, который LoadLibraryA
Получатели будут усечены. Могут быть и другие проблемы, но это место для начала.
Других решений пока нет …