Pull to refresh

Comments 29

Все это бесполезно если не использовать IL2CPP. Две минуты и ваша защита уже отломана.

Да и возникает вопрос вполне справедливых наездов пользователей из-за ложных срабатываний - переустановка или клонирование винды (а иногда и даже банальный апгрейд на новую версию десятки) практически наверняка закончится изменением серийника тома и отваливанием защиты.

Насчёт шифрования это понятно. Меня вот мучает другой вопрос, как с помощью Unity выдрать серийный номер самого жёсткого диска? Т.к. с библиотекой System.Management Unity не умеет работать... :(

Вряд ли поможет, но под админом можно спросить у диска напрямую:
Скрытый текст
[StructLayout(LayoutKind.Sequential, Size = 12)]
private class DRIVERSTATUS
{
	public byte DriveError;
	public byte IDEStatus;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
	public byte[] Reserved;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
	public int[] Reserved2;
	public DRIVERSTATUS()
	{
		Reserved = new byte[2];
		Reserved2 = new int[2];
	}
}

[StructLayout(LayoutKind.Sequential)]
private class IDSECTOR
{
	public short GenConfig;
	public short NumberCylinders;
	public short Reserved;
	public short NumberHeads;
	public short BytesPerTrack;
	public short BytesPerSector;
	public short SectorsPerTrack;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
	public short[] VendorUnique;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
	public char[] SerialNumber;
	public short BufferClass;
	public short BufferSize;
	public short ECCSize;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
	public char[] FirmwareRevision;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
	public char[] ModelNumber;
	public short MoreVendorUnique;
	public short DoubleWordIO;
	public short Capabilities;
	public short Reserved1;
	public short PIOTiming;
	public short DMATiming;
	public short BS;
	public short NumberCurrentCyls;
	public short NumberCurrentHeads;
	public short NumberCurrentSectorsPerTrack;
	public int CurrentSectorCapacity;
	public short MultipleSectorCapacity;
	public short MultipleSectorStuff;
	public int TotalAddressableSectors;
	public short SingleWordDMA;
	public short MultiWordDMA;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst = 382)]
	public byte[] Reserved2;
	public IDSECTOR()
	{
		VendorUnique = new short[3];
		Reserved2 = new byte[382];
		FirmwareRevision = new char[8];
		SerialNumber = new char[20];
		ModelNumber = new char[40];
	}
}

[StructLayout(LayoutKind.Sequential)]
private class SENDCMDOUTPARAMS
{
	public int BufferSize;
	public DRIVERSTATUS Status;
	public IDSECTOR IDS;
	public SENDCMDOUTPARAMS()
	{
		Status = new DRIVERSTATUS();
		IDS = new IDSECTOR();
	}
}

[DllImport("kernel32.dll")]
private static extern int CloseHandle(int hObject);

[DllImport("kernel32.dll")]
private static extern int CreateFile(
	string lpFileName,
	uint dwDesiredAccess,
	int dwShareMode,
	int lpSecurityAttributes,
	int dwCreationDisposition,
	int dwFlagsAndAttributes,
	int hTemplateFile
);

[DllImport("kernel32.dll")]
private static extern int DeviceIoControl(
	int hDevice,
	int dwIoControlCode,
	[In(), Out()] SENDCMDINPARAMS lpInBuffer,
	int lpInBufferSize,
	[In(), Out()] SENDCMDOUTPARAMS lpOutBuffer,
	int lpOutBufferSize,
	ref int lpBytesReturned,
	int lpOverlapped
);

private const int OPEN_EXISTING = 3;

private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;

private const int FILE_SHARE_READ = 0x1;
private const int FILE_SHARE_WRITE = 0x2;

private const int INVALID_HANDLE_VALUE = -1;

private const int DFP_RECEIVE_DRIVE_DATA = 0x7C088;

