Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Исходник [Гайд] Обход HWID через \\.\spaceport — Недокументированный метод получения GUID

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
429
Реакции
10
Здорова, реверсеры. Нашел одну интересную тему, которую мало кто копает в контексте HWID-спуфинга. Речь об определении дисков через устройство \\.\spaceport.

Обычно все стандартные пасты дрочат SMART или Partition ID, но этот вектор позволяет вытащить GUID дисков напрямую через недокументированные IOCTL. Фишка в том, что если вы грамотно заспуфаете эти запросы, вам больше не придется выполнять Reset-PhysicalDisk *. Как мы знаем, использование этой команды — само по себе жирный флаг для античитов, который сигнализирует о попытке подмены железа.

Ниже прикрепляю код чекера (логгера), который вытягивает инфу через интерфейс SpacePort. Используйте для анализа того, что видит система:

Код:
Expand Collapse Copy
#define IOCTL_SPACEPORT_GET_DRIVES     0xE70404
#define IOCTL_SPACEPORT_GET_DRIVE_INFO 0xE70408
 
void ExtractWideStrings(const BYTE* data, DWORD size)
{
    for (DWORD i = 0; i + 1 < size; i += 2)
    {
        const WCHAR* wstr = (const WCHAR*)(data + i);
        DWORD len = 0;
        while (i + (len + 1) * 2 <= size && wstr[len] >= 0x20 && wstr[len] <= 0x7E)
            len++;
 
        if (len >= 4)
        {
            printf("  [0x%04X] \"", i);
            for (DWORD j = 0; j < len; j++)
                printf("%c", (char)wstr[j]);
            printf("\"\n");
            i += len * 2 - 2;
        }
    }
}
 
void PrintGUID(const GUID* g)
{
    printf("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
        g->Data1, g->Data2, g->Data3,
        g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3],
        g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
}
 
int main()
{
    printf("=== SpfChecker: SpacePort Drive Logger ===\n\n");
 
    HANDLE hDevice = CreateFileW(L"\\\\.\\spaceport",
        GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING, 0, NULL);
 
    if (hDevice == INVALID_HANDLE_VALUE)
    {
        printf("[!] Failed to open \\\\.\\spaceport (error %lu)\n", GetLastError());
        system("pause");
        return 1;
    }
 
    printf("[+] Opened \\\\.\\spaceport\n\n");
 
    BYTE getDrivesIn[64] = {};
    *(DWORD*)getDrivesIn = 64;
 
    DWORD outSize = 65536;
    BYTE* outBuf = (BYTE*)calloc(1, outSize);
    DWORD ret = 0;
 
    BOOL ok = DeviceIoControl(hDevice, IOCTL_SPACEPORT_GET_DRIVES,
        getDrivesIn, sizeof(getDrivesIn), outBuf, outSize, &ret, NULL);
 
    if (!ok)
    {
        printf("[!] GetDrives failed (error %lu)\n", GetLastError());
        getDrivesIn[20] = 1;
        ok = DeviceIoControl(hDevice, IOCTL_SPACEPORT_GET_DRIVES,
            getDrivesIn, sizeof(getDrivesIn), outBuf, outSize, &ret, NULL);
 
        if (!ok)
            printf("[!] Still failed (error %lu)\n\n", GetLastError());
    }
 
    GUID driveGuids[64] = {};
    int driveCount = 0;
 
    if (ok && ret >= 4)
    {
        DWORD count = *(DWORD*)outBuf;
        printf("[+] GetDrives returned %lu bytes, %lu drives\n\n", ret, count);
 
        ExtractWideStrings(outBuf, ret);
        printf("\n");
 
        DWORD pos = 4;
        for (DWORD d = 0; d < count && d < 64 && pos + 16 <= ret; d++)
        {
            GUID* g = (GUID*)(outBuf + pos);
            if (g->Data1 != 0 || g->Data2 != 0 || g->Data3 != 0)
            {
                driveGuids[driveCount] = *g;
                printf("  Drive %d GUID: ", driveCount);
                PrintGUID(g);
                printf("\n");
                driveCount++;
            }
            pos += 16;
        }
        printf("\n");
    }
 
    if (driveCount > 0)
    {
        DWORD infoOutSize = 0x2000;
        BYTE* infoOut = (BYTE*)calloc(1, infoOutSize);
 
        for (int d = 0; d < driveCount; d++)
        {
            printf("--- Drive %d ---\n", d);
            printf("  GUID: ");
            PrintGUID(&driveGuids[d]);
            printf("\n");
 
            BYTE in[40] = {};
            *(DWORD*)in = 40;
            memcpy(in + 20, &driveGuids[d], sizeof(GUID));
 
            memset(infoOut, 0, infoOutSize);
            ret = 0;
 
            ok = DeviceIoControl(hDevice, IOCTL_SPACEPORT_GET_DRIVE_INFO,
                in, sizeof(in), infoOut, infoOutSize, &ret, NULL);
 
            if (!ok)
            {
                memset(in, 0, sizeof(in));
                *(DWORD*)in = 40;
                memcpy(in + 4, &driveGuids[d], sizeof(GUID));
 
                ok = DeviceIoControl(hDevice, IOCTL_SPACEPORT_GET_DRIVE_INFO,
                    in, sizeof(in), infoOut, infoOutSize, &ret, NULL);
 
                if (!ok)
                {
                    printf("  [!] Failed (error %lu)\n\n", GetLastError());
                    continue;
                }
            }
 
            printf("  [+] %lu bytes\n", ret);
            ExtractWideStrings(infoOut, ret);
            printf("\n");
        }
 
        free(infoOut);
    }
    else
    {
        DWORD infoOutSize = 0x2000;
        BYTE* infoOut = (BYTE*)calloc(1, infoOutSize);
 
        BYTE in[40] = {};
        *(DWORD*)in = 40;
        ret = 0;
 
        ok = DeviceIoControl(hDevice, IOCTL_SPACEPORT_GET_DRIVE_INFO,
            in, sizeof(in), infoOut, infoOutSize, &ret, NULL);
 
        if (ok && ret > 0)
        {
            printf("[+] GetDriveInfo(NULL) returned %lu bytes\n", ret);
            ExtractWideStrings(infoOut, ret);
        }
        else
            printf("[!] GetDriveInfo(NULL) failed (error %lu)\n", GetLastError());
 
        free(infoOut);
    }
 
    free(outBuf);
    CloseHandle(hDevice);
 
    printf("\n=== Done ===\n");
    system("pause");
    return 0;
}

Почему это важно для обхода античитов?
Современные AC (особенно те, что работают на уровне ядра, типа BattlEye или EAC) постоянно расширяют список опрашиваемых интерфейсов. Если ваш спуфер меняет серийники только в классических структурах, но оставляет оригинальный GUID в SpacePort — это моментальный флаг или отложенный бан по железу.

При реализации спуфера на уровне драйвера (kernel mode) вам нужно будет перехватывать запросы к этому устройству и подменять возвращаемые структуры данных, чтобы они соответствовали вашим новым идентификаторам дисков.

Кто-нибудь уже пробовал ковырять этот метод в своих драйверах?
 
Что быстрее и менее заметно для получения этого же GUID — прямой вызов к \\.\spaceport или запрос WMI классу MSFT_PhysicalDisk (namespace Root\Microsoft\Windows\Storage)? Использует ли WMI провайдер тот же самый IOCTL внутри или дергает API VIRTUAL_STORAGE?
 
Назад
Сверху Снизу