Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.06.03;
Скачать: [xml.tar.bz2];

Вниз

Ожидание окончания "инициализации процесса".   Найти похожие ветки 

 
Riply ©   (2007-05-10 22:07) [0]

Здавствуйте !
Пытаюсь запустить процесс и тут же в него "внедриться" :)
Делаю, примерно так:
if ProcessCreate(ExeName, ..., Proc_Info) then
try
 if WaitForInputIdle(Proc_Info.hProcess, INFINITE) = WAIT_OBJECT_0 then
  begin
   // Здесь внедряемся и что-то делаем, например перехватываем API.
  end;
finally
 ProcessInfo_CloseHandles(@Proc_Info);
end
else Result := GetLastError;

Все работает, но мне не совсем нравиться :(
Во-первых: в отношении консольного процесса Help какой-то двусмысленный:
с одной стороны:
"until the specified process has finished its initialization and is waiting for user input",
а с другой:
"If this process is a console application or does not have a message queue,
WaitForInputIdle returns immediately".
Объясните мне пожалуйста WaitForInputIdle (в случае, например, консоли)
дожидается "the specified process has finished its initialization", или "returns immediately" ?
И еще: меня не устраивает ожидание по TimeOut - интервалу.
Как можно организовать одновременное ожидание WaitForInputIdle и моего StopWaitEvent ?
P.S.
Я понимаю, что это можно сделать с помощью, например RegisterWaitForSingleObject,
но не заводить же пул потоков ради перехвата
одной единственной маленькой и несчастной функции :)


 
Leonid Troyanovsky ©   (2007-05-10 22:18) [1]


> Riply ©   (10.05.07 22:07)  

> Объясните мне пожалуйста WaitForInputIdle (в случае, например,
>  консоли)
> дожидается "the specified process has finished its initialization",
>  или "returns immediately" ?

Если консоль запущена в окне, то дожидается.

> Я понимаю, что это можно сделать с помощью, например RegisterWaitForSingleObject,

IMHO, наверное, WaitForDebugEvent нужной длл.

--
Regards, LVT.


 
Riply ©   (2007-05-10 22:25) [2]

> [1] Leonid Troyanovsky ©   (10.05.07 22:18)
>Если консоль запущена в окне, то дожидается.
Т.е. я могу быть уверена, что, при таком подходе,
я случайно не начну внедрятся "раньше чем можно" ? :)
>IMHO, наверное, WaitForDebugEvent нужной длл.
Спасибо.


 
homm ©   (2007-05-10 22:29) [3]

Удалено модератором
Примечание: оффтоп


 
Leonid Troyanovsky ©   (2007-05-10 22:30) [4]


> Riply ©   (10.05.07 22:25) [2]

> >Если консоль запущена в окне, то дожидается.
> Т.е. я могу быть уверена, что, при таком подходе,
> я случайно не начну внедрятся "раньше чем можно" ? :)

Полную гарантию дает только страховой полис ;)

Но, из практики WaitForInutIdle имеет смысл только
для "оконных"  (не полноэкранных) консолей.

--
Regards, LVT.


 
Riply ©   (2007-05-10 22:37) [5]

> [4] Leonid Troyanovsky ©   (10.05.07 22:30)
>Полную гарантию дает только страховой полис ;)
Ну, мне достаточто вероятности сбоя 1/10^39 :)
>Но, из практики WaitForInutIdle имеет смысл только
>для "оконных"  (не полноэкранных) консолей.
А чем, тогда его (WaitForInutIdle) можно заменить ?
Или WaitForDebugEvent - решит все проблемы ? (Я еще не успела его посмотреть)


 
Riply ©   (2007-05-10 22:38) [6]

Удалено модератором
Примечание: оффтоп


 
Leonid Troyanovsky ©   (2007-05-10 22:44) [7]


> Riply ©   (10.05.07 22:37) [5]

> Или WaitForDebugEvent - решит все проблемы ? (Я еще не успела
> его посмотреть)

Там есть на что посмотреть :)

Насчет решения всех проблем не уверен, бо допускаю, что
правильная инициализация знаменуется загрузкой последней
из потребных библиотек.

Хотя, возможно, что для подмен достаточен лишь факт
загрузки искомой библиотеки.

--
Regards, LVT.


 
Riply ©   (2007-05-11 23:16) [8]

>[7] Leonid Troyanovsky © (10.05.07 22:44)
>> Или WaitForDebugEvent - решит все проблемы ? (Я еще не успела его посмотреть)
>Там есть на что посмотреть :)
И, правда: там много интересного :)
>Хотя, возможно, что для подмен достаточен лишь факт загрузки искомой библиотеки.
Практика показала, что это зависит от способа внедрения.
Если внедряться "по Рихтеру", по после события загрузки ntdll.dll или
"искомой библиотеки"(если она не ntdll.dll) у меня ошибок не возникало.
Но, если использовать хуки, то наступает Ваш случай:
>Насчет решения всех проблем не уверен, бо допускаю, что
>правильная инициализация знаменуется загрузкой последней
>из потребных библиотек.
К сожалению, Вы, как всегда, правы :( ("К сожалению" - в смысле, что это действительно так :)
Пыталась внедриться после загрузки следующих библиотек:
USER32.dll, ADVAPI32.dll, comctl32.dll (дальше экспериментировать уже не видела смысла).
if CreateProcess(PChar(aFileName), ..., CREATE_DEFAULT_ERROR_MODE or
                DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS,...) then
try
 while WaitForDebugEvent(DbgEvent, INFINITE) do
  begin
   case DbgEvent.dwDebugEventCode of
    EXIT_PROCESS_DEBUG_EVENT: Break;
    LOAD_DLL_DEBUG_EVENT:
     begin
      Get_ModuleFileName(DWord(DbgEvent.LoadDll.lpBaseOfDll), LibName);
      if CompareText(LibName, WaitLibName) = 0 then
       begin
        ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadid, DBG_CONTINUE);
        Result := DebugActiveProcessStop(DbgEvent.dwProcessId);
//         WaitForInputIdle(Proc_Info.hProcess, INFINITE);
        InjectError := Hook_SetThread(WH_SHELL, Proc_Info.dwThreadID);
        Break;
       end;
     end;
  end;
  ContinueDebugEvent(DbgEvent.dwProcessId, DbgEvent.dwThreadid, DBG_CONTINUE);
 end;
finally
 ProcessInfo_CloseHandles(@Proc_Info);
end;

Результат один и тот же: Хуком внедриться не удается.(только используя WaitForInputIdle)
Отсюда вопрос: какого события нужно дождаться, чтобы знать, что процесс закончил свою инициализацию
(вне зависимости, от того он "have a message queue" или нет)?
И еще несколько вопросов:
Допустим, мы ждем загрузки ntdll.dll.
А вдруг она успеет загрузиться до первого вызова WaitForDebugEvent ?
Как можно узнать, что она уже там и ждать нет смысла ?
Меня еще смущает, что мы вынуждены стартовать процесс DEBUG_... флагами.
Не сказывается ли это на его "нормальной работе", т.е. вносим ли мы, какие то коррективы в его поведение ?
Попытка стартовать его "нормально" и использовать DebugActiveProcess показала,
что для этого способа, сначала нужно дождаться "правильной инициализации процесса" :)


 
Leonid Troyanovsky ©   (2007-05-12 10:33) [9]


> Riply ©   (11.05.07 23:16) [8]

> Если внедряться "по Рихтеру", по после события загрузки
> ntdll.dll или
> "искомой библиотеки"(если она не ntdll.dll) у меня ошибок
> не возникало.

По Рихтеру - это CreateRemoteThread?

> Но, если использовать хуки, то наступает Ваш случай:

Стоп-стоп.
Сначала была речь про консольные приложения, ну,
а если уж хуки, то WaitForInputIdle хватит вполне.

И все же, какая поставлена цель?
В самом общем случае тема не так уж и проста,
см., например
http://groups.google.com/group/microsoft.public.win32.programmer.kernel/browse_thread/thread/a2a16a5753f49252/c49a244670a56d2b#c49a244670a56d2b

Кста,  в этом треде отметились не самые последние эксперты
по подобным вопросам.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-12 10:49) [10]


> Riply ©   (11.05.07 23:16) [8]

> Допустим, мы ждем загрузки ntdll.dll.
> А вдруг она успеет загрузиться до первого вызова WaitForDebugEvent
> ?
> Как можно узнать, что она уже там и ждать нет смысла ?

Ты ж стартуешь процесс в отладочном режиме.
Или ты не уверена в готовности потока отладчика?
Ну, тогда, видимо, можно запустить suspended,
а по готовности потока отладчика - resume.
Хотя, IMHO, это все излишне.


> Меня еще смущает, что мы вынуждены стартовать процесс DEBUG_.
> .. флагами.
> Не сказывается ли это на его "нормальной работе", т.е. вносим
> ли мы, какие то коррективы в его поведение ?

Ну, если отлаживаемый процесс изменяет свою работу
в зависимости от присутствия отладчика - то вносим.
Но, это явно необычные приложения.

Кста, установка хуков - это тоже отладочная техника,
т.е. тоже меняет поведение.

> Попытка стартовать его "нормально" и использовать DebugActiveProcess

IMHO, сразу отлаживать предпочтительней, он полностью под контролем.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-12 12:23) [11]


> Leonid Troyanovsky ©   (12.05.07 10:33) [9]

> Кста,  в этом треде отметились не самые последние эксперты

См. также
http://groups.google.com/group/microsoft.public.win32.programmer.kernel/browse_thread/thread/1a2ec9ccd98d3979/77e944f5640757f3#77e944f5640757f3

--
Regards, LVT.


 
Riply ©   (2007-05-12 14:09) [12]

> [9] Leonid Troyanovsky © (12.05.07 10:33)
> По Рихтеру - это CreateRemoteThread?
Да. Мне этот способ очень нравится: "просто и со вкусом" :)
>Стоп-стоп.
>Сначала была речь про консольные приложения, ну,
>а если уж хуки, то WaitForInputIdle хватит вполне.
Хуки я использовала только, как тест закончена инициализация или нет.
Реально же работать с ними не очень удобно.
>И все же, какая поставлена цель?
>В самом общем случае тема не так уж и проста,
Ну, разумеется, цель была поставлена в "самом общем случае".
Мы же не хухры-мухры, а с Delphimaster.ru :)
Интересовала возможность "спокойной работы" с любым процессом
или обход некоторых ограничений налагаемых на DllMain - функцию.
Да и само "определения окончания инициализации процесса" имеет довольно широкую область применения.
Например, как тест, я пыталась загружать библиотеку, использующею SysUtils из AppInit_DLLs.
Эксперимент, в котором, подопытная DLL дожидается (разумеется, надуманным способом)
окончания инициализации целевого процесса и, только после этого,
инициализирует сама себя(некузяво выразилась, но не знаю, как сказать по-другому) прошел удачно.
А мне кажется, что уж если мы нормально грузимся из AppInit_DLLs,
то любой другой вид загрузки пройдет "на ура". :)
>Ну, тогда, видимо, можно запустить suspended,
>а по готовности потока отладчика - resume.
>Хотя, IMHO, это все излишне.
"это все излишне" - всегда приятно слышать, т.к. меньше головной боли :))
>Ну, если отлаживаемый процесс изменяет свою работу
>в зависимости от присутствия отладчика - то вносим.
>Но, это явно необычные приложения.
Боюсь, что этих "необычных приложений" гораздо больше, чем нам кажется :(
Сейчас все кому не лень защищают свои программы в т.ч. и от отладчика :)
P.S.
Пойду изучать ссылки.


 
Leonid Troyanovsky ©   (2007-05-13 11:01) [13]


> Riply ©   (12.05.07 14:09) [12]

> Пойду изучать ссылки.

Я немного поразмышлял над сказанным by Slava Usov,
и пришел к выводу, что вот такая схема может быть
весьма интересна:

var
  pi: TProcessInformation;
  si: TStartupInfo;
  p : Pointer;
  d:DWord;
begin
  Fillchar(si, SizeOf(si),0);
  ..

  CreateProcess(
       nil,
       "имя.exe",
       nil,
       nil,
       False,
       CREATE_SUSPENDED,
       nil,
       nil,
       si,
       pi
   );
   p:= VirtualAllocEx(pi.hProcess,..);
   {и пишем туда код  процедуры с таким прототипом:  
   procedure APCProc(dwParam: Dword); stdcall;}
   VirtualProtectEx(..);

   QueueUserAPC(p,  pi.hThread,    {параметр} ); // вот оно

   ResumeThread(pi.hThread);
   CloseHandle(pi.hThread);
   CloseHandle(pi.hProcess);
end;

Требования к коду APCProc такие же, как в случае с CRT.
APCProc должна сработать сразу после инициализации процесса.

Ну, а интересного в этой схеме то, что она, видимо, может
сработать даже в 9х (если подыскать замену VirtualAllocEx).

Правда, проверять мне все это влом,
оставляю заинтересованным лицам :)

--
Regards, LVT.


 
Riply ©   (2007-05-13 21:43) [14]

> [13] Leonid Troyanovsky ©   (13.05.07 11:01)
>Я немного поразмышлял над сказанным by Slava Usov,
>и пришел к выводу, что вот такая схема может быть
>весьма интересна:
Спасибо, за подсказку. Т.к. являюсь "оставляю заинтересованным лицом",
попробовала что-то изобразить и совсем запуталась :(
Делаю, примерно, так:
(Прошу не обращать внимание на попытки обращения к
возможно уже несуществующему процессу. Я там все исправлю... Потом.. Может быть..  :)
Это наша тестовая тред-функция:
function _RemoteThreadBase(pRem_Info: Pointer): DWord; stdcall;
var
 _MessageBoxA: function(hWnd: HWND; lpText, lpCaption: PChar; uType: UINT): Integer; stdcall;
begin
Result := 0;
with PRemoteThreadInfo(pRem_Info)^ do
 begin
  RetResult := 1;
  if hUser32 = 0 then hUser32 := _GetModuleHandleA(User32_Name);
  if hUser32 <> 0 then
   begin
    RetResult := 2;
    @_MessageBoxA := _GetProcAddress(hUser32, MessageBoxA_Name);
    if @_MessageBoxA <> nil then _MessageBoxA(0, MessageBoxA_Name, User32_Name, 0) else RetResult := 4;
   end
  else LastErr := _GetLastError;
 end;
end;

Сама попытка реализации:
function Proc_CreateAndWaitInitialization(const FName: string; aTimeOut: DWord): DWord;
var
Rem_Info: TRemoteThreadInfo;
Proc_Info: PROCESS_INFORMATION;
cbCodeSize: DWord;
pRemoteData, pRemoteThread: Pointer;
BaseThreadCode: TByte_Array;
begin
if Rem_Info.InitSelf then // Заполняет Rem_Info адресами функций из kernel32 и прочей ерундой :)
 begin
  FillChar(Proc_Info, SizeOf(PROCESS_INFORMATION), 0);
  if Process_Create(FName, "", ExtractFileDir(FName), CREATE_SUSPENDED, SW_SHOWDEFAULT, Proc_Info) then
   try
    pRemoteData := nil;
    if Proc_WriteMemory(Proc_Info.hProcess, @Rem_Info, SizeOf(TRemoteThreadInfo), MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE, pRemoteData) then
     try
      pRemoteThread := nil;
      cbCodeSize := _RemoteThreadBaseSize; // вычисляет размер кода нашей тред-функции
      SetLength(BaseThreadCode, cbCodeSize);
      CopyMemory(@BaseThreadCode[0], @_RemoteThreadBase, cbCodeSize);
      if Proc_WriteMemory(Proc_Info.hProcess, @BaseThreadCode[0], cbCodeSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE, pRemoteThread) then
       try
        if QueueUserAPC(pRemoteThread, Proc_Info.hThread, DWord(pRemoteData)) then
         begin
          if ResumeThread(Proc_Info.hThread) <> MAXDWORD then Result := ERROR_SUCCESS else Result := GetLastError;
          WaitForSingleObject(Proc_Info.hProcess, aTimeOut); // ??? а вот здесь не знаю что и делать.
         end
        else Result := GetLastError;
       finally
        VirtualFreeEx(Proc_Info.hProcess, pRemoteThread, 0, MEM_RELEASE);
       end
      else Result := GetLastError
     finally
      VirtualFreeEx(Proc_Info.hProcess, pRemoteData, 0, MEM_RELEASE);
     end
    else Result := GetLastError
   finally
    ResumeThread(Proc_Info.hThread);
    ProcessInfo_CloseHandles(@Proc_Info);
   end
  else Result := GetLastError
 end
else Result := ERROR_BAD_ARGUMENTS;
end;

С грехом пополам удалось добиться запука целевого процесса и нашей нити в нем.
А как быть дальше ? (На строчке, помеченной "???")
Как нам дождаться завершения работы тред-функции ? Если бы мы ее создавали с помощью RemoteThread,
то все понятно - у нас был бы хэндл. А так где его взять ? Вытаскивать в тред-функцию что-то типа
PostThreadMessage очень некузяво, ибо придется подгружать доп. библиотеку (как с MessageBox),
да еще и организовывать нить ожидающую ответа.
Очень хочеться что-нибудь типа глобального(?) Event-а,
чтобы можно было просто использовать какую-нибудь из WaitFor... Как это можно сделать ?


 
Riply ©   (2007-05-13 23:47) [15]

>[14] Riply ©   (13.05.07 21:43)
>А как быть дальше ? (На строчке, помеченной "???")
Получилось :)
Ответ прост - передать в тред-функцию hEvent и свой ProcessID, а в ней использовать DuplicateHandle.
Теперь надо тестировать. Не очень мне нравиться этим заниматься :)


 
Leonid Troyanovsky ©   (2007-05-14 00:03) [16]


> Riply ©   (13.05.07 21:43) [14]

>           WaitForSingleObject(Proc_Info.hProcess, aTimeOut);
>  // ??? а вот здесь не знаю что и делать.

Тут, видимо, и следует использовать технику "двойного APC",
которую упоминал Slava.
Т.е., это, наверное, QUAPC, посылаемый вызывающему потоку
(который передал в качестве параметра свой tid),
и ожидающему своей очереди в an alertable state.
Также можно передать в параметре значение хендла event
(+ DuplicateHandle) для оповещения вызывающего потока.

Так как эти функции из kernel32, то их адреса можно
также передать из вызывающего потока.

Кста, в том, что APC вызывается в контексте первичного
потока, есть и свои преимущества, т.е., синхронизаций
при подменах не потребуется.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 00:07) [17]


> Riply ©   (13.05.07 23:47) [15]

> Ответ прост - передать в тред-функцию hEvent и свой ProcessID,
>  а в ней использовать DuplicateHandle.

DH можно вызвать в вызывающем потоке - так потребуется
передать лишь один параметр.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 00:16) [18]


> Riply ©   (13.05.07 21:43) [14]

VirtualProtectEx, IMHO, не стоит пренебрегать, бо,
в скором времени, MS может запретит исполнение кода в -rw.

--
Regards, LVT.


 
Riply ©   (2007-05-14 01:28) [19]

> [16] Leonid Troyanovsky © (14.05.07 00:03)
>Тут, видимо, и следует использовать технику "двойного APC",
>которую упоминал Slava.
>Т.е., это, наверное, QUAPC, посылаемый вызывающему потоку
>(который передал в качестве параметра свой tid),
>и ожидающему своей очереди в an alertable state.
Что-то начинает становиться сложновато для меня :(
Не смогла понять, приведенную выше цитату.
>Кста, в том, что APC вызывается в контексте первичного
>потока, есть и свои преимущества, т.е., синхронизаций
>при подменах не потребуется.
Имеется ввиду организация перехвата прямо из APC ?
Мне кажется, что это не так просто. Ведь, если я правильно понимаю,
то в нашей трэд-функции даже блоки try/finally нельзя использовать.
Очень сложно будет написать такой код. Или я ошибаюсь ?

>[17] Leonid Troyanovsky © (14.05.07 00:07)
>DH можно вызвать в вызывающем потоке - так потребуется
>передать лишь один параметр.
А ведь и правда !
Мне же пришлось вместе с ProcessID еще передавать адреса
самой DuplicateHandle, CloseHandle, OpenProcess и GetCurrentProcess.
Вот что значит торопиться.
Пойду переделывать.


 
Германн ©   (2007-05-14 02:48) [20]


> Riply ©   (14.05.07 01:28) [19]
>
...
> Пойду переделывать.


Когда-нибудь, пусть даже и в мемуарах, опиши свой "метод ненаучного тыка". Со всеми подробностями и деталями. Может кому пригодится?
:-)


 
Riply ©   (2007-05-14 03:08) [21]

> [20] Германн ©   (14.05.07 02:48)
Все очень просто.
Пусть М это множество мест, куда можно "ткнуться".
Сортируем его, а дальше тыкаемся методом половинного деления.
Представляешь во сколько раз нам придется совершить меньше "тыков" ? :)


 
Leonid Troyanovsky ©   (2007-05-14 09:31) [22]


> Riply ©   (14.05.07 01:28) [19]

> >Т.е., это, наверное, QUAPC, посылаемый вызывающему потоку
> >(который передал в качестве параметра свой tid),
> >и ожидающему своей очереди в an alertable state.
> Что-то начинает становиться сложновато для меня :(
> Не смогла понять, приведенную выше цитату.

Неудачно назвал "вызывающий поток", точнее "запускающий",
в смысле тот поток, который запускает целевой процесс.
Имелось ввиду, что после QUAPC запускающий поток
сам переходит, например, в SleepEx(INFINITE, True),
дожидаясь ответного QUAPC.
Но, этот случай, в отличии от Event потребует передачи двух
параметров.

Кстати, насчет параметров. Если их несколько, а также
есть необходимость возврата результата, то удобно
воспользоваться memory mapped file, передавая его хендл
также, как event. MapViewofFile & etc - также из kernel32.
Так что, одного параметра вполне достаточно.

> Имеется ввиду организация перехвата прямо из APC ?
> Мне кажется, что это не так просто. Ведь, если я правильно
> понимаю,
> то в нашей трэд-функции даже блоки try/finally нельзя использовать.

Там, вообще, мало чего можно пользовать (также, как в remote thread),
но LoadLibrary, GetProcAddress могут сделать жизнь вполне комфортной.
Зато, перехват можно делать спокойно, не беспокоясь о том,
что в целевом процессе начнет выполняться его (процесса) код.

--
Regards, LVT.


 
Riply ©   (2007-05-14 11:37) [23]

>[22] Leonid Troyanovsky © (14.05.07 09:31)
>Кстати, насчет параметров. Если их несколько, а также
>есть необходимость возврата результата, то удобно
>воспользоваться memory mapped file, передавая его хендл
>также, как event. MapViewofFile & etc - также из kernel32.
>Так что, одного параметра вполне достаточно.
А я собиралась получать результат через ReadProcessMemory
и меня очень смущало, что процесса может уже и не быть,
когда я соизволю поинтересоваться результатами :)

На настоящий момент основная функция имеет такой вид:
function Proc_CreateAndWaitInitialization(const FName, aParam: string; const cbFlag, cbShowWin, TimeOut: DWord; var WaitResult: DWord; var Proc_Info: PROCESS_INFORMATION): DWord;
var
Rem_Info: TInitThreadInfo;
WaitArr: Int64;
cbCodeSize: DWord;
hEvent, hDupEvent: THandle;
pRemoteData, pRemoteThread: Pointer;
_OK: Boolean;
begin
_OK := False;
WaitArr := WAIT_ABANDONED;
hEvent := CreateEvent(nil, True, False, nil);
if hEvent <> 0 then
 try
  FillChar(Proc_Info, SizeOf(PROCESS_INFORMATION), 0);
  if Process_Create(FName, aParam, ExtractFileDir(FName), CREATE_SUSPENDED or cbFlag, cbShowWin, Proc_Info) then
   try
    if DuplicateHandle(GetCurrentProcess, hEvent, Proc_Info.hProcess, @hDupEvent, 0, False, DUPLICATE_SAME_ACCESS) then
     try
      if Rem_Info.InitSelf(hDupEvent) then
       begin
        pRemoteData := nil;
        if Proc_WriteAndProtectMemory(Proc_Info.hProcess, @Rem_Info, SizeOf(TInitThreadInfo),
                                      MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE, pRemoteData)  then
         try
          pRemoteThread := nil;
          cbCodeSize := _RemoteInitThreadCodeSize;
          if Proc_WriteAndProtectMemory(Proc_Info.hProcess, @_RemoteInitThread, cbCodeSize,
                                        MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE, pRemoteThread) then
           try
            if QueueUserAPC(pRemoteThread, Proc_Info.hThread, DWord(pRemoteData)) then
             begin
              _OK := ResumeThread(Proc_Info.hThread) <> MAXDWORD;
              if _OK then
               begin
                WaitArr := Make_Int64(Proc_Info.hProcess, hEvent);
                WaitResult := WaitForMultipleObjects(2, @WaitArr, False, TimeOut);
                case WaitResult of
                 WAIT_OBJECT_0: Result := ERROR_SUCCESS;
                 else Result := GetLastError;
                end;
               end
              else Result := GetLastError;
             end
            else Result := GetLastError;
           finally
            VirtualFreeEx(Proc_Info.hProcess, pRemoteThread, 0, MEM_RELEASE);
           end
          else Result := GetLastError;
         finally
          VirtualFreeEx(Proc_Info.hProcess, pRemoteData, 0, MEM_RELEASE);
         end
        else Result := GetLastError
       end
      else Result := ERROR_BAD_ARGUMENTS;
     finally
//       CloseHandle(hDupEvent); hDupEvent is valid in the context of the target process
     end
    else Result := GetLastError;
   finally { TODO : What to do if we can"t to resume process ? Terminate ? }
    if not _OK then
     if ResumeThread(Proc_Info.hThread) = MAXDWORD then ProcessInfo_CloseHandles(@Proc_Info);
   end
  else Result := GetLastError;
 finally
  CloseHandle(hEvent);
 end
else Result := GetLastError;
end;

И у меня есть пара вопросов:
Сейчас я закрываю Duplicate-Handle в тред-функции.
А как быть, если нам не удалось его в нее передать (не сработало WriteProcessMemory или QUAPC) ?
Мы дважды пишем в память целевого процесса. (Рихтер в своих примерах так делает, ну я и слизала :)
Чем черевато объединение двух блоков pRemoteData и pRemoteThread,
и примерно такая передача указателей: Pointer(DWord(pRemoteCommonMem) + SizeOf(ThreadData)) ?


 
Riply ©   (2007-05-14 12:31) [24]

[22] Leonid Troyanovsky © (14.05.07 09:31)
>Там, вообще, мало чего можно пользовать (также, как в remote thread),
>но LoadLibrary, GetProcAddress могут сделать жизнь вполне комфортной.
>Зато, перехват можно делать спокойно, не беспокоясь о том,
>что в целевом процессе начнет выполняться его (процесса) код.
А если целевой процесс IsMultiThread = True ?
Мне это не дает покоя. :)
Так ли необходимо, перед подменой "супендить" все нити, а, потом восстанавливать ?


 
Leonid Troyanovsky ©   (2007-05-14 13:19) [25]


> Riply ©   (14.05.07 11:37) [23]

>  PAGE_EXECUTE_READWRITE, pRemoteThread)

Так ты, все таки CRT?
Я, все ж, советую попробывать без него.
Код, выполняемый APC работает в контексте первичного потока,
и, как мы убедились, срабатывает _непосредственно_после_
инициализации процесса, но _до_выполнения main & etc.
Т.е., там, в этот момент, даже вторичных потоков нет,
если, конечно, кто-то не умудрился, например, CRT :)

Алгоритм примерно такой:
в APCfunc делаем LoadLibraryA, GetProcAddress и вызов
нужной функции, которая и сделает всю необходимую работу.
После она может SetEvent о завершении своей работы.

Выгружать библиотеку, видимо, нет смысла, бо она
содержит подменяемый код.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 13:30) [26]


> Riply ©   (14.05.07 11:37) [23]

> Сейчас я закрываю Duplicate-Handle в тред-функции.
> А как быть, если нам не удалось его в нее передать (не сработало
> WriteProcessMemory или QUAPC)

Закрыть его можно тем же DH with DUPLICATE_CLOSE_SOURCE.

> Чем черевато объединение двух блоков pRemoteData и pRemoteThread,
>
> и примерно такая передача указателей: Pointer(DWord(pRemoteCommonMem)
> + SizeOf(ThreadData)) ?


Да ничем особенным.
Я в свое время пользовал примерно такие структуры
(подсмотрел у Prasad Dabak):

type
TInjectInfo = packed record
  SetVar: function ( name : PChar; Value: PChar):Bool;stdcall; // kernel32 func
  Name: Array [0..MAX_PATH] of Char; // param1
  Value: Array [0..MAX_PATH] of Char; // param2
  ..
  InjectInfoCode : Array [0..100] of Byte; // code of thread func
end;
PInjectInfo= ^TInjectInfo;

function RemoteFunc(p : PInjectInfo):Dword;stdcall; // thread func
begin
   Result := Dword(p.SetVar(p.Name, p.Value));
end;


--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 13:34) [27]


> Riply ©   (14.05.07 12:31) [24]

> Мне это не дает покоя. :)
> Так ли необходимо, перед подменой "супендить" все нити,
> а, потом восстанавливать ?

Если CRT - то нужно как-то синхронизировать доступ.
Если только QUAPC - то будет "естественная" синхронизация.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 13:40) [28]


> Leonid Troyanovsky ©   (14.05.07 13:30) [26]

> > Чем черевато объединение двух блоков pRemoteData и pRemoteThread,

Да, насчет возвращемого ты уже в курсе :)

Вообще, WriteProcessMemory только то, что необходимо потоку
в первую очередь.

Все остальное, включая например, имя длл, функции,
лучше передавать, например, через mmf.
Т.е., будет гибче, да и отладить проще.

--
Regards, LVT.


 
Riply ©   (2007-05-14 17:27) [29]

>[25] Leonid Troyanovsky © (14.05.07 13:19)
>Так ты, все таки CRT?
>Я, все ж, советую попробывать без него.
Вроде работает из без него :)
>Код, выполняемый APC работает в контексте первичного потока,
>и, как мы убедились, срабатывает _непосредственно_после_
>инициализации процесса, но _до_выполнения main & etc.
>Т.е., там, в этот момент, даже вторичных потоков нет
Уж очень мне нравится это место срабатывания:
"_непосредственно_после_инициализации процесса, но _до_выполнения main & etc" :)
>Выгружать библиотеку, видимо, нет смысла, бо она
>содержит подменяемый код.
Мне кажется, что это довольно важный аспект - первым делом подготовить пути для бегства
с восстановлением "status quo" жертвы :)
Во всяком случае, это очень помогает при отладке,
когда целевой процесс создан не нами(не надо его прибивать каждый раз :).

>[26] Leonid Troyanovsky © (14.05.07 13:30)
>> Сейчас я закрываю Duplicate-Handle в тред-функции.
>> А как быть, если нам не удалось его в нее передать (не сработало
>> WriteProcessMemory или QUAPC)
>Закрыть его можно тем же DH with DUPLICATE_CLOSE_SOURCE.
Что-то я не понимаю как ? (наверное переработала)
hEvent := CreateEvent(... // создали наш Event
DuplicateHandle(GetCurrentProcess, hEvent, Proc_Info.hProcess, @hDupEvent..., DUPLICATE_SAME_ACCESS)
и дублируем его(hEvent), не закрывая, т.к. он нам понадобится в:
//WaitArr := Make_Int64(Proc_Info.hProcess, hEvent);
//WaitResult := WaitForMultipleObjects(2, @WaitArr, False, TimeOut);
но попытка передать hDupEvent в целевой процесс закончлась неудачей, а ведь только там он "is valid"
Как нам теперь закрыть hDupEvent с помощью DH или без нее ?
Ведь в нашем(родном) процессе этот неприкаянный бедалага (hDupEvent) совсем не valid :)
В хелпе по этому вопросу нашла только следующее:
"If the process that calls DuplicateHandle is not the target process, the duplicating process
must use interprocess communication to pass the value of the duplicate handle to the target process."


 
Leonid Troyanovsky ©   (2007-05-14 18:08) [30]


> Riply ©   (14.05.07 17:27) [29]

> Во всяком случае, это очень помогает при отладке,
> когда целевой процесс создан не нами(не надо его прибивать
> каждый раз :).

Если твой код не отработал до конца правильно, откатиться
в случае использования QUAPC не получится, т.е. повторно
(на уже работающем процессе) QUAPC не пройдет - поток
выйдет из alertable state.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 18:46) [31]


> Riply ©   (14.05.07 17:27) [29]

> Как нам теперь закрыть hDupEvent с помощью DH или без нее

В соответствии с [29] оно в данной задаче не понадобиться,
но хендл в удаленном процессе (rh) можно закрыть так
(код исполняется  в текущем процессе):
var
 h, rh: THandle;

{дублируем h в запущенный процесс}

Win32Check(DuplicateHandle(GetCurrentProcess, h, pi.hProcess, @rh, 0,  false, DUPLICATE_SAME_ACCESS));

{закрываем этот хендл (rh) в запущенном процессе}
Win32Check(DuplicateHandle(pi.hprocess, rh, GetCurrentProcess, @h, DUPLICATE_SAME_ACCESS,  false, DUPLICATE_CLOSE_SOURCE));

Кстати, можно избежать DH для передачи хендлов в дочерний
процесс, сделав хендлы наследуемыми (но не псевдохендлы!)
однако, передавать значения хендлов, все равно, придется.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-14 18:50) [32]


> Leonid Troyanovsky ©   (14.05.07 18:46) [31]

> В соответствии с [29] оно в данной задаче не понадобиться,

Имелось ввиду [30].
Sorry.

--
Regards, LVT.


 
Riply ©   (2007-05-15 14:07) [33]

>[26] Leonid Troyanovsky © (14.05.07 13:30)
>Я в свое время пользовал примерно такие структуры
>(подсмотрел у Prasad Dabak):
Чуть изменила код, подсмотренный у Prasad Dabak :)
type
PInjectInfo = ^TInjectInfo;
TInjectInfo = packed record
 ...
 function pInjectThreadCode: Pointer;
end;

function TInjectInfo.pInjectThreadCode: Pointer;
begin
Result := Pointer(DWord(@Self) + SizeOf(TInjectInfo));
end;

procedure InjectInfo_AllocMem(var pInjectData: PInjectInfo; const pRemoteThread: Pointer; const cbCodeSize: DWord);
begin
ReallocMem(pInjectData, cbCodeSize + SizeOf(TInjectInfo));
Move(pRemoteThread^, pInjectInfo.pInjectThreadCode^, cbCodeSize);
end;

Так удалось избежать сакраментального числа "100" :)

>[30] Leonid Troyanovsky © (14.05.07 18:08)
>Если твой код не отработал до конца правильно, откатиться
>в случае использования QUAPC не получится, т.е. повторно
>(на уже работающем процессе) QUAPC не пройдет - поток
>выйдет из alertable state.
И это печально. Но, будем надеятся, что он отработает правильно,
а откат можно делать, например, из подгруженной Dll"ки.

Leonid, спасибо большое !
Тестировала на разных целевых процессах. Все работает как часы. :)


 
Leonid Troyanovsky ©   (2007-05-15 20:03) [34]


> Riply ©   (15.05.07 14:07) [33]

> Так удалось избежать сакраментального числа "100" :)

Избежать его не так уж сложно.
В действительности, от этой записи нам потребуется лишь
PInjectInfo, т.е., объявленная длина array of byte может быть
столь длинной насколько позволяет компилятор
(или, в зависимости от агрессивности планов интервенции :)
Распределять для этой структуры память, мы, все равно,
будем путем VirtualAlloc(Ex). А использовать - только указатель.
Требуемая для структуры  память распределяется с учетом
длины постоянной части + размер кода.

Если хочется обойтись одним WriteProcessMemory, то размер
промежуточной структуры определим по той же схеме.
Ес-но, что контроль за выходом за границу массива на
нашей совести.

Кста, раньше (D<4) SizeOf умела определять размер
кода функций в байтах . Потом ее отучили :)

Я использовал разность между адресами самой функции
и следующей за ней procedure dummy; begin end;

Кто-то (не упомню) говорил, что это не всегда верно,
но, подобные исключения мне встречались.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-15 20:24) [35]


> Riply ©   (15.05.07 14:07) [33]

> Leonid, спасибо большое !

Slava - слава ;)

На здоровье, приходи еще :)

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2007-05-15 20:32) [36]


> Leonid Troyanovsky ©   (15.05.07 20:03) [34]

> но, подобные исключения мне встречались.

Не встречались, sorry.

--

Regards, LVT.


 
Riply ©   (2007-05-16 02:08) [37]

>[35] Leonid Troyanovsky © (15.05.07 20:24)
>На здоровье, приходи еще :)
Leonid, ну раз Вы пригласили, то вот - пришла :)

>[33] Riply © (15.05.07 14:07)
>Тестировала на разных целевых процессах. Все работает как часы. :)
Не говори "гоп" пока не перепрыгнул.(с)
Видимо, мне еще до "Все работает как часы" - как до луны :)
Возникла довольно неожиданная проблемма:
На настоящий момент, тред-функция выглядит, примерно, так:
function _RemoteThreadBase(pInjectInfo: Pointer): DWord; stdcall;
var
uLib: TUNICODE_STRING;
aFunct: TANSI_STRING;
_CallBack: function(Parameter: DWord): DWord; stdcall;
OldState: Boolean;
begin
Result := 0;
with PInjectInfoBase(pInjectInfo)^, NtFuntionsTable do
 begin
  if (hInjectLib = 0) and (InjectLibName[0] <> #0) then
   begin
    _RtlCreateUnicodeStringFromAsciiz(@uLib, @InjectLibName[0]);
    LastStatus := _LdrGetDllHandle(nil, nil, @uLib, @hInjectLib);
    if LastStatus <> STATUS_SUCCESS then
     begin
      LastStatus := _LdrLoadDll(nil, nil, @uLib, @hInjectLib);
      if hInjectLib = 0 then LastErr := _RtlGetLastWin32Error;
     end;
    _RtlFreeUnicodeString(@uLib);
   end;

  if (hInjectLib <> 0) and (CallBackName[0] <> #0) then
   begin
    _RtlInitAnsiString(aFunct, @CallBackName[0]);
    LastStatus := _LdrGetProcedureAddress(Pointer(hInjectLib), @aFunct, nil, @@_CallBack);
    if (LastStatus = STATUS_SUCCESS) and (@_CallBack <> nil)
     then RetResult := _CallBack(RetResult)// *** Если здесь мы не очень торопимся :)
     else LastErr := _RtlGetLastWin32Error;
   end;

  if hReadyEvent <> 0 then
   begin
    LastStatus := _NtSetEvent(hReadyEvent, @OldState);
    if LastStatus = STATUS_SUCCESS then
     begin
      LastStatus := _NtClose(hReadyEvent);
      if LastStatus <> STATUS_SUCCESS then LastErr := _RtlGetLastWin32Error;
     end
   end;
 end;
end;

Иногда, (если замешкаться в _CallBack*** - DLL_функции)
наша тред функция(_RemoteThreadBase) может быть вызвана не один раз.
Допустим, узнать, что нас вызвали повторно - не сложоно.
Но, я никак не могу сообразить, почему это может происходить ?


 
Riply ©   (2007-05-16 02:27) [38]

>[37] Riply ©   (16.05.07 02:08)
>На настоящий момент, тред-функция выглядит, примерно, так:
Посмотрела на нее(функцию) со стороны - столько огрехов :(
Строго не судить. Она сырая и в стадии тестирования :)


 
Leonid Troyanovsky ©   (2007-05-16 09:29) [39]


> Riply ©   (16.05.07 02:08) [37]

> На настоящий момент, тред-функция выглядит, примерно, так:

> function _RemoteThreadBase(pInjectInfo: Pointer): DWord;
>  stdcall;

Погоди, почему снова RemoteThread?
Прототип APCfunc:
procedure APCfunc(p: PInjectInfo); stdcall;


> Иногда, (если замешкаться в _CallBack*** - DLL_функции)
> наша тред функция(_RemoteThreadBase) может быть вызвана
> не один раз.

А что значит "замешкаться"?
Приведи код, воспроизводящий проблему.

Кстати, в одном из тредов Gary Nebbett говорил, что вызов
Beep приводил к некоему искажению результатов (изменению
порядка вызовов APC), т.к. эта функция, в свою очередь,
SleepEx(.., True)

--
Regards, LVT.


 
Riply ©   (2007-05-16 10:33) [40]

>[39] Leonid Troyanovsky © (16.05.07 09:29)
>Погоди, почему снова RemoteThread?
Осталось с давних времен. Забыла переделать :)

>А что значит "замешкаться"?
>Приведи код, воспроизводящий проблему.
Замешкиваюсь я так: Sleep(5000);

>Кстати, в одном из тредов Gary Nebbett говорил, что вызов
>Beep приводил к некоему искажению результатов (изменению
>порядка вызовов APC), т.к. эта функция, в свою очередь, SleepEx(.., True)
Ну какже я могла обойтись без бипов ? У меня в _CallBack их целый зоопарк :)
Убрала: вроде, эффект исчез.



Страницы: 1 2 вся ветка

Форум: "Начинающим";
Текущий архив: 2007.06.03;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.64 MB
Время: 0.051 c
2-1179146463
[Salo]Phantom
2007-05-14 16:41
2007.06.03
Компоненты


2-1178939593
Новичек
2007-05-12 07:13
2007.06.03
Никак не придумаю, как тему назвать.


8-1148998595
TGX
2006-05-30 18:16
2007.06.03
Вопрос связаный с Flash.


1-1175524308
DelphiLexx
2007-04-02 18:31
2007.06.03
Аналог TNotebook, но поддерживающий наследование


6-1164191295
nali
2006-11-22 13:28
2007.06.03
Проверка соединения с интернет





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский