CreateProcessAsUserW код ошибки 6 Неверный дескриптор JNA

#java #c #windows #winapi #jna

Вопрос:

Я использую JNA для вызова API Windows. Я хочу запустить процесс (неважно, какой) от имени конкретного пользователя. Два вызова API, которые я использую, это:

LogonUserW выполняется успешно, но CreateProcessAsUserW завершается ошибкой 6. Согласно документу с кодами системных ошибок Windows, это соответствует «ERROR_INVALID_HANDLE».

Насколько я могу судить, единственный дескриптор, который я передаю, — это дескриптор пользователя. Я не вижу, что в этом может быть плохого. Согласно документу LogonUserW,

В большинстве случаев возвращаемый дескриптор является основным маркером, который можно использовать при вызовах функции CreateProcessAsUser. Однако если вы укажете флаг LOGON32_LOGON_NETWORK, LogonUser возвращает маркер олицетворения, который нельзя использовать в CreateProcessAsUser, если не вызвать DuplicateTokenEx, чтобы преобразовать его в основной маркер.

Однако я не использую LOGON32_LOGON_NETWORK.

Некоторые параметры структуры имеют дескрипторы, но я либо передаю значение NULL, либо они заполняются вызовом API вместо меня.

Вот суть моего кода:

 final PointerByReference userPrimaryToken =
    new PointerByReference();
System.out.printf(
    "ptr.peer = %dn",
    Pointer.nativeValue(userPrimaryToken.getValue())
);

final boolean logonOk = MyWinBase.INSTANCE.LogonUserW(
    toCString(<my-username>), // hidden
    toCString("ANT"),
    toCString(<my-password>), // hidden
    /* This logon type is intended for batch servers, where
    processes may be executing on behalf of a user without their
    direct intervention. This type is also for higher
    performance servers that process many plaintext
    authentication attempts at a time, such as mail or web
    servers.*/
    WinBase.LOGON32_LOGON_BATCH,
    WinBase.LOGON32_PROVIDER_DEFAULT,
    userPrimaryToken
);
System.out.printf("ok = %bn", logonOk);
System.out.printf(
    "ptr.peer = %dn",
    Pointer.nativeValue(userPrimaryToken.getValue())
);

final STARTUPINFOW.ByReference startupInfoW =
    new STARTUPINFOW.ByReference();
startupInfoW.cb = startupInfoW.size();
startupInfoW.lpReserved = Pointer.NULL;
startupInfoW.lpDesktop = Pointer.NULL;
startupInfoW.lpTitle = Pointer.NULL;
startupInfoW.dwFlags
    = startupInfoW.dwX = startupInfoW.dwY
    = startupInfoW.dwXSize = startupInfoW.dwYSize
    = startupInfoW.dwXCountChars = startupInfoW.dwYCountChars
    = startupInfoW.dwFillAttribute
    = startupInfoW.wShowWindow
    = 0;
startupInfoW.cbReserved2  = 0;
startupInfoW.lpReserved2 = Pointer.NULL;
startupInfoW.hStdInput = startupInfoW.hStdOutput
    = startupInfoW.hStdError
    = Pointer.NULL;

final PROCESS_INFORMATION.ByReference processInformation =
    new PROCESS_INFORMATION.ByReference();
processInformation.hProcess = processInformation.hThread
    = Pointer.NULL;
processInformation.dwProcessId = processInformation.dwThreadId
    = 0;

final boolean createProcessOk = MyProcessThreadsApi.INSTANCE
    .CreateProcessAsUserW(
        userPrimaryToken.getPointer(),
        toCString("C:\Windows\System32\cmd.exe"),
        // execute and terminate
        toCString("/c whoami > whoami.txt"),
        Pointer.NULL,
        Pointer.NULL,
        false,
        WinBase.CREATE_UNICODE_ENVIRONMENT,
        new PointerByReference(),
        Pointer.NULL,
        startupInfoW,
        processInformation
    );
System.out.printf("ok = %bn", createProcessOk);
System.out.printf(
    "dwProcessId = %dn", processInformation.dwProcessId
);
System.out.printf(
    "last err code = %dn",
    ErrHandlingApi.INSTANCE.GetLastError()
);
 

Вот мой вывод:

 ptr.peer = 0
ok = true
ptr.peer = 1040
ok = false
dwProcessId = 0
last err code = 6
 

Есть какие-нибудь предложения?

Ответ №1:

Глядя на этот фрагмент кода:

 final PointerByReference userPrimaryToken = ...;
 

В онлайн-документации говорится, что он представляет собой указатель на указатель, обозначение C void**
https://java-native-access.github.io/jna/4.2.1/com/sun/jna/ptr/PointerByReference.html
В документации для LogonUser ожидается указатель KINDLE на ДЕСКРИПТОР, который аналогичен указателю на указатель, поскольку ДЕСКРИПТОР аналогичен указателю (он объявлен как typedef void *HANDLE; ).
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonuserw

 BOOL LogonUserW(
  ....
  DWORD   dwLogonProvider,
  PHANDLE phToken
);
 

Но в документации к CreateProcessAsUser указано, что эта функция принимает ДЕСКРИПТОР, а не ФАНДЛ
https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw

 BOOL CreateProcessAsUserW(
  HANDLE                hToken,
  LPCWSTR               lpApplicationName,
  ....
);
 

Поэтому я бы ожидал, что вы передадите скорее GetValue, чем getPointer. Используя getPointer, вы получаете сам указатель, который, скорее всего, является указателем на указатель в вашем случае. Я не знаю JNA, но ожидания связаны со знанием WinAPI

 final boolean createProcessOk = MyProcessThreadsApi.INSTANCE
    .CreateProcessAsUserW(
        userPrimaryToken.getValue(),
        ....
    );
 

Комментарии:

1. Спасибо!!! Я должен был прочитать описание метода. Вы совершенно правы насчет пустоты* против пустоты** путаница значений. У меня все еще есть другие ошибки с другими кодами ошибок, но они больше касаются разрешений и доступа. Я продолжу отладку.