public static string getSerial()
{
	int returnSize = 0;
	int driveNumber = 0;
	SENDCMDINPARAMS sci = new SENDCMDINPARAMS();
	SENDCMDOUTPARAMS sco = new SENDCMDOUTPARAMS();

	if (System.Environment.OSVersion.Platform != PlatformID.Win32NT)
	{
		return null;
	}

	int handle = CreateFile(@"\\.\PhysicalDrive0", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
	if (handle == INVALID_HANDLE_VALUE)
	{
		return null;
	}

	sci.DriveNumber = (byte)driveNumber;
	sci.BufferSize = Marshal.SizeOf(sco);
	sci.DriveRegs.DriveHead = (byte)(0xA0 | driveNumber << 4);
	sci.DriveRegs.Command = 0xEC;
	sci.DriveRegs.SectorCount = 1;
	sci.DriveRegs.SectorNumber = 1;

	var result = DeviceIoControl(handle, DFP_RECEIVE_DRIVE_DATA, sci, Marshal.SizeOf(sci), sco, Marshal.SizeOf(sco), ref returnSize, 0);
	CloseHandle(handle);

	if (result != 0)
	{
		// Fix byte order
		char[] buf = new char[sco.IDS.SerialNumber.Length];
		buf[sco.IDS.SerialNumber.Length - 1] = sco.IDS.SerialNumber[sco.IDS.SerialNumber.Length - 1];
		for (int i = 0; i < sco.IDS.SerialNumber.Length; i += 2)
		{
			buf[i] = sco.IDS.SerialNumber[i + 1];
			buf[i + 1] = sco.IDS.SerialNumber[i];
		}

		return new string(buf);
	}

	return null;
}

Получилось решить данную задачу чуть-чуть проще: написанием отдельной библиотеки dll и подключением ее к Unity

https://github.com/FranzDevBy/Serial-Number-Disk-in-Unity

Ну, такое себе. У меня WMI-запрос «Select * from Win32_DiskDrive» первым выдал съёмный диск PhysicalDrive7 (и вообще порядок дисков разве чем-гарантируется?). А вы берёте первый диск из выдачи и привязываетесь к его серийнику. Это может кончится тем, что при вставке новой флешки игра начнёт обвинять легального пользователя в пиратстве )))

Гарантируется латинским алфавитом.

Вы не читали, что я выше написал? У меня первым выпал PhysicalDrive7, на котором назначена буква диска E:

А PhysicalDrive7 в какой разъём SATA (порядковый номер) на материнской плате приходит? Или вообще в разъем M.2?

Если вам интересно (хотя к проблеме не имеет никакого отношения), системный M2 SSD (диск C:) Windows 10 видит как PhysicalDrive5, а WMI в списке дисков его показывает последним. Тут полный рандом. Есть диск, подключенный через SATA-контроллер в PCI-e слоте, его номер PhysicalDrive4. И до него по нумерации, и после него, есть диски из штатных SATA-линий с материнки.

При чём здесь SATA? Я втыкаю флешку — появляется PhysicalDrive8, и в списке «Select * from Win32_DiskDrive» он где-то в середине (а мог бы и первым встать, и попасть вашей «защите» на проверку серийника — гарантий никаких нет, что так не будет).

Это так наивно. Ваша защита ломается даже интересующимися студентом за часы. Не говоря уже о людях с опытом.

Мне наивным больше кажется очередной пересчёт скачиваний торрента в упущенную прибыль…

Ну какой-то убыток вполне может быть от этого.

Я бы, если игра хорошая получилась, сам бы сделал демку на 10-20% геймлея и первый залил на популярные трекеры прямо в момент релиза. С возможностью переноса сейвов, конечно.

Насколько увеличился доход от продаж после внедрения вами этой защиты в свой продукт?

Вместо этой защиты, как мне кажется, больший результат принесли бы другие действия, например: Показ сообщения что вы типа пират и это не хорошо, купите игру со скидкой например или задонатьте для отключения этого сообщения ну или что-то в этом роде. В игру играть, я бы на вашем месте разрешил, даже в пиратскую.

По мне так гуманнее будет сделать внутриигровую авторизацию через стимовский аккаунт. Удобнее и надежнее

Против этого есть стандартные эмуляторы стим, которые подсунут игре аккаунт с любым заранее настроенным именем (например, CODEX :) ) и скажут игре, что она куплена на этом аккаунте.

Так против всего есть методы. Денува ломается так же как и все остальное.

Просто то, что в статье - какой-то гемор для пользователя.

Против Денувы нет метода, даже несмотря на то, что она ломается.

Денува - это сервис гибридной войны против пиратов. Там не только протектор, с ним как раз всё плохо. Там и телеметрия похлеще виндовой и как следствие - работа с со спецслужбами, и армия ботов и доктрина с целью - чтобы никто не взламывал

Очень интересная конспирология, прям как я люблю. Где можно про это почитать?

нет метода...она ломается

Ломается. Так что метод как таковой есть ))

В теории ломается всё, что выполняется на клиентской стороне. Но это не значит, что существует универсальный «метод». Сам процесс взлома это не метод.

С заинлайненными проверками, например, не всё так просто. Каждый случай уникален и сложность взлома зависит скорей от того, как именно разработчики применили защиту и насколько легко автоматически распознать такие вставки или подменить для них входные данные.

Статья вроде про инди-игру. Какая вообще денува?

То есть, пират может легко выложить свою копию игры на трекер, если после установки из steam он ни разу не запускал игру, и файл settings.dat ещё не перезаписан серийным номером диска. Даже код ковырять не надо.

Что прямо вот правда "Кому" "воруют"? На рутрекере нет ее.

Видите, защита работает!

Ну ведь хоть какая-то защита, но она будет

Вспомнил, как в начале карьеры не получил ни копей прибыли от готового продукта, так как сильно боялся, что его будут воровать, а сообразить должную защиту я не смог :)

Вызовы Close внутри using выглядят лишними.

Sign up to leave a comment.

Articles