Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,34 @@ The DLL is not a system module. It should be loaded, executed, and then unloaded

**For those who do not code and just want to download/use Bad Storage:** it is built into the latest version of <a href="https://github.com/Byrom90/XeUnshackle">XeUnshackle</a>, so just download that and it is all you need. Make sure to format your drive using FATXplorer as well.

## Fork: unauthenticated disk support

This fork adds support for disks that never passed the console's security-sector check — no
Microsoft signature, no SSD Maker, no HDD Maker. Format the drive as a normal Xbox 360 hard disk
with FATXplorer and it mounts.

It works by finishing the partition device initialisation that `SataDiskInitialize` skips when
authentication fails, rather than trying to defeat the authentication check itself (which cannot
work — that gate is evaluated once at boot, before any exploit payload runs).

Capacity comes from the drive's own reported sector count rather than any declared model size, so
there is no retail-style size ceiling. Tested from a 240 GB SSD up to 1 TB mechanical disks.

It can also reformat the disk from the console itself by holding LT, which recovers a drive already
flashed by SSD Maker even without its `undo.bin`.

Note that a disk set up this way cannot serve an exploit entry point that reads from the internal
drive, such as BadAvatarHDD — an unauthenticated disk has no readable filesystem until the bypass
has run, and the bypass needs an exploit to already be running. USB-based entry points are
unaffected.

Requires a retail console on kernel 17559 with Bad Update + XeUnshackle. See the
[wiki](https://github.com/Angelpro09xd/BadStorage/wiki) for how it works, the kernel internals it relies on, and its limitations.

Upstream Bad Storage behaviour is unchanged: on an authenticated, BSTOR-formatted disk this fork
does exactly what it always did.


# Compiling

The recommended development environment is:
Expand Down
83 changes: 77 additions & 6 deletions src/BadStorage-DLL/BadStorage/BadStorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "stdafx.h"
#include "BadStorage.h"
#include "UnauthenticatedDisk.h"
#include "FatxFormat.h"

VOID Print(const PCHAR Format, ...)
{
Expand Down Expand Up @@ -144,20 +146,68 @@ EXTERN_C BOOLEAN Execute(PBOOLEAN IsRetailFormatted)
return FALSE;
}

//Held left trigger means "reformat this disk". Checked before anything else, because on an
//SSD Maker flashed drive the disk still authenticates at its flashed size and the code below
//would happily carry on using the smaller layout.
if (IsFormatRequested())
{
Print("Left trigger held, reformatting the internal disk.");
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"Bad Storage: Formatting internal disk, do not power off.", 0);

if (FormatInternalDisk())
{
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"Bad Storage: Disk formatted at full size. Reboot and run the exploit again.", 0);
}
else
{
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"Bad Storage FAILURE: Format did not complete.", 0);
}
return FALSE;
}

if ((XboxHardwareInfo->Flags & XBOX_HW_FLAG_HDD) != XBOX_HW_FLAG_HDD)
{
//If this value got filled in, it means there is a disk.
if (*(PULONG)SataDiskUserAddressableSectors_Offset == 0)
{
//Nothing was identified on the SATA bus at all. That is a lower-level problem than
//authentication, so there is nothing to recover here.
Print("No disk.");
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"BadStorage FAILURE: No internal disk connected.", 0);
return FALSE;
}
else

//A disk is present and identified, it just never passed the security-sector check, so the
//kernel left every partition device object half-initialised. Finish them off.
Print("Disk did not authenticate, initialising its partition devices.");

ULONG initialisedCount = 0;
if (!InitializeUnauthenticatedDisk(&initialisedCount))
{
Print("Disk not genuine.");
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"BadStorage FAILURE: Disk not genuine/flashed. Flash using FATXplorer.", 0);
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"Bad Storage FAILURE: Could not initialise the internal disk.", 0);
return FALSE;
}
return FALSE;

//Tell the dashboard the hard disk is there now.
XContent_DEVICEADDREMOVETASK addTask;
addTask.pszDevicePath = PARTITION_1_PATH;
addTask.Action = DEVICESTATE_ADD;
addTask.DeviceType = XCONTENTDEVICETYPE_HDD;
if (!((pfnXContentDeviceProcessAddRemove)XContentDeviceProcessAddRemove_Offset)(&addTask))
{
Print("XContent::DeviceProcessAddRemove (add) returned FALSE.");
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"BadStorage FAILURE: Failed to process HDD addition.", 0);
return FALSE;
}

((pfnBroadcastStorageDevicesChanged)BroadcastStorageDevicesChanged_Offset)();

//The remainder of Bad Storage resizes a BSTOR formatted disk. A retail-formatted disk is
//fully usable at this point, so stop here.
Print("Unauthenticated disk mounted (%u partition devices initialised).", initialisedCount);
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"Bad Storage: Auth bypass active - internal disk mounted.", 0);
if (IsRetailFormatted != NULL) *IsRetailFormatted = TRUE;
return TRUE;
}

PDEVICE_OBJECT phyDiskDeviceObject = NULL;
Expand Down Expand Up @@ -208,8 +258,29 @@ EXTERN_C BOOLEAN Execute(PBOOLEAN IsRetailFormatted)
if (!CheckBSTOR(diskHandle))
{
if (IsRetailFormatted != NULL) *IsRetailFormatted = TRUE;
//Opting to not show this to avoid it being annoying.
//XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"BadStorage FAILURE: Disk is not formatted for Bad Storage. Reformat using FATXplorer.", 0);

//A disk flashed by SSD Maker authenticates, but it does so by presenting itself as an
//official Xbox 360 drive, so the console only ever sees that model's capacity. Point out
//when that is costing a meaningful amount of space.
//
//Deliberately only a message. The partition length cannot simply be raised here: FATX does
//not record its own volume size, so the kernel derives the cluster count - and with it the
//size of the allocation table - from the partition length the driver reports. Enlarging the
//partition under a volume that was formatted smaller moves where the data area is expected
//to start and corrupts the whole thing. Bad Storage can enlarge a BSTOR volume safely only
//because FATXplorer sizes its allocation table for the full capacity up front.
LONGLONG driveSize = (LONGLONG)(*(PULONG)SataDiskUserAddressableSectors_Offset) * 512;
LONGLONG claimed = p1DiskPartitionInfo->StartingOffset.QuadPart + p1DiskPartitionInfo->PartitionLength.QuadPart;
LONGLONG unused = driveSize - claimed;

//A quarter of the drive is enough to be worth telling someone about.
if (unused > driveSize / 4)
{
Print("Drive is %I64d MB but only %I64d MB is in use; %I64d MB unreachable because of the flashed disk identity.",
driveSize / (1024 * 1024), claimed / (1024 * 1024), unused / (1024 * 1024));
XNotifyQueueUI(XNOTIFYUI_TYPE_AVOID_REVIEW, XUSER_INDEX_ANY, XNOTIFYUI_PRIORITY_HIGH, L"Bad Storage: Drive is larger than its flashed size. Reformat with FATXplorer to use all of it.", 0);
}

goto cleanupAndExit;
}

Expand Down
5 changes: 4 additions & 1 deletion src/BadStorage-DLL/BadStorage/BadStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ const UCHAR BADSTORAGE_DID_PATCH_DATA[8] = { 0x3B, 0xC0, 0x00, 0x01, 0x48, 0x00,
const PCHAR PHYSICAL_DISK_PATH = "\\Device\\Harddisk0\\PhysicalDisk";
const PCHAR PARTITION_0_PATH = "\\Device\\Harddisk0\\Partition0";
const PCHAR PARTITION_1_PATH = "\\Device\\Harddisk0\\Partition1";
const PCHAR PARTITION_1_PATH_FILE = "\\Device\\Harddisk0\\Partition1\\";
const PCHAR PARTITION_1_PATH_FILE = "\\Device\\Harddisk0\\Partition1\\";

//Defined in BadStorage.cpp; declared here so other translation units can log too.
VOID Print(const PCHAR Format, ...);
4 changes: 4 additions & 0 deletions src/BadStorage-DLL/BadStorage/BadStorage.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@
<ClInclude Include="stdafx.h" />
<ClInclude Include="XenonExports.h" />
<ClInclude Include="BadStorage.h" />
<ClInclude Include="UnauthenticatedDisk.h" />
<ClInclude Include="FatxFormat.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
Expand All @@ -166,6 +168,8 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Xbox 360'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="BadStorage.cpp" />
<ClCompile Include="UnauthenticatedDisk.cpp" />
<ClCompile Include="FatxFormat.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="BadStorage.def" />
Expand Down
212 changes: 212 additions & 0 deletions src/BadStorage-DLL/BadStorage/FatxFormat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// On-console FATX formatting for Bad Storage.
// Copyright © 2026. Licensed under the same MIT terms as Bad Storage.

#include "stdafx.h"
#include "FatxFormat.h"
#include "BadStorage.h"
#include "UnauthenticatedDisk.h"

//Written in chunks so the FAT can be cleared without allocating anything large.
#define FORMAT_CHUNK_SIZE 0x10000
static BYTE g_FormatChunk[FORMAT_CHUNK_SIZE];

//One entry per partition the retail layout defines. Offsets are absolute byte positions on the
//drive, so everything is written through PhysicalDisk rather than through the partitions
//themselves - which is essential here, because while the disk still authenticates as a smaller
//model the partition devices do not even span the area being written.
typedef struct _FORMAT_PARTITION {
const CHAR* Name;
LONGLONG Offset;
LONGLONG Length; //0 means "everything left on the drive"
} FORMAT_PARTITION;

BOOLEAN IsFormatRequested(void)
{
//Any connected pad counts, so it does not matter which port is used.
for (DWORD i = 0; i < 4; i++)
{
XINPUT_STATE state;
ZeroMemory(&state, sizeof(state));
if (XInputGetState(i, &state) == ERROR_SUCCESS)
{
//Well past the trigger's resting position, so a resting pad cannot trip it.
if (state.Gamepad.bLeftTrigger > 200) return TRUE;
}
}
return FALSE;
}

//Opens the whole drive for writing. Partitions are deliberately not used - see the note above.
static HANDLE OpenPhysicalDiskForWrite(void)
{
OBJECT_STRING str;
RtlInitAnsiString(&str, PHYSICAL_DISK_PATH);

OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &str, OBJ_CASE_INSENSITIVE, NULL, NULL);

HANDLE handle;
IO_STATUS_BLOCK iosb;
NTSTATUS status = NtOpenFile(&handle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &oa, &iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(status))
{
Print("Could not open the disk for writing: 0x%08X", status);
return INVALID_HANDLE_VALUE;
}
return handle;
}

static BOOLEAN WriteAt(HANDLE Disk, LONGLONG Offset, PVOID Buffer, ULONG Length)
{
IO_STATUS_BLOCK iosb;
LARGE_INTEGER offset;
offset.QuadPart = Offset;

NTSTATUS status = NtWriteFile(Disk, NULL, NULL, NULL, &iosb, Buffer, Length, &offset);
if (!NT_SUCCESS(status))
{
Print("Write failed at %I64d (%u bytes): 0x%08X", Offset, Length, status);
return FALSE;
}
return TRUE;
}

//Clears a span by writing zeroes over it.
static BOOLEAN ZeroRange(HANDLE Disk, LONGLONG Offset, LONGLONG Length)
{
ZeroMemory(g_FormatChunk, sizeof(g_FormatChunk));

LONGLONG written = 0;
while (written < Length)
{
ULONG chunk = (ULONG)((Length - written) > FORMAT_CHUNK_SIZE ? FORMAT_CHUNK_SIZE : (Length - written));
if (!WriteAt(Disk, Offset + written, g_FormatChunk, chunk)) return FALSE;
written += chunk;
}
return TRUE;
}

/*
Writes one FATX volume.

Layout, matching what a retail-formatted partition reads back as:
0x0000 volume header
0x1000 allocation table
0x1000 + fatSize data area, cluster 1 first, holding the root directory

The cluster count depends on the size of the allocation table, which itself depends on the
cluster count, so it is solved by iterating twice - which is enough to settle.
*/
static BOOLEAN FormatPartition(HANDLE Disk, const CHAR* Name, LONGLONG Offset, LONGLONG Length)
{
const ULONG clusterBytes = FATX_SECTORS_PER_CLUSTER * 512;

ULONG clusterCount = (ULONG)((Length - FATX_HEADER_SIZE) / clusterBytes);
ULONG entrySize = (clusterCount >= FATX_FAT16_CLUSTER_LIMIT) ? 4 : 2;
LONGLONG fatSize = ((LONGLONG)clusterCount * entrySize + 0xFFF) & ~0xFFFLL;

//Settle it now that the table's size is known.
clusterCount = (ULONG)((Length - FATX_HEADER_SIZE - fatSize) / clusterBytes);
entrySize = (clusterCount >= FATX_FAT16_CLUSTER_LIMIT) ? 4 : 2;
fatSize = ((LONGLONG)clusterCount * entrySize + 0xFFF) & ~0xFFFLL;

LONGLONG dataStart = Offset + FATX_HEADER_SIZE + fatSize;

Print("Formatting %s at %I64d, %I64d MB, %u clusters, %u-bit table (%I64d KB)",
Name, Offset, Length / (1024 * 1024), clusterCount, entrySize * 8, fatSize / 1024);

//Header. Unused space in it reads as 0xFF on a retail volume.
ZeroMemory(g_FormatChunk, FATX_HEADER_SIZE);
memset(g_FormatChunk, 0xFF, FATX_HEADER_SIZE);
PULONG header = (PULONG)g_FormatChunk;
header[0] = FATX_MAGIC;
//Derived from the drive's own reported size so two partitions never share an id.
header[1] = (ULONG)(Offset >> 12) ^ (ULONG)Length ^ 0x5A4D0000;
header[2] = FATX_SECTORS_PER_CLUSTER;
header[3] = FATX_ROOT_CLUSTER;
if (!WriteAt(Disk, Offset, g_FormatChunk, FATX_HEADER_SIZE)) return FALSE;

//Allocation table: everything free except the reserved entry and the root directory's chain.
if (!ZeroRange(Disk, Offset + FATX_HEADER_SIZE, fatSize)) return FALSE;

ZeroMemory(g_FormatChunk, FORMAT_CHUNK_SIZE);
if (entrySize == 4)
{
PULONG fat = (PULONG)g_FormatChunk;
fat[0] = FATX_FAT_RESERVED0;
fat[1] = FATX_FAT_END_OF_CHAIN;
}
else
{
PUSHORT fat = (PUSHORT)g_FormatChunk;
fat[0] = (USHORT)FATX_FAT_RESERVED0;
fat[1] = (USHORT)FATX_FAT_END_OF_CHAIN;
}
if (!WriteAt(Disk, Offset + FATX_HEADER_SIZE, g_FormatChunk, 0x1000)) return FALSE;

//Root directory. An unused FATX directory entry starts with 0xFF.
memset(g_FormatChunk, 0xFF, clusterBytes > FORMAT_CHUNK_SIZE ? FORMAT_CHUNK_SIZE : clusterBytes);
if (!WriteAt(Disk, dataStart, g_FormatChunk, clusterBytes)) return FALSE;

return TRUE;
}

BOOLEAN FormatInternalDisk(void)
{
ULONG sectors = *(PULONG)SataDiskUserAddressableSectors_Offset;
if (sectors == 0)
{
Print("No disk identified, nothing to format.");
return FALSE;
}

LONGLONG driveSize = (LONGLONG)sectors * 512;
if (driveSize <= (LONGLONG)UD_CONTENT_PARTITION_OFFSET)
{
Print("Disk is too small for the standard partition layout.");
return FALSE;
}

Print("Formatting the internal disk: %I64d MB. Everything on it will be lost.",
driveSize / (1024 * 1024));

HANDLE disk = OpenPhysicalDiskForWrite();
if (disk == INVALID_HANDLE_VALUE) return FALSE;

BOOLEAN ok = TRUE;

//Invalidate the security sector first. Until this is gone the disk keeps authenticating as
//whatever model it was flashed as, and the partitions get sized to that model again on the
//next boot no matter how the filesystems look.
Print("Clearing the security sector at 0x%X.", SECURITY_SECTOR_OFFSET);
if (!ZeroRange(disk, SECURITY_SECTOR_OFFSET, SECURITY_SECTOR_LENGTH)) ok = FALSE;

//Same layout the kernel itself uses, so a formatted disk matches what the bypass will publish.
static const FORMAT_PARTITION partitions[] = {
{ "Cache0", UD_CACHE0_OFFSET, UD_CACHE_LENGTH },
{ "Cache1", UD_CACHE1_OFFSET, UD_CACHE_LENGTH },
{ "SystemURLCachePartition", UD_SYSTEM_URL_CACHE_OFFSET, UD_SYSTEM_URL_CACHE_LENGTH },
{ "TitleURLCachePartition", UD_TITLE_URL_CACHE_OFFSET, UD_TITLE_URL_CACHE_LENGTH },
{ "SystemExtPartition", UD_SYSTEM_EXT_OFFSET, UD_SYSTEM_EXT_LENGTH },
{ "SystemAuxPartition", UD_SYSTEM_AUX_OFFSET, UD_SYSTEM_AUX_LENGTH },
{ "SystemPartition", UD_SYSTEM_PARTITION_OFFSET, UD_SYSTEM_PARTITION_LENGTH },
{ "Partition1", UD_CONTENT_PARTITION_OFFSET, 0 },
};

for (int i = 0; ok && i < (int)(sizeof(partitions) / sizeof(partitions[0])); i++)
{
LONGLONG length = partitions[i].Length != 0
? partitions[i].Length
: driveSize - partitions[i].Offset;

if (!FormatPartition(disk, partitions[i].Name, partitions[i].Offset, length)) ok = FALSE;
}

NtClose(disk);

if (ok) Print("Format complete. Reboot, then run the exploit again to pick up the full size.");
else Print("Format did not complete.");

return ok;
}
Loading