Welcome to hackvshack.net Forum!
Download Free HvH CS2/CS:GO Cheats, CFG, LUA/JS Scripts, and More!
Register

Bypass Kernel Advanced Disk spoofing

  • Thread starter Thread starter jake
  • Start date Start date

jake

Expert HvHer
User ID:
24499
Messages:
104
Reactions:
152
Badges:
17
REP:
−0/0+
Level:
182
There have been like five threads on UC this week from people getting delay banned with their "p100 temp spoofer."

Every reply lands on the same retarded answers and nobody actually helps. They also do not mention their spoofer logic most of the time — just them being detected.

My guess is most of those builds came out of some old shitty base, and they did not even take the time to actually reverse the driver they're writing into.

I've also seen people do some crazy claims like "no temp works for Fortnite/Rust" just because Reported is down, lmfao.




So here's what I do

This is the logic I use. I've tested every serial query path I know of, and it reads as spoofed everywhere — which is what a temp spoofer is supposed to do.

It works on high tier EAC, ACE and BE just fine. And yes, that includes Fortnite, Rust, R6, ABI, Delta Force, etc.

Most of the time the problem with temp spoofers isn't hook detection. It's their architecture.

Windows does not have a single "disk serial" variable.
It has a handful of copies of one, spread across drivers that never talk to each other.

If you've ever made a cheat and edited local health inside the pawn only to watch the bar stay frozen, you already get this. The widget isn't reading the pawn. It's reading a copy some thread cached three seconds ago, and the server is validating a third copy neither of you can see.

Storage IDs work the same way. Get-Disk, Get-PhysicalDisk and WMI are three different HUDs reading three different driver caches. No shared pointer, so patching one does nothing to the others.

What follows is where those copies actually live, which query reads which buffer, and how to grab the ground truth off the drive before you start poking kernel memory.

Offsets are for Win 11 24H2, build 26100.
Please don't DM me for offsets of your specific build. There's more than enough info here to open IDA and find them yourself.

Reference serial throughout is 6479_A780_4000_4F2E — yours will look similar.




1) Get-Disk doesn't print the drive's serial

Everyone I've talked to about this assumes the serial Windows prints is the 20-byte ASCII serial out of Identify Controller.

Identify Controller does have a serial field. Twenty bytes, space padded, no null terminator, 0x04, and the model number starts at 0x18 right after it, 40 bytes. Drivers do read it. Windows just doesn't show it to you on a consumer NVMe drive.

What it shows you is the Namespace EUI-64.

Identify Namespace has:
  • NGUID at +0x68 (16 bytes)
  • EUI64 at +0x78 (8 bytes)

The PowerShell or cmd string is those 8 bytes as hex pairs with underscores, plus a dot stuck on the end, because whoever wrote that formatter felt like it.

Code:
Expand Collapse Copy
Raw EUI64 bytes : 64 79 A7 80 40 00 4F 2E
Get-Disk        : 6479_A780_4000_4F2E
UniqueId        : eui.6479A78040004F2E

UniqueId is those same 8 bytes with the underscores taken out and eui. in front. So the ASCII string is a rendering of the 8 bytes and not the other way round.

Patch the rendering and every query that reads the raw EUI, or builds its own UniqueId string off the buffer, reads the original.
Renaming yourself while the anti-cheat reads the SteamID64.

Get-PhysicalDisk keeps the trailing dot, Get-Disk drops it. Two sprintf calls, two drivers, same 8 bytes. If those two commands disagree by more than that dot, you're missing a copy.

I've seen people try to patch the dot in so the outputs match. That's not the problem.




2) Get the hardware value first

You want what the device says, not what a driver cached. Anything read through WMI or Get-Disk on a box you've already been testing on is suspect, because you're the one who wrote to those caches.

IOCTL_STORAGE_QUERY_PROPERTY, Property ID 50 (StorageDeviceProtocolSpecificProperty).

Input is a STORAGE_PROPERTY_QUERY and then a STORAGE_PROTOCOL_SPECIFIC_DATA:

Code:
Expand Collapse Copy
+0x00  PropertyId                    50   (StorageDeviceProtocolSpecificProperty)
+0x04  QueryType                     0    (PropertyStandardQuery)
+0x08  ProtocolType                  3    (ProtocolTypeNvme)
+0x0C  DataType                      1    (NVMeDataTypeIdentify)
+0x10  ProtocolDataRequestValue      CNS  (0 = Namespace, 1 = Controller)
+0x14  ProtocolDataRequestSubValue   NSID (1 on a single-namespace consumer drive)
+0x18  ProtocolDataOffset            40
+0x1C  ProtocolDataLength            4096

Send it twice.

  • CNS 0 → Identify Namespace
  • CNS 1 → Identify Controller

Namespace first. Eight bytes at +0x78, format with the underscore rule, compare against Get-Disk. If they match, that's ground truth and every copy you find from here on is a copy of it.

If they don't match on a clean boot, your drive is one of the ones that reports the controller serial instead of the EUI. That happens. Don't burn a day on it.

CNS 1 second: 20-byte serial at +0x04 and the 40-byte model at +0x18. You want both, because those are the two shapes you'll be recognising later when you're walking pointers with no symbols: Identify Controller looks like 20 printable ASCII at +0x04 and 40 printable ASCII at +0x18, Identify Namespace looks like the 8-byte EUI64 sitting at +0x78.

Header sits in front of the 4096 bytes. STORAGE_PROTOCOL_DATA_DESCRIPTOR, 8 bytes, ProtocolDataOffset is at byte 16, the data starts at 40. Everybody hardcodes 0 once.

And if a pointer doesn't match one of those two shapes, leave it alone. It's probably some other pointer or a vtable, and that's how you end up bluescreening on boot and making a thread about it.




3) Storport

StorageDeviceProperty never reaches the drive. It reads classpnp's copy, and classpnp filled that copy from storport. No RAID card needed either — Windows lays NVMe and SATA out as RAID units regardless. \Driver\stornvme, \Driver\storahci.

Device objects in \Driver\stornvme, DeviceType == 7 (FILE_DEVICE_DISK).
_DEVICE_OBJECT + 0x40 is DeviceExtension, and that lands you in _RAID_UNIT_EXTENSION.

Build 26100:

Code:
Expand Collapse Copy
+0x018  Adapter
+0x070  Identity               (_STOR_IDENTITY)
+0x0A8  VendorId
+0x0B1  ProductId
+0x0C2  SerialNumber           (ANSI_STRING)
+0xDD0  IdentifyNamespaceData  (pointer to cached Identify Namespace)

Four serials live in that one extension. Not two. Four, and most people find the first one and stop looking.

  • +0xC2 is an ANSI_STRING. Length, MaximumLength, Buffer. Buffer points at a pool allocation and that's where the characters actually are. memcpy over +0xC2 and you overwrite the lengths plus whatever sits after them, and something dies on the next read. Follow the pointer, check Length is under 128, check the bytes are printable, write there.
  • +0x70 → +0x08 is another ANSI_STRING in a separate allocation, same drive, same serial. So you find +0xC2, patch it, query again, get the real serial back, and decide you got the offset wrong. You didn't. Patch both.
  • +0x20 in _STOR_IDENTITY is DeviceId, points at VPD page 0x83. That's a descriptor list, code set, type, length, then the bytes. The EUI shows up in it twice, once as ASCII and once as the 8 raw bytes, so leaving the page alone leaves two live copies behind.
  • +0xDD0 is IdentifyNamespaceData, the cached 4096-byte Identify Namespace with the EUI-64 still at +0x78. classpnp rebuilds identifiers out of that buffer whenever something asks. So you can patch all three ANSI_STRINGs, walk away satisfied, and get snapped back to the real serial the next time anything queries the drive, because it rebuilt from the buffer you didn't touch. Then Get-Disk and Get-PhysicalDisk disagree and you get to work out why.




4) Stornvme's copy

_NVME_ADAPTER_EXTENSION, +0x18 off the RAID unit extension. Internal offsets, not in the public PDB, so don't guess at them.

FormInquirySerialNumberData, first 0x300 bytes:

Code:
Expand Collapse Copy
4C 8B A9 xx xx xx xx      mov r13, [rcx+disp32]         ; Identify Controller ptr
4A 8B 84 E6 xx xx xx xx   mov rax, [rsi+r12*8+disp32]   ; namespace descriptor table
4C 8D 50 xx               lea r10, [rax+disp8]          ; NGUID inside wrapper
4C 8D 78 xx               lea r15, [rax+disp8]          ; EUI64 inside wrapper

FormInquiryDeviceIdentifiersData:

Code:
Expand Collapse Copy
44 3B A6 xx xx xx xx      cmp r12d, [rsi+disp32]        ; namespace count

26100 offsets:
  • ident_ctrl → 0x578
  • ns_count → 0xE0
  • ns_table → 0x5E8

The table entries at 0x5E8 are wrapper structs, not the spec struct.
NGUID +0x44, EUI64 +0x54.
0x68 and 0x78 are the spec offsets and they don't apply here — the wrapper isn't the same struct with padding in it. That one cost me an evening.

If a Windows build ever breaks your sigs, don't go pull hardcoded offsets out of some old UC thread. Walk the pointers in the adapter extension and check them against the shapes from section 2.




5) \Driver\disk caches the descriptor

Everything patched, box up an hour, Get-Disk shows the old serial again.

classpnp built a STORAGE_DEVICE_DESCRIPTOR on the first query and parked the pointer on the disk FDO. It doesn't ask storport again after that.

DeviceType == 7, extension +0x40. No stable offset for the descriptor, so scan the first page: every qword that looks like a kernel pointer, check the target.

Code:
Expand Collapse Copy
+0x00  Version             between 0x20 and 0x400
+0x04  Size                >= Version, and < 0x2000
+0x1C  BusType             between 1 and 0x20
+0x24  SerialNumberOffset  non-zero, and < Size

Then descriptor + SerialNumberOffset, and that's the ASCII serial with the dot.

Second pointer nearby usually goes to a STORAGE_DEVICE_ID_DESCRIPTOR. Version, Size, NumberOfIdentifiers. Walk it with NextOffset and actually follow NextOffset or you walk straight off the buffer. CodeSet == 1 with a size of 8, 16 or 20 is the binary EUI, NGUID or binary serial. Those need patching too, or a rebuild from storport hands you two different answers.




6) Spaceport

This is where it stops being funny.
Get-Disk and wmic show your serial. Get-PhysicalDisk shows the factory one.

Storage Spaces doesn't send the property IOCTL at all. It sends IOCTL 0xE70408 to spaceport.sys, which lands in SpIoctlGetDriveInfo (?SpIoctlGetDriveInfo@@YAJPEAU_IRP@@@Z). Mine was at RVA 0xB3070, one direct call, mov rcx, rdi then call.

Trap one — the ioctl code in the stack location, _IO_STACK_LOCATION. Old notes say +0x10. On x64 the DeviceIoControl parameters are laid out like this, and IoControlCode sits at +0x18.

Code:
Expand Collapse Copy
+0x08  OutputBufferLength
+0x10  InputBufferLength
+0x18  IoControlCode
+0x20  Type3InputBuffer

spaceport's dispatcher does mov eax, [rbx+18h], rbx being [irp+0xB8]. Check +0x10 and you're comparing the input length against 0xE70408. Never matches, hook never fires, and you spend the evening sure you hooked the wrong function.

Trap two — the output buffer. Output length 0x1C30, so the first 0xC00 bytes are header and GUIDs. Record starts at 0xC00.

Code:
Expand Collapse Copy
+0xC00  UTF-16  serial, controller-style, or the formatted EUI on some drives
+0xC42  UTF-16  slot, bus, device, function
+0xCC0  ASCII   eui.6479A78040004F2E
+0xCD8  UTF-16  SCSI\Disk&Ven_NVMe&Prod_...

SerialNumber and UniqueId in Get-PhysicalDisk both come out of there. If 0xCC0 doesn't match what you put in storport, the two commands disagree, which is something you will notice.

Trap three is the one that actually kills people. The prologue spills rbx, rsi and rdi into the caller's shadow space before it pushes anything:

Code:
Expand Collapse Copy
4C 8B DC                  mov r11, rsp
55                        push rbp
41 54                     push r12
49 8D 6B C8               lea rbp, [r11-38h]
48 81 EC 28 01 00 00      sub rsp, 128h

Spills land at [r11+0x10], [r11+0x18], [r11+0x20]. Epilogue restores r15 r14 r13 rbx rsi rdi out of its own frame, and the dispatcher after the call still wants the IRP in rdi for mov [rdi+30h], ebx. Trash rdi and you crash inside spaceport, on a pool address, which takes a while to work out.

And patch the output buffer after the call returns. Patch before and the function writes over your bytes while filling the buffer, which is a good way to lose an afternoon.




7) Volume and partition GUIDs

Get-Volume UniqueId is \??\Volume{GUID}, a symlink mountmgr.sys creates under \Device\MountPointManager. Inside mountmgr that name is a hash table key, so writing new GUID bytes into the object leaves the entry in the wrong bucket and lookups stop working. You can't just poke it.

Code:
Expand Collapse Copy
CREATE_POINT   0x6DC000
DELETE_POINTS  0x6DC004
QUERY_POINTS   0x6D0008

Access bits on the first two are 3 (READ | WRITE), not 1, because they're built CTL_CODE(0x6D, n, BUFFERED, 3). Build 0x6D4000 and you get ERROR_INVALID_FUNCTION, which looks like the ioctl simply isn't there. DELETE_POINTS also wants a MOUNTMGR_MOUNT_POINT carrying the unique ID and a real output buffer, since it writes the deleted points back out. Query, build the new point from what it returns, delete the old one. That's what inserts the new key and drops the old.

Get-Partition Guid is partmgr.sys. PmBuildUniqueId and PmBuildStableGuid read the partition extension, which is the part everyone patches, and it isn't enough, because the ioctl goes through PmGetDriveLayoutEx and that reads a cached layout at different offsets. Extension patched, cache not → GUID doesn't move.




The byte set

Generate once, use everywhere.

Code:
Expand Collapse Copy
Raw EUI64 bytes  : 64 79 A7 80 40 00 4F 2E
Get-Disk         : 6479_A780_4000_4F2E
Get-PhysicalDisk : 6479_A780_4000_4F2E.
UniqueId / VPD   : eui.6479A78040004F2E
Controller SN    : 20-byte space-padded ASCII (different value, keep it consistent)




If you take one thing from this:

Grab the real 8 bytes off the drive with the protocol query before you touch a single driver. Everything you find after that — storport, stornvme, \Driver\disk, spaceport — is a raw copy of those 8 bytes, a formatted string of them, or the 20-byte controller serial.

Patch all of them, or the queries snitch on each other.

This is just one of the things you have to deal with when temp spoofing, but hopefully I solved it for you.
 
A correction for my earlier statement: AMD Raid leaks disk SMART serials, Intel doesnt, but both will work 100% of cases even against valorant
 

Who has read this thread (Total: 0) in last 1 hours View details