Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Ожидание окончания "инициализации процесса".


Страницы:   1 2

Riply ©   (2007-05-10 22:07:00) 

Здавствуйте !
Пытаюсь запустить процесс и тут же в него "внедриться" :)
Делаю, примерно так:
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 initializationand 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,
но не заводить же пул потоков ради перехвата
одной единственной маленькой и несчастной функции :)


Riply   (2007-05-16 10:33:00) [1]

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

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

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


Leonid Troyanovsky   (2007-05-16 09:29:00) [2]


> 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 02:27:00) [3]

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


Riply   (2007-05-16 02:08:00) [4]

>[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) может быть вызвана не один раз.
Допустим, узнать, что нас вызвали повторно - не сложоно.
Но, я никак не могу сообразить, почему это может происходить ?


Leonid Troyanovsky   (2007-05-15 20:32:00) [5]


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

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

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

--

Regards, LVT.


Leonid Troyanovsky   (2007-05-15 20:24:00) [6]


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

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

Slava - слава ;)

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

--
Regards, LVT.


Leonid Troyanovsky   (2007-05-15 20:03:00) [7]


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

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

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

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

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

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

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

--
Regards, LVT.


Riply   (2007-05-15 14:07:00) [8]

>[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-14 18:50:00) [9]


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

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

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

--
Regards, LVT.


Leonid Troyanovsky   (2007-05-14 18:46:00) [10]


> 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:08:00) [11]


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

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

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

--
Regards, LVT.


Riply   (2007-05-14 17:27:00) [12]

>[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 13:40:00) [13]


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

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

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

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

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

--
Regards, LVT.


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


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

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

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

--
Regards, LVT.


Leonid Troyanovsky   (2007-05-14 13:30:00) [15]


> 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:19:00) [16]


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

>  PAGE_EXECUTE_READWRITE, pRemoteThread)

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

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

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

--
Regards, LVT.


Riply   (2007-05-14 12:31:00) [17]

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


Riply   (2007-05-14 11:37:00) [18]

>[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)) ?


Leonid Troyanovsky   (2007-05-14 09:31:00) [19]


> 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 03:08:00) [20]

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


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо