- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 429
- Реакции
- 10
Здорова, реверсеры. Нашел одну интересную тему, которую мало кто копает в контексте HWID-спуфинга. Речь об определении дисков через устройство \\.\spaceport.
Обычно все стандартные пасты дрочат SMART или Partition ID, но этот вектор позволяет вытащить GUID дисков напрямую через недокументированные IOCTL. Фишка в том, что если вы грамотно заспуфаете эти запросы, вам больше не придется выполнять Reset-PhysicalDisk *. Как мы знаем, использование этой команды — само по себе жирный флаг для античитов, который сигнализирует о попытке подмены железа.
Ниже прикрепляю код чекера (логгера), который вытягивает инфу через интерфейс SpacePort. Используйте для анализа того, что видит система:
Почему это важно для обхода античитов?
Современные AC (особенно те, что работают на уровне ядра, типа BattlEye или EAC) постоянно расширяют список опрашиваемых интерфейсов. Если ваш спуфер меняет серийники только в классических структурах, но оставляет оригинальный GUID в SpacePort — это моментальный флаг или отложенный бан по железу.
Кто-нибудь уже пробовал ковырять этот метод в своих драйверах?
Обычно все стандартные пасты дрочат SMART или Partition ID, но этот вектор позволяет вытащить GUID дисков напрямую через недокументированные IOCTL. Фишка в том, что если вы грамотно заспуфаете эти запросы, вам больше не придется выполнять Reset-PhysicalDisk *. Как мы знаем, использование этой команды — само по себе жирный флаг для античитов, который сигнализирует о попытке подмены железа.
Ниже прикрепляю код чекера (логгера), который вытягивает инфу через интерфейс SpacePort. Используйте для анализа того, что видит система:
Код:
#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) вам нужно будет перехватывать запросы к этому устройству и подменять возвращаемые структуры данных, чтобы они соответствовали вашим новым идентификаторам дисков.
Кто-нибудь уже пробовал ковырять этот метод в своих драйверах?