Вопрос по c, wlanapi, wep, encryption, windows-vista – Расшифруйте ключ профиля WEP WLAN с помощью CryptUnprotectData

7

Я пытаюсь расшифровать ключ профиля WEP с помощьюCryptUnprotectData, Я получил ключ профиля, экспортировав профиль с помощью netsh.

netsh wlan export profile name="MyWEP" folder="./"

На данный момент я вручную скопировал материал ключа из XML-файла, сгенерированного командой netsh, в свою программу. И, кстати, я расшифровываю это -

DATA_BLOB DataOut, DataVerify;
DataOut.cbData = encryptData.length();
DataOut.pbData = (BYTE*)("I_Manually_Copy_The_WEP_Key_Here");

if (CryptUnprotectData( &DataOut,
                        NULL,
                        NULL,
                        NULL,
                        NULL,
                        0,
                        &DataVerify))
{
    printf("The decrypted data is: %s\n", DataVerify.pbData);
}
else
{
    printf("Failed. Error Code: %d", GetLastError());
}

Но я получаю код ошибки13 ссылаясь на неверные данные. Что я делаю неправильно ? На Win 7 и позже я могу напрямую использоватьWlanGetProfile с параметромWLAN_PROFILE_GET_PLAINTEXT_KEY , Но у меня естьNO вариант наVista чем использовать функцию CryptUnprotectData. Я видел похожие постыВот, Вот но не получил много полезной информации. Кроме того, я использую ту же систему с теми же учетными данными пользователя. Может ли кто-нибудь предложить мне, как действовать?

PS: я разместил тот же вопрос на форумах Windows Desktop SDK, но пока не получил ответа. Испытываю удачу на ТАК.

Ваш Ответ

1   ответ
8

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

В вашем случае вы уже сделали первый шаг, используяnetsh.exe wlan export profile ... экспортировать данные из профиля WLAN в файл XML. Файл содержит<keyMaterial> элемент. Данные внутри элемента являются двоичными данными, закодированными как Hex: (что-то вроде01000000D08C9DDF0115D1118C7A00C0...).

Поэтому прежде всего необходимо декодировать строку в двоичные данные. Ты можешь использоватьCryptStringToBinary сCRYPT_STRING_HEX параметр для декодирования строки в двоичный файл.

Следующим шагом будет заполнениеDATA_BLOB с двоичными данными и вызовомCryptUnprotectData чтобы получить результат, но ... Есть небольшая проблема. Как вы можете прочитать в документацииWlanGetProfile следующие

By default, the keyMaterial element returned in the profile pointed to by the pstrProfileXml is encrypted. If your process runs in the context of the LocalSystem account on the same computer, then you can unencrypt key material by calling the CryptUnprotectData function.

Windows Server 2008 and Windows Vista: The keyMaterial element returned in the profile schema pointed to by the pstrProfileXml is always encrypted. If your process runs in the context of the LocalSystem account, then you can unencrypt key material by calling the CryptUnprotectData function.

Таким образом, чтобы иметь возможность расшифровать ключ, мы должны позвонитьCryptUnprotectData вLocalSystem контекст безопасности. Если ваша программа уже запущена подLocalSystem Контекст вы можете сделать это напрямую. Если это не так, но у вас есть права администратора или, по крайней мере, вы имеете право на отладку, вы можете «одолжить».LocalSystem токен от какого-то другого процесса, запущенного на компьютере. Например, можно получить токен процесса «winlogon.exe». обрабатывать и выдавать себя за него.

Следующая демонстрационная программа перечисляет процессы, используяNtQuerySystemInformation метод (см. мойстарый ответ) который я лично предпочитаю. Можно использоватьEnumProcesses или другие известные способы сделать то же самое. Вот код, который работал у меня

#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment (lib, "Crypt32.lib")

#define STATUS_SUCCESS               ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH  ((NTSTATUS)0xC0000004L)

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemProcessInformation = 5
} SYSTEM_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;

typedef LONG KPRIORITY; // Thread priority

typedef struct _SYSTEM_PROCESS_INFORMATION_DETAILD {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER SpareLi1;
    LARGE_INTEGER SpareLi2;
    LARGE_INTEGER SpareLi3;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    ULONG InheritedFromUniqueProcessId;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION_DETAILD, *PSYSTEM_PROCESS_INFORMATION_DETAILD;

typedef NTSTATUS (WINAPI *PFN_NT_QUERY_SYSTEM_INFORMATION)(
  IN       SYSTEM_INFORMATION_CLASS SystemInformationClass,
  IN OUT   PVOID SystemInformation,
  IN       ULONG SystemInformationLength,
  OUT OPTIONAL  PULONG ReturnLength
);

//
// The function changes a privilege named pszPrivilege for
// the current process. If bEnablePrivilege is FALSE, the privilege
// will be disabled, otherwise it will be enabled.
//
BOOL SetCurrentPrivilege (LPCTSTR pszPrivilege,   // Privilege to enable/disable
                          BOOL bEnablePrivilege)  // to enable or disable privilege
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    TOKEN_PRIVILEGES tpPrevious;
    DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
    BOOL bSuccess = FALSE;

    if (!LookupPrivilegeValue(NULL, pszPrivilege, &luid)) return FALSE;

    if (!OpenProcessToken (GetCurrentProcess(),
                           TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
                           &hToken
                          )) return FALSE;

    //
    // first pass.  get current privilege setting
    //
    tp.PrivilegeCount           = 1;
    tp.Privileges[0].Luid       = luid;
    tp.Privileges[0].Attributes = 0;

    AdjustTokenPrivileges(
            hToken,
            FALSE,
            &tp,
            sizeof(TOKEN_PRIVILEGES),
            &tpPrevious,
            &cbPrevious);

    if (GetLastError() == ERROR_SUCCESS) {
        //
        // second pass.  set privilege based on previous setting
        //
        tpPrevious.PrivilegeCount     = 1;
        tpPrevious.Privileges[0].Luid = luid;

        if(bEnablePrivilege)
            tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
        else
            tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
                tpPrevious.Privileges[0].Attributes);

        AdjustTokenPrivileges(
                hToken,
                FALSE,
                &tpPrevious,
                cbPrevious,
                NULL,
                NULL);

        if (GetLastError() == ERROR_SUCCESS) bSuccess=TRUE;

        CloseHandle(hToken);
    }
    else {
        DWORD dwErrorCode = GetLastError();

        CloseHandle(hToken);
        SetLastError(dwErrorCode);
    }

    return bSuccess;
}

DWORD GetProcessIdByProcessName (LPCWSTR pszProcessName)
{
    SIZE_T bufferSize = 1024*sizeof(SYSTEM_PROCESS_INFORMATION_DETAILD);
    PSYSTEM_PROCESS_INFORMATION_DETAILD pspid = NULL;
    HANDLE hHeap = GetProcessHeap();
    PBYTE pBuffer = NULL;
    ULONG ReturnLength;
    PFN_NT_QUERY_SYSTEM_INFORMATION pfnNtQuerySystemInformation = (PFN_NT_QUERY_SYSTEM_INFORMATION)
        GetProcAddress (GetModuleHandle(TEXT("ntdll.dll")), "NtQuerySystemInformation");
    NTSTATUS status;
    int uLen = lstrlenW(pszProcessName)*sizeof(WCHAR);

    __try {
        pBuffer = (PBYTE) HeapAlloc (hHeap, 0, bufferSize);
#pragma warning(disable: 4127)
        while (TRUE) {
#pragma warning(default: 4127)
            status = pfnNtQuerySystemInformation (SystemProcessInformation, (PVOID)pBuffer,
                                                  bufferSize, &ReturnLength);
            if (status == STATUS_SUCCESS)
                break;
            else if (status != STATUS_INFO_LENGTH_MISMATCH) { // 0xC0000004L
                _tprintf (TEXT("ERROR 0x%X\n"), status);
                return 1;   // error
            }

            bufferSize *= 2;
            pBuffer = (PBYTE) HeapReAlloc (hHeap, 0, (PVOID)pBuffer, bufferSize);
        }

        for (pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD)pBuffer; ;
             pspid = (PSYSTEM_PROCESS_INFORMATION_DETAILD)(pspid->NextEntryOffset + (PBYTE)pspid)) {

            if (pspid->ImageName.Length == uLen && lstrcmpiW(pspid->ImageName.Buffer, pszProcessName) == 0)
                return (DWORD)pspid->UniqueProcessId;

            if (pspid->NextEntryOffset == 0) break;
        }
    }
    __finally {
        pBuffer = (PBYTE) HeapFree (hHeap, 0, pBuffer);
    }
    return 0;
}

int _tmain()
{
    BOOL bIsSuccess, bImpersonated = FALSE;
    HANDLE hProcess = NULL, hProcessToken = NULL;
    DATA_BLOB DataOut, DataVerify;
    // !!! in the next line you should copy the string from <keyMaterial>
    WCHAR szKey[] = L"01000000D08C9DDF0115D1118C7....";
    BYTE byKey[1024];
    DWORD cbBinary, dwFlags, dwSkip;
    DWORD dwProcessId = GetProcessIdByProcessName(L"winlogon.exe");
    if (dwProcessId == 0) return 1;

    bIsSuccess = SetCurrentPrivilege(SE_DEBUG_NAME, TRUE);
    if (!bIsSuccess) return GetLastError();

    __try {
        hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId);
        if (!hProcess) __leave;
        bIsSuccess = OpenProcessToken (hProcess, MAXIMUM_ALLOWED, &hProcessToken);
        if (!bIsSuccess) __leave;
        bIsSuccess = ImpersonateLoggedOnUser(hProcessToken);
        if (!bIsSuccess) __leave;
        bImpersonated = TRUE;

        cbBinary = sizeof(byKey);
        bIsSuccess = CryptStringToBinary (szKey, lstrlenW(szKey), CRYPT_STRING_HEX, // CRYPT_STRING_HEX_ANY
            byKey, &cbBinary, &dwSkip, &dwFlags);
        if (!bIsSuccess) __leave;
        DataOut.cbData = cbBinary;
        DataOut.pbData = (BYTE*)byKey;

        if (CryptUnprotectData (&DataOut, NULL, NULL, NULL, NULL, 0, &DataVerify)) {
            _tprintf(TEXT("The decrypted data is: %hs\n"), DataVerify.pbData);
        }
    }
    __finally {
        if (bImpersonated)
            RevertToSelf();
        if (hProcess)
            CloseHandle(hProcess);
        if (hProcessToken)
            CloseHandle(hProcessToken);
    }

    return 0;
}
@Mahesh: Вы правы! Данные изDataVerify.pbData должен быть не пустым. Таким образом, нужно исправить линию_tprintf(TEXT("The decrypted data is: %hs\n"), DataVerify.pbData); отображать толькоDataVerify.cbData байты данных. Можно использовать например_tprintf(TEXT("The decrypted data is: %*hs\n"), DataVerify.cbData, DataVerify.pbData); или просто скопируйте строковые данные и добавьте туда\0.
Я вручную добавил сеть типа WEP вNetwork and Sharing Center -> Manage Wireless Networks, Еще раз, большое спасибо :) Mahesh
Хорошо. Большое большое спасибо. Вы сделали мою работу. Спасибо за код :) Mahesh
Я никогда не заботился о срокеLocalSystem потому что я бежал какAdministrator и было впечатление, чтоAdministrator больше похоже на супер пользователя и может делать все что угодно. Mahesh
Большое спасибо за подробное объяснение. Код работает нормально для всех сетей и частично для сети WEP. При получении ключа от сети WEP,DataVerify.pbData имеет значение мусора в конце (LLLMEM+это значение соответствует). Тем не мение,DataVerify.cbData показывает длину ключа правильно. Можете ли вы проверить, происходит ли то же самое на вашей машине? Я использую VS 2010. Mahesh

Похожие вопросы