На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! user posted image
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.

Соблюдайте общие правила форума

Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как запустить программу/файл? (и дождаться ее завершения)
5. Как перехватить API-функции, поставить hook? (перехват сообщений от мыши, клавиатуры - внедрение в удаленное адресное прстранство)
... (продолжение следует) ...

Внимание:
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки - бан.
Мат в разделе - бан на три месяца...

Полезные ссылки:
user posted image MSDN Library user posted image FAQ раздела user posted image Поиск по разделу user posted image Как правильно задавать вопросы


Выразить свое отношение к модераторам раздела можно здесь: user posted image Rouse_, user posted image Krid

Модераторы: Rouse_, Krid
  
> Вызов утилиты через консоль в Delphi10 , Вызов утилиты через консоль в Delphi10
    Господа, прошу помощи.

    Взял за основу пример Паши Голубя, пробую заточить его под свои нужды.
    Идея в чем, мне нужно вызвать утилиту openssl чтобы подписать некий текст своим ключем.
    вот собственно сама строка:
    openss smime -sign -signer 16901.cer -inkey private.key -nochain -nocerts -outform PEM -nodetach -passin pass:vynico890

    Вот процедура:
    ExpandedWrap disabled
      Function GetDosOutput( CommandLine, Pass: String; var
      ResultCode: Cardinal ): String;
      var StdOutPipeRead, StdOutPipeWrite, StdInPipeRead, StdInPipeWrite: THandle;
         SA                             : TSecurityAttributes;
         SI                             : TStartupInfo;
         PI                             : TProcessInformation;
         WasOK                          : Boolean;
         Buffer                         : array[0..255] of AnsiChar;
         BytesRead                      : Cardinal;
         Line                           : String;
         Written                        : DWORD;
         WorkDir                        : String;
         FConsoleInput: string;
      Begin
      FConsoleInput :=  'TEST';
       
         //Application.ProcessMessages;
         With SA do
         Begin
            nLength := SizeOf( SA );
            bInheritHandle := True;
            lpSecurityDescriptor := nil;
         end;
         // создаём пайп для перенаправления стандартного вывода
         CreatePipe( StdOutPipeRead,  // дескриптор чтения
                     StdOutPipeWrite, // дескриптор записи
                     @SA,              // аттрибуты безопасности
                     0                // количество байт принятых для пайпа - 0 по умолчанию
                    );
         // создаём пайп для перенаправления стандартного ввода
         CreatePipe( StdInPipeRead,  // дескриптор чтения
                     StdInPipeWrite, // дескриптор записи
                     @SA,              // аттрибуты безопасности
                     0                // количество байт принятых для пайпа - 0 по умолчанию
                    );
       
         try
          // Создаём дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
          // а так же проверяем, чтобы он не показывался на экране.
          with SI do
          Begin
             FillChar( SI, SizeOf( SI ), 0 );
             cb := SizeOf( SI );
             dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
             wShowWindow := SW_HIDE or SW_SHOWMINNOACTIVE;
             hStdInput := StdInPipeRead; //GetStdHandle(STD_INPUT_HANDLE);
             hStdOutput := StdOutPipeWrite;
             hStdError := StdOutPipeWrite;
          end;
       
          //Запускаем компилятор из командной строки
          WorkDir := ExtractFilePath(getcurrentdir + '\openssl.exe');
          WasOK := CreateProcess( nil,
                                  PWideChar( CommandLine ),
                                  nil,
                                  nil,
                                  True,
                                  0,
                                  nil,
                                  //nil,
                                  PWideChar(WorkDir),
                                  SI,
                                  PI );
       
          // если процесс может быть создан, то дескриптор, это его вывод
          Win32Check(CloseHandle( StdOutPipeWrite ));
          if not WasOK then
           raise Exception.Create( 'Ошибка выполнения или компиляции: ' +
                  Chr( 10 ) + Chr( 13 ) + CommandLine )
          else
            try
              if FConsoleInput <> '' then
              begin
                WriteFile(StdinPipeWrite, FConsoleInput[1], length(FConsoleInput) * SizeOf(AnsiChar),
                   Written, nil);
              end;
              // получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
              Line := '';
              Repeat
                 // читаем блок символов (могут содержать возвраты каретки и переводы строки)
                 WasOK := ReadFile( StdOutPipeRead, Buffer, 255, BytesRead, nil );
                 // есть ли что-нибудь ещё для чтения?
                 if BytesRead > 0 then
                 Begin
                    // завершаем буфер PChar-ом
                    Buffer[BytesRead] := #0;
                    // добавляем буфер в общий вывод
                    Line := Line + Buffer;
                 end;
                 //Application.ProcessMessages;
              Until not WasOK or ( BytesRead = 0 );
              // ждём, пока завершится консольное приложение
              WaitForSingleObject( pi.hProcess, INFINITE );
              ResultCode := 0;
              GetExitCodeProcess( pi.hProcess, ResultCode );
            finally
              // Закрываем все оставшиеся дескрипторы
              CloseHandle( PI.hThread );
              CloseHandle( pi.hProcess );
            end;
         finally
           Result := Line;
           CloseHandle( StdOutPipeRead );
           CloseHandle( StdInPipeRead );
           Win32Check(CloseHandle( StdInPipeWrite ));
       end;
      end;


    Вызываю в коде:
    FAppPath := 'openssl';
    FParam := 'smime -sign -signer 16901.cer -inkey private.key -nochain -nocerts -outform PEM -nodetach -passin pass:vynico890'+#13#10;
    CommandLine := FAppPath + ' ' + FParam;
    s := GetDosOutPut(CommandLine,'', FResultCode);
    ShowModal(s);

    Запускаю команду в cmd выдает то что нужно

    -----BEGIN PKCS7-----
    MIICtAYJKoZIhvcNAQcCoIICpTCCAqECAQExDzANBglghkgBZQMEAgEFADAVBgkq
    hkiG9w0BBwGgCAQGVEVTVA0KMYICcjCCAm4CAQEwYDBJMQswCQYDVQQGEwJSVTEY
    MBYGA1UEChMPUFMgWWFuZGV4Lk1vbmV5MSAwHgYDVQQDExdZYW5kZXggTW9uZXkg
    SXNzdWluZyBDQQITMgAAQ8pWVf/72bB0tgACAABDyjANBglghkgBZQMEAgEFAKCB
    5DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNjAx
    MjYwODQzMjJaMC8GCSqGSIb3DQEJBDEiBCCrlzIBE+XnNf+Sea8KcTLXUbv+vqBy
    WMnjiqu+/kMStjB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjALBglghkgB
    ZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIAgDAN
    BggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDANBgkqhkiG9w0B
    AQEFAASCAQC4S67qvraXa8SDupaMeki04QK+ZaDwNK6jorm5XQPZrsF32vDYGaQW
    la+mTxHq9JtRHwSDU2gM17Tz56T2DGl0bwG40NcoNlVDcyqIkxRNk3Jr1Sqdvorm
    PkykhyZXJftPsXGhyYtIYQ9fs6MJQnCkt/rvi1YBsILZ9LG4Uhkpv5rrUSanEYCc
    O0u/aH3KXbuVqPkvvOyX2azWuXXxp8H+DYfXESHeYySSmpA8l/ZPZ1OhCazUStq4
    FaEKC/6vvEFK6deGHpxzNqqz9VVZFnVFAcbvq0MxW7s1PA/0eoraxfaasnlC5XHK
    k2GzmS92fHWbKftSH9/+DbXtetRmYlo4
    -----END PKCS7-----

    Запускаю через делфи пишет:

    unable to load signing key file
    2088:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:.\crypto\evp\evp_enc.c:529:
    2088:error:0906A065:PEM routines:PEM_do_header:bad decrypt:.\crypto\pem\pem_lib.c:482:


    У меня подозрения на то что при записи в консоль текста у меня он передается в консоль неверно или не весь.

    Кстати Delphi у меня 10, тобиш unicode
      У меня в CreateProcess CreationFlags такие: CREATE_NEW_CONSOLE{$IFDEF UNICODE} or CREATE_UNICODE_ENVIRONMENT{$ENDIF}; правда, не знаю, в этом ли дело.

      К WorkDir бессмысленно добавлять имя файла.
      Проверь, что WorkDir - именно та папка, где лежит signing key file.
        Цитата Fr0sT @
        Проверь, что WorkDir - именно та папка, где лежит signing key file.
        с кода ниже понятно что нет ))))
        Цитата dimonf78 @
        ExpandedWrap disabled
               WorkDir := ExtractFilePath(getcurrentdir + '\openssl.exe');
              WasOK := CreateProcess( nil,
                                      PWideChar( CommandLine ),
                                      nil,
                                      nil,
                                      True,
                                      0,
                                      nil,
                                      //nil,
                                      PWideChar(WorkDir),
                                      SI,
                                      PI );

        вообще какая то странная комбинация
        Цитата
        getcurrentdir + '\openssl.exe'
        Сообщение отредактировано: ViktorXP -
          Цитата dimonf78 @
          Запускаю через делфи пишет:
          unable to load signing key file

          И, видимо, ResultCode = 2 (входной файл не найден или не м.б. прочитан)?
          А если вместо WorkDir передавать nil, то что получается (ты, видимо уже пробовал)?

          Цитата dimonf78 @
          У меня подозрения на то что при записи в консоль текста у меня он передается в консоль неверно или не весь.

          Разумеется, т.к. FConsoleInput у тебя объявлена как UnicodeString (по умолчанию), а записываешь ты ее как Ansi. Замени объявление на AnsiString.
          А также в конце строки нужно бы добавить #13#10, т.к. иначе прога может висеть в ожидании продолжения ввода
            Цитата ViktorXP @
            с кода ниже понятно что нет ))))

            Ну, все хозяйство может лежать рядом с прогой. Хотя currentdir вообще ненадежная штука, ее лучше избегать.
            Цитата leo @
            А также в конце строки нужно бы добавить #13#10, т.к. иначе прога может висеть в ожидании продолжения ввода

            Кстати, функция сама по себе не очень правильная. Проги ждут ввода с stdin, пока не поступит eof (который можно сотворить только закрыв свой конец трубы). К тому же трубы - это вообще менее удобные штуки, чем, к примеру, сокеты. Так, пока программа не прочитает из своего конца, запись в другой конец будет висеть в ожидании. Я долго ковырялся с этими фокусами на своем эмуляторе, пробовал асинхронность и неблокирующий ввод, в итоге пришел к выводу, что легче все сделать на тредах.
              Цитата Fr0sT @
              Проги ждут ввода с stdin, пока не поступит eof (который можно сотворить только закрыв свой конец трубы)

              Да, с переводом строки я поспешил. Тут обсуждалась точно такая же проблема, и не понятно, удалось ее решить или нет. Насколько я понял, само вводимое\подписываемое сообщение может содержать переводы строк, поэтому даже в cmd для завершения ввода нужно жать не Enter, а Ctrl-Z. Однако чел уверяет, что ни запись символа $1A (аналог Ctrl-Z), ни закрытие хэндла StdInPipeWrite после записи не привели к успеху (устранению висяка на последующем ReadFile). Хотя этот висяк мог быть вызван и другими причинами. По крайней мере, в примере мсдн хэндл StdInWrite закрывают сразу после окончания записи (so the child process stops reading), и кроме того, хэндлы своих концов пайпов делают не наследуемыми (SetHandleInformation до вызова CreateProcess). Но судя по комментариям, висяки на ReadFile все равно могут иметь место (где-то PeekNamedPipe перед ReadFile спасает, а где-то нет). Например, есть гарантия что openssl сразу закрывается после выдачи подписи, а не висит в ожидании press any key? К тому же после запуска она выполняет некую инициализацию с выдачей сообщения - может нужно дождаться его выдачи и только потом вводить текст сообщения?

              Цитата Fr0sT @
              К тому же трубы - это вообще менее удобные штуки, чем, к примеру, сокеты

              Насчет удобства - не знаю. А в плане надежности\предсказуемости в данном случае проще использовать ввод\вывод в файлы (опции -in и -out) без всяких заморочек с перенаправлениями. Да и объем кода может получиться меньше (если использовать высокоуровневые штучки типа Memo1.Lines.SaveToFile \ LoadFromFile и т.п.)
              Сообщение отредактировано: leo -
                Цитата Fr0sT @
                К тому же трубы - это вообще менее удобные штуки, чем, к примеру, сокеты.

                не согласен. имхо.

                но я только что обратил внимание на то что в пайп сначала пишется что то. и тут:

                Цитата dimonf78 @

                ...через консоль в Delphi10
                ExpandedWrap disabled
                  Function GetDosOutput( CommandLine, Pass: String; var
                  ResultCode: Cardinal ): String;
                  var
                  ...
                     FConsoleInput: string; <- UnicodeString
                  Begin
                  ...
                            WriteFile(StdinPipeWrite, FConsoleInput[1], length(FConsoleInput) * SizeOf(AnsiChar),
                               Written, nil);
                :)))))))))))))))))))))))))))))))))))))))))
                  ViktorXP, отсутствие буферизации на трубах очень напрягает. Чуть что - и всё намертво встает.
                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                  0 пользователей:


                  Рейтинг@Mail.ru
                  [ Script execution time: 0,0520 ]   [ 17 queries used ]   [ Generated: 16.04.24, 17:34 GMT ]