Господа здравствуйте! Уперся в реализацию APC, не могу ее победить. В интернете много информации по этому поводу: (более-менее полные статьи) http://www.rsdn.ru/article/baseserv/InjectDll.xml http://www.opening-windows.com/techart_windows_vista_apc_internals.htm Но к сожалению из имеющейся информации сложно собрать нечто работающее. Мне требуется заинжектить в режиме юзера тестовую dll'ку в тестовый процесс, выполнить код в этом процессе. Код (Text): bool Inject(HANDLE hProcess, HANDLE hThread, LPCWSTR dllPath) { SIZE_T dllPathLen = wcslen(dllPath); void *pmem = VirtualAllocEx(hProcess, NULL, dllPathLen + 1, MEM_COMMIT, PAGE_READWRITE); if (pmem) { SIZE_T bytesWritten = 0; WriteProcessMemory(hProcess, pmem, dllPath, dllPathLen, &bytesWritten); if (dllPathLen == bytesWritten) { if (QueueUserAPC((PAPCFUNC)GetProcAddress(LoadLibraryA("kernel32.dll"), "LoadLibraryA"), hThread, (ULONG_PTR)pmem)) { return true; } } } return false; } Представленная функция отрабатывает успешно (происходит 'return true'), но библиотека не инжектится, не отрабатывает DllMain. Где я тупанул? Заранее благодарен!
Уважаемый Xml правильно ли я понял? --Получается что APC могут быть добавлены в очередь (на выполнение) тредами которые принадлежат процессу. Иными словами из чужого треда APC добавить не получится, необходимо найти тред целевого процесса и добавить ему в очередь выполнения мой APC? Ну с этим все понятно... --Получается что тред не инструктируется о необходимости выполнении APC в случае когда он не находится в состоянии "alertable". Вопрос состоит в очередности, тред должен перейти в состояние "alertable" после того как я добавил свой APC, или тред необходимо перевести в это состояние до того как добавлять свой APC
APC будет вызвана только если поток находится в состоянии alertable из режима пользователя для удаленного потока в общем случае это(узнать/переключить его состояние) не решить
sysluck Чужими тоже могут, но это не рекомендуется - типа "кабы чего не вышло" Вопрос не в очередности, а в том, можешь ли ты быть уверен в том, что целевой поток вообще когда-либо переходит в состояние alertable, поскольку, если он сам в это состояние не переходит (вызовом соотв-х Ex-функций), то заставить его сделать это извне невозможно (если только исхитриться выставить APC потоку, который уже создан, но еще не начал выполняться - тогда APC будет вызвана перед стартом потока)
sysluck Если бы данное действие было бы реализовано в кернеле, то никто не мешает выставить флаг Alertable в структуре EPROCCESS.
TermoSINteZ Насколько я понимаю, этого не достаточно -- надо сделать, чтобы поток уснул, а не только выставлять Alertable.
Mika0x65 Хм.. ну тут на самом деле я встречался с разными ситуациями , в зависимости от версии Винды. Помоему в 2003 сервере сп1 была такая фигня, что выставляя Alertable, APC все равно не исполнялось. Но уже не помню. Давно делал. на всякий случай запощу ссылку, где довольно немало инфы про APC. линк. Вдруг кто не видел еще. Ну и конечно же там 7 страниц, так что не просмотрите.
Уважаемый leo проблема в каком-то другом месте, инжект APC не происходит для треда который гарантированно переходит в состояние "alertable" Программа с потоком который переходит в состояние "alertable" (гарантировано) Код (Text): // alertable.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" #include <iostream> #include <string> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { string str; while ((cin >> str) != "exit") { SleepEx(1000, true); cout << str; } return 0; } Вот код dll'ки которую хочу заинжектить Код (Text): // dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { MessageBeep(MB_ICONWARNING); return TRUE; } Вот код самого инжектора: Код (Text): // test.cpp : Defines the processEntry point for the console application. // #include "stdafx.h" #include <windows.h> #include <tlhelp32.h> typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT, *PSECTION_INHERIT; //typedef unsigned long NTSTATUS; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; #ifdef MIDL_PASS [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer; #else // MIDL_PASS PWSTR Buffer; #endif // MIDL_PASS } UNICODE_STRING, *PUNICODE_STRING; typedef struct _OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; bool Inject(HANDLE hProcess, HANDLE hThread, LPCWSTR dllPath) { SIZE_T dllPathLen = wcslen(dllPath); void *pmem = VirtualAllocEx(hProcess, NULL, dllPathLen + 1, MEM_COMMIT, PAGE_READWRITE); if (pmem) { SIZE_T bytesWritten = 0; WriteProcessMemory(hProcess, pmem, dllPath, dllPathLen, &bytesWritten); if (dllPathLen == bytesWritten) { if (QueueUserAPC((PAPCFUNC)GetProcAddress(LoadLibraryA("kernel32.dll"), "LoadLibraryA"), hThread, (ULONG_PTR)pmem)) { return true; } } } return false; } int main(void) { HANDLE hThread; HANDLE hProcess; DWORD threadID; DWORD processID; OBJECT_ATTRIBUTES attributes = {sizeof(OBJECT_ATTRIBUTES)}; THREADENTRY32 threadEntry; PROCESSENTRY32 processEntry; threadEntry.dwSize = sizeof(THREADENTRY32); processEntry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, NULL); if (Process32First(snapshot, &processEntry) == TRUE) { while (Process32Next(snapshot, &processEntry) == TRUE) { if (_wcsicmp(processEntry.szExeFile, L"alertable.exe") == 0) { hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID = processEntry.th32ProcessID); break; } } } if (Thread32First(snapshot, &threadEntry) == TRUE) { while (Thread32Next(snapshot, &threadEntry)) { if (threadEntry.th32OwnerProcessID == processID) { hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, threadID = threadEntry.th32ThreadID); break; } } } Inject(hProcess, hThread, L"skunk.dll"); CloseHandle(snapshot); return 0; }
Переводить чужой поток в alertable опастное дело ... Не которые функции, не коректно работают с alertable потоками. Например connect(кажется она, было не сколько месяцов назад), при входе в которую будет отрабатыватся APC, и функция возврощает success, а вот WSAGetLastError говорил об обратном ... Могут быть крахи приложений ...
Плохой способ. Я бы обождал тред, получил его контекст, получил стек, понизил SFN до необходимого(это например если тред в загрузчике) и выполнил свой код. APC в функциях, в которых не планируется её доставка нарушит работу потока.
sysluck Оригинально - берешь юникодную строку, копируешь из нее dllPathLen байт и пытаешься вызвать LoadLibraryA в надежде, что из этой каши получится нечто удобоваримое?!
leo можешь собой гордиться, единственный человек действительно вникнувший в проблему. Порой перемкнет, и проблему пробуешь найти там где ее нет